| 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"); | |
| } | |
| } | |
| } | |
| } |