Skip to content

Commit 8490862

Browse files
committed
Fix path_to_res for enum inherent items
1 parent 2950c8e commit 8490862

File tree

2 files changed

+40
-59
lines changed

2 files changed

+40
-59
lines changed

clippy_lints/src/utils/mod.rs

Lines changed: 37 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
3131
use std::borrow::Cow;
3232
use std::collections::hash_map::Entry;
3333
use std::hash::BuildHasherDefault;
34-
use std::mem;
3534

3635
use if_chain::if_chain;
3736
use rustc_ast::ast::{self, Attribute, LitKind};
@@ -40,7 +39,7 @@ use rustc_data_structures::fx::FxHashMap;
4039
use rustc_errors::Applicability;
4140
use rustc_hir as hir;
4241
use rustc_hir::def::{DefKind, Res};
43-
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
42+
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
4443
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
4544
use rustc_hir::Node;
4645
use rustc_hir::{
@@ -49,6 +48,7 @@ use rustc_hir::{
4948
};
5049
use rustc_infer::infer::TyCtxtInferExt;
5150
use rustc_lint::{LateContext, Level, Lint, LintContext};
51+
use rustc_middle::hir::exports::Export;
5252
use rustc_middle::hir::map::Map;
5353
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
5454
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 {
309309
}
310310

311311
/// Gets the definition associated to a path.
312-
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
313-
let crates = cx.tcx.crates();
314-
let krate = crates
315-
.iter()
316-
.find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]);
317-
if let Some(krate) = krate {
318-
let krate = DefId {
319-
krate: *krate,
320-
index: CRATE_DEF_INDEX,
321-
};
322-
let mut current_item = None;
323-
let mut items = cx.tcx.item_children(krate);
324-
let mut path_it = path.iter().skip(1).peekable();
325-
326-
loop {
327-
let segment = match path_it.next() {
328-
Some(segment) => segment,
329-
None => return None,
330-
};
331-
332-
// `get_def_path` seems to generate these empty segments for extern blocks.
333-
// We can just ignore them.
334-
if segment.is_empty() {
335-
continue;
336-
}
337-
338-
let result = SmallVec::<[_; 8]>::new();
339-
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
340-
if item.ident.name.as_str() == *segment {
341-
if path_it.peek().is_none() {
342-
return Some(item.res);
343-
}
344-
345-
current_item = Some(item);
346-
items = cx.tcx.item_children(item.res.def_id());
347-
break;
348-
}
349-
}
312+
#[allow(clippy::shadow_unrelated)] // false positive #6563
313+
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<Res> {
314+
fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
315+
tcx.item_children(def_id)
316+
.iter()
317+
.find(|item| item.ident.name.as_str() == name)
318+
}
350319

351-
// The segment isn't a child_item.
352-
// Try to find it under an inherent impl.
353-
if_chain! {
354-
if path_it.peek().is_none();
355-
if let Some(current_item) = current_item;
356-
let item_def_id = current_item.res.def_id();
357-
if cx.tcx.def_kind(item_def_id) == DefKind::Struct;
358-
then {
359-
// Bad `find_map` suggestion. See #4193.
360-
#[allow(clippy::find_map)]
361-
return cx.tcx.inherent_impls(item_def_id).iter()
362-
.flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id))
363-
.find(|item| item.ident.name.as_str() == *segment)
364-
.map(|item| item.res);
365-
}
320+
let (krate, first, path) = match *path {
321+
[krate, first, ref path @ ..] => (krate, first, path),
322+
_ => return None,
323+
};
324+
let tcx = cx.tcx;
325+
let crates = tcx.crates();
326+
let krate = crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)?;
327+
let first = item_child_by_name(tcx, krate.as_def_id(), first)?;
328+
let last = path
329+
.iter()
330+
.copied()
331+
// `get_def_path` seems to generate these empty segments for extern blocks.
332+
// We can just ignore them.
333+
.filter(|segment| !segment.is_empty())
334+
// for each segment, find the child item
335+
.try_fold(first, |item, segment| {
336+
let def_id = item.res.def_id();
337+
if let Some(item) = item_child_by_name(tcx, def_id, segment) {
338+
Some(item)
339+
} else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
340+
// it is not a child item so check inherent impl items
341+
tcx.inherent_impls(def_id)
342+
.iter()
343+
.find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
344+
} else {
345+
None
366346
}
367-
}
368-
} else {
369-
None
370-
}
347+
})?;
348+
Some(last.res)
371349
}
372350

373351
pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {

tests/ui-internal/invalid_paths.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ mod paths {
1818

1919
// Path with bad module
2020
pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
21+
22+
// Path to method on an enum inherent impl
23+
pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"];
2124
}
2225

2326
fn main() {}

0 commit comments

Comments
 (0)