Auto merge of #6567 - camsteffen:path-to-res-enum, r=Manishearth
Fix path_to_res for enum inherent items changelog: none I tried to add `Option::is_some` to the paths but got a false positive from the invalid paths lint. Turns out the `path_to_res` function does not find inherent impls for enums. I fixed this and took the liberty to do some additional cleanup in the method.
This commit is contained in:
commit
d990c31990
@ -31,7 +31,6 @@
|
||||
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_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_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<def::Res> {
|
||||
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<Res> {
|
||||
fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
|
||||
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 {
|
||||
|
@ -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() {}
|
||||
|
Loading…
Reference in New Issue
Block a user