about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/eq_op.rs37
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--tests/ui/auxiliary/proc_macro_derive.rs1
-rw-r--r--tests/ui/double_parens.rs2
-rw-r--r--tests/ui/eq_op_early.rs15
-rw-r--r--tests/ui/eq_op_early.stderr16
-rw-r--r--tests/ui/used_underscore_binding.rs2
7 files changed, 72 insertions, 3 deletions
diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs
index e16ec783fab..7126c98a0b4 100644
--- a/clippy_lints/src/eq_op.rs
+++ b/clippy_lints/src/eq_op.rs
@@ -1,9 +1,13 @@
+use crate::utils::ast_utils::eq_expr;
 use crate::utils::{
     eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then,
 };
+use if_chain::if_chain;
+use rustc_ast::{ast, token};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
+use rustc_parse::parser;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -23,6 +27,12 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if x + 1 == x + 1 {}
     /// ```
+    /// or
+    /// ```rust
+    /// # let a = 3;
+    /// # let b = 4;
+    /// assert_eq!(a, a);
+    /// ```
     pub EQ_OP,
     correctness,
     "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
@@ -52,6 +62,31 @@ declare_clippy_lint! {
 
 declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
 
+impl EarlyLintPass for EqOp {
+    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {
+        if_chain! {
+            if mac.path == sym!(assert_eq);
+            let tokens = mac.args.inner_tokens();
+            let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None);
+            if let Ok(left) = parser.parse_expr();
+            if parser.eat(&token::Comma);
+            if let Ok(right) = parser.parse_expr();
+            let left_expr = left.into_inner();
+            let right_expr = right.into_inner();
+            if eq_expr(&left_expr, &right_expr);
+
+            then {
+                span_lint(
+                    cx,
+                    EQ_OP,
+                    left_expr.span.to(right_expr.span),
+                    "identical args used in this `assert_eq!` macro call",
+                );
+            }
+        }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for EqOp {
     #[allow(clippy::similar_names, clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index fc4afde9d9e..dd99b6b9040 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -348,6 +348,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
     store.register_pre_expansion_pass(|| box write::Write::default());
     store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
     store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
+    store.register_pre_expansion_pass(|| box eq_op::EqOp);
 }
 
 #[doc(hidden)]
@@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     let vec_box_size_threshold = conf.vec_box_size_threshold;
     store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
     store.register_late_pass(|| box booleans::NonminimalBool);
+    store.register_early_pass(|| box eq_op::EqOp);
     store.register_late_pass(|| box eq_op::EqOp);
     store.register_late_pass(|| box enum_clike::UnportableVariant);
     store.register_late_pass(|| box float_literal::FloatLiteral);
diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs
index 3df8be6c232..e369f62f8bf 100644
--- a/tests/ui/auxiliary/proc_macro_derive.rs
+++ b/tests/ui/auxiliary/proc_macro_derive.rs
@@ -3,6 +3,7 @@
 
 #![crate_type = "proc-macro"]
 #![feature(repr128, proc_macro_quote)]
+#![allow(clippy::eq_op)]
 
 extern crate proc_macro;
 
diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs
index 9c7590c7dd6..ff1dc76ab63 100644
--- a/tests/ui/double_parens.rs
+++ b/tests/ui/double_parens.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::double_parens)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::eq_op)]
 #![feature(custom_inner_attributes)]
 #![rustfmt::skip]
 
diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs
new file mode 100644
index 00000000000..cf5660ea98d
--- /dev/null
+++ b/tests/ui/eq_op_early.rs
@@ -0,0 +1,15 @@
+#![warn(clippy::eq_op)]
+
+fn main() {
+    let a = 1;
+    let b = 2;
+
+    // lint identical args in `assert_eq!` (see #3574)
+    assert_eq!(a, a);
+    assert_eq!(a + 1, a + 1);
+
+    // ok
+    assert_eq!(a, b);
+    assert_eq!(a, a + 1);
+    assert_eq!(a + 1, b + 1);
+}
diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr
new file mode 100644
index 00000000000..9206e9026e9
--- /dev/null
+++ b/tests/ui/eq_op_early.stderr
@@ -0,0 +1,16 @@
+error: identical args used in this `assert_eq!` macro call
+  --> $DIR/eq_op_early.rs:8:16
+   |
+LL |     assert_eq!(a, a);
+   |                ^^^^
+   |
+   = note: `-D clippy::eq-op` implied by `-D warnings`
+
+error: identical args used in this `assert_eq!` macro call
+  --> $DIR/eq_op_early.rs:9:16
+   |
+LL |     assert_eq!(a + 1, a + 1);
+   |                ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs
index 8e0243c49aa..d8bda7e8f48 100644
--- a/tests/ui/used_underscore_binding.rs
+++ b/tests/ui/used_underscore_binding.rs
@@ -3,7 +3,7 @@
 
 #![feature(rustc_private)]
 #![warn(clippy::all)]
-#![allow(clippy::blacklisted_name)]
+#![allow(clippy::blacklisted_name, clippy::eq_op)]
 #![warn(clippy::used_underscore_binding)]
 
 #[macro_use]