Skip to content

Commit eaaf3ab

Browse files
committed
Tighten up assignment operator representations.
In the AST, currently we use `BinOpKind` within `ExprKind::AssignOp` and `AssocOp::AssignOp`, even though this allows some nonsensical combinations. E.g. there is no `&&=` operator. Likewise for HIR and THIR. This commit introduces `AssignOpKind` which only includes the ten assignable operators, and uses it in `ExprKind::AssignOp` and `AssocOp::AssignOp`. (And does similar things for `hir::ExprKind` and `thir::ExprKind`.) This avoids the possibility of nonsensical combinations, as seen by the removal of the `bug!` case in `lang_item_for_binop`. The commit is mostly plumbing, including: - Adds an `impl From<AssignOpKind> for BinOpKind` (AST) and `impl From<AssignOp> for BinOp` (MIR/THIR). - `BinOpCategory` can now be created from both `BinOpKind` and `AssignOpKind`. - Replaces the `IsAssign` type with `Op`, which has more information and a few methods. - `suggest_swapping_lhs_and_rhs`: moves the condition to the call site, it's easier that way. - `check_expr_inner`: had to factor out some code into a separate method. I'm on the fence about whether avoiding the nonsensical combinations is worth the extra code.
1 parent 1f4abb4 commit eaaf3ab

10 files changed

+48
-21
lines changed

