]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_lint/src/noop_method_call.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / compiler / rustc_lint / src / noop_method_call.rs
CommitLineData
6a06907d 1use crate::context::LintContext;
6a06907d
XL
2use crate::LateContext;
3use crate::LateLintPass;
064997fb 4use rustc_errors::fluent;
6a06907d
XL
5use rustc_hir::def::DefKind;
6use rustc_hir::{Expr, ExprKind};
7use rustc_middle::ty;
8use rustc_span::symbol::sym;
9
10declare_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
38declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
39
40impl<'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}