-use crate::utils::{
- get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
-};
-use crate::utils::{higher, sugg};
use if_chain::if_chain;
use rustc::hir;
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
+use crate::utils::{
+ get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
+};
+use crate::utils::{higher, sugg};
+
declare_clippy_lint! {
/// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
/// patterns.
/// implementations that differ from the regular `Op` impl.
///
/// **Example:**
- /// ```ignore
+ /// ```rust
/// let mut a = 5;
/// ...
/// a = a + b;
/// op= b`.
///
/// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have
- /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore it suggests both.
+ /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
/// If `a op= a op b` is really the correct behaviour it should be
/// written as `a = a op a op b` as it's less confusing.
///
/// **Example:**
- /// ```ignore
+ /// ```rust
/// let mut a = 5;
/// ...
/// a += a + b;
match &expr.node {
hir::ExprKind::AssignOp(op, lhs, rhs) => {
if let hir::ExprKind::Binary(binop, l, r) = &rhs.node {
- if op.node == binop.node {
- let lint = |assignee: &hir::Expr, rhs_other: &hir::Expr| {
- span_lint_and_then(
- cx,
- MISREFACTORED_ASSIGN_OP,
- expr.span,
- "variable appears on both sides of an assignment operation",
- |db| {
- if let (Some(snip_a), Some(snip_r)) =
- (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span))
- {
- let a = &sugg::Sugg::hir(cx, assignee, "..");
- let r = &sugg::Sugg::hir(cx, rhs, "..");
- let long =
- format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
- db.span_suggestion(
- expr.span,
- &format!(
- "Did you mean {} = {} {} {} or {}? Consider replacing it with",
- snip_a,
- snip_a,
- op.node.as_str(),
- snip_r,
- long
- ),
- format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
- Applicability::MachineApplicable,
- );
- db.span_suggestion(
- expr.span,
- "or",
- long,
- Applicability::MachineApplicable, // snippet
- );
- }
- },
- );
- };
- // lhs op= l op r
- if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
- lint(lhs, r);
- }
- // lhs op= l commutative_op r
- if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
- lint(lhs, l);
- }
+ if op.node != binop.node {
+ return;
+ }
+ // lhs op= l op r
+ if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
+ lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
+ }
+ // lhs op= l commutative_op r
+ if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
+ lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
}
}
},
}
}
+fn lint_misrefactored_assign_op(
+ cx: &LateContext<'_, '_>,
+ expr: &hir::Expr,
+ op: hir::BinOp,
+ rhs: &hir::Expr,
+ assignee: &hir::Expr,
+ rhs_other: &hir::Expr,
+) {
+ span_lint_and_then(
+ cx,
+ MISREFACTORED_ASSIGN_OP,
+ expr.span,
+ "variable appears on both sides of an assignment operation",
+ |db| {
+ if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
+ let a = &sugg::Sugg::hir(cx, assignee, "..");
+ let r = &sugg::Sugg::hir(cx, rhs, "..");
+ let long = format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
+ db.span_suggestion(
+ expr.span,
+ &format!(
+ "Did you mean {} = {} {} {} or {}? Consider replacing it with",
+ snip_a,
+ snip_a,
+ op.node.as_str(),
+ snip_r,
+ long
+ ),
+ format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
+ Applicability::MachineApplicable,
+ );
+ db.span_suggestion(
+ expr.span,
+ "or",
+ long,
+ Applicability::MachineApplicable, // snippet
+ );
+ }
+ },
+ );
+}
+
fn is_commutative(op: hir::BinOpKind) -> bool {
use rustc::hir::BinOpKind::*;
match op {