Replace str path utils with new PathLookup type
This commit is contained in:
parent
ea13461967
commit
b768fbe4bc
70 changed files with 799 additions and 1400 deletions
|
|
@ -96,8 +96,9 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_data_structures::unhash::UnhashMap;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS};
|
||||
use rustc_hir::def::{DefKind, Namespace, Res};
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::definitions::{DefPath, DefPathData};
|
||||
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
|
||||
|
|
@ -105,8 +106,8 @@ use rustc_hir::{
|
|||
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
|
||||
CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg,
|
||||
GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource,
|
||||
Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath,
|
||||
Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
|
||||
Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt,
|
||||
StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
|
||||
};
|
||||
use rustc_lexer::{TokenKind, tokenize};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
|
|
@ -347,14 +348,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if the method call given in `expr` belongs to the given trait.
|
||||
/// This is a deprecated function, consider using [`is_trait_method`].
|
||||
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
|
||||
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
|
||||
let trt_id = cx.tcx.trait_of_item(def_id);
|
||||
trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
|
||||
}
|
||||
|
||||
/// Checks if the given method call expression calls an inherent method.
|
||||
pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
|
|
@ -438,44 +431,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc
|
|||
})
|
||||
}
|
||||
|
||||
/// THIS METHOD IS DEPRECATED. Matches a `QPath` against a slice of segment string literals.
|
||||
///
|
||||
/// This method is deprecated and will eventually be removed since it does not match against the
|
||||
/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
|
||||
/// `QPath::Resolved.1.res.opt_def_id()`.
|
||||
///
|
||||
/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
|
||||
/// `rustc_hir::QPath`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,ignore
|
||||
/// match_qpath(path, &["std", "rt", "begin_unwind"])
|
||||
/// ```
|
||||
pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
|
||||
match *path {
|
||||
QPath::Resolved(_, path) => match_path(path, segments),
|
||||
QPath::TypeRelative(ty, segment) => match ty.kind {
|
||||
TyKind::Path(ref inner_path) => {
|
||||
if let [prefix @ .., end] = segments
|
||||
&& match_qpath(inner_path, prefix)
|
||||
{
|
||||
return segment.ident.name.as_str() == *end;
|
||||
}
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
QPath::LangItem(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
|
||||
///
|
||||
/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
|
||||
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
|
||||
path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
|
||||
}
|
||||
|
||||
/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
|
||||
/// it matches the given lang item.
|
||||
pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
|
||||
|
|
@ -492,34 +447,6 @@ pub fn is_path_diagnostic_item<'tcx>(
|
|||
path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
|
||||
}
|
||||
|
||||
/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals.
|
||||
///
|
||||
/// This method is deprecated and will eventually be removed since it does not match against the
|
||||
/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
|
||||
/// `QPath::Resolved.1.res.opt_def_id()`.
|
||||
///
|
||||
/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
|
||||
/// `rustc_hir::Path`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// if match_path(&trait_ref.path, &paths::HASH) {
|
||||
/// // This is the `std::hash::Hash` trait.
|
||||
/// }
|
||||
///
|
||||
/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
|
||||
/// // This is a `rustc_middle::lint::Lint`.
|
||||
/// }
|
||||
/// ```
|
||||
pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
|
||||
path.segments
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(segments.iter().rev())
|
||||
.all(|(a, b)| a.ident.name.as_str() == *b)
|
||||
}
|
||||
|
||||
/// If the expression is a path to a local, returns the canonical `HirId` of the local.
|
||||
pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
|
||||
|
|
@ -586,60 +513,57 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>
|
|||
path_res(cx, maybe_path).opt_def_id()
|
||||
}
|
||||
|
||||
fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
|
||||
fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] {
|
||||
let ty = match name {
|
||||
"bool" => SimplifiedType::Bool,
|
||||
"char" => SimplifiedType::Char,
|
||||
"str" => SimplifiedType::Str,
|
||||
"array" => SimplifiedType::Array,
|
||||
"slice" => SimplifiedType::Slice,
|
||||
sym::bool => SimplifiedType::Bool,
|
||||
sym::char => SimplifiedType::Char,
|
||||
sym::str => SimplifiedType::Str,
|
||||
sym::array => SimplifiedType::Array,
|
||||
sym::slice => SimplifiedType::Slice,
|
||||
// FIXME: rustdoc documents these two using just `pointer`.
|
||||
//
|
||||
// Maybe this is something we should do here too.
|
||||
"const_ptr" => SimplifiedType::Ptr(Mutability::Not),
|
||||
"mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
|
||||
"isize" => SimplifiedType::Int(IntTy::Isize),
|
||||
"i8" => SimplifiedType::Int(IntTy::I8),
|
||||
"i16" => SimplifiedType::Int(IntTy::I16),
|
||||
"i32" => SimplifiedType::Int(IntTy::I32),
|
||||
"i64" => SimplifiedType::Int(IntTy::I64),
|
||||
"i128" => SimplifiedType::Int(IntTy::I128),
|
||||
"usize" => SimplifiedType::Uint(UintTy::Usize),
|
||||
"u8" => SimplifiedType::Uint(UintTy::U8),
|
||||
"u16" => SimplifiedType::Uint(UintTy::U16),
|
||||
"u32" => SimplifiedType::Uint(UintTy::U32),
|
||||
"u64" => SimplifiedType::Uint(UintTy::U64),
|
||||
"u128" => SimplifiedType::Uint(UintTy::U128),
|
||||
"f32" => SimplifiedType::Float(FloatTy::F32),
|
||||
"f64" => SimplifiedType::Float(FloatTy::F64),
|
||||
_ => {
|
||||
return [].iter().copied();
|
||||
},
|
||||
sym::const_ptr => SimplifiedType::Ptr(Mutability::Not),
|
||||
sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut),
|
||||
sym::isize => SimplifiedType::Int(IntTy::Isize),
|
||||
sym::i8 => SimplifiedType::Int(IntTy::I8),
|
||||
sym::i16 => SimplifiedType::Int(IntTy::I16),
|
||||
sym::i32 => SimplifiedType::Int(IntTy::I32),
|
||||
sym::i64 => SimplifiedType::Int(IntTy::I64),
|
||||
sym::i128 => SimplifiedType::Int(IntTy::I128),
|
||||
sym::usize => SimplifiedType::Uint(UintTy::Usize),
|
||||
sym::u8 => SimplifiedType::Uint(UintTy::U8),
|
||||
sym::u16 => SimplifiedType::Uint(UintTy::U16),
|
||||
sym::u32 => SimplifiedType::Uint(UintTy::U32),
|
||||
sym::u64 => SimplifiedType::Uint(UintTy::U64),
|
||||
sym::u128 => SimplifiedType::Uint(UintTy::U128),
|
||||
sym::f32 => SimplifiedType::Float(FloatTy::F32),
|
||||
sym::f64 => SimplifiedType::Float(FloatTy::F64),
|
||||
_ => return &[],
|
||||
};
|
||||
|
||||
tcx.incoherent_impls(ty).iter().copied()
|
||||
tcx.incoherent_impls(ty)
|
||||
}
|
||||
|
||||
fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
|
||||
fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> {
|
||||
match tcx.def_kind(def_id) {
|
||||
DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
|
||||
.module_children(def_id)
|
||||
.iter()
|
||||
.filter(|item| item.ident.name == name)
|
||||
.map(|child| child.res.expect_non_local())
|
||||
.collect(),
|
||||
DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| {
|
||||
if child.ident.name == name && ns.matches(child.res.ns()) {
|
||||
child.res.opt_def_id()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
DefKind::Impl { .. } => tcx
|
||||
.associated_item_def_ids(def_id)
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
|
||||
.map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
.find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
|
||||
fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option<DefId> {
|
||||
let root_mod;
|
||||
let item_kind = match tcx.hir_node_by_def_id(local_id) {
|
||||
Node::Crate(r#mod) => {
|
||||
|
|
@ -647,138 +571,147 @@ fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symb
|
|||
&root_mod
|
||||
},
|
||||
Node::Item(item) => &item.kind,
|
||||
_ => return Vec::new(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let res = |ident: Ident, owner_id: OwnerId| {
|
||||
if ident.name == name {
|
||||
let def_id = owner_id.to_def_id();
|
||||
Some(Res::Def(tcx.def_kind(def_id), def_id))
|
||||
if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) {
|
||||
Some(owner_id.to_def_id())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
match item_kind {
|
||||
ItemKind::Mod(_, r#mod) => r#mod
|
||||
.item_ids
|
||||
.iter()
|
||||
.filter_map(|&item_id| {
|
||||
let ident = tcx.hir_item(item_id).kind.ident()?;
|
||||
res(ident, item_id.owner_id)
|
||||
})
|
||||
.collect(),
|
||||
ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| {
|
||||
let ident = tcx.hir_item(item_id).kind.ident()?;
|
||||
res(ident, item_id.owner_id)
|
||||
}),
|
||||
ItemKind::Impl(r#impl) => r#impl
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
|
||||
.collect(),
|
||||
.find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
|
||||
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
|
||||
.iter()
|
||||
.filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
.find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
|
||||
fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> {
|
||||
if let Some(local_id) = def_id.as_local() {
|
||||
local_item_children_by_name(tcx, local_id, name)
|
||||
local_item_child_by_name(tcx, local_id, ns, name)
|
||||
} else {
|
||||
non_local_item_children_by_name(tcx, def_id, name)
|
||||
non_local_item_child_by_name(tcx, def_id, ns, name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the crates called `name`, may be multiple due to multiple major versions.
|
||||
pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
|
||||
tcx.crates(())
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(move |&num| tcx.crate_name(num) == name)
|
||||
.map(CrateNum::as_def_id)
|
||||
.map(|id| Res::Def(tcx.def_kind(id), id))
|
||||
.collect()
|
||||
pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] {
|
||||
static BY_NAME: OnceLock<FxHashMap<Symbol, Vec<DefId>>> = OnceLock::new();
|
||||
let map = BY_NAME.get_or_init(|| {
|
||||
let mut map = FxHashMap::default();
|
||||
map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]);
|
||||
for &num in tcx.crates(()) {
|
||||
map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id());
|
||||
}
|
||||
map
|
||||
});
|
||||
match map.get(&name) {
|
||||
Some(def_ids) => def_ids,
|
||||
None => &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an
|
||||
/// arbitrary namespace
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum PathNS {
|
||||
Type,
|
||||
Value,
|
||||
Macro,
|
||||
|
||||
/// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return
|
||||
/// either the macro or the module but **not** both
|
||||
///
|
||||
/// Must only be used when the specific resolution is unimportant such as in
|
||||
/// `missing_enforced_import_renames`
|
||||
Arbitrary,
|
||||
}
|
||||
|
||||
impl PathNS {
|
||||
fn matches(self, ns: Option<Namespace>) -> bool {
|
||||
let required = match self {
|
||||
PathNS::Type => TypeNS,
|
||||
PathNS::Value => ValueNS,
|
||||
PathNS::Macro => MacroNS,
|
||||
PathNS::Arbitrary => return true,
|
||||
};
|
||||
|
||||
ns == Some(required)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves a def path like `std::vec::Vec`.
|
||||
///
|
||||
/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
|
||||
/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
|
||||
///
|
||||
/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
|
||||
/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
|
||||
/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple:
|
||||
/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0
|
||||
/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls
|
||||
/// ([1], [2], [3])
|
||||
///
|
||||
/// This function is expensive and should be used sparingly.
|
||||
pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
|
||||
let (base, path) = match path {
|
||||
[primitive] => {
|
||||
return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
|
||||
},
|
||||
[base, path @ ..] => (base, path),
|
||||
_ => return Vec::new(),
|
||||
///
|
||||
/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast
|
||||
/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1
|
||||
/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2
|
||||
pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec<DefId> {
|
||||
let (root, rest) = match *path {
|
||||
[] | [_] => return Vec::new(),
|
||||
[root, ref rest @ ..] => (root, rest),
|
||||
};
|
||||
|
||||
let base_sym = Symbol::intern(base);
|
||||
|
||||
let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
|
||||
Some(LOCAL_CRATE.as_def_id())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let crates = find_primitive_impls(tcx, base)
|
||||
.chain(local_crate)
|
||||
.map(|id| Res::Def(tcx.def_kind(id), id))
|
||||
.chain(find_crates(tcx, base_sym))
|
||||
.collect();
|
||||
|
||||
def_path_res_with_base(tcx, crates, path)
|
||||
let mut out = Vec::new();
|
||||
for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) {
|
||||
lookup_path_with_base(tcx, base, ns, rest, &mut out);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Resolves a def path like `vec::Vec` with the base `std`.
|
||||
///
|
||||
/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up
|
||||
/// items from the same crate repeatedly, although should still be used sparingly.
|
||||
pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
|
||||
while let [segment, rest @ ..] = path {
|
||||
path = rest;
|
||||
let segment = Symbol::intern(segment);
|
||||
fn lookup_path_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec<DefId>) {
|
||||
loop {
|
||||
match *path {
|
||||
[segment] => {
|
||||
out.extend(item_child_by_name(tcx, base, ns, segment));
|
||||
|
||||
base = base
|
||||
.into_iter()
|
||||
.filter_map(|res| res.opt_def_id())
|
||||
.flat_map(|def_id| {
|
||||
// When the current def_id is e.g. `struct S`, check the impl items in
|
||||
// `impl S { ... }`
|
||||
let inherent_impl_children = tcx
|
||||
.inherent_impls(def_id)
|
||||
.inherent_impls(base)
|
||||
.iter()
|
||||
.flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
|
||||
.filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment));
|
||||
out.extend(inherent_impl_children);
|
||||
|
||||
let direct_children = item_children_by_name(tcx, def_id, segment);
|
||||
|
||||
inherent_impl_children.chain(direct_children)
|
||||
})
|
||||
.collect();
|
||||
return;
|
||||
},
|
||||
[segment, ref rest @ ..] => {
|
||||
path = rest;
|
||||
let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else {
|
||||
return;
|
||||
};
|
||||
base = child;
|
||||
},
|
||||
[] => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
base
|
||||
}
|
||||
|
||||
/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
|
||||
pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
|
||||
def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
|
||||
}
|
||||
|
||||
/// Convenience function to get the `DefId` of a trait by path.
|
||||
/// It could be a trait or trait alias.
|
||||
/// Equivalent to a [`lookup_path`] after splitting the input string on `::`
|
||||
///
|
||||
/// This function is expensive and should be used sparingly.
|
||||
pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
|
||||
def_path_res(tcx, path).into_iter().find_map(|res| match res {
|
||||
Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
|
||||
_ => None,
|
||||
})
|
||||
pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec<DefId> {
|
||||
let path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
|
||||
lookup_path(tcx, ns, &path)
|
||||
}
|
||||
|
||||
/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
|
||||
|
|
@ -2065,24 +1998,6 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
|
||||
/// any.
|
||||
///
|
||||
/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
|
||||
pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
|
||||
let search_path = cx.get_def_path(did);
|
||||
paths
|
||||
.iter()
|
||||
.position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
|
||||
}
|
||||
|
||||
/// Checks if the given `DefId` matches the path.
|
||||
pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
|
||||
// We should probably move to Symbols in Clippy as well rather than interning every time.
|
||||
let path = cx.get_def_path(did);
|
||||
syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
|
||||
}
|
||||
|
||||
/// Checks if the given `DefId` matches the `libc` item.
|
||||
pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
|
||||
let path = cx.get_def_path(did);
|
||||
|
|
|
|||
|
|
@ -4,62 +4,125 @@
|
|||
//! Whenever possible, please consider diagnostic items over hardcoded paths.
|
||||
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
|
||||
|
||||
// Paths inside rustc
|
||||
pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
|
||||
pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
|
||||
["rustc_lint_defs", "Applicability", "Unspecified"],
|
||||
["rustc_lint_defs", "Applicability", "HasPlaceholders"],
|
||||
["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
|
||||
["rustc_lint_defs", "Applicability", "MachineApplicable"],
|
||||
];
|
||||
pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"];
|
||||
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
|
||||
pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
|
||||
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
|
||||
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
|
||||
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
|
||||
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
|
||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
|
||||
pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
|
||||
pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
|
||||
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
|
||||
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
|
||||
use crate::{MaybePath, PathNS, lookup_path, path_def_id, sym};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::{STDLIB_STABLE_CRATES, Symbol};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`].
|
||||
///
|
||||
/// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple:
|
||||
/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0
|
||||
/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls
|
||||
/// ([1], [2], [3])
|
||||
///
|
||||
/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast
|
||||
/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1
|
||||
/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2
|
||||
pub struct PathLookup {
|
||||
ns: PathNS,
|
||||
path: &'static [Symbol],
|
||||
once: OnceLock<Vec<DefId>>,
|
||||
}
|
||||
|
||||
impl PathLookup {
|
||||
/// Only exported for tests and `clippy_lints_internal`
|
||||
#[doc(hidden)]
|
||||
pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self {
|
||||
Self {
|
||||
ns,
|
||||
path,
|
||||
once: OnceLock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of [`DefId`]s that the path resolves to
|
||||
pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] {
|
||||
self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path))
|
||||
}
|
||||
|
||||
/// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into
|
||||
/// stdlib crates to avoid the issue of multiple [`DefId`]s being returned
|
||||
///
|
||||
/// May return [`None`] in `no_std`/`no_core` environments
|
||||
pub fn only(&self, cx: &LateContext<'_>) -> Option<DefId> {
|
||||
let ids = self.get(cx);
|
||||
debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0]));
|
||||
debug_assert!(ids.len() <= 1, "{ids:?}");
|
||||
ids.first().copied()
|
||||
}
|
||||
|
||||
/// Checks if the path resolves to the given `def_id`
|
||||
pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
self.get(cx).contains(&def_id)
|
||||
}
|
||||
|
||||
/// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it
|
||||
pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool {
|
||||
path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id))
|
||||
}
|
||||
|
||||
/// Checks if the path resolves to `ty`'s definition, must be an `Adt`
|
||||
pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did()))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! path_macros {
|
||||
($($name:ident: $ns:expr,)*) => {
|
||||
$(
|
||||
/// Only exported for tests and `clippy_lints_internal`
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! $name {
|
||||
($$($$seg:ident $$(::)?)*) => {
|
||||
PathLookup::new($ns, &[$$(sym::$$seg,)*])
|
||||
};
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
path_macros! {
|
||||
type_path: PathNS::Type,
|
||||
value_path: PathNS::Value,
|
||||
macro_path: PathNS::Macro,
|
||||
}
|
||||
|
||||
// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
|
||||
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
|
||||
pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
|
||||
pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
|
||||
pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"];
|
||||
|
||||
// Paths in clippy itself
|
||||
pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"];
|
||||
pub const CLIPPY_SYM_MODULE: [&str; 2] = ["clippy_utils", "sym"];
|
||||
pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of);
|
||||
pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit);
|
||||
pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new);
|
||||
pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other);
|
||||
pub static ITER_STEP: PathLookup = type_path!(core::iter::Step);
|
||||
pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref);
|
||||
|
||||
// Paths in external crates
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
|
||||
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
|
||||
pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
|
||||
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
|
||||
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
|
||||
pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"];
|
||||
pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"];
|
||||
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"];
|
||||
pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"];
|
||||
pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"];
|
||||
pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"];
|
||||
pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
|
||||
pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);
|
||||
pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple);
|
||||
pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [
|
||||
type_path!(lock_api::mutex::MutexGuard),
|
||||
type_path!(lock_api::rwlock::RwLockReadGuard),
|
||||
type_path!(lock_api::rwlock::RwLockWriteGuard),
|
||||
];
|
||||
pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new);
|
||||
pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new);
|
||||
pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new);
|
||||
pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new);
|
||||
pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new);
|
||||
pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new);
|
||||
pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize);
|
||||
pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor);
|
||||
pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options);
|
||||
pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt);
|
||||
pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt);
|
||||
pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions);
|
||||
pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new);
|
||||
pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static);
|
||||
pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy);
|
||||
pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new);
|
||||
|
||||
// Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol};
|
||||
use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT;
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use rustc_span::sym::*;
|
||||
|
|
@ -24,33 +24,45 @@ macro_rules! generate {
|
|||
];
|
||||
|
||||
$(
|
||||
pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
|
||||
pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
generate! {
|
||||
abs,
|
||||
align_of,
|
||||
as_bytes,
|
||||
as_deref_mut,
|
||||
as_deref,
|
||||
as_mut,
|
||||
AsyncReadExt,
|
||||
AsyncWriteExt,
|
||||
Binary,
|
||||
build_hasher,
|
||||
bytes,
|
||||
cargo_clippy: "cargo-clippy",
|
||||
Cargo_toml: "Cargo.toml",
|
||||
cast,
|
||||
chars,
|
||||
CLIPPY_ARGS,
|
||||
CLIPPY_CONF_DIR,
|
||||
clippy_utils,
|
||||
clone_into,
|
||||
cloned,
|
||||
collect,
|
||||
const_ptr,
|
||||
contains,
|
||||
copied,
|
||||
CRLF: "\r\n",
|
||||
Current,
|
||||
de,
|
||||
Deserialize,
|
||||
diagnostics,
|
||||
EarlyLintPass,
|
||||
ends_with,
|
||||
error,
|
||||
ErrorKind,
|
||||
exp,
|
||||
extend,
|
||||
finish_non_exhaustive,
|
||||
|
|
@ -58,48 +70,87 @@ generate! {
|
|||
flat_map,
|
||||
for_each,
|
||||
from_raw,
|
||||
from_ref,
|
||||
from_str_radix,
|
||||
fs,
|
||||
futures_util,
|
||||
get,
|
||||
hygiene,
|
||||
insert,
|
||||
int_roundings,
|
||||
into_bytes,
|
||||
into_owned,
|
||||
IntoIter,
|
||||
io,
|
||||
is_ascii,
|
||||
is_empty,
|
||||
is_err,
|
||||
is_none,
|
||||
is_ok,
|
||||
is_some,
|
||||
itertools,
|
||||
Itertools,
|
||||
kw,
|
||||
last,
|
||||
lazy_static,
|
||||
Lazy,
|
||||
LF: "\n",
|
||||
Lint,
|
||||
lock_api,
|
||||
LowerExp,
|
||||
LowerHex,
|
||||
max,
|
||||
MAX,
|
||||
mem,
|
||||
min,
|
||||
MIN,
|
||||
mode,
|
||||
msrv,
|
||||
msrvs,
|
||||
MsrvStack,
|
||||
mut_ptr,
|
||||
mutex,
|
||||
next_tuple,
|
||||
Octal,
|
||||
once_cell,
|
||||
OpenOptions,
|
||||
or_default,
|
||||
Other,
|
||||
parse,
|
||||
PathLookup,
|
||||
paths,
|
||||
push,
|
||||
regex,
|
||||
Regex,
|
||||
RegexBuilder,
|
||||
RegexSet,
|
||||
reserve,
|
||||
resize,
|
||||
restriction,
|
||||
rustc_lint_defs,
|
||||
rustc_lint,
|
||||
rustc_span,
|
||||
rustfmt_skip,
|
||||
rwlock,
|
||||
serde,
|
||||
set_len,
|
||||
set_mode,
|
||||
set_readonly,
|
||||
signum,
|
||||
span_lint_and_then,
|
||||
split_whitespace,
|
||||
split,
|
||||
Start,
|
||||
Step,
|
||||
symbol,
|
||||
Symbol,
|
||||
SyntaxContext,
|
||||
take,
|
||||
TBD,
|
||||
then_some,
|
||||
to_digit,
|
||||
to_owned,
|
||||
tokio,
|
||||
unused_extern_crates,
|
||||
unwrap_err,
|
||||
unwrap_or_default,
|
||||
|
|
@ -107,6 +158,7 @@ generate! {
|
|||
UpperHex,
|
||||
V4,
|
||||
V6,
|
||||
Visitor,
|
||||
Weak,
|
||||
with_capacity,
|
||||
wrapping_offset,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use std::assert_matches::debug_assert_matches;
|
|||
use std::collections::hash_map::Entry;
|
||||
use std::iter;
|
||||
|
||||
use crate::{def_path_def_ids, match_def_path, path_res};
|
||||
use crate::{PathNS, lookup_path_str, path_res};
|
||||
|
||||
mod type_certainty;
|
||||
pub use type_certainty::expr_type_is_certain;
|
||||
|
|
@ -229,9 +229,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<
|
|||
/// Checks whether a type implements a trait.
|
||||
/// The function returns false in case the type contains an inference variable.
|
||||
///
|
||||
/// See:
|
||||
/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
|
||||
/// * [Common tools for writing lints] for an example how to use this function and other options.
|
||||
/// See [Common tools for writing lints] for an example how to use this function and other options.
|
||||
///
|
||||
/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
|
||||
pub fn implements_trait<'tcx>(
|
||||
|
|
@ -424,17 +422,6 @@ pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
|
|||
matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
|
||||
}
|
||||
|
||||
/// Checks if type is struct, enum or union type with the given def path.
|
||||
///
|
||||
/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
|
||||
/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
|
||||
pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the drop order for a type matters.
|
||||
///
|
||||
/// Some std types implement drop solely to deallocate memory. For these types, and composites
|
||||
|
|
@ -1131,10 +1118,7 @@ impl<'tcx> InteriorMut<'tcx> {
|
|||
pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self {
|
||||
let ignored_def_ids = ignore_interior_mutability
|
||||
.iter()
|
||||
.flat_map(|ignored_ty| {
|
||||
let path: Vec<&str> = ignored_ty.split("::").collect();
|
||||
def_path_def_ids(tcx, path.as_slice())
|
||||
})
|
||||
.flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@
|
|||
//! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should
|
||||
//! be considered a bug.
|
||||
|
||||
use crate::def_path_res;
|
||||
use crate::{PathNS, lookup_path};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty};
|
||||
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
mod certainty;
|
||||
use certainty::{Certainty, Meet, join, meet};
|
||||
|
|
@ -194,7 +194,7 @@ fn path_segment_certainty(
|
|||
path_segment: &PathSegment<'_>,
|
||||
resolves_to_type: bool,
|
||||
) -> Certainty {
|
||||
let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) {
|
||||
let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) {
|
||||
// A definition's type is certain if it refers to something without generics (e.g., a crate or module, or
|
||||
// an unparameterized type), or the generics are instantiated with arguments that are certain.
|
||||
//
|
||||
|
|
@ -267,17 +267,24 @@ fn path_segment_certainty(
|
|||
|
||||
/// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`.
|
||||
/// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`.
|
||||
fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option<Res> {
|
||||
fn update_res(
|
||||
cx: &LateContext<'_>,
|
||||
parent_certainty: Certainty,
|
||||
path_segment: &PathSegment<'_>,
|
||||
resolves_to_type: bool,
|
||||
) -> Option<Res> {
|
||||
if path_segment.res == Res::Err
|
||||
&& let Some(def_id) = parent_certainty.to_def_id()
|
||||
{
|
||||
let mut def_path = cx.get_def_path(def_id);
|
||||
def_path.push(path_segment.ident.name);
|
||||
let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>());
|
||||
if let [res] = reses.as_slice() { Some(*res) } else { None }
|
||||
} else {
|
||||
None
|
||||
let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value };
|
||||
if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() {
|
||||
return Some(Res::Def(cx.tcx.def_kind(id), id));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue