Skip to content

Commit ebbbbeb

Browse files
Replace all item identification utils (#15682)
This introduces a new way of identifying items/paths using extension traits with a composable set of functions rather than unique functions for various combinations of starting points and target items. Altogether this is a set of five traits: * `MaybeTypeckRes`: Allows both `LateContext` and `TypeckResults` to be used for type-dependent lookup. The implementation here will avoid ICEs by returning `None` when debug assertions are disabled. With assertions this will assert that we don't silently lookup anything from a different body than the current one and that a definition actually exists. * `HasHirId`: Simply a convenience to allow not typing `.hir_id` at call sites. * `MaybeQPath`: This is the old `MaybePath`. Extension functions for type-dependent path lookups exist here. A lot of these functions aren't used in the current PR, but what they accomplish is done in various places I haven't cleaned up yet. * `MaybeResPath`: Like `MaybeQPath`, but only does non-type-dependent lookup (`QPath::Resolved`). * `MaybeDef`: Extension functions for identifying the current definition and accessing properties. Implemented for several types for convenience. `MaybeDef` is implemented for `Option` to allow chaining methods together. e.g. `cx.ty_based_def(e).opt_parent(cx).opt_impl_ty(cx).is_diag_item(..)` would chaining `and_then` or `if let` on every step. `MaybeQPath` and `MaybeResPath` are also implemented for `Option` for the same reason. `ty_based_def` is just a shorter name for `type_dependent_def`. I'm not really attached to it, but it's nice that it's a little shorter. changelog: none
2 parents 42f2ba1 + 6bfb524 commit ebbbbeb

File tree

282 files changed

+2051
-1324
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

282 files changed

+2051
-1324
lines changed

book/src/development/adding_lints.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -759,8 +759,7 @@ for some users. Adding a configuration is done in the following steps:
759759
Here are some pointers to things you are likely going to need for every lint:
760760

761761
* [Clippy utils][utils] - Various helper functions. Maybe the function you need
762-
is already in here ([`is_type_diagnostic_item`], [`implements_trait`],
763-
[`snippet`], etc)
762+
is already in here ([`implements_trait`], [`snippet`], etc)
764763
* [Clippy diagnostics][diagnostics]
765764
* [Let chains][let-chains]
766765
* [`from_expansion`][from_expansion] and
@@ -790,7 +789,6 @@ get away with copying things from existing similar lints. If you are stuck,
790789
don't hesitate to ask on [Zulip] or in the issue/PR.
791790

792791
[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
793-
[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
794792
[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
795793
[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
796794
[let-chains]: https://github.com/rust-lang/rust/pull/94927

book/src/development/common_tools_writing_lints.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ to check for. All of these methods only check for the base type, generic
8585
arguments have to be checked separately.
8686

8787
```rust
88-
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
8988
use clippy_utils::paths;
89+
use clippy_utils::res::MaybeDef;
9090
use rustc_span::symbol::sym;
9191
use rustc_hir::LangItem;
9292

@@ -97,12 +97,12 @@ impl LateLintPass<'_> for MyStructLint {
9797

9898
// 1. Using diagnostic items
9999
// The last argument is the diagnostic item to check for
100-
if is_type_diagnostic_item(cx, ty, sym::Option) {
100+
if ty.is_diag_item(cx, sym::Option) {
101101
// The type is an `Option`
102102
}
103103

104104
// 2. Using lang items
105-
if is_type_lang_item(cx, ty, LangItem::RangeFull) {
105+
if ty.is_lang_item(cx, LangItem::RangeFull) {
106106
// The type is a full range like `.drain(..)`
107107
}
108108

@@ -124,26 +124,28 @@ diagnostic item, lang item or neither.
124124

125125
```rust
126126
use clippy_utils::ty::implements_trait;
127-
use clippy_utils::is_trait_method;
128127
use rustc_span::symbol::sym;
129128

130129
impl LateLintPass<'_> for MyStructLint {
131130
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
132-
// 1. Using diagnostic items with the expression
133-
// we use `is_trait_method` function from Clippy's utils
134-
if is_trait_method(cx, expr, sym::Iterator) {
135-
// method call in `expr` belongs to `Iterator` trait
136-
}
137131

138-
// 2. Using lang items with the expression type
132+
// 1. Get the `DefId` of the trait.
133+
// via lang items
134+
let trait_id = cx.tcx.lang_items().drop_trait();
135+
// via diagnostic items
136+
let trait_id = cx.tcx.get_diagnostic_item(sym::Eq);
137+
138+
// 2. Check for the trait implementation via the `implements_trait` util.
139139
let ty = cx.typeck_results().expr_ty(expr);
140-
if cx.tcx.lang_items()
141-
// we are looking for the `DefId` of `Drop` trait in lang items
142-
.drop_trait()
143-
// then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
144-
.is_some_and(|id| implements_trait(cx, ty, id, &[])) {
145-
// `expr` implements `Drop` trait
146-
}
140+
if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[])) {
141+
// `ty` implements the trait.
142+
}
143+
144+
// 3. If the trait requires additional generic arguments
145+
let trait_id = cx.tcx.lang_items().eq_trait();
146+
if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[ty])) {
147+
// `ty` implements `PartialEq<Self>`
148+
}
147149
}
148150
}
149151
```
@@ -173,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
173175
// We can also check it has a parameter `self`
174176
&& signature.decl.implicit_self.has_implicit_self()
175177
// We can go further and even check if its return type is `String`
176-
&& is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String)
178+
&& return_ty(cx, impl_item.hir_id).is_lang_item(cx, LangItem::String)
177179
{
178180
// ...
179181
}

book/src/development/method_checking.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ the [`ExprKind`] that we can access from `expr.kind`:
1616
use rustc_hir as hir;
1717
use rustc_lint::{LateContext, LateLintPass};
1818
use rustc_span::sym;
19-
use clippy_utils::is_trait_method;
19+
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
2020

2121
impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint {
2222
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
@@ -28,7 +28,7 @@ impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint {
2828
// (It's necessary if we want to check that method is specifically belonging to a specific trait,
2929
// for example, a `map` method could belong to user-defined trait instead of to `Iterator`)
3030
// See the next section for more information.
31-
&& is_trait_method(cx, self_arg, sym::OurFancyTrait)
31+
&& cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait)
3232
{
3333
println!("`expr` is a method call for `our_fancy_method`");
3434
}
@@ -56,7 +56,7 @@ Let us take a look at how we might check for the implementation of
5656
`our_fancy_method` on a type:
5757

5858
```rust
59-
use clippy_utils::ty::is_type_diagnostic_item;
59+
use clippy_utils::res::MaybeDef;
6060
use clippy_utils::return_ty;
6161
use rustc_hir::{ImplItem, ImplItemKind};
6262
use rustc_lint::{LateContext, LateLintPass};
@@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
7171
// We can also check it has a parameter `self`
7272
&& signature.decl.implicit_self.has_implicit_self()
7373
// We can go even further and even check if its return type is `String`
74-
&& is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::String)
74+
&& return_ty(cx, impl_item.hir_id).is_diag_item(cx, sym::String)
7575
{
7676
println!("`our_fancy_method` is implemented!");
7777
}

