diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index 6969ac949d84..b15fe65352ab 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -1,12 +1,11 @@ -use crate::utils::paths::STRING; -use crate::utils::{match_def_path, span_lint_and_help}; +use crate::utils::span_lint_and_help; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Spanned, Span}; +use rustc_span::{source_map::Spanned, symbol::sym, Span}; declare_clippy_lint! { /// **What it does:** @@ -59,7 +58,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & return Some(span); }, ty::Adt(&ty::AdtDef { did, .. }, _) => { - if match_def_path(ctx, did, &STRING) { + if ctx.tcx.is_diagnostic_item(sym::string_type, did) { return Some(span); } }, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index e8510bde9adc..382a045a3f7c 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, match_path, span_lint_and_help, + get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; @@ -293,7 +293,7 @@ fn check_ord_partial_ord<'tcx>( /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { - if match_path(&trait_ref.path, &paths::CLONE_TRAIT) { + if cx.tcx.lang_items().clone_trait() == trait_ref.trait_def_id() { if !is_copy(cx, ty) { return; } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 50e6383263dd..5fed58b82109 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,5 +1,4 @@ -use crate::utils::paths; -use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method}; +use crate::utils::{in_macro, span_lint, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::{ walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, @@ -8,8 +7,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, - TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, + TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; @@ -300,7 +299,7 @@ fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::>().len() } -const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; +const CLOSURE_TRAIT_BOUNDS: [LangItem; 3] = [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]; /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { @@ -361,7 +360,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { let trait_ref = &poly_tref.trait_ref; if CLOSURE_TRAIT_BOUNDS .iter() - .any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + .any(|&item| trait_ref.trait_def_id() == self.cx.tcx.lang_items().items()[item as usize]) { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_trait_ref(trait_ref); diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index bd0be8802890..eda3f6f40eaa 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -79,7 +79,9 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { }, hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! { if ident_eq(name, obj) && method.ident.name == sym::clone; - if match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT); + if let Some(closure_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); + if let Some(trait_id) = cx.tcx.trait_of_item(closure_id); + if Some(trait_id) == cx.tcx.lang_items().clone_trait(); // no autoderefs if !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 06adbb523d70..d74ba10ac506 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -165,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) = is_call_with_ref_arg(cx, mir, &pred_terminator.kind); if res == cloned; - if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD); + if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id); if match_type(cx, pred_arg_ty, &paths::PATH_BUF) || match_type(cx, pred_arg_ty, &paths::OS_STRING); then { diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f71b1651bfe9..d1d9ef7d9021 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -11,8 +11,8 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, - ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, - StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, + QPath, Stmt, StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -23,7 +23,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_typeck::hir_ty_to_ty; @@ -32,9 +32,9 @@ use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant, - is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, - multispan_sugg, numeric_literal::NumericLiteral, reindent_multiline, sext, snippet, snippet_opt, + clip, comparisons, differing_macro_contexts, get_qpath_generic_tys, higher, in_constant, indent_of, int_bits, + is_hir_ty_cfg_dependant, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, + method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; @@ -287,37 +287,55 @@ impl<'tcx> LateLintPass<'tcx> for Types { } } -/// Checks if `qpath` has last segment with type parameter matching `path` -fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str]) -> Option { - let last = last_path_segment(qpath); - if_chain! { - if let Some(ref params) = last.args; - if !params.parenthesized; - if let Some(ty) = params.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); - if let TyKind::Path(ref qpath) = ty.kind; - if let Some(did) = cx.qpath_res(qpath, ty.hir_id).opt_def_id(); - if match_def_path(cx, did, path); - then { - return Some(ty.span); - } +/// Checks if the first type parameter is a lang item. +fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> { + let ty = get_qpath_generic_tys(qpath).next()?; + + if let TyKind::Path(qpath) = &ty.kind { + cx.qpath_res(qpath, ty.hir_id) + .opt_def_id() + .and_then(|id| (cx.tcx.lang_items().items()[item as usize] == Some(id)).then(|| ty)) + } else { + None } - None } -fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { - if match_type_parameter(cx, qpath, &paths::STRING).is_some() { - return Some("str"); +/// Checks if the first type parameter is a diagnostic item. +fn is_ty_param_diagnostic_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: Symbol) -> Option<&'tcx hir::Ty<'tcx>> { + let ty = get_qpath_generic_tys(qpath).next()?; + + if let TyKind::Path(qpath) = &ty.kind { + cx.qpath_res(qpath, ty.hir_id) + .opt_def_id() + .and_then(|id| cx.tcx.is_diagnostic_item(item, id).then(|| ty)) + } else { + None } - if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() { - return Some("std::ffi::OsStr"); +} + +/// Checks if the first type parameter is a given item. +fn is_ty_param_path(cx: &LateContext<'_>, qpath: &QPath<'tcx>, path: &[&str]) -> Option<&'tcx hir::Ty<'tcx>> { + let ty = get_qpath_generic_tys(qpath).next()?; + + if let TyKind::Path(qpath) = &ty.kind { + cx.qpath_res(qpath, ty.hir_id) + .opt_def_id() + .and_then(|id| match_def_path(cx, id, path).then(|| ty)) + } else { + None } - if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() { - return Some("std::path::Path"); +} + +fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { + if is_ty_param_diagnostic_item(cx, qpath, sym::string_type).is_some() { + Some("str") + } else if is_ty_param_path(cx, qpath, &paths::OS_STRING).is_some() { + Some("std::ffi::OsStr") + } else if is_ty_param_path(cx, qpath, &paths::PATH_BUF).is_some() { + Some("std::path::Path") + } else { + None } - None } fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { @@ -381,7 +399,7 @@ impl Types { ); return; // don't recurse into the type } - if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + if is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some() { span_lint_and_help( cx, BOX_VEC, @@ -393,7 +411,7 @@ impl Types { return; // don't recurse into the type } } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { - if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) { + if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -401,21 +419,18 @@ impl Types { hir_ty.span, "usage of `Rc>`", "try", - snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + snippet_with_applicability(cx, ty.span, "..", &mut applicability).to_string(), applicability, ); return; // don't recurse into the type } - if match_type_parameter(cx, qpath, &paths::BOX).is_some() { - let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { - GenericArg::Type(ty) => match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return, - }, + if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) { + let qpath = match &ty.kind { + TyKind::Path(qpath) => qpath, _ => return, }; - let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { - GenericArg::Type(ty) => ty.span, + let inner_span = match get_qpath_generic_tys(qpath).next() { + Some(ty) => ty.span, _ => return, }; let mut applicability = Applicability::MachineApplicable; @@ -445,16 +460,13 @@ impl Types { ); return; // don't recurse into the type } - if match_type_parameter(cx, qpath, &paths::VEC).is_some() { - let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { - GenericArg::Type(ty) => match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return, - }, + if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::vec_type) { + let qpath = match &ty.kind { + TyKind::Path(qpath) => qpath, _ => return, }; - let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { - GenericArg::Type(ty) => ty.span, + let inner_span = match get_qpath_generic_tys(qpath).next() { + Some(ty) => ty.span, _ => return, }; let mut applicability = Applicability::MachineApplicable; @@ -498,16 +510,13 @@ impl Types { ); return; // don't recurse into the type } - if match_type_parameter(cx, qpath, &paths::VEC).is_some() { - let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { - GenericArg::Type(ty) => match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return, - }, + if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::vec_type) { + let qpath = match &ty.kind { + TyKind::Path(qpath) => qpath, _ => return, }; - let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { - GenericArg::Type(ty) => ty.span, + let inner_span = match get_qpath_generic_tys(qpath).next() { + Some(ty) => ty.span, _ => return, }; let mut applicability = Applicability::MachineApplicable; @@ -563,7 +572,7 @@ impl Types { } } } else if cx.tcx.is_diagnostic_item(sym::option_type, def_id) { - if match_type_parameter(cx, qpath, &paths::OPTION).is_some() { + if is_ty_param_diagnostic_item(cx, qpath, sym::option_type).is_some() { span_lint( cx, OPTION_OPTION, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2380ea4c7bfa..08e260b7d94a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -62,8 +62,9 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ - def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind, - MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, + def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, GenericArgs, HirId, ImplItem, ImplItemKind, Item, + ItemKind, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, + Unsafety, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -271,6 +272,27 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { } } +pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> { + match path { + QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args), + QPath::TypeRelative(_, s) => s.args, + QPath::LangItem(..) => None, + } +} + +pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator> { + get_qpath_generics(path) + .map_or([].as_ref(), |a| a.args) + .iter() + .filter_map(|a| { + if let hir::GenericArg::Type(ty) = a { + Some(ty) + } else { + None + } + }) +} + pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { match *path { QPath::Resolved(_, ref path) => path.segments.get(0), diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index e61786796475..14c38459ddea 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -12,11 +12,9 @@ pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"]; pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; -pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; -pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; @@ -43,9 +41,6 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; -pub const FN: [&str; 3] = ["core", "ops", "Fn"]; -pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; -pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; @@ -114,7 +109,6 @@ pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_ pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; -pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; @@ -143,7 +137,6 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; -pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"];