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:
bors 2021-01-20 21:35:11 +00:00
commit d990c31990
2 changed files with 41 additions and 60 deletions

View File

@ -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 {

View File

@ -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() {}