From fb7dffeac9df2f15c70c84c5defa5918af63b6af Mon Sep 17 00:00:00 2001 From: royrustdev Date: Fri, 26 Aug 2022 12:46:41 +0530 Subject: [PATCH] add `multi_assignments` lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/multi_assignments.rs | 65 +++++++++++++++++++++ tests/ui/multi_assignments.rs | 9 +++ tests/ui/multi_assignments.stderr | 40 +++++++++++++ 8 files changed, 120 insertions(+) create mode 100644 clippy_lints/src/multi_assignments.rs create mode 100644 tests/ui/multi_assignments.rs create mode 100644 tests/ui/multi_assignments.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index caa488a7a9c..c285c1e6859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3897,6 +3897,7 @@ Released 2018-09-13 [`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions [`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic [`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one +[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments [`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions [`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl [`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index dac6aed61c0..eaeb201ff50 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -233,6 +233,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(misc_early::ZERO_PREFIXED_LITERAL), LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), + LintId::of(multi_assignments::MULTI_ASSIGNMENTS), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index be58e144184..e058c45217e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -398,6 +398,7 @@ store.register_lints(&[ mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, module_style::MOD_MODULE_FILES, module_style::SELF_NAMED_MODULE_FILES, + multi_assignments::MULTI_ASSIGNMENTS, mut_key::MUTABLE_KEY_TYPE, mut_mut::MUT_MUT, mut_reference::UNNECESSARY_MUT_PASSED, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 369d4b4eed6..6f480c52cc9 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -24,6 +24,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::MUT_RANGE_BOUND), LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(multi_assignments::MULTI_ASSIGNMENTS), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 64c43d9acc4..a3a4267abea 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -289,6 +289,7 @@ mod missing_enforced_import_rename; mod missing_inline; mod mixed_read_write_in_expression; mod module_style; +mod multi_assignments; mod mut_key; mod mut_mut; mod mut_reference; @@ -896,6 +897,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(|| Box::new(manual_empty_string_creations::ManualEmptyStringCreations)); store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable)); + store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/multi_assignments.rs b/clippy_lints/src/multi_assignments.rs new file mode 100644 index 00000000000..81eb1a085ae --- /dev/null +++ b/clippy_lints/src/multi_assignments.rs @@ -0,0 +1,65 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for nested assignments. + /// + /// ### Why is this bad? + /// While this is in most cases already a type mismatch, + /// the result of an assignment being `()` can throw off people coming from languages like python or C, + /// where such assignments return a copy of the assigned value. + /// + /// ### Example + /// ```rust + ///# let (a, b); + /// a = b = 42; + /// ``` + /// Use instead: + /// ```rust + ///# let (a, b); + /// b = 42; + /// a = b; + /// ``` + #[clippy::version = "1.65.0"] + pub MULTI_ASSIGNMENTS, + suspicious, + "instead of using `a = b = c;` use `a = c; b = c;`" +} + +declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]); + +fn strip_paren_blocks(expr: &Expr) -> &Expr { + match &expr.kind { + ExprKind::Paren(e) => strip_paren_blocks(e), + ExprKind::Block(b, _) => { + if let [ + Stmt { + kind: StmtKind::Expr(e), + .. + }, + ] = &b.stmts[..] + { + strip_paren_blocks(e) + } else { + expr + } + }, + _ => expr, + } +} + +impl EarlyLintPass for MultiAssignments { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::Assign(target, source, _) = &expr.kind { + if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind { + span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); + }; + if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind { + span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); + } + }; + } +} diff --git a/tests/ui/multi_assignments.rs b/tests/ui/multi_assignments.rs new file mode 100644 index 00000000000..b186bf8bbdb --- /dev/null +++ b/tests/ui/multi_assignments.rs @@ -0,0 +1,9 @@ +#![warn(clippy::multi_assignments)] +fn main() { + let (mut a, mut b, mut c, mut d) = ((), (), (), ()); + a = b = c; + a = b = c = d; + a = b = { c }; + a = { b = c }; + a = (b = c); +} diff --git a/tests/ui/multi_assignments.stderr b/tests/ui/multi_assignments.stderr new file mode 100644 index 00000000000..d6c42bb698c --- /dev/null +++ b/tests/ui/multi_assignments.stderr @@ -0,0 +1,40 @@ +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:4:5 + | +LL | a = b = c; + | ^^^^^^^^^ + | + = note: `-D clippy::multi-assignments` implied by `-D warnings` + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:5:5 + | +LL | a = b = c = d; + | ^^^^^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:5:9 + | +LL | a = b = c = d; + | ^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:6:5 + | +LL | a = b = { c }; + | ^^^^^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:7:5 + | +LL | a = { b = c }; + | ^^^^^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:8:5 + | +LL | a = (b = c); + | ^^^^^^^^^^^ + +error: aborting due to 6 previous errors +