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