summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint/src/noop_method_call.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /compiler/rustc_lint/src/noop_method_call.rs
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_lint/src/noop_method_call.rs')
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs117
1 files changed, 84 insertions, 33 deletions
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index d67a00619..d56c35bb6 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -1,10 +1,13 @@
use crate::context::LintContext;
-use crate::lints::NoopMethodCallDiag;
+use crate::lints::{
+ NoopMethodCallDiag, SuspiciousDoubleRefCloneDiag, SuspiciousDoubleRefDerefDiag,
+};
use crate::LateContext;
use crate::LateLintPass;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
use rustc_span::symbol::sym;
declare_lint! {
@@ -35,32 +38,62 @@ declare_lint! {
"detects the use of well-known noop methods"
}
-declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
+declare_lint! {
+ /// The `suspicious_double_ref_op` lint checks for usage of `.clone()`/`.borrow()`/`.deref()`
+ /// on an `&&T` when `T: !Deref/Borrow/Clone`, which means the call will return the inner `&T`,
+ /// instead of performing the operation on the underlying `T` and can be confusing.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// # #![allow(unused)]
+ /// struct Foo;
+ /// let foo = &&Foo;
+ /// let clone: &Foo = foo.clone();
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Since `Foo` doesn't implement `Clone`, running `.clone()` only dereferences the double
+ /// reference, instead of cloning the inner type which should be what was intended.
+ pub SUSPICIOUS_DOUBLE_REF_OP,
+ Warn,
+ "suspicious call of trait method on `&&T`"
+}
+
+declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL, SUSPICIOUS_DOUBLE_REF_OP]);
impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// We only care about method calls.
- let ExprKind::MethodCall(call, receiver, ..) = &expr.kind else {
- return
+ let ExprKind::MethodCall(call, receiver, _, call_span) = &expr.kind else {
+ return;
};
+
+ if call_span.from_expansion() {
+ return;
+ }
+
// We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
// traits and ignore any other method call.
- let did = match cx.typeck_results().type_dependent_def(expr.hir_id) {
- // Verify we are dealing with a method/associated function.
- Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
- // Check that we're dealing with a trait method for one of the traits we care about.
- Some(trait_id)
- if matches!(
- cx.tcx.get_diagnostic_name(trait_id),
- Some(sym::Borrow | sym::Clone | sym::Deref)
- ) =>
- {
- did
- }
- _ => return,
- },
- _ => return,
+
+ let Some((DefKind::AssocFn, did)) =
+ cx.typeck_results().type_dependent_def(expr.hir_id)
+ else {
+ return;
};
+
+ let Some(trait_id) = cx.tcx.trait_of_item(did) else { return };
+
+ if !matches!(
+ cx.tcx.get_diagnostic_name(trait_id),
+ Some(sym::Borrow | sym::Clone | sym::Deref)
+ ) {
+ return;
+ };
+
let substs = cx
.tcx
.normalize_erasing_regions(cx.param_env, cx.typeck_results().node_substs(expr.hir_id));
@@ -70,25 +103,43 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
};
// (Re)check that it implements the noop diagnostic.
let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
- if !matches!(
- name,
- sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
- ) {
- return;
- }
+
let receiver_ty = cx.typeck_results().expr_ty(receiver);
let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
- if receiver_ty != expr_ty {
- // This lint will only trigger if the receiver type and resulting expression \
- // type are the same, implying that the method call is unnecessary.
+ let arg_adjustments = cx.typeck_results().expr_adjustments(receiver);
+
+ // If there is any user defined auto-deref step, then we don't want to warn.
+ // https://github.com/rust-lang/rust-clippy/issues/9272
+ if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) {
return;
}
+
let expr_span = expr.span;
let span = expr_span.with_lo(receiver.span.hi());
- cx.emit_spanned_lint(
- NOOP_METHOD_CALL,
- span,
- NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },
- );
+
+ if receiver_ty == expr_ty {
+ cx.emit_spanned_lint(
+ NOOP_METHOD_CALL,
+ span,
+ NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },
+ );
+ } else {
+ match name {
+ // If `type_of(x) == T` and `x.borrow()` is used to get `&T`,
+ // then that should be allowed
+ sym::noop_method_borrow => return,
+ sym::noop_method_clone => cx.emit_spanned_lint(
+ SUSPICIOUS_DOUBLE_REF_OP,
+ span,
+ SuspiciousDoubleRefCloneDiag { ty: expr_ty },
+ ),
+ sym::noop_method_deref => cx.emit_spanned_lint(
+ SUSPICIOUS_DOUBLE_REF_OP,
+ span,
+ SuspiciousDoubleRefDerefDiag { ty: expr_ty },
+ ),
+ _ => return,
+ }
+ }
}
}