]>
Commit | Line | Data |
---|---|---|
6a06907d | 1 | use crate::context::LintContext; |
064997fb | 2 | use crate::rustc_middle::ty::TypeVisitable; |
6a06907d XL |
3 | use crate::LateContext; |
4 | use crate::LateLintPass; | |
064997fb | 5 | use rustc_errors::fluent; |
6a06907d XL |
6 | use rustc_hir::def::DefKind; |
7 | use rustc_hir::{Expr, ExprKind}; | |
8 | use rustc_middle::ty; | |
9 | use rustc_span::symbol::sym; | |
10 | ||
11 | declare_lint! { | |
12 | /// The `noop_method_call` lint detects specific calls to noop methods | |
13 | /// such as a calling `<&T as Clone>::clone` where `T: !Clone`. | |
14 | /// | |
15 | /// ### Example | |
16 | /// | |
17 | /// ```rust | |
18 | /// # #![allow(unused)] | |
19 | /// #![warn(noop_method_call)] | |
20 | /// struct Foo; | |
21 | /// let foo = &Foo; | |
22 | /// let clone: &Foo = foo.clone(); | |
23 | /// ``` | |
24 | /// | |
25 | /// {{produces}} | |
26 | /// | |
27 | /// ### Explanation | |
28 | /// | |
29 | /// Some method calls are noops meaning that they do nothing. Usually such methods | |
30 | /// are the result of blanket implementations that happen to create some method invocations | |
31 | /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but | |
32 | /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything | |
33 | /// as references are copy. This lint detects these calls and warns the user about them. | |
34 | pub NOOP_METHOD_CALL, | |
35 | Allow, | |
36 | "detects the use of well-known noop methods" | |
37 | } | |
38 | ||
39 | declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); | |
40 | ||
41 | impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { | |
42 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
43 | // We only care about method calls. | |
5099ac24 | 44 | let ExprKind::MethodCall(call, elements, _) = &expr.kind else { |
a2a8927a | 45 | return |
6a06907d XL |
46 | }; |
47 | // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` | |
48 | // traits and ignore any other method call. | |
49 | let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) { | |
50 | // Verify we are dealing with a method/associated function. | |
51 | Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) { | |
52 | // Check that we're dealing with a trait method for one of the traits we care about. | |
53 | Some(trait_id) | |
c295e0f8 XL |
54 | if matches!( |
55 | cx.tcx.get_diagnostic_name(trait_id), | |
56 | Some(sym::Borrow | sym::Clone | sym::Deref) | |
57 | ) => | |
6a06907d XL |
58 | { |
59 | (trait_id, did) | |
60 | } | |
61 | _ => return, | |
62 | }, | |
63 | _ => return, | |
64 | }; | |
65 | let substs = cx.typeck_results().node_substs(expr.hir_id); | |
5099ac24 | 66 | if substs.needs_subst() { |
6a06907d | 67 | // We can't resolve on types that require monomorphization, so we don't handle them if |
5e7ed085 | 68 | // we need to perform substitution. |
6a06907d XL |
69 | return; |
70 | } | |
71 | let param_env = cx.tcx.param_env(trait_id); | |
72 | // Resolve the trait method instance. | |
a2a8927a XL |
73 | let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else { |
74 | return | |
6a06907d XL |
75 | }; |
76 | // (Re)check that it implements the noop diagnostic. | |
a2a8927a XL |
77 | let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return }; |
78 | if !matches!( | |
79 | name, | |
80 | sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref | |
81 | ) { | |
82 | return; | |
83 | } | |
a2a8927a XL |
84 | let receiver = &elements[0]; |
85 | let receiver_ty = cx.typeck_results().expr_ty(receiver); | |
86 | let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); | |
87 | if receiver_ty != expr_ty { | |
88 | // This lint will only trigger if the receiver type and resulting expression \ | |
89 | // type are the same, implying that the method call is unnecessary. | |
90 | return; | |
6a06907d | 91 | } |
a2a8927a | 92 | let expr_span = expr.span; |
a2a8927a XL |
93 | let span = expr_span.with_lo(receiver.span.hi()); |
94 | cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { | |
064997fb FG |
95 | lint.build(fluent::lint::noop_method_call) |
96 | .set_arg("method", call.ident.name) | |
97 | .set_arg("receiver_ty", receiver_ty) | |
98 | .span_label(span, fluent::lint::label) | |
99 | .note(fluent::lint::note) | |
100 | .emit(); | |
a2a8927a | 101 | }); |
6a06907d XL |
102 | } |
103 | } |