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