1 use clippy_utils::diagnostics::span_lint;
2 use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
3 use rustc_lint::{EarlyContext, EarlyLintPass};
4 use rustc_session::{declare_lint_pass, declare_tool_lint};
5
6 declare_clippy_lint! {
7 /// ### What it does
8 /// Checks for nested assignments.
9 ///
10 /// ### Why is this bad?
11 /// While this is in most cases already a type mismatch,
12 /// the result of an assignment being `()` can throw off people coming from languages like python or C,
13 /// where such assignments return a copy of the assigned value.
14 ///
15 /// ### Example
16 /// ```rust
17 ///# let (a, b);
18 /// a = b = 42;
19 /// ```
20 /// Use instead:
21 /// ```rust
22 ///# let (a, b);
23 /// b = 42;
24 /// a = b;
25 /// ```
26 #[clippy::version = "1.65.0"]
27 pub MULTI_ASSIGNMENTS,
28 suspicious,
29 "instead of using `a = b = c;` use `a = c; b = c;`"
30 }
31
32 declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]);
33
strip_paren_blocks(expr: &Expr) -> &Expr34 fn strip_paren_blocks(expr: &Expr) -> &Expr {
35 match &expr.kind {
36 ExprKind::Paren(e) => strip_paren_blocks(e),
37 ExprKind::Block(b, _) => {
38 if let [
39 Stmt {
40 kind: StmtKind::Expr(e),
41 ..
42 },
43 ] = &b.stmts[..]
44 {
45 strip_paren_blocks(e)
46 } else {
47 expr
48 }
49 },
50 _ => expr,
51 }
52 }
53
54 impl EarlyLintPass for MultiAssignments {
check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr)55 fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
56 if let ExprKind::Assign(target, source, _) = &expr.kind {
57 if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
58 span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
59 };
60 if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
61 span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
62 }
63 };
64 }
65 }
66