Return multiple resolutions from def_path_res

This commit is contained in:
Alex Macleod 2022-11-04 21:58:07 +00:00
parent 7600535511
commit 1e1ac2b498
15 changed files with 280 additions and 235 deletions

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
use rustc_lint::{LateContext, LateLintPass};
@ -189,7 +188,7 @@ impl LateLintPass<'_> for AwaitHolding {
fn check_crate(&mut self, cx: &LateContext<'_>) {
for conf in &self.conf_invalid_types {
let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.def_ids.insert(id, conf.clone());
}
}

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty};
use rustc_lint::{LateContext, LateLintPass};
@ -89,7 +88,7 @@ impl DisallowedMacros {
&format!("use of a disallowed macro `{}`", conf.path()),
|diag| {
if let Some(reason) = conf.reason() {
diag.note(&format!("{reason} (from clippy.toml)"));
diag.note(reason);
}
},
);
@ -104,7 +103,7 @@ impl LateLintPass<'_> for DisallowedMacros {
fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) {
for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.disallowed.insert(id, index);
}
}

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@ -79,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) {
for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.disallowed.insert(id, index);
}
}
@ -104,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
let msg = format!("use of a disallowed method `{}`", conf.path());
span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
if let Some(reason) = conf.reason() {
diag.note(&format!("{reason} (from clippy.toml)"));
diag.note(reason);
}
});
}

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{Namespace, Res};
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
use rustc_lint::{LateContext, LateLintPass};
@ -53,8 +53,8 @@ declare_clippy_lint! {
#[derive(Clone, Debug)]
pub struct DisallowedTypes {
conf_disallowed: Vec<conf::DisallowedPath>,
def_ids: FxHashMap<DefId, Option<String>>,
prim_tys: FxHashMap<PrimTy, Option<String>>,
def_ids: FxHashMap<DefId, usize>,
prim_tys: FxHashMap<PrimTy, usize>,
}
impl DisallowedTypes {
@ -69,13 +69,13 @@ impl DisallowedTypes {
fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
match res {
Res::Def(_, did) => {
if let Some(reason) = self.def_ids.get(did) {
emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
if let Some(&index) = self.def_ids.get(did) {
emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]);
}
},
Res::PrimTy(prim) => {
if let Some(reason) = self.prim_tys.get(prim) {
emit(cx, prim.name_str(), span, reason.as_deref());
if let Some(&index) = self.prim_tys.get(prim) {
emit(cx, prim.name_str(), span, &self.conf_disallowed[index]);
}
},
_ => {},
@ -87,20 +87,22 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
fn check_crate(&mut self, cx: &LateContext<'_>) {
for conf in &self.conf_disallowed {
for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect();
let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)"));
match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
for res in clippy_utils::def_path_res(cx, &segs) {
match res {
Res::Def(_, id) => {
self.def_ids.insert(id, reason);
self.def_ids.insert(id, index);
},
Res::PrimTy(ty) => {
self.prim_tys.insert(ty, reason);
self.prim_tys.insert(ty, index);
},
_ => {},
}
}
}
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Use(path, UseKind::Single) = &item.kind {
@ -119,14 +121,14 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
}
}
fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) {
span_lint_and_then(
cx,
DISALLOWED_TYPES,
span,
&format!("`{name}` is not allowed according to config"),
|diag| {
if let Some(reason) = reason {
if let Some(reason) = conf.reason() {
diag.note(reason);
}
},

View file

@ -59,7 +59,7 @@ impl LateLintPass<'_> for ImportRename {
fn check_crate(&mut self, cx: &LateContext<'_>) {
for Rename { path, rename } in &self.conf_renames {
let segs = path.split("::").collect::<Vec<_>>();
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) {
for id in clippy_utils::def_path_def_ids(cx, &segs) {
self.renames.insert(id, Symbol::intern(rename));
}
}

View file

@ -1,8 +1,7 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{def_path_res, trait_ref_of_method};
use clippy_utils::{def_path_def_ids, trait_ref_of_method};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeVisitable;
use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
@ -94,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
let mut path = Vec::new();
for ty in &self.ignore_interior_mutability {
path.extend(ty.split("::"));
if let Some(id) = def_path_res(cx, &path[..], Some(Namespace::TypeNS)).opt_def_id() {
for id in def_path_def_ids(cx, &path[..]) {
self.ignore_mut_def_ids.insert(id);
}
path.clear();

View file

@ -53,11 +53,11 @@ impl DisallowedPath {
path
}
pub fn reason(&self) -> Option<&str> {
pub fn reason(&self) -> Option<String> {
match self {
Self::WithReason {
reason: Some(reason), ..
} => Some(reason),
} => Some(format!("{reason} (from clippy.toml)")),
_ => None,
}
}

View file

@ -2,7 +2,7 @@ use clippy_utils::consts::{constant_simple, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::match_type;
use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths};
use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
}
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() {
for def_id in def_path_def_ids(cx, module) {
for item in cx.tcx.module_children(def_id).iter() {
if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res;

View file

@ -3,7 +3,7 @@ use clippy_utils::def_path_res;
use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def::DefKind;
use rustc_hir::Item;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
// This is not a complete resolver for paths. It works on all the paths currently used in the paths
// module. That's all it does and all it needs to do.
pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
if def_path_res(cx, path, None) != Res::Err {
if !def_path_res(cx, path).is_empty() {
return true;
}

View file

@ -1,19 +1,19 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace, Res};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, Local, Mutability, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty};
use rustc_middle::ty::{self, DefIdTree, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use std::str;
@ -110,7 +110,7 @@ impl UnnecessaryDefPath {
// Extract the path to the matched type
if let Some(segments) = path_to_matched_type(cx, item_arg);
let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
if let Some(def_id) = inherent_def_path_res(cx, &segments[..]);
if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next();
then {
// Check if the target item is a diagnostic item or LangItem.
#[rustfmt::skip]
@ -209,7 +209,7 @@ impl UnnecessaryDefPath {
fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) {
let Some(path) = path_from_array(elements) else { return };
if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
self.array_def_ids.insert((def_id, span));
}
}
@ -293,41 +293,11 @@ fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> {
.collect()
}
// def_path_res will match field names before anything else, but for this we want to match
// inherent functions first.
fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option<DefId> {
def_path_res(cx, segments, None).opt_def_id().map(|def_id| {
if cx.tcx.def_kind(def_id) == DefKind::Field {
let method_name = *segments.last().unwrap();
cx.tcx
.def_key(def_id)
.parent
.and_then(|parent_idx| {
cx.tcx
.inherent_impls(DefId {
index: parent_idx,
krate: def_id.krate,
})
.iter()
.find_map(|impl_id| {
cx.tcx.associated_items(*impl_id).find_by_name_and_kind(
cx.tcx,
Ident::from_str(method_name),
AssocKind::Fn,
*impl_id,
)
})
})
.map_or(def_id, |item| item.def_id)
} else {
def_id
}
})
}
fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<Symbol> {
if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) {
let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id();
let lang_items = def_path_def_ids(cx, &["rustc_hir", "lang_items", "LangItem"])
.next()
.unwrap();
let item_name = cx
.tcx
.adt_def(lang_items)

View file

@ -80,17 +80,16 @@ use rustc_ast::ast::{self, LitKind};
use rustc_ast::Attribute;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace, Res};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr,
ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
TraitRef, TyKind, UnOp,
self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination,
Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, Level, Lint, LintContext};
@ -112,7 +111,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::source_map::SourceMap;
use rustc_span::sym;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::Integer;
@ -525,165 +524,177 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>
path_res(cx, maybe_path).opt_def_id()
}
fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
let single = |ty| tcx.incoherent_impls(ty).iter().copied();
let empty = || [].iter().copied();
match name {
"bool" => single(BoolSimplifiedType),
"char" => single(CharSimplifiedType),
"str" => single(StrSimplifiedType),
"array" => single(ArraySimplifiedType),
"slice" => single(SliceSimplifiedType),
fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
let ty = match name {
"bool" => BoolSimplifiedType,
"char" => CharSimplifiedType,
"str" => StrSimplifiedType,
"array" => ArraySimplifiedType,
"slice" => SliceSimplifiedType,
// FIXME: rustdoc documents these two using just `pointer`.
//
// Maybe this is something we should do here too.
"const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
"mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
"isize" => single(IntSimplifiedType(IntTy::Isize)),
"i8" => single(IntSimplifiedType(IntTy::I8)),
"i16" => single(IntSimplifiedType(IntTy::I16)),
"i32" => single(IntSimplifiedType(IntTy::I32)),
"i64" => single(IntSimplifiedType(IntTy::I64)),
"i128" => single(IntSimplifiedType(IntTy::I128)),
"usize" => single(UintSimplifiedType(UintTy::Usize)),
"u8" => single(UintSimplifiedType(UintTy::U8)),
"u16" => single(UintSimplifiedType(UintTy::U16)),
"u32" => single(UintSimplifiedType(UintTy::U32)),
"u64" => single(UintSimplifiedType(UintTy::U64)),
"u128" => single(UintSimplifiedType(UintTy::U128)),
"f32" => single(FloatSimplifiedType(FloatTy::F32)),
"f64" => single(FloatSimplifiedType(FloatTy::F64)),
_ => empty(),
}
"const_ptr" => PtrSimplifiedType(Mutability::Not),
"mut_ptr" => PtrSimplifiedType(Mutability::Mut),
"isize" => IntSimplifiedType(IntTy::Isize),
"i8" => IntSimplifiedType(IntTy::I8),
"i16" => IntSimplifiedType(IntTy::I16),
"i32" => IntSimplifiedType(IntTy::I32),
"i64" => IntSimplifiedType(IntTy::I64),
"i128" => IntSimplifiedType(IntTy::I128),
"usize" => UintSimplifiedType(UintTy::Usize),
"u8" => UintSimplifiedType(UintTy::U8),
"u16" => UintSimplifiedType(UintTy::U16),
"u32" => UintSimplifiedType(UintTy::U32),
"u64" => UintSimplifiedType(UintTy::U64),
"u128" => UintSimplifiedType(UintTy::U128),
"f32" => FloatSimplifiedType(FloatTy::F32),
"f64" => FloatSimplifiedType(FloatTy::F64),
_ => return [].iter().copied(),
};
tcx.incoherent_impls(ty).iter().copied()
}
/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate
/// between `std::vec` the module and `std::vec` the macro
///
/// This function is expensive and should be used sparingly.
pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option<Namespace>) -> Res {
fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option<Res> {
fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
match tcx.def_kind(def_id) {
DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
.module_children(def_id)
.iter()
.find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local()))
.map(|child| child.res.expect_non_local()),
.filter(|item| item.ident.name == name)
.map(|child| child.res.expect_non_local())
.collect(),
DefKind::Impl => tcx
.associated_item_def_ids(def_id)
.iter()
.copied()
.find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
.map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
DefKind::Struct | DefKind::Union => tcx
.adt_def(def_id)
.non_enum_variant()
.fields
.iter()
.find(|f| f.name.as_str() == name)
.map(|f| Res::Def(DefKind::Field, f.did)),
_ => None,
}
.filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
.map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
.collect(),
_ => Vec::new(),
}
}
fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
tcx.crates(())
.iter()
.copied()
.find(|&num| tcx.crate_name(num).as_str() == name)
.map(CrateNum::as_def_id)
}
fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
let hir = tcx.hir();
let (base, path) = match *path {
[primitive] => {
return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
let root_mod;
let item_kind = match hir.find_by_def_id(local_id) {
Some(Node::Crate(r#mod)) => {
root_mod = ItemKind::Mod(r#mod);
&root_mod
},
[base, ref path @ ..] => {
let crate_name = cx.sess().opts.crate_name.as_deref();
if Some(base) == crate_name {
return def_path_res_local(cx, path);
}
(base, path)
},
_ => return Res::Err,
};
let tcx = cx.tcx;
let starts = find_primitive(tcx, base)
.chain(find_crate(tcx, base))
.map(|id| Res::Def(tcx.def_kind(id), id));
for first in starts {
let last = path
.iter()
.copied()
.enumerate()
// for each segment, find the child item
.try_fold(first, |res, (idx, segment)| {
let matches_ns = |res: Res| {
// If at the last segment in the path, respect the namespace hint
if idx == path.len() - 1 {
match namespace_hint {
Some(ns) => res.matches_ns(ns),
None => true,
}
} else {
res.matches_ns(Namespace::TypeNS)
}
Some(Node::Item(item)) => &item.kind,
_ => return Vec::new(),
};
let def_id = res.def_id();
if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) {
Some(item)
} else if matches!(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, matches_ns))
let res = |ident: Ident, owner_id: OwnerId| {
if ident.name == name {
let def_id = owner_id.to_def_id();
Some(Res::Def(tcx.def_kind(def_id), def_id))
} else {
None
}
});
};
if let Some(last) = last {
return last;
match item_kind {
ItemKind::Mod(r#mod) => r#mod
.item_ids
.iter()
.filter_map(|&item_id| res(hir.item(item_id).ident, item_id.def_id))
.collect(),
ItemKind::Impl(r#impl) => r#impl
.items
.iter()
.filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.def_id))
.collect(),
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
.iter()
.filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.def_id))
.collect(),
_ => Vec::new(),
}
}
Res::Err
}
fn def_path_res_local(cx: &LateContext<'_>, mut path: &[&str]) -> Res {
let map = cx.tcx.hir();
let mut ids = map.root_module().item_ids;
while let Some(&segment) = path.first() {
let mut next_ids = None;
for i in ids {
if let Some(Node::Item(hir::Item {
ident,
kind,
def_id: item_def_id,
..
})) = map.find(i.hir_id())
{
if ident.name.as_str() == segment {
path = &path[1..];
if path.is_empty() {
let def_id = item_def_id.to_def_id();
return Res::Def(cx.tcx.def_kind(def_id), def_id);
}
if let ItemKind::Mod(m) = kind {
next_ids = Some(m.item_ids);
};
break;
}
}
}
if let Some(next_ids) = next_ids {
ids = next_ids;
fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
if let Some(local_id) = def_id.as_local() {
local_item_children_by_name(tcx, local_id, name)
} else {
break;
non_local_item_children_by_name(tcx, def_id, name)
}
}
/// Resolves a def path like `std::vec::Vec`.
///
/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
///
/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
///
/// This function is expensive and should be used sparingly.
pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> {
fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
tcx.crates(())
.iter()
.copied()
.filter(move |&num| tcx.crate_name(num) == name)
.map(CrateNum::as_def_id)
}
Res::Err
let tcx = cx.tcx;
let (base, mut path) = match *path {
[primitive] => {
return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
},
[base, ref path @ ..] => (base, path),
_ => return Vec::new(),
};
let base_sym = Symbol::intern(base);
let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
Some(LOCAL_CRATE.as_def_id())
} else {
None
};
let starts = find_primitive_impls(tcx, base)
.chain(find_crates(tcx, base_sym))
.chain(local_crate)
.map(|id| Res::Def(tcx.def_kind(id), id));
let mut resolutions: Vec<Res> = starts.collect();
while let [segment, rest @ ..] = path {
path = rest;
let segment = Symbol::intern(segment);
resolutions = resolutions
.into_iter()
.filter_map(|res| res.opt_def_id())
.flat_map(|def_id| {
// When the current def_id is e.g. `struct S`, check the impl items in
// `impl S { ... }`
let inherent_impl_children = tcx
.inherent_impls(def_id)
.iter()
.flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
let direct_children = item_children_by_name(tcx, def_id, segment);
inherent_impl_children.chain(direct_children)
})
.collect();
}
resolutions
}
/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id())
}
/// Convenience function to get the `DefId` of a trait by path.
@ -691,10 +702,10 @@ fn def_path_res_local(cx: &LateContext<'_>, mut path: &[&str]) -> Res {
///
/// This function is expensive and should be used sparingly.
pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
match def_path_res(cx, path, Some(Namespace::TypeNS)) {
def_path_res(cx, path).into_iter().find_map(|res| match res {
Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
_ => None,
}
})
}
/// Gets the `hir::TraitRef` of the trait the given method is implemented for.

View file

@ -4,7 +4,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy
LL | let _x = String::from("hello");
| ^^
|
= note: strings are bad
= note: strings are bad (from clippy.toml)
= note: `-D clippy::await-holding-invalid-type` implied by `-D warnings`
error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
@ -19,7 +19,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy
LL | let _x = String::from("hi!");
| ^^
|
= note: strings are bad
= note: strings are bad (from clippy.toml)
error: aborting due to 3 previous errors

View file

@ -8,4 +8,10 @@ disallowed-methods = [
{ path = "regex::Regex::is_match", reason = "no matching allowed" },
# can use an inline table but omit reason
{ path = "regex::Regex::new" },
# local paths
"conf_disallowed_methods::local_fn",
"conf_disallowed_methods::local_mod::f",
"conf_disallowed_methods::Struct::method",
"conf_disallowed_methods::Trait::provided_method",
"conf_disallowed_methods::Trait::implemented_method",
]

View file

@ -1,3 +1,5 @@
// compile-flags: --crate-name conf_disallowed_methods
#![warn(clippy::disallowed_methods)]
extern crate futures;
@ -6,6 +8,27 @@ extern crate regex;
use futures::stream::{empty, select_all};
use regex::Regex;
fn local_fn() {}
struct Struct;
impl Struct {
fn method(&self) {}
}
trait Trait {
fn provided_method(&self) {}
fn implemented_method(&self);
}
impl Trait for Struct {
fn implemented_method(&self) {}
}
mod local_mod {
pub fn f() {}
}
fn main() {
let re = Regex::new(r"ab.*c").unwrap();
re.is_match("abc");
@ -26,4 +49,11 @@ fn main() {
// resolve ambiguity between `futures::stream::select_all` the module and the function
let same_name_as_module = select_all(vec![empty::<()>()]);
local_fn();
local_mod::f();
let s = Struct;
s.method();
s.provided_method();
s.implemented_method();
}

View file

@ -1,5 +1,5 @@
error: use of a disallowed method `regex::Regex::new`
--> $DIR/conf_disallowed_methods.rs:10:14
--> $DIR/conf_disallowed_methods.rs:33:14
|
LL | let re = Regex::new(r"ab.*c").unwrap();
| ^^^^^^^^^^^^^^^^^^^^
@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap();
= note: `-D clippy::disallowed-methods` implied by `-D warnings`
error: use of a disallowed method `regex::Regex::is_match`
--> $DIR/conf_disallowed_methods.rs:11:5
--> $DIR/conf_disallowed_methods.rs:34:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
@ -15,46 +15,76 @@ LL | re.is_match("abc");
= note: no matching allowed (from clippy.toml)
error: use of a disallowed method `std::iter::Iterator::sum`
--> $DIR/conf_disallowed_methods.rs:14:5
--> $DIR/conf_disallowed_methods.rs:37:5
|
LL | a.iter().sum::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `slice::sort_unstable`
--> $DIR/conf_disallowed_methods.rs:16:5
--> $DIR/conf_disallowed_methods.rs:39:5
|
LL | a.sort_unstable();
| ^^^^^^^^^^^^^^^^^
error: use of a disallowed method `f32::clamp`
--> $DIR/conf_disallowed_methods.rs:18:13
--> $DIR/conf_disallowed_methods.rs:41:13
|
LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `regex::Regex::new`
--> $DIR/conf_disallowed_methods.rs:21:61
--> $DIR/conf_disallowed_methods.rs:44:61
|
LL | let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new;
| ^^^^^^^^^^
error: use of a disallowed method `f32::clamp`
--> $DIR/conf_disallowed_methods.rs:24:28
--> $DIR/conf_disallowed_methods.rs:47:28
|
LL | let in_call = Box::new(f32::clamp);
| ^^^^^^^^^^
error: use of a disallowed method `regex::Regex::new`
--> $DIR/conf_disallowed_methods.rs:25:53
--> $DIR/conf_disallowed_methods.rs:48:53
|
LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new);
| ^^^^^^^^^^
error: use of a disallowed method `futures::stream::select_all`
--> $DIR/conf_disallowed_methods.rs:28:31
--> $DIR/conf_disallowed_methods.rs:51:31
|
LL | let same_name_as_module = select_all(vec![empty::<()>()]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 9 previous errors
error: use of a disallowed method `conf_disallowed_methods::local_fn`
--> $DIR/conf_disallowed_methods.rs:53:5
|
LL | local_fn();
| ^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::local_mod::f`
--> $DIR/conf_disallowed_methods.rs:54:5
|
LL | local_mod::f();
| ^^^^^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::Struct::method`
--> $DIR/conf_disallowed_methods.rs:56:5
|
LL | s.method();
| ^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method`
--> $DIR/conf_disallowed_methods.rs:57:5
|
LL | s.provided_method();
| ^^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method`
--> $DIR/conf_disallowed_methods.rs:58:5
|
LL | s.implemented_method();
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 14 previous errors