rust/clippy_lints/src/multi_assignments.rs

66 lines
2.0 KiB
Rust

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