rustc_lint/
noop_method_call.rs1use rustc_hir::def::DefKind;
2use rustc_hir::{Expr, ExprKind};
3use rustc_middle::ty;
4use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind};
5use rustc_session::{declare_lint, declare_lint_pass};
6use rustc_span::sym;
7
8use crate::context::LintContext;
9use crate::lints::{
10 NoopMethodCallDiag, SuspiciousDoubleRefCloneDiag, SuspiciousDoubleRefDerefDiag,
11};
12use crate::{LateContext, LateLintPass};
13
14#[doc =
r" The `noop_method_call` lint detects specific calls to noop methods"]
#[doc = r" such as a calling `<&T as Clone>::clone` where `T: !Clone`."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" # #![allow(unused)]"]
#[doc = r" struct Foo;"]
#[doc = r" let foo = &Foo;"]
#[doc = r" let clone: &Foo = foo.clone();"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Some method calls are noops meaning that they do nothing. Usually such methods"]
#[doc =
r" are the result of blanket implementations that happen to create some method invocations"]
#[doc =
r" that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but"]
#[doc =
r" calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything"]
#[doc =
r" as references are copy. This lint detects these calls and warns the user about them."]
pub static NOOP_METHOD_CALL: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "NOOP_METHOD_CALL",
default_level: ::rustc_lint_defs::Warn,
desc: "detects the use of well-known noop methods",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
15 pub NOOP_METHOD_CALL,
37 Warn,
38 "detects the use of well-known noop methods"
39}
40
41#[doc =
r" The `suspicious_double_ref_op` lint checks for usage of `.clone()`/`.borrow()`/`.deref()`"]
#[doc =
r" on an `&&T` when `T: !Deref/Borrow/Clone`, which means the call will return the inner `&T`,"]
#[doc =
r" instead of performing the operation on the underlying `T` and can be confusing."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" # #![allow(unused)]"]
#[doc = r" struct Foo;"]
#[doc = r" let foo = &&Foo;"]
#[doc = r" let clone: &Foo = foo.clone();"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Since `Foo` doesn't implement `Clone`, running `.clone()` only dereferences the double"]
#[doc =
r" reference, instead of cloning the inner type which should be what was intended."]
pub static SUSPICIOUS_DOUBLE_REF_OP: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "SUSPICIOUS_DOUBLE_REF_OP",
default_level: ::rustc_lint_defs::Warn,
desc: "suspicious call of trait method on `&&T`",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
42 pub SUSPICIOUS_DOUBLE_REF_OP,
62 Warn,
63 "suspicious call of trait method on `&&T`"
64}
65
66pub struct NoopMethodCall;
#[automatically_derived]
impl ::core::marker::Copy for NoopMethodCall { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for NoopMethodCall { }
#[automatically_derived]
impl ::core::clone::Clone for NoopMethodCall {
#[inline]
fn clone(&self) -> NoopMethodCall { *self }
}
impl ::rustc_lint_defs::LintPass for NoopMethodCall {
fn name(&self) -> &'static str { "NoopMethodCall" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([NOOP_METHOD_CALL,
SUSPICIOUS_DOUBLE_REF_OP]))
}
}
impl NoopMethodCall {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([NOOP_METHOD_CALL,
SUSPICIOUS_DOUBLE_REF_OP]))
}
}declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL, SUSPICIOUS_DOUBLE_REF_OP]);
67
68impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
69 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
70 let ExprKind::MethodCall(call, receiver, _, call_span) = &expr.kind else {
72 return;
73 };
74
75 if call_span.from_expansion() {
76 return;
77 }
78
79 let Some((DefKind::AssocFn, did)) = cx.typeck_results().type_dependent_def(expr.hir_id)
83 else {
84 return;
85 };
86
87 let Some(trait_id) = cx.tcx.trait_of_assoc(did) else { return };
88
89 let Some(trait_) = cx.tcx.get_diagnostic_name(trait_id) else { return };
90
91 if !#[allow(non_exhaustive_omitted_patterns)] match trait_ {
sym::Borrow | sym::Clone | sym::Deref => true,
_ => false,
}matches!(trait_, sym::Borrow | sym::Clone | sym::Deref) {
92 return;
93 };
94
95 let args = cx
96 .tcx
97 .normalize_erasing_regions(cx.typing_env(), cx.typeck_results().node_args(expr.hir_id));
98 let Ok(Some(i)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), did, args) else {
100 return;
101 };
102 let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
104 if !#[allow(non_exhaustive_omitted_patterns)] match name {
sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
=> true,
_ => false,
}matches!(
105 name,
106 sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
107 ) {
108 return;
109 }
110
111 let receiver_ty = cx.typeck_results().expr_ty(receiver);
112 let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
113 let arg_adjustments = cx.typeck_results().expr_adjustments(receiver);
114
115 if arg_adjustments
118 .iter()
119 .any(|adj| #[allow(non_exhaustive_omitted_patterns)] match adj.kind {
Adjust::Deref(DerefAdjustKind::Overloaded(_)) => true,
_ => false,
}matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_))))
120 {
121 return;
122 }
123
124 let expr_span = expr.span;
125 let span = expr_span.with_lo(receiver.span.hi());
126
127 let orig_ty = expr_ty.peel_refs();
128
129 if receiver_ty == expr_ty {
130 let suggest_derive = match orig_ty.kind() {
131 ty::Adt(def, _) => Some(cx.tcx.def_span(def.did()).shrink_to_lo()),
132 _ => None,
133 };
134 cx.emit_span_lint(
135 NOOP_METHOD_CALL,
136 span,
137 NoopMethodCallDiag {
138 method: call.ident,
139 orig_ty,
140 trait_,
141 label: span,
142 suggest_derive,
143 },
144 );
145 } else {
146 match name {
147 sym::noop_method_borrow => return,
150 sym::noop_method_clone => cx.emit_span_lint(
151 SUSPICIOUS_DOUBLE_REF_OP,
152 span,
153 SuspiciousDoubleRefCloneDiag { ty: expr_ty },
154 ),
155 sym::noop_method_deref => cx.emit_span_lint(
156 SUSPICIOUS_DOUBLE_REF_OP,
157 span,
158 SuspiciousDoubleRefDerefDiag { ty: expr_ty },
159 ),
160 _ => return,
161 }
162 }
163 }
164}