| 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_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 |
| /// ```no_run |
| ///# let (a, b); |
| /// a = b = 42; |
| /// ``` |
| /// Use instead: |
| /// ```no_run |
| ///# 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"); |
| } |
| } |
| } |
| } |