diff options
| author | bors <bors@rust-lang.org> | 2022-11-19 09:05:50 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-11-19 09:05:50 +0000 |
| commit | f60186f35d5f6d77101b21e7c574531d2f366561 (patch) | |
| tree | cb1da7fe787c68779fe6bff68793145233bc9695 | |
| parent | e144c7d1aefd0b789adf1c98b96068cebdb11405 (diff) | |
| parent | 1e1ac2b4985a1236003ef23b92e3edf3af8f5dfe (diff) | |
| download | rust-f60186f35d5f6d77101b21e7c574531d2f366561.tar.gz rust-f60186f35d5f6d77101b21e7c574531d2f366561.zip | |
Auto merge of #9800 - Alexendoo:def_path_res_multiple, r=dswij
Return multiple resolutions from `def_path_res`
Changes `def_path_res` to return all the resolutions matching the path rather than the first one (with a namespace hint that covered some cases). This would fix any issues that come up with multiple versions of the same crate being present as they all have the same crate name
It also adds resolution of `impl _ {}` items for local items, and removes struct field resolution as it didn't seem to be used anywhere
I tested it on a local crate and it worked for the multiple crate issue, but I couldn't come up with a test that worked well with `// aux-build`, maybe `// aux-crate` after https://github.com/rust-lang/rust/pull/103266 could work but I'm not sure on that either
changelog: [`disallowed_methods`], [`disallowed_types`], [`disallowed_macros`]: fix path resolution with multiple versions of the same crate
changelog: [`disallowed_methods`]: Resolve methods in `impl`s in the current crate
| -rw-r--r-- | clippy_lints/src/await_holding_invalid.rs | 3 | ||||
| -rw-r--r-- | clippy_lints/src/disallowed_macros.rs | 5 | ||||
| -rw-r--r-- | clippy_lints/src/disallowed_methods.rs | 5 | ||||
| -rw-r--r-- | clippy_lints/src/disallowed_types.rs | 40 | ||||
| -rw-r--r-- | clippy_lints/src/missing_enforced_import_rename.rs | 2 | ||||
| -rw-r--r-- | clippy_lints/src/mut_key.rs | 5 | ||||
| -rw-r--r-- | clippy_lints/src/utils/conf.rs | 4 | ||||
| -rw-r--r-- | clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs | 4 | ||||
| -rw-r--r-- | clippy_lints/src/utils/internal_lints/invalid_paths.rs | 4 | ||||
| -rw-r--r-- | clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs | 48 | ||||
| -rw-r--r-- | clippy_utils/src/lib.rs | 307 | ||||
| -rw-r--r-- | tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr | 4 | ||||
| -rw-r--r-- | tests/ui-toml/toml_disallowed_methods/clippy.toml | 6 | ||||
| -rw-r--r-- | tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs | 30 | ||||
| -rw-r--r-- | tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr | 50 |
15 files changed, 281 insertions, 236 deletions
diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 34717811866..d40a385435a 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -189,7 +188,7 @@ impl LateLintPass<'_> for AwaitHolding { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_invalid_types { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.def_ids.insert(id, conf.clone()); } } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 5ab7144e290..68122b4cef5 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_lint::{LateContext, LateLintPass}; @@ -89,7 +88,7 @@ impl DisallowedMacros { &format!("use of a disallowed macro `{}`", conf.path()), |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }, ); @@ -104,7 +103,7 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 6ac85606d9c..ca8671c8f1a 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } @@ -104,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }); } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index c7131fc164d..aee3d8c4f08 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -53,8 +53,8 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedTypes { conf_disallowed: Vec<conf::DisallowedPath>, - def_ids: FxHashMap<DefId, Option<String>>, - prim_tys: FxHashMap<PrimTy, Option<String>>, + def_ids: FxHashMap<DefId, usize>, + prim_tys: FxHashMap<PrimTy, usize>, } impl DisallowedTypes { @@ -69,13 +69,13 @@ impl DisallowedTypes { fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { match res { Res::Def(_, did) => { - if let Some(reason) = self.def_ids.get(did) { - emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref()); + if let Some(&index) = self.def_ids.get(did) { + emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]); } }, Res::PrimTy(prim) => { - if let Some(reason) = self.prim_tys.get(prim) { - emit(cx, prim.name_str(), span, reason.as_deref()); + if let Some(&index) = self.prim_tys.get(prim) { + emit(cx, prim.name_str(), span, &self.conf_disallowed[index]); } }, _ => {}, @@ -87,17 +87,19 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { + for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)")); - match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { - Res::Def(_, id) => { - self.def_ids.insert(id, reason); - }, - Res::PrimTy(ty) => { - self.prim_tys.insert(ty, reason); - }, - _ => {}, + + for res in clippy_utils::def_path_res(cx, &segs) { + match res { + Res::Def(_, id) => { + self.def_ids.insert(id, index); + }, + Res::PrimTy(ty) => { + self.prim_tys.insert(ty, index); + }, + _ => {}, + } } } } @@ -119,14 +121,14 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { } } -fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { +fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) { span_lint_and_then( cx, DISALLOWED_TYPES, span, &format!("`{name}` is not allowed according to config"), |diag| { - if let Some(reason) = reason { + if let Some(reason) = conf.reason() { diag.note(reason); } }, diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 872679f25ab..4712846939e 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -59,7 +59,7 @@ impl LateLintPass<'_> for ImportRename { fn check_crate(&mut self, cx: &LateContext<'_>) { for Rename { path, rename } in &self.conf_renames { let segs = path.split("::").collect::<Vec<_>>(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.renames.insert(id, Symbol::intern(rename)); } } diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 551b6ed1227..f0f152904f7 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{def_path_res, trait_ref_of_method}; +use clippy_utils::{def_path_def_ids, trait_ref_of_method}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::def::Namespace; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty}; @@ -94,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { let mut path = Vec::new(); for ty in &self.ignore_interior_mutability { path.extend(ty.split("::")); - if let Some(id) = def_path_res(cx, &path[..], Some(Namespace::TypeNS)).opt_def_id() { + for id in def_path_def_ids(cx, &path[..]) { self.ignore_mut_def_ids.insert(id); } path.clear(); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index ab58e9b8b68..b37d4239477 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -53,11 +53,11 @@ impl DisallowedPath { path } - pub fn reason(&self) -> Option<&str> { + pub fn reason(&self) -> Option<String> { match self { Self::WithReason { reason: Some(reason), .. - } => Some(reason), + } => Some(format!("{reason} (from clippy.toml)")), _ => None, } } diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 096b601572b..4b33d492a0e 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -2,7 +2,7 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; -use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths}; +use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { + for def_id in def_path_def_ids(cx, module) { for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 25532dd4e26..4e2e1cc8d77 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -3,7 +3,7 @@ use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::DefKind; use rustc_hir::Item; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path, None) != Res::Err { + if !def_path_res(cx, path).is_empty() { return true; } diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 4cf76f53625..0d91398b74f 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -1,19 +1,19 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; +use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Symbol; use rustc_span::Span; use std::str; @@ -110,7 +110,7 @@ impl UnnecessaryDefPath { // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, item_arg); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); + if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next(); then { // Check if the target item is a diagnostic item or LangItem. #[rustfmt::skip] @@ -209,7 +209,7 @@ impl UnnecessaryDefPath { fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { let Some(path) = path_from_array(elements) else { return }; - if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { + for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { self.array_def_ids.insert((def_id, span)); } } @@ -293,41 +293,11 @@ fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> { .collect() } -// def_path_res will match field names before anything else, but for this we want to match -// inherent functions first. -fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option<DefId> { - def_path_res(cx, segments, None).opt_def_id().map(|def_id| { - if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx - .def_key(def_id) - .parent - .and_then(|parent_idx| { - cx.tcx - .inherent_impls(DefId { - index: parent_idx, - krate: def_id.krate, - }) - .iter() - .find_map(|impl_id| { - cx.tcx.associated_items(*impl_id).find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - }) - }) - .map_or(def_id, |item| item.def_id) - } else { - def_id - } - }) -} - fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<Symbol> { if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let lang_items = def_path_def_ids(cx, &["rustc_hir", "lang_items", "LangItem"]) + .next() + .unwrap(); let item_name = cx .tcx .adt_def(lang_items) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7458cc41ef7..670b740d841 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -80,17 +80,16 @@ use rustc_ast::ast::{self, LitKind}; use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr, - ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, - Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, - TraitRef, TyKind, UnOp, + self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, + Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local, + MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -112,7 +111,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::source_map::SourceMap; use rustc_span::sym; -use rustc_span::symbol::{kw, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; @@ -525,165 +524,177 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx { - let single = |ty| tcx.incoherent_impls(ty).iter().copied(); - let empty = || [].iter().copied(); - match name { - "bool" => single(BoolSimplifiedType), - "char" => single(CharSimplifiedType), - "str" => single(StrSimplifiedType), - "array" => single(ArraySimplifiedType), - "slice" => single(SliceSimplifiedType), +fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx { + let ty = match name { + "bool" => BoolSimplifiedType, + "char" => CharSimplifiedType, + "str" => StrSimplifiedType, + "array" => ArraySimplifiedType, + "slice" => SliceSimplifiedType, // FIXME: rustdoc documents these two using just `pointer`. // // Maybe this is something we should do here too. - "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), - "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), - "isize" => single(IntSimplifiedType(IntTy::Isize)), - "i8" => single(IntSimplifiedType(IntTy::I8)), - "i16" => single(IntSimplifiedType(IntTy::I16)), - "i32" => single(IntSimplifiedType(IntTy::I32)), - "i64" => single(IntSimplifiedType(IntTy::I64)), - "i128" => single(IntSimplifiedType(IntTy::I128)), - "usize" => single(UintSimplifiedType(UintTy::Usize)), - "u8" => single(UintSimplifiedType(UintTy::U8)), - "u16" => single(UintSimplifiedType(UintTy::U16)), - "u32" => single(UintSimplifiedType(UintTy::U32)), - "u64" => single(UintSimplifiedType(UintTy::U64)), - "u128" => single(UintSimplifiedType(UintTy::U128)), - "f32" => single(FloatSimplifiedType(FloatTy::F32)), - "f64" => single(FloatSimplifiedType(FloatTy::F64)), - _ => empty(), - } -} - -/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate -/// between `std::vec` the module and `std::vec` the macro -/// -/// This function is expensive and should be used sparingly. -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option<Namespace>) -> Res { - fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option<Res> { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local())) - .map(|child| child.res.expect_non_local()), - DefKind::Impl => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), - DefKind::Struct | DefKind::Union => tcx - .adt_def(def_id) - .non_enum_variant() - .fields - .iter() - .find(|f| f.name.as_str() == name) - .map(|f| Res::Def(DefKind::Field, f.did)), - _ => None, + "const_ptr" => PtrSimplifiedType(Mutability::Not), + "mut_ptr" => PtrSimplifiedType(Mutability::Mut), + "isize" => IntSimplifiedType(IntTy::Isize), + "i8" => IntSimplifiedType(IntTy::I8), + "i16" => IntSimplifiedType(IntTy::I16), + "i32" => IntSimplifiedType(IntTy::I32), + "i64" => IntSimplifiedType(IntTy::I64), + "i128" => IntSimplifiedType(IntTy::I128), + "usize" => UintSimplifiedType(UintTy::Usize), + "u8" => UintSimplifiedType(UintTy::U8), + "u16" => UintSimplifiedType(UintTy::U16), + "u32" => UintSimplifiedType(UintTy::U32), + "u64" => UintSimplifiedType(UintTy::U64), + "u128" => UintSimplifiedType(UintTy::U128), + "f32" => FloatSimplifiedType(FloatTy::F32), + "f64" => FloatSimplifiedType(FloatTy::F64), + _ => return [].iter().copied(), + }; + + tcx.incoherent_impls(ty).iter().copied() +} + +fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> { + 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::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(), + } +} + +fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> { + let hir = tcx.hir(); + + let root_mod; + let item_kind = match hir.find_by_def_id(local_id) { + Some(Node::Crate(r#mod)) => { + root_mod = ItemKind::Mod(r#mod); + &root_mod + }, + Some(Node::Item(item)) => &item.kind, + _ => return Vec::new(), + }; + + 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)) + } else { + None } + }; + + match item_kind { + ItemKind::Mod(r#mod) => r#mod + .item_ids + .iter() + .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.def_id)) + .collect(), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.def_id)) + .collect(), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.def_id)) + .collect(), + _ => Vec::new(), + } +} + +fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> { + if let Some(local_id) = def_id.as_local() { + local_item_children_by_name(tcx, local_id, name) + } else { + non_local_item_children_by_name(tcx, def_id, name) } +} - fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> { +/// 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 mulitple paths under the same name e.g. `std::vec` +/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. +/// +/// This function is expensive and should be used sparingly. +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> { + fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ { tcx.crates(()) .iter() .copied() - .find(|&num| tcx.crate_name(num).as_str() == name) + .filter(move |&num| tcx.crate_name(num) == name) .map(CrateNum::as_def_id) } - let (base, path) = match *path { + let tcx = cx.tcx; + + let (base, mut path) = match *path { [primitive] => { - return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); - }, - [base, ref path @ ..] => { - let crate_name = cx.sess().opts.crate_name.as_deref(); - if Some(base) == crate_name { - return def_path_res_local(cx, path); - } - (base, path) + return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; }, - _ => return Res::Err, + [base, ref path @ ..] => (base, path), + _ => return Vec::new(), }; - let tcx = cx.tcx; - let starts = find_primitive(tcx, base) - .chain(find_crate(tcx, base)) + + 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 starts = find_primitive_impls(tcx, base) + .chain(find_crates(tcx, base_sym)) + .chain(local_crate) .map(|id| Res::Def(tcx.def_kind(id), id)); - for first in starts { - let last = path - .iter() - .copied() - .enumerate() - // for each segment, find the child item - .try_fold(first, |res, (idx, segment)| { - let matches_ns = |res: Res| { - // If at the last segment in the path, respect the namespace hint - if idx == path.len() - 1 { - match namespace_hint { - Some(ns) => res.matches_ns(ns), - None => true, - } - } else { - res.matches_ns(Namespace::TypeNS) - } - }; - - let def_id = res.def_id(); - if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) { - Some(item) - } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { - // it is not a child item so check inherent impl items - tcx.inherent_impls(def_id) - .iter() - .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns)) - } else { - None - } - }); + let mut resolutions: Vec<Res> = starts.collect(); - if let Some(last) = last { - return last; - } + while let [segment, rest @ ..] = path { + path = rest; + let segment = Symbol::intern(segment); + + resolutions = resolutions + .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) + .iter() + .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); + + let direct_children = item_children_by_name(tcx, def_id, segment); + + inherent_impl_children.chain(direct_children) + }) + .collect(); } - Res::Err + + resolutions } -fn def_path_res_local(cx: &LateContext<'_>, mut path: &[&str]) -> Res { - let map = cx.tcx.hir(); - let mut ids = map.root_module().item_ids; - while let Some(&segment) = path.first() { - let mut next_ids = None; - for i in ids { - if let Some(Node::Item(hir::Item { - ident, - kind, - def_id: item_def_id, - .. - })) = map.find(i.hir_id()) - { - if ident.name.as_str() == segment { - path = &path[1..]; - if path.is_empty() { - let def_id = item_def_id.to_def_id(); - return Res::Def(cx.tcx.def_kind(def_id), def_id); - } - if let ItemKind::Mod(m) = kind { - next_ids = Some(m.item_ids); - }; - break; - } - } - } - if let Some(next_ids) = next_ids { - ids = next_ids; - } else { - break; - } - } - Res::Err +/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. +pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> { + def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id()) } /// Convenience function to get the `DefId` of a trait by path. @@ -691,10 +702,10 @@ fn def_path_res_local(cx: &LateContext<'_>, mut path: &[&str]) -> Res { /// /// This function is expensive and should be used sparingly. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> { - match def_path_res(cx, path, Some(Namespace::TypeNS)) { + def_path_res(cx, path).into_iter().find_map(|res| match res { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), _ => None, - } + }) } /// Gets the `hir::TraitRef` of the trait the given method is implemented for. diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index 4c75998437f..825aa1487e7 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -4,7 +4,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy LL | let _x = String::from("hello"); | ^^ | - = note: strings are bad + = note: strings are bad (from clippy.toml) = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` @@ -19,7 +19,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy LL | let _x = String::from("hi!"); | ^^ | - = note: strings are bad + = note: strings are bad (from clippy.toml) error: aborting due to 3 previous errors diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index 28774db625b..41dbd506847 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -8,4 +8,10 @@ disallowed-methods = [ { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason { path = "regex::Regex::new" }, + # local paths + "conf_disallowed_methods::local_fn", + "conf_disallowed_methods::local_mod::f", + "conf_disallowed_methods::Struct::method", + "conf_disallowed_methods::Trait::provided_method", + "conf_disallowed_methods::Trait::implemented_method", ] diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index b483f160028..2f3160c8338 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -1,3 +1,5 @@ +// compile-flags: --crate-name conf_disallowed_methods + #![warn(clippy::disallowed_methods)] extern crate futures; @@ -6,6 +8,27 @@ extern crate regex; use futures::stream::{empty, select_all}; use regex::Regex; +fn local_fn() {} + +struct Struct; + +impl Struct { + fn method(&self) {} +} + +trait Trait { + fn provided_method(&self) {} + fn implemented_method(&self); +} + +impl Trait for Struct { + fn implemented_method(&self) {} +} + +mod local_mod { + pub fn f() {} +} + fn main() { let re = Regex::new(r"ab.*c").unwrap(); re.is_match("abc"); @@ -26,4 +49,11 @@ fn main() { // resolve ambiguity between `futures::stream::select_all` the module and the function let same_name_as_module = select_all(vec![empty::<()>()]); + + local_fn(); + local_mod::f(); + let s = Struct; + s.method(); + s.provided_method(); + s.implemented_method(); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 6d78c32e127..148d1cae51f 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:10:14 + --> $DIR/conf_disallowed_methods.rs:33:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = note: `-D clippy::disallowed-methods` implied by `-D warnings` error: use of a disallowed method `regex::Regex::is_match` - --> $DIR/conf_disallowed_methods.rs:11:5 + --> $DIR/conf_disallowed_methods.rs:34:5 | LL | re.is_match("abc"); | ^^^^^^^^^^^^^^^^^^ @@ -15,46 +15,76 @@ LL | re.is_match("abc"); = note: no matching allowed (from clippy.toml) error: use of a disallowed method `std::iter::Iterator::sum` - --> $DIR/conf_disallowed_methods.rs:14:5 + --> $DIR/conf_disallowed_methods.rs:37:5 | LL | a.iter().sum::<i32>(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `slice::sort_unstable` - --> $DIR/conf_disallowed_methods.rs:16:5 + --> $DIR/conf_disallowed_methods.rs:39:5 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:18:13 + --> $DIR/conf_disallowed_methods.rs:41:13 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:21:61 + --> $DIR/conf_disallowed_methods.rs:44:61 | LL | let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:24:28 + --> $DIR/conf_disallowed_methods.rs:47:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:25:53 + --> $DIR/conf_disallowed_methods.rs:48:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> $DIR/conf_disallowed_methods.rs:28:31 + --> $DIR/conf_disallowed_methods.rs:51:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: use of a disallowed method `conf_disallowed_methods::local_fn` + --> $DIR/conf_disallowed_methods.rs:53:5 + | +LL | local_fn(); + | ^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::local_mod::f` + --> $DIR/conf_disallowed_methods.rs:54:5 + | +LL | local_mod::f(); + | ^^^^^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Struct::method` + --> $DIR/conf_disallowed_methods.rs:56:5 + | +LL | s.method(); + | ^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` + --> $DIR/conf_disallowed_methods.rs:57:5 + | +LL | s.provided_method(); + | ^^^^^^^^^^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` + --> $DIR/conf_disallowed_methods.rs:58:5 + | +LL | s.implemented_method(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors |
