Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions book/src/development/adding_lints.md
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,7 @@ for some users. Adding a configuration is done in the following steps:
Here are some pointers to things you are likely going to need for every lint:

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

[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
[let-chains]: https://github.com/rust-lang/rust/pull/94927
Expand Down
38 changes: 20 additions & 18 deletions book/src/development/common_tools_writing_lints.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ to check for. All of these methods only check for the base type, generic
arguments have to be checked separately.

```rust
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::paths;
use clippy_utils::res::MaybeDef;
use rustc_span::symbol::sym;
use rustc_hir::LangItem;

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

// 1. Using diagnostic items
// The last argument is the diagnostic item to check for
if is_type_diagnostic_item(cx, ty, sym::Option) {
if ty.is_diag_item(cx, sym::Option) {
// The type is an `Option`
}

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

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

```rust
use clippy_utils::ty::implements_trait;
use clippy_utils::is_trait_method;
use rustc_span::symbol::sym;

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

// 2. Using lang items with the expression type
// 1. Get the `DefId` of the trait.
// via lang items
let trait_id = cx.tcx.lang_items().drop_trait();
// via diagnostic items
let trait_id = cx.tcx.get_diagnostic_item(sym::Eq);

// 2. Check for the trait implementation via the `implements_trait` util.
let ty = cx.typeck_results().expr_ty(expr);
if cx.tcx.lang_items()
// we are looking for the `DefId` of `Drop` trait in lang items
.drop_trait()
// then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
.is_some_and(|id| implements_trait(cx, ty, id, &[])) {
// `expr` implements `Drop` trait
}
if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[])) {
// `ty` implements the trait.
}

// 3. If the trait requires additional generic arguments
let trait_id = cx.tcx.lang_items().eq_trait();
if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[ty])) {
// `ty` implements `PartialEq<Self>`
}
}
}
```
Expand Down Expand Up @@ -173,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
// We can also check it has a parameter `self`
&& signature.decl.implicit_self.has_implicit_self()
// We can go further and even check if its return type is `String`
&& is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String)
&& return_ty(cx, impl_item.hir_id).is_lang_item(cx, LangItem::String)
{
// ...
}
Expand Down
8 changes: 4 additions & 4 deletions book/src/development/method_checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ the [`ExprKind`] that we can access from `expr.kind`:
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::sym;
use clippy_utils::is_trait_method;
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};

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

```rust
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::res::MaybeDef;
use clippy_utils::return_ty;
use rustc_hir::{ImplItem, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass};
Expand All @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
// We can also check it has a parameter `self`
&& signature.decl.implicit_self.has_implicit_self()
// We can go even further and even check if its return type is `String`
&& is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::String)
&& return_ty(cx, impl_item.hir_id).is_diag_item(cx, sym::String)
{
println!("`our_fancy_method` is implemented!");
}
Expand Down
5 changes: 3 additions & 2 deletions clippy_lints/src/arc_with_non_send_sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::res::MaybeDef;
use clippy_utils::ty::implements_trait;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
Expand Down Expand Up @@ -46,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
&& let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind
&& func_name.ident.name == sym::new
&& !expr.span.from_expansion()
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc)
&& cx.typeck_results().node_type(func_ty.hir_id).is_diag_item(cx, sym::Arc)
&& let arg_ty = cx.typeck_results().expr_ty(arg)
// make sure that the type is not and does not contain any type parameters
&& arg_ty.walk().all(|arg| {
Expand Down
9 changes: 5 additions & 4 deletions clippy_lints/src/assertions_on_result_states.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node};
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
use clippy_utils::sym;
use clippy_utils::ty::{has_debug_impl, is_copy};
use clippy_utils::usage::local_used_after_expr;
use clippy_utils::{path_res, sym};
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind, Node};
Expand Down Expand Up @@ -55,13 +56,13 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
&& let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind
&& let result_type_with_refs = cx.typeck_results().expr_ty(recv)
&& let result_type = result_type_with_refs.peel_refs()
&& is_type_diagnostic_item(cx, result_type, sym::Result)
&& result_type.is_diag_item(cx, sym::Result)
&& let ty::Adt(_, args) = result_type.kind()
{
if !is_copy(cx, result_type) {
if result_type_with_refs != result_type {
return;
} else if let Res::Local(binding_id) = path_res(cx, recv)
} else if let Res::Local(binding_id) = *recv.basic_res()
&& local_used_after_expr(cx, binding_id, recv)
{
return;
Expand Down
21 changes: 11 additions & 10 deletions clippy_lints/src/assigning_clones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::sugg::Sugg;
use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym};
use clippy_utils::{is_in_test, last_path_segment, local_is_initialized, sym};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
Expand Down Expand Up @@ -68,36 +69,36 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if let ExprKind::Assign(lhs, rhs, _) = e.kind
&& let typeck = cx.typeck_results()
&& let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind {
&& let (call_kind, fn_name, fn_def, fn_arg, fn_gen_args) = match rhs.kind {
ExprKind::Call(f, [arg])
if let ExprKind::Path(fn_path) = &f.kind
&& let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() =>
&& let Some(def) = typeck.qpath_res(fn_path, f.hir_id).opt_def(cx) =>
{
(CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id))
(CallKind::Ufcs, last_path_segment(fn_path).ident.name, def, arg, typeck.node_args(f.hir_id))
},
ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => {
(CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id))
ExprKind::MethodCall(name, recv, [], _) if let Some(def) = typeck.type_dependent_def(rhs.hir_id) => {
(CallKind::Method, name.ident.name, def, recv, typeck.node_args(rhs.hir_id))
},
_ => return,
}
&& let ctxt = e.span.ctxt()
// Don't lint in macros.
&& ctxt.is_root()
&& let which_trait = match fn_name {
sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone,
sym::clone if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Clone) => CloneTrait::Clone,
sym::to_owned
if is_diag_trait_item(cx, fn_id, sym::ToOwned)
if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::ToOwned)
&& self.msrv.meets(cx, msrvs::CLONE_INTO) =>
{
CloneTrait::ToOwned
},
_ => return,
}
&& let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_id, fn_gen_args)
&& let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_def.1, fn_gen_args)
// TODO: This check currently bails if the local variable has no initializer.
// That is overly conservative - the lint should fire even if there was no initializer,
// but the variable has been initialized before `lhs` was evaluated.
&& path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs))
&& lhs.res_local_id().is_none_or(|lhs| local_is_initialized(cx, lhs))
&& let Some(resolved_impl) = cx.tcx.impl_of_assoc(resolved_fn.def_id())
// Derived forms don't implement `clone_from`/`clone_into`.
// See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305
Expand Down
7 changes: 3 additions & 4 deletions clippy_lints/src/booleans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::higher::has_let_expr;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::ty::implements_trait;
use clippy_utils::{eq_expr_value, sym};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
Expand Down Expand Up @@ -431,9 +432,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
},
ExprKind::MethodCall(path, receiver, args, _) => {
let type_of_receiver = cx.typeck_results().expr_ty(receiver);
if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
&& !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
{
if !type_of_receiver.is_diag_item(cx, sym::Option) && !type_of_receiver.is_diag_item(cx, sym::Result) {
return None;
}
METHODS_WITH_NEGATION
Expand Down
7 changes: 4 additions & 3 deletions clippy_lints/src/box_default.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_default_equivalent;
use clippy_utils::macros::macro_backtrace;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::ty::expr_sig;
use clippy_utils::{is_default_equivalent, path_def_id};
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind};
use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LangItem, LetStmt, Node, QPath, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass;
use rustc_span::{Span, sym};
Expand Down Expand Up @@ -44,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault {
// And that method is `new`
&& seg.ident.name == sym::new
// And the call is that of a `Box` method
&& path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box())
&& ty.basic_res().is_lang_item(cx, LangItem::OwnedBox)
// And the single argument to the call is another function call
// This is the `T::default()` (or default equivalent) of `Box::new(T::default())`
&& let ExprKind::Call(arg_path, _) = arg.kind
Expand Down
5 changes: 3 additions & 2 deletions clippy_lints/src/casts/manual_dangling_ptr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym};
use clippy_utils::{expr_or_init, std_or_core, sym};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
Expand Down Expand Up @@ -53,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) ->

fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
&& is_path_diagnostic_item(cx, fun, sym::mem_align_of)
&& fun.basic_res().is_diag_item(cx, sym::mem_align_of)
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
&& let [GenericArg::Type(generic_ty)] = args.args
{
Expand Down
7 changes: 4 additions & 3 deletions clippy_lints/src/casts/unnecessary_cast.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::res::MaybeResPath;
use clippy_utils::source::{SpanRangeExt, snippet_opt};
use clippy_utils::visitors::{Visitable, for_each_expr_without_closures};
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias};
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
Expand Down Expand Up @@ -167,11 +168,11 @@ pub(super) fn check<'tcx>(
sym::assert_ne_macro,
sym::debug_assert_ne_macro,
];
matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if
matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if
cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym)))
}

if let Some(id) = path_to_local(cast_expr)
if let Some(id) = cast_expr.res_local_id()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be opt_res_local_id() for consistency with opt_diag_name() and others?

Copy link
Contributor Author

@Jarcho Jarcho Sep 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally had it as opt_local_id, but changed it to res_local_id to match all the other names to get a path's resolution. I'm not strongly against opt_res_local_id, but it's getting a little on the wordy side. I'm not really attached to many of the names here.

File you want to look at for the new functions is clippy_utils/src/res.rs. Just noticed this is 279 files.

&& !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span)
{
// Binding context is different than the identifiers context.
Expand Down
5 changes: 3 additions & 2 deletions clippy_lints/src/cloned_ref_to_slice_refs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
use clippy_utils::sugg::Sugg;
use clippy_utils::visitors::is_const_evaluatable;
use clippy_utils::{is_in_const_context, is_mutable, is_trait_method};
use clippy_utils::{is_in_const_context, is_mutable};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
Expand Down Expand Up @@ -73,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> {

// check for clones
&& let ExprKind::MethodCall(_, val, _, _) = item.kind
&& is_trait_method(cx, item, sym::Clone)
&& cx.ty_based_def(item).opt_parent(cx).is_diag_item(cx, sym::Clone)

// check for immutability or purity
&& (!is_mutable(cx, val) || is_const_evaluatable(cx, val))
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/cognitive_complexity.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::res::MaybeDef;
use clippy_utils::source::{IntoSpan, SpanRangeExt};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{LimitStack, get_async_fn_body, sym};
use core::ops::ControlFlow;
Expand Down Expand Up @@ -93,7 +93,7 @@ impl CognitiveComplexity {
});

let ret_ty = cx.typeck_results().node_type(expr.hir_id);
let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) {
let ret_adjust = if ret_ty.is_diag_item(cx, sym::Result) {
returns
} else {
#[expect(clippy::integer_division)]
Expand Down
Loading