about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyan Levick <ryan.levick@gmail.com>2021-01-05 10:07:50 +0100
committerRyan Levick <me@ryanlevick.com>2021-03-03 11:22:16 +0100
commit040735c110026bbd494a23c86182ebda201d720b (patch)
treed022539550c7bd531cf70ce7324e0e51a560e52d
parentcbca5689a5a0c63c6c5fda22bb0678164b52fec3 (diff)
downloadrust-040735c110026bbd494a23c86182ebda201d720b.tar.gz
rust-040735c110026bbd494a23c86182ebda201d720b.zip
First version of noop-lint
-rw-r--r--compiler/rustc_lint/src/lib.rs3
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs71
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/clone.rs2
4 files changed, 77 insertions, 0 deletions
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 638b73c27a8..b0c22154c0b 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -57,6 +57,7 @@ mod methods;
 mod non_ascii_idents;
 mod non_fmt_panic;
 mod nonstandard_style;
+mod noop_method_call;
 mod passes;
 mod redundant_semicolon;
 mod traits;
@@ -83,6 +84,7 @@ use methods::*;
 use non_ascii_idents::*;
 use non_fmt_panic::NonPanicFmt;
 use nonstandard_style::*;
+use noop_method_call::*;
 use redundant_semicolon::*;
 use traits::*;
 use types::*;
@@ -170,6 +172,7 @@ macro_rules! late_lint_passes {
                 DropTraitConstraints: DropTraitConstraints,
                 TemporaryCStringAsPtr: TemporaryCStringAsPtr,
                 NonPanicFmt: NonPanicFmt,
+                NoopMethodCall: NoopMethodCall,
             ]
         );
     };
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
new file mode 100644
index 00000000000..098c50d5abe
--- /dev/null
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -0,0 +1,71 @@
+use crate::context::LintContext;
+use crate::rustc_middle::ty::TypeFoldable;
+use crate::LateContext;
+use crate::LateLintPass;
+use rustc_hir::def::DefKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_middle::ty;
+use rustc_span::symbol::sym;
+
+declare_lint! {
+    /// The `noop_method_call` lint detects specific calls to noop methods
+    /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// struct Foo;
+    /// let foo = &Foo;
+    /// let clone: &Foo = foo.clone();
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Some method calls are noops meaning that they do nothing. Usually such methods
+    /// are the result of blanket implementations that happen to create some method invocations
+    /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
+    /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
+    /// as references are copy. This lint detects these calls and warns the user about them.
+    pub NOOP_METHOD_CALL,
+    Warn,
+    "detects the use of well-known noop methods"
+}
+
+declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
+
+impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        // We only care about method calls
+        if let ExprKind::MethodCall(..) = expr.kind {
+            // Get the `DefId` only when dealing with an `AssocFn`
+            if let Some((DefKind::AssocFn, did)) =
+                cx.typeck_results().type_dependent_def(expr.hir_id)
+            {
+                // Check that we're dealing with a trait method
+                if let Some(trait_id) = cx.tcx.trait_of_item(did) {
+                    let substs = cx.typeck_results().node_substs(expr.hir_id);
+                    // We can't resolve on types that recursively require monomorphization,
+                    // so check that we don't need to perfom substitution
+                    if !substs.needs_subst() {
+                        let param_env = cx.tcx.param_env(trait_id);
+                        // Resolve the trait method instance
+                        if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) {
+                            // Check that it implements the noop diagnostic
+                            if cx.tcx.is_diagnostic_item(sym::ref_clone_method, i.def_id()) {
+                                let span = expr.span;
+
+                                cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
+                                    let message = "Call to noop method";
+                                    lint.build(&message).emit()
+                                });
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 27bb45bcc85..2207deb8fb5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -915,6 +915,7 @@ symbols! {
         receiver,
         recursion_limit,
         reexport_test_harness_main,
+        ref_clone_method,
         reference,
         reflect,
         reg,
diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs
index a953a3a4182..12f0f9629a3 100644
--- a/library/core/src/clone.rs
+++ b/library/core/src/clone.rs
@@ -104,6 +104,7 @@
 /// [impls]: #implementors
 #[stable(feature = "rust1", since = "1.0.0")]
 #[lang = "clone"]
+#[rustc_diagnostic_item = "Clone"]
 pub trait Clone: Sized {
     /// Returns a copy of the value.
     ///
@@ -221,6 +222,7 @@ mod impls {
     #[stable(feature = "rust1", since = "1.0.0")]
     impl<T: ?Sized> Clone for &T {
         #[inline]
+        #[rustc_diagnostic_item = "ref_clone_method"]
         fn clone(&self) -> Self {
             *self
         }