summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/multi_assignments.rs
blob: 81eb1a085aea9163eca1c0029766d95f99d9d280 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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");
            }
        };
    }
}