clippy_lints/src/arc_with_non_send_sync.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::is_from_proc_macro;
3-
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
3+
use clippy_utils::res::MaybeDef;
4+
use clippy_utils::ty::implements_trait;
45
use rustc_hir::{Expr, ExprKind, QPath};
56
use rustc_lint::{LateContext, LateLintPass};
67
use rustc_middle::ty;
@@ -46,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
4647
&& let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind
4748
&& func_name.ident.name == sym::new
4849
&& !expr.span.from_expansion()
49-
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc)
50+
&& cx.typeck_results().node_type(func_ty.hir_id).is_diag_item(cx, sym::Arc)
5051
&& let arg_ty = cx.typeck_results().expr_ty(arg)
5152
// make sure that the type is not and does not contain any type parameters
5253
&& arg_ty.walk().all(|arg| {

clippy_lints/src/assertions_on_result_states.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node};
3+
use clippy_utils::res::{MaybeDef, MaybeResPath};
34
use clippy_utils::source::snippet_with_context;
4-
use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
5+
use clippy_utils::sym;
6+
use clippy_utils::ty::{has_debug_impl, is_copy};
57
use clippy_utils::usage::local_used_after_expr;
6-
use clippy_utils::{path_res, sym};
78
use rustc_errors::Applicability;
89
use rustc_hir::def::Res;
910
use rustc_hir::{Expr, ExprKind, Node};
@@ -55,13 +56,13 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
5556
&& let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind
5657
&& let result_type_with_refs = cx.typeck_results().expr_ty(recv)
5758
&& let result_type = result_type_with_refs.peel_refs()
58-
&& is_type_diagnostic_item(cx, result_type, sym::Result)
59+
&& result_type.is_diag_item(cx, sym::Result)
5960
&& let ty::Adt(_, args) = result_type.kind()
6061
{
6162
if !is_copy(cx, result_type) {
6263
if result_type_with_refs != result_type {
6364
return;
64-
} else if let Res::Local(binding_id) = path_res(cx, recv)
65+
} else if let Res::Local(binding_id) = *recv.basic_res()
6566
&& local_used_after_expr(cx, binding_id, recv)
6667
{
6768
return;

clippy_lints/src/assigning_clones.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir};
44
use clippy_utils::msrvs::{self, Msrv};
5+
use clippy_utils::res::{MaybeDef, MaybeResPath};
56
use clippy_utils::sugg::Sugg;
6-
use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym};
7+
use clippy_utils::{is_in_test, last_path_segment, local_is_initialized, sym};
78
use rustc_errors::Applicability;
89
use rustc_hir::{self as hir, Expr, ExprKind};
910
use rustc_lint::{LateContext, LateLintPass};
@@ -68,36 +69,36 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
6869
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
6970
if let ExprKind::Assign(lhs, rhs, _) = e.kind
7071
&& let typeck = cx.typeck_results()
71-
&& let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind {
72+
&& let (call_kind, fn_name, fn_def, fn_arg, fn_gen_args) = match rhs.kind {
7273
ExprKind::Call(f, [arg])
7374
if let ExprKind::Path(fn_path) = &f.kind
74-
&& let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() =>
75+
&& let Some(def) = typeck.qpath_res(fn_path, f.hir_id).opt_def(cx) =>
7576
{
76-
(CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id))
77+
(CallKind::Ufcs, last_path_segment(fn_path).ident.name, def, arg, typeck.node_args(f.hir_id))
7778
},
78-
ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => {
79-
(CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id))
79+
ExprKind::MethodCall(name, recv, [], _) if let Some(def) = typeck.type_dependent_def(rhs.hir_id) => {
80+
(CallKind::Method, name.ident.name, def, recv, typeck.node_args(rhs.hir_id))
8081
},
8182
_ => return,
8283
}
8384
&& let ctxt = e.span.ctxt()
8485
// Don't lint in macros.
8586
&& ctxt.is_root()
8687
&& let which_trait = match fn_name {
87-
sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone,
88+
sym::clone if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Clone) => CloneTrait::Clone,
8889
sym::to_owned
89-
if is_diag_trait_item(cx, fn_id, sym::ToOwned)
90+
if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::ToOwned)
9091
&& self.msrv.meets(cx, msrvs::CLONE_INTO) =>
9192
{
9293
CloneTrait::ToOwned
9394
},
9495
_ => return,
9596
}
96-
&& let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_id, fn_gen_args)
97+
&& let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_def.1, fn_gen_args)
9798
// TODO: This check currently bails if the local variable has no initializer.
9899
// That is overly conservative - the lint should fire even if there was no initializer,
99100
// but the variable has been initialized before `lhs` was evaluated.
100-
&& path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs))
101+
&& lhs.res_local_id().is_none_or(|lhs| local_is_initialized(cx, lhs))
101102
&& let Some(resolved_impl) = cx.tcx.impl_of_assoc(resolved_fn.def_id())
102103
// Derived forms don't implement `clone_from`/`clone_into`.
103104
// See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305

