Move lookup_path and similar into clippy_utils::paths
This commit is contained in:
parent
b768fbe4bc
commit
f23772ce8c
15 changed files with 243 additions and 236 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::PathNS;
|
||||
use clippy_utils::paths::{PathNS, find_crates, lookup_path};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::PrimTy;
|
||||
|
|
@ -148,7 +148,7 @@ pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
|
|||
for disallowed_path in disallowed_paths {
|
||||
let path = disallowed_path.path();
|
||||
let sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
|
||||
let mut resolutions = clippy_utils::lookup_path(tcx, ns, &sym_path);
|
||||
let mut resolutions = lookup_path(tcx, ns, &sym_path);
|
||||
resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id)));
|
||||
|
||||
let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice()
|
||||
|
|
@ -164,10 +164,10 @@ pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
|
|||
&& !disallowed_path.allow_invalid
|
||||
// Don't warn about unloaded crates:
|
||||
// https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221
|
||||
&& (sym_path.len() < 2 || !clippy_utils::find_crates(tcx, sym_path[0]).is_empty())
|
||||
&& (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty())
|
||||
{
|
||||
// Relookup the path in an arbitrary namespace to get a good `expected, found` message
|
||||
let found_def_ids = clippy_utils::lookup_path(tcx, PathNS::Arbitrary, &sym_path);
|
||||
let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path);
|
||||
let message = if let Some(&def_id) = found_def_ids.first() {
|
||||
let (article, description) = tcx.article_and_description(def_id);
|
||||
format!("expected a {predicate_description}, found {article} {description}")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{PathNS, paths};
|
||||
use clippy_utils::paths::{self, PathNS};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::PathNS;
|
||||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use clippy_utils::paths::PathNS;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::PathNS;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths::PathNS;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::PathNS;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths::PathNS;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ mod too_many_lines;
|
|||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::{PathNS, lookup_path_str};
|
||||
use clippy_utils::paths::{PathNS, lookup_path_str};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::paths::{PathNS, lookup_path_str};
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::{PathNS, lookup_path_str};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str};
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{PathNS, find_crates, fn_def_id, is_no_std_crate, lookup_path_str, path_def_id, paths, sym};
|
||||
use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::paths::PathLookup;
|
||||
use clippy_utils::{PathNS, sym, type_path, value_path};
|
||||
use clippy_utils::paths::{PathLookup, PathNS};
|
||||
use clippy_utils::{sym, type_path, value_path};
|
||||
|
||||
// Paths inside rustc
|
||||
pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::internal_paths;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{PathNS, lookup_path, path_def_id, peel_ref_operators};
|
||||
use clippy_utils::paths::{PathNS, lookup_path};
|
||||
use clippy_utils::{path_def_id, peel_ref_operators};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
|
|||
|
|
@ -96,30 +96,29 @@ 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::Namespace::{MacroNS, TypeNS, ValueNS};
|
||||
use rustc_hir::def::{DefKind, Namespace, Res};
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::definitions::{DefPath, DefPathData};
|
||||
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
|
||||
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, QPath, Stmt,
|
||||
StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
|
||||
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
|
||||
CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
|
||||
ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
|
||||
Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
|
||||
TraitItemKind, TraitRef, TyKind, UnOp, def,
|
||||
};
|
||||
use rustc_lexer::{TokenKind, tokenize};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::lint::LevelAndSource;
|
||||
use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty,
|
||||
TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
|
||||
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
|
||||
TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
|
||||
};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
|
|
@ -132,7 +131,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
|
|||
use crate::higher::Range;
|
||||
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
||||
use crate::visitors::for_each_expr_without_closures;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! extract_msrv_attr {
|
||||
|
|
@ -240,7 +238,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
|
|||
/// * const blocks (or inline consts)
|
||||
/// * associated constants
|
||||
pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
|
||||
use ConstContext::{Const, ConstFn, Static};
|
||||
use rustc_hir::ConstContext::{Const, ConstFn, Static};
|
||||
let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
|
||||
return false;
|
||||
};
|
||||
|
|
@ -513,207 +511,6 @@ 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: TyCtxt<'_>, name: Symbol) -> &[DefId] {
|
||||
let ty = match name {
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
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().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()
|
||||
.find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
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) => {
|
||||
root_mod = ItemKind::Mod(Ident::dummy(), r#mod);
|
||||
&root_mod
|
||||
},
|
||||
Node::Item(item) => &item.kind,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let res = |ident: Ident, owner_id: OwnerId| {
|
||||
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().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()
|
||||
.find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
|
||||
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
|
||||
.iter()
|
||||
.find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
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_child_by_name(tcx, local_id, ns, name)
|
||||
} else {
|
||||
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) -> &'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`.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// [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 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`.
|
||||
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));
|
||||
|
||||
// When the current def_id is e.g. `struct S`, check the impl items in
|
||||
// `impl S { ... }`
|
||||
let inherent_impl_children = tcx
|
||||
.inherent_impls(base)
|
||||
.iter()
|
||||
.filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment));
|
||||
out.extend(inherent_impl_children);
|
||||
|
||||
return;
|
||||
},
|
||||
[segment, ref rest @ ..] => {
|
||||
path = rest;
|
||||
let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else {
|
||||
return;
|
||||
};
|
||||
base = child;
|
||||
},
|
||||
[] => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Equivalent to a [`lookup_path`] after splitting the input string on `::`
|
||||
///
|
||||
/// This function is expensive and should be used sparingly.
|
||||
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.
|
||||
///
|
||||
/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
|
||||
|
|
|
|||
|
|
@ -4,13 +4,48 @@
|
|||
//! Whenever possible, please consider diagnostic items over hardcoded paths.
|
||||
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
|
||||
|
||||
use crate::{MaybePath, PathNS, lookup_path, path_def_id, sym};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use crate::{MaybePath, path_def_id, sym};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS};
|
||||
use rustc_hir::def::{DefKind, Namespace};
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::{STDLIB_STABLE_CRATES, Symbol};
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy};
|
||||
use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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:
|
||||
|
|
@ -126,3 +161,175 @@ 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`
|
||||
|
||||
/// Equivalent to a [`lookup_path`] after splitting the input string on `::`
|
||||
///
|
||||
/// This function is expensive and should be used sparingly.
|
||||
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)
|
||||
}
|
||||
|
||||
/// Resolves a def path like `std::vec::Vec`.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// [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 mut out = Vec::new();
|
||||
for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) {
|
||||
lookup_with_base(tcx, base, ns, rest, &mut out);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Finds the crates called `name`, may be multiple due to multiple major versions.
|
||||
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 => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] {
|
||||
let ty = match name {
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
/// Resolves a def path like `vec::Vec` with the base `std`.
|
||||
fn lookup_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));
|
||||
|
||||
// When the current def_id is e.g. `struct S`, check the impl items in
|
||||
// `impl S { ... }`
|
||||
let inherent_impl_children = tcx
|
||||
.inherent_impls(base)
|
||||
.iter()
|
||||
.filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment));
|
||||
out.extend(inherent_impl_children);
|
||||
|
||||
return;
|
||||
},
|
||||
[segment, ref rest @ ..] => {
|
||||
path = rest;
|
||||
let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else {
|
||||
return;
|
||||
};
|
||||
base = child;
|
||||
},
|
||||
[] => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_child_by_name(tcx, local_id, ns, name)
|
||||
} else {
|
||||
non_local_item_child_by_name(tcx, def_id, ns, name)
|
||||
}
|
||||
}
|
||||
|
||||
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) => {
|
||||
root_mod = ItemKind::Mod(Ident::dummy(), r#mod);
|
||||
&root_mod
|
||||
},
|
||||
Node::Item(item) => &item.kind,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let res = |ident: Ident, owner_id: OwnerId| {
|
||||
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().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()
|
||||
.find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
|
||||
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
|
||||
.iter()
|
||||
.find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
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().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()
|
||||
.find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ use std::assert_matches::debug_assert_matches;
|
|||
use std::collections::hash_map::Entry;
|
||||
use std::iter;
|
||||
|
||||
use crate::{PathNS, lookup_path_str, path_res};
|
||||
use crate::path_res;
|
||||
use crate::paths::{PathNS, lookup_path_str};
|
||||
|
||||
mod type_certainty;
|
||||
pub use type_certainty::expr_type_is_certain;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
//! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should
|
||||
//! be considered a bug.
|
||||
|
||||
use crate::{PathNS, lookup_path};
|
||||
use crate::paths::{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};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(rustc_private)]
|
||||
|
||||
use clippy_utils::paths::PathLookup;
|
||||
use clippy_utils::{PathNS, macro_path, sym, type_path, value_path};
|
||||
use clippy_utils::paths::{PathLookup, PathNS};
|
||||
use clippy_utils::{macro_path, sym, type_path, value_path};
|
||||
|
||||
static OPTION: PathLookup = type_path!(core::option::Option);
|
||||
//~^ unnecessary_def_path
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue