|
1 | 1 | use crate::context::LintContext;
|
2 |
| -use crate::lints::NoopMethodCallDiag; |
| 2 | +use crate::lints::{NoopMethodCallDiag, SuspiciousDoubleRefDiag}; |
3 | 3 | use crate::LateContext;
|
4 | 4 | use crate::LateLintPass;
|
5 | 5 | use rustc_hir::def::DefKind;
|
6 | 6 | use rustc_hir::{Expr, ExprKind};
|
7 | 7 | use rustc_middle::ty;
|
| 8 | +use rustc_middle::ty::adjustment::Adjust; |
8 | 9 | use rustc_span::symbol::sym;
|
9 | 10 |
|
10 | 11 | declare_lint! {
|
@@ -35,14 +36,44 @@ declare_lint! {
|
35 | 36 | "detects the use of well-known noop methods"
|
36 | 37 | }
|
37 | 38 |
|
38 |
| -declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); |
| 39 | +declare_lint! { |
| 40 | + /// The `suspicious_double_ref_op` lint checks for usage of `.clone()`/`.borrow()`/`.deref()` |
| 41 | + /// on an `&&T` when `T: !Deref/Borrow/Clone`, which means the call will return the inner `&T`, |
| 42 | + /// instead of performing the operation on the underlying `T` and can be confusing. |
| 43 | + /// |
| 44 | + /// ### Example |
| 45 | + /// |
| 46 | + /// ```rust |
| 47 | + /// # #![allow(unused)] |
| 48 | + /// struct Foo; |
| 49 | + /// let foo = &&Foo; |
| 50 | + /// let clone: &Foo = foo.clone(); |
| 51 | + /// ``` |
| 52 | + /// |
| 53 | + /// {{produces}} |
| 54 | + /// |
| 55 | + /// ### Explanation |
| 56 | + /// |
| 57 | + /// Since `Foo` doesn't implement `Clone`, running `.clone()` only dereferences the double |
| 58 | + /// reference, instead of cloning the inner type which should be what was intended. |
| 59 | + pub SUSPICIOUS_DOUBLE_REF_OP, |
| 60 | + Warn, |
| 61 | + "suspicious call of trait method on `&&T`" |
| 62 | +} |
| 63 | + |
| 64 | +declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL, SUSPICIOUS_DOUBLE_REF_OP]); |
39 | 65 |
|
40 | 66 | impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
|
41 | 67 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
42 | 68 | // We only care about method calls.
|
43 |
| - let ExprKind::MethodCall(call, receiver, ..) = &expr.kind else { |
44 |
| - return |
| 69 | + let ExprKind::MethodCall(call, receiver, _, call_span) = &expr.kind else { |
| 70 | + return; |
45 | 71 | };
|
| 72 | + |
| 73 | + if call_span.from_expansion() { |
| 74 | + return; |
| 75 | + } |
| 76 | + |
46 | 77 | // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
|
47 | 78 | // traits and ignore any other method call.
|
48 | 79 | let did = match cx.typeck_results().type_dependent_def(expr.hir_id) {
|
@@ -70,25 +101,39 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
|
70 | 101 | };
|
71 | 102 | // (Re)check that it implements the noop diagnostic.
|
72 | 103 | let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
|
73 |
| - if !matches!( |
74 |
| - name, |
75 |
| - sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref |
76 |
| - ) { |
77 |
| - return; |
78 |
| - } |
| 104 | + |
| 105 | + let op = match name { |
| 106 | + sym::noop_method_borrow => "borrow", |
| 107 | + sym::noop_method_clone => "clone", |
| 108 | + sym::noop_method_deref => "deref", |
| 109 | + _ => return, |
| 110 | + }; |
| 111 | + |
79 | 112 | let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
80 | 113 | let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
|
81 |
| - if receiver_ty != expr_ty { |
82 |
| - // This lint will only trigger if the receiver type and resulting expression \ |
83 |
| - // type are the same, implying that the method call is unnecessary. |
| 114 | + let arg_adjustments = cx.typeck_results().expr_adjustments(receiver); |
| 115 | + |
| 116 | + // If there is any user defined auto-deref step, then we don't want to warn. |
| 117 | + // https://github.com/rust-lang/rust-clippy/issues/9272 |
| 118 | + if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) { |
84 | 119 | return;
|
85 | 120 | }
|
| 121 | + |
86 | 122 | let expr_span = expr.span;
|
87 | 123 | let span = expr_span.with_lo(receiver.span.hi());
|
88 |
| - cx.emit_spanned_lint( |
89 |
| - NOOP_METHOD_CALL, |
90 |
| - span, |
91 |
| - NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span }, |
92 |
| - ); |
| 124 | + |
| 125 | + if receiver_ty == expr_ty { |
| 126 | + cx.emit_spanned_lint( |
| 127 | + NOOP_METHOD_CALL, |
| 128 | + span, |
| 129 | + NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span }, |
| 130 | + ); |
| 131 | + } else { |
| 132 | + cx.emit_spanned_lint( |
| 133 | + SUSPICIOUS_DOUBLE_REF_OP, |
| 134 | + span, |
| 135 | + SuspiciousDoubleRefDiag { call: call.ident.name, ty: expr_ty, op }, |
| 136 | + ) |
| 137 | + } |
93 | 138 | }
|
94 | 139 | }
|
0 commit comments