clippy_lints/src/format_push_string.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::higher;
33
use clippy_utils::ty::is_type_lang_item;
4-
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
4+
use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource};
55
use rustc_lint::{LateContext, LateLintPass};
66
use rustc_session::declare_lint_pass;
77
use rustc_span::sym;
@@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
7777
return;
7878
}
7979
},
80-
ExprKind::AssignOp(op, left, arg) if op.node == BinOpKind::Add && is_string(cx, left) => arg,
80+
ExprKind::AssignOp(op, left, arg) if op.node == AssignOpKind::AddAssign && is_string(cx, left) => arg,
8181
_ => return,
8282
};
8383
if is_format(cx, arg) {

clippy_lints/src/implicit_saturating_add.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_context;
55
use rustc_ast::ast::{LitIntType, LitKind};
66
use rustc_data_structures::packed::Pu128;
77
use rustc_errors::Applicability;
8-
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
8+
use rustc_hir::{AssignOpKind, BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
99
use rustc_lint::{LateContext, LateLintPass};
1010
use rustc_middle::ty::{IntTy, Ty, UintTy};
1111
use rustc_session::declare_lint_pass;
@@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
6868
&& ex.span.ctxt() == ctxt
6969
&& expr1.span.ctxt() == ctxt
7070
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
71-
&& BinOpKind::Add == op1.node
71+
&& AssignOpKind::AddAssign == op1.node
7272
&& let ExprKind::Lit(lit) = value.kind
7373
&& let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node
7474
&& block.expr.is_none()

clippy_lints/src/implicit_saturating_sub.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use clippy_utils::{
88
use rustc_ast::ast::LitKind;
99
use rustc_data_structures::packed::Pu128;
1010
use rustc_errors::Applicability;
11-
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath};
11+
use rustc_hir::{AssignOpKind, BinOp, BinOpKind, Expr, ExprKind, QPath};
1212
use rustc_lint::{LateContext, LateLintPass};
1313
use rustc_session::impl_lint_pass;
1414
use rustc_span::Span;
@@ -366,7 +366,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
366366
match peel_blocks_with_stmt(expr).kind {
367367
ExprKind::AssignOp(ref op1, target, value) => {
368368
// Check if literal being subtracted is one
369-
(BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
369+
(AssignOpKind::SubAssign == op1.node && is_integer_literal(value, 1)).then_some(target)
370370
},
371371
ExprKind::Assign(target, value, _) => {
372372
if let ExprKind::Binary(ref op1, left1, right1) = value.kind

clippy_lints/src/loops/utils.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc
33
use rustc_ast::ast::{LitIntType, LitKind};
44
use rustc_errors::Applicability;
55
use rustc_hir::intravisit::{Visitor, walk_expr, walk_local};
6-
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind};
6+
use rustc_hir::{
7+
AssignOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind
8+
};
79
use rustc_lint::LateContext;
810
use rustc_middle::hir::nested_filter;
911
use rustc_middle::ty::{self, Ty};
@@ -58,7 +60,7 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> {
5860
match parent.kind {
5961
ExprKind::AssignOp(op, lhs, rhs) => {
6062
if lhs.hir_id == expr.hir_id {
61-
*state = if op.node == BinOpKind::Add
63+
*state = if op.node == AssignOpKind::AddAssign
6264
&& is_integer_const(self.cx, rhs, 1)
6365
&& *state == IncrementVisitorVarState::Initial
6466
&& self.depth == 0

clippy_lints/src/mixed_read_write_in_expression.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,11 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St
261261
| ExprKind::Assign(..)
262262
| ExprKind::Index(..)
263263
| ExprKind::Repeat(_, _)
264-
| ExprKind::Struct(_, _, _) => {
264+
| ExprKind::Struct(_, _, _)
265+
| ExprKind::AssignOp(_, _, _) => {
265266
walk_expr(vis, expr);
266267
},
267-
ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
268+
ExprKind::Binary(op, _, _) => {
268269
if op.node == BinOpKind::And || op.node == BinOpKind::Or {
269270
// x && y and x || y always evaluate x first, so these are
270271
// strictly sequenced.

clippy_lints/src/operators/arithmetic_side_effects.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,12 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
335335
return;
336336
}
337337
match &expr.kind {
338-
hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => {
338+
hir::ExprKind::Binary(op, lhs, rhs) => {
339339
self.manage_bin_ops(cx, expr, op.node, lhs, rhs);
340340
},
341+
hir::ExprKind::AssignOp(op, lhs, rhs) => {
342+
self.manage_bin_ops(cx, expr, op.node.into(), lhs, rhs);
343+
},
341344
hir::ExprKind::MethodCall(ps, receiver, args, _) => {
342345
self.manage_method_call(args, cx, expr, ps, receiver);
343346
},

clippy_lints/src/operators/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -913,9 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
913913
);
914914
},
915915
ExprKind::AssignOp(op, lhs, rhs) => {
916-
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
917-
misrefactored_assign_op::check(cx, e, op.node, lhs, rhs);
918-
modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false);
916+
let bin_op = op.node.into();
917+
self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs);
918+
misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs);
919+
modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false);
919920
},
920921
ExprKind::Assign(lhs, rhs, _) => {
921922
assign_op_pattern::check(cx, e, lhs, rhs);

clippy_lints/src/suspicious_trait_impl.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use core::ops::ControlFlow;
55
use rustc_hir as hir;
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_session::declare_lint_pass;
8+
use rustc_span::Span;
89

910
declare_clippy_lint! {
1011
/// ### What it does
@@ -56,8 +57,27 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_
5657

5758
impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
5859
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
59-
if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind
60-
&& let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node)
60+
match expr.kind {
61+
hir::ExprKind::Binary(op, _, _) => {
62+
self.check_expr_inner(cx, expr, op.node, op.span);
63+
}
64+
hir::ExprKind::AssignOp(op, _, _) => {
65+
self.check_expr_inner(cx, expr, op.node.into(), op.span);
66+
}
67+
_ => {}
68+
}
69+
}
70+
}
71+
72+
impl<'tcx> SuspiciousImpl {
73+
fn check_expr_inner(
74+
&mut self,
75+
cx: &LateContext<'tcx>,
76+
expr: &'tcx hir::Expr<'_>,
77+
binop: hir::BinOpKind,
78+
span: Span,
79+
) {
80+
if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop)
6181
&& let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang)
6282
&& let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang)
6383

@@ -82,10 +102,10 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
82102
span_lint(
83103
cx,
84104
lint,
85-
binop.span,
105+
span,
86106
format!(
87107
"suspicious use of `{}` in `{}` impl",
88-
binop.node.as_str(),
108+
binop.as_str(),
89109
cx.tcx.item_name(trait_id)
90110
),
91111
);

clippy_lints/src/swap.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxIndexSet;
1010
use rustc_hir::intravisit::{Visitor, walk_expr};
1111

1212
use rustc_errors::Applicability;
13-
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind};
13+
use rustc_hir::{AssignOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind};
1414
use rustc_lint::{LateContext, LateLintPass, LintContext};
1515
use rustc_middle::ty;
1616
use rustc_session::declare_lint_pass;
@@ -307,7 +307,7 @@ fn extract_sides_of_xor_assign<'a, 'hir>(
307307
if let StmtKind::Semi(expr) = stmt.kind
308308
&& let ExprKind::AssignOp(
309309
Spanned {
310-
node: BinOpKind::BitXor,
310+
node: AssignOpKind::BitXorAssign,
311311
..
312312
},
313313
lhs,

clippy_utils/src/sugg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
357357
match op {
358358
AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()),
359359
AssocOp::Assign => format!("{lhs} = {rhs}"),
360-
AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()),
360+
AssocOp::AssignOp(op) => format!("{lhs} {} {rhs}", op.as_str()),
361361
AssocOp::Cast => format!("{lhs} as {rhs}"),
362362
AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()),
363363
}

0 commit comments

Comments
 (0)