diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 62aae749c17..8698a618f90 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -31,7 +31,6 @@ pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; -use std::mem; use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, LitKind}; @@ -40,7 +39,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ @@ -49,6 +48,7 @@ use rustc_hir::{ }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; @@ -309,65 +309,43 @@ pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { } /// Gets the definition associated to a path. -pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { - let crates = cx.tcx.crates(); - let krate = crates - .iter() - .find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]); - if let Some(krate) = krate { - let krate = DefId { - krate: *krate, - index: CRATE_DEF_INDEX, - }; - let mut current_item = None; - let mut items = cx.tcx.item_children(krate); - let mut path_it = path.iter().skip(1).peekable(); - - loop { - let segment = match path_it.next() { - Some(segment) => segment, - None => return None, - }; - - // `get_def_path` seems to generate these empty segments for extern blocks. - // We can just ignore them. - if segment.is_empty() { - continue; - } - - let result = SmallVec::<[_; 8]>::new(); - for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { - if item.ident.name.as_str() == *segment { - if path_it.peek().is_none() { - return Some(item.res); - } - - current_item = Some(item); - items = cx.tcx.item_children(item.res.def_id()); - break; - } - } - - // The segment isn't a child_item. - // Try to find it under an inherent impl. - if_chain! { - if path_it.peek().is_none(); - if let Some(current_item) = current_item; - let item_def_id = current_item.res.def_id(); - if cx.tcx.def_kind(item_def_id) == DefKind::Struct; - then { - // Bad `find_map` suggestion. See #4193. - #[allow(clippy::find_map)] - return cx.tcx.inherent_impls(item_def_id).iter() - .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id)) - .find(|item| item.ident.name.as_str() == *segment) - .map(|item| item.res); - } - } - } - } else { - None +#[allow(clippy::shadow_unrelated)] // false positive #6563 +pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { + fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> { + tcx.item_children(def_id) + .iter() + .find(|item| item.ident.name.as_str() == name) } + + let (krate, first, path) = match *path { + [krate, first, ref path @ ..] => (krate, first, path), + _ => return None, + }; + let tcx = cx.tcx; + let crates = tcx.crates(); + let krate = crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)?; + let first = item_child_by_name(tcx, krate.as_def_id(), first)?; + let last = path + .iter() + .copied() + // `get_def_path` seems to generate these empty segments for extern blocks. + // We can just ignore them. + .filter(|segment| !segment.is_empty()) + // for each segment, find the child item + .try_fold(first, |item, segment| { + let def_id = item.res.def_id(); + if let Some(item) = item_child_by_name(tcx, def_id, segment) { + Some(item) + } else if matches!(item.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)) + } else { + None + } + })?; + Some(last.res) } pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index 01e28ae5e9d..a3b19c2e394 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -18,6 +18,9 @@ mod paths { // Path with bad module pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + + // Path to method on an enum inherent impl + pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; } fn main() {}