]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/suspicious_trait_impl.rs
Merge pull request #2473 from phansch/handle_multiline_attributes
[rust.git] / clippy_lints / src / suspicious_trait_impl.rs
1 use rustc::lint::*;
2 use rustc::hir;
3 use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
4 use syntax::ast;
5 use utils::{get_trait_def_id, span_lint};
6
7 /// **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g.
8 /// subtracting elements in an Add impl.
9 ///
10 /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.
11 ///
12 /// **Known problems:** None.
13 ///
14 /// **Example:**
15 /// ```rust
16 /// impl Add for Foo {
17 ///     type Output = Foo;
18 ///
19 ///     fn add(self, other: Foo) -> Foo {
20 ///         Foo(self.0 - other.0)
21 ///     }
22 /// }
23 /// ```
24 declare_lint! {
25     pub SUSPICIOUS_ARITHMETIC_IMPL,
26     Warn,
27     "suspicious use of operators in impl of arithmetic trait"
28 }
29
30 /// **What it does:** Lints for suspicious operations in impls of OpAssign, e.g.
31 /// subtracting elements in an AddAssign impl.
32 ///
33 /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.
34 ///
35 /// **Known problems:** None.
36 ///
37 /// **Example:**
38 /// ```rust
39 /// impl AddAssign for Foo {
40 ///     fn add_assign(&mut self, other: Foo) {
41 ///         *self = *self - other;
42 ///     }
43 /// }
44 /// ```
45 declare_lint! {
46     pub SUSPICIOUS_OP_ASSIGN_IMPL,
47     Warn,
48     "suspicious use of operators in impl of OpAssign trait"
49 }
50
51 #[derive(Copy, Clone)]
52 pub struct SuspiciousImpl;
53
54 impl LintPass for SuspiciousImpl {
55     fn get_lints(&self) -> LintArray {
56         lint_array![SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]
57     }
58 }
59
60 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SuspiciousImpl {
61     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
62         use rustc::hir::BinOp_::*;
63         if let hir::ExprBinary(binop, _, _) = expr.node {
64             // Check if the binary expression is part of another binary expression
65             // as a child node
66             let mut parent_expr = cx.tcx.hir.get_parent_node(expr.id);
67             while parent_expr != ast::CRATE_NODE_ID {
68                 if_chain! {
69                     if let hir::map::Node::NodeExpr(e) = cx.tcx.hir.get(parent_expr);
70                     if let hir::ExprBinary(_, _, _) = e.node;
71                     then {
72                         return
73                     }
74                 }
75
76                 parent_expr = cx.tcx.hir.get_parent_node(parent_expr);
77             }
78             // as a parent node
79             let mut visitor = BinaryExprVisitor {
80                 in_binary_expr: false,
81             };
82             walk_expr(&mut visitor, expr);
83
84             if visitor.in_binary_expr {
85                 return;
86             }
87
88             if let Some(impl_trait) = check_binop(
89                 cx,
90                 expr,
91                 &binop.node,
92                 &["Add", "Sub", "Mul", "Div"],
93                 &[BiAdd, BiSub, BiMul, BiDiv],
94             ) {
95                 span_lint(
96                     cx,
97                     SUSPICIOUS_ARITHMETIC_IMPL,
98                     binop.span,
99                     &format!(
100                         r#"Suspicious use of binary operator in `{}` impl"#,
101                         impl_trait
102                     ),
103                 );
104             }
105
106             if let Some(impl_trait) = check_binop(
107                 cx,
108                 expr,
109                 &binop.node,
110                 &[
111                     "AddAssign",
112                     "SubAssign",
113                     "MulAssign",
114                     "DivAssign",
115                     "BitAndAssign",
116                     "BitOrAssign",
117                     "BitXorAssign",
118                     "RemAssign",
119                     "ShlAssign",
120                     "ShrAssign",
121                 ],
122                 &[
123                     BiAdd, BiSub, BiMul, BiDiv, BiBitAnd, BiBitOr, BiBitXor, BiRem, BiShl, BiShr
124                 ],
125             ) {
126                 span_lint(
127                     cx,
128                     SUSPICIOUS_OP_ASSIGN_IMPL,
129                     binop.span,
130                     &format!(
131                         r#"Suspicious use of binary operator in `{}` impl"#,
132                         impl_trait
133                     ),
134                 );
135             }
136         }
137     }
138 }
139
140 fn check_binop<'a>(
141     cx: &LateContext,
142     expr: &hir::Expr,
143     binop: &hir::BinOp_,
144     traits: &[&'a str],
145     expected_ops: &[hir::BinOp_],
146 ) -> Option<&'a str> {
147     let mut trait_ids = vec![];
148     let [krate, module] = ::utils::paths::OPS_MODULE;
149
150     for t in traits {
151         let path = [krate, module, t];
152         if let Some(trait_id) = get_trait_def_id(cx, &path) {
153             trait_ids.push(trait_id);
154         } else {
155             return None;
156         }
157     }
158
159     // Get the actually implemented trait
160     let parent_fn = cx.tcx.hir.get_parent(expr.id);
161     let parent_impl = cx.tcx.hir.get_parent(parent_fn);
162
163     if_chain! {
164         if parent_impl != ast::CRATE_NODE_ID;
165         if let hir::map::Node::NodeItem(item) = cx.tcx.hir.get(parent_impl);
166         if let hir::Item_::ItemImpl(_, _, _, _, Some(ref trait_ref), _, _) = item.node;
167         if let Some(idx) = trait_ids.iter().position(|&tid| tid == trait_ref.path.def.def_id());
168         if *binop != expected_ops[idx];
169         then{
170             return Some(traits[idx])
171         }
172     }
173
174     None
175 }
176
177 struct BinaryExprVisitor {
178     in_binary_expr: bool,
179 }
180
181 impl<'a, 'tcx: 'a> Visitor<'tcx> for BinaryExprVisitor {
182     fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
183         if let hir::ExprBinary(_, _, _) = expr.node {
184             self.in_binary_expr = true;
185         }
186
187         walk_expr(self, expr);
188     }
189     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
190         NestedVisitorMap::None
191     }
192 }