]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/assign_ops.rs
Merge branch 'master' into sugg
[rust.git] / clippy_lints / src / assign_ops.rs
1 use rustc::hir;
2 use rustc::lint::*;
3 use utils::{span_lint_and_then, span_lint, snippet_opt, SpanlessEq, get_trait_def_id, implements_trait};
4 use utils::{higher, sugg};
5
6 /// **What it does:** This lint checks for `+=` operations and similar.
7 ///
8 /// **Why is this bad?** Projects with many developers from languages without those operations may
9 /// find them unreadable and not worth their weight.
10 ///
11 /// **Known problems:** Types implementing `OpAssign` don't necessarily implement `Op`.
12 ///
13 /// **Example:**
14 /// ```
15 /// a += 1;
16 /// ```
17 declare_restriction_lint! {
18     pub ASSIGN_OPS,
19     "any assignment operation"
20 }
21
22 /// **What it does:** Check for `a = a op b` or `a = b commutative_op a` patterns.
23 ///
24 /// **Why is this bad?** These can be written as the shorter `a op= b`.
25 ///
26 /// **Known problems:** While forbidden by the spec, `OpAssign` traits may have implementations that differ from the regular `Op` impl.
27 ///
28 /// **Example:**
29 ///
30 /// ```
31 /// let mut a = 5;
32 /// ...
33 /// a = a + b;
34 /// ```
35 declare_lint! {
36     pub ASSIGN_OP_PATTERN,
37     Warn,
38     "assigning the result of an operation on a variable to that same variable"
39 }
40
41 #[derive(Copy, Clone, Default)]
42 pub struct AssignOps;
43
44 impl LintPass for AssignOps {
45     fn get_lints(&self) -> LintArray {
46         lint_array!(ASSIGN_OPS, ASSIGN_OP_PATTERN)
47     }
48 }
49
50 impl LateLintPass for AssignOps {
51     fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
52         match expr.node {
53             hir::ExprAssignOp(op, ref lhs, ref rhs) => {
54                 span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
55                     let lhs = &sugg::Sugg::hir(cx, lhs, "..");
56                     let rhs = &sugg::Sugg::hir(cx, rhs, "..");
57
58                     db.span_suggestion(expr.span,
59                                        "replace it with",
60                                        format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)));
61                 });
62             }
63             hir::ExprAssign(ref assignee, ref e) => {
64                 if let hir::ExprBinary(op, ref l, ref r) = e.node {
65                     let lint = |assignee: &hir::Expr, rhs: &hir::Expr| {
66                         let ty = cx.tcx.expr_ty(assignee);
67                         if ty.walk_shallow().next().is_some() {
68                             return; // implements_trait does not work with generics
69                         }
70                         let rty = cx.tcx.expr_ty(rhs);
71                         if rty.walk_shallow().next().is_some() {
72                             return; // implements_trait does not work with generics
73                         }
74                         macro_rules! ops {
75                             ($op:expr, $cx:expr, $ty:expr, $rty:expr, $($trait_name:ident:$full_trait_name:ident),+) => {
76                                 match $op {
77                                     $(hir::$full_trait_name => {
78                                         let [krate, module] = ::utils::paths::OPS_MODULE;
79                                         let path = [krate, module, concat!(stringify!($trait_name), "Assign")];
80                                         let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) {
81                                             trait_id
82                                         } else {
83                                             return; // useless if the trait doesn't exist
84                                         };
85                                         implements_trait($cx, $ty, trait_id, vec![$rty])
86                                     },)*
87                                     _ => false,
88                                 }
89                             }
90                         }
91                         if ops!(op.node,
92                                 cx,
93                                 ty,
94                                 rty,
95                                 Add: BiAdd,
96                                 Sub: BiSub,
97                                 Mul: BiMul,
98                                 Div: BiDiv,
99                                 Rem: BiRem,
100                                 And: BiAnd,
101                                 Or: BiOr,
102                                 BitAnd: BiBitAnd,
103                                 BitOr: BiBitOr,
104                                 BitXor: BiBitXor,
105                                 Shr: BiShr,
106                                 Shl: BiShl) {
107                             if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span),
108                                                                    snippet_opt(cx, rhs.span)) {
109                                 span_lint_and_then(cx,
110                                                    ASSIGN_OP_PATTERN,
111                                                    expr.span,
112                                                    "manual implementation of an assign operation",
113                                                    |db| {
114                                                        db.span_suggestion(expr.span,
115                                                                           "replace it with",
116                                                                           format!("{} {}= {}", snip_a, op.node.as_str(), snip_r));
117                                                    });
118                             } else {
119                                 span_lint(cx,
120                                           ASSIGN_OP_PATTERN,
121                                           expr.span,
122                                           "manual implementation of an assign operation");
123                             }
124                         }
125                     };
126                     // a = a op b
127                     if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
128                         lint(assignee, r);
129                     }
130                     // a = b commutative_op a
131                     if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) {
132                         match op.node {
133                             hir::BiAdd | hir::BiMul | hir::BiAnd | hir::BiOr | hir::BiBitXor | hir::BiBitAnd |
134                             hir::BiBitOr => {
135                                 lint(assignee, l);
136                             }
137                             _ => {}
138                         }
139                     }
140                 }
141             }
142             _ => {}
143         }
144     }
145 }