clippy_lints/src/booleans.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use clippy_config::Conf;
22
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
33
use clippy_utils::higher::has_let_expr;
44
use clippy_utils::msrvs::{self, Msrv};
5+
use clippy_utils::res::MaybeDef;
56
use clippy_utils::source::SpanRangeExt;
67
use clippy_utils::sugg::Sugg;
7-
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
8+
use clippy_utils::ty::implements_trait;
89
use clippy_utils::{eq_expr_value, sym};
910
use rustc_ast::ast::LitKind;
1011
use rustc_errors::Applicability;
@@ -431,9 +432,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
431432
},
432433
ExprKind::MethodCall(path, receiver, args, _) => {
433434
let type_of_receiver = cx.typeck_results().expr_ty(receiver);
434-
if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
435-
&& !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
436-
{
435+
if !type_of_receiver.is_diag_item(cx, sym::Option) && !type_of_receiver.is_diag_item(cx, sym::Result) {
437436
return None;
438437
}
439438
METHODS_WITH_NEGATION

clippy_lints/src/box_default.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_default_equivalent;
23
use clippy_utils::macros::macro_backtrace;
4+
use clippy_utils::res::{MaybeDef, MaybeResPath};
35
use clippy_utils::ty::expr_sig;
4-
use clippy_utils::{is_default_equivalent, path_def_id};
56
use rustc_errors::Applicability;
67
use rustc_hir::def::Res;
78
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
8-
use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind};
9+
use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LangItem, LetStmt, Node, QPath, Ty, TyKind};
910
use rustc_lint::{LateContext, LateLintPass, LintContext};
1011
use rustc_session::declare_lint_pass;
1112
use rustc_span::{Span, sym};
@@ -44,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault {
4445
// And that method is `new`
4546
&& seg.ident.name == sym::new
4647
// And the call is that of a `Box` method
47-
&& path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box())
48+
&& ty.basic_res().is_lang_item(cx, LangItem::OwnedBox)
4849
// And the single argument to the call is another function call
4950
// This is the `T::default()` (or default equivalent) of `Box::new(T::default())`
5051
&& let ExprKind::Call(arg_path, _) = arg.kind

