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::borrow::Cow;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::ast::{self, Attribute, LitKind};
|
use rustc_ast::ast::{self, Attribute, LitKind};
|
||||||
@ -40,7 +39,7 @@
|
|||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
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::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::Node;
|
use rustc_hir::Node;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
@ -49,6 +48,7 @@
|
|||||||
};
|
};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||||
|
use rustc_middle::hir::exports::Export;
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||||
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
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.
|
/// Gets the definition associated to a path.
|
||||||
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
|
#[allow(clippy::shadow_unrelated)] // false positive #6563
|
||||||
let crates = cx.tcx.crates();
|
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<Res> {
|
||||||
let krate = crates
|
fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
|
||||||
.iter()
|
tcx.item_children(def_id)
|
||||||
.find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]);
|
.iter()
|
||||||
if let Some(krate) = krate {
|
.find(|item| item.ident.name.as_str() == name)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
|
||||||
|
@ -18,6 +18,9 @@ mod paths {
|
|||||||
|
|
||||||
// Path with bad module
|
// Path with bad module
|
||||||
pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
|
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() {}
|
fn main() {}
|
||||||
|
Loading…
Reference in New Issue
Block a user