clippy_lints/src/casts/manual_dangling_ptr.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::res::{MaybeDef, MaybeResPath};
23
use clippy_utils::source::SpanRangeExt;
3-
use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym};
4+
use clippy_utils::{expr_or_init, std_or_core, sym};
45
use rustc_ast::LitKind;
56
use rustc_errors::Applicability;
67
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
@@ -53,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) ->
5354

5455
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
5556
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
56-
&& is_path_diagnostic_item(cx, fun, sym::mem_align_of)
57+
&& fun.basic_res().is_diag_item(cx, sym::mem_align_of)
5758
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
5859
&& let [GenericArg::Type(generic_ty)] = args.args
5960
{

clippy_lints/src/casts/unnecessary_cast.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::numeric_literal::NumericLiteral;
3+
use clippy_utils::res::MaybeResPath;
34
use clippy_utils::source::{SpanRangeExt, snippet_opt};
45
use clippy_utils::visitors::{Visitable, for_each_expr_without_closures};
5-
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
6+
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias};
67
use rustc_ast::{LitFloatType, LitIntType, LitKind};
78
use rustc_errors::Applicability;
89
use rustc_hir::def::{DefKind, Res};
@@ -167,11 +168,11 @@ pub(super) fn check<'tcx>(
167168
sym::assert_ne_macro,
168169
sym::debug_assert_ne_macro,
169170
];
170-
matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if
171+
matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if
171172
cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym)))
172173
}
173174

174-
if let Some(id) = path_to_local(cast_expr)
175+
if let Some(id) = cast_expr.res_local_id()
175176
&& !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span)
176177
{
177178
// Binding context is different than the identifiers context.

0 commit comments

Comments
 (0)