|
1 | 1 | use clippy_utils::diagnostics::span_lint; |
2 | | -use clippy_utils::{get_trait_def_id, paths, trait_ref_of_method}; |
| 2 | +use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; |
3 | 3 | use if_chain::if_chain; |
4 | 4 | use rustc_hir as hir; |
5 | 5 | use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; |
@@ -55,135 +55,48 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ |
55 | 55 |
|
56 | 56 | impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { |
57 | 57 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { |
58 | | - if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind { |
59 | | - match binop.node { |
60 | | - hir::BinOpKind::Eq |
61 | | - | hir::BinOpKind::Lt |
62 | | - | hir::BinOpKind::Le |
63 | | - | hir::BinOpKind::Ne |
64 | | - | hir::BinOpKind::Ge |
65 | | - | hir::BinOpKind::Gt => return, |
66 | | - _ => {}, |
67 | | - } |
| 58 | + if_chain! { |
| 59 | + if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind; |
| 60 | + if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node); |
| 61 | + if let Ok(binop_trait_id) = cx.tcx.lang_items().require(binop_trait_lang); |
| 62 | + if let Ok(op_assign_trait_id) = cx.tcx.lang_items().require(op_assign_trait_lang); |
68 | 63 |
|
69 | 64 | // Check for more than one binary operation in the implemented function |
70 | 65 | // Linting when multiple operations are involved can result in false positives |
71 | 66 | let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); |
72 | | - if_chain! { |
73 | | - if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn); |
74 | | - if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; |
75 | | - then { |
76 | | - let body = cx.tcx.hir().body(body_id); |
77 | | - let mut visitor = BinaryExprVisitor { nb_binops: 0 }; |
78 | | - walk_expr(&mut visitor, &body.value); |
79 | | - if visitor.nb_binops > 1 { |
80 | | - return; |
81 | | - } |
82 | | - } |
83 | | - } |
84 | | - |
85 | | - if let Some(impl_trait) = check_binop( |
86 | | - cx, |
87 | | - expr, |
88 | | - binop.node, |
89 | | - &[ |
90 | | - "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr", |
91 | | - ], |
92 | | - &[ |
93 | | - hir::BinOpKind::Add, |
94 | | - hir::BinOpKind::Sub, |
95 | | - hir::BinOpKind::Mul, |
96 | | - hir::BinOpKind::Div, |
97 | | - hir::BinOpKind::Rem, |
98 | | - hir::BinOpKind::BitAnd, |
99 | | - hir::BinOpKind::BitOr, |
100 | | - hir::BinOpKind::BitXor, |
101 | | - hir::BinOpKind::Shl, |
102 | | - hir::BinOpKind::Shr, |
103 | | - ], |
104 | | - ) { |
105 | | - span_lint( |
106 | | - cx, |
107 | | - SUSPICIOUS_ARITHMETIC_IMPL, |
108 | | - binop.span, |
109 | | - &format!("suspicious use of binary operator in `{}` impl", impl_trait), |
110 | | - ); |
111 | | - } |
112 | | - |
113 | | - if let Some(impl_trait) = check_binop( |
114 | | - cx, |
115 | | - expr, |
116 | | - binop.node, |
117 | | - &[ |
118 | | - "AddAssign", |
119 | | - "SubAssign", |
120 | | - "MulAssign", |
121 | | - "DivAssign", |
122 | | - "BitAndAssign", |
123 | | - "BitOrAssign", |
124 | | - "BitXorAssign", |
125 | | - "RemAssign", |
126 | | - "ShlAssign", |
127 | | - "ShrAssign", |
128 | | - ], |
129 | | - &[ |
130 | | - hir::BinOpKind::Add, |
131 | | - hir::BinOpKind::Sub, |
132 | | - hir::BinOpKind::Mul, |
133 | | - hir::BinOpKind::Div, |
134 | | - hir::BinOpKind::BitAnd, |
135 | | - hir::BinOpKind::BitOr, |
136 | | - hir::BinOpKind::BitXor, |
137 | | - hir::BinOpKind::Rem, |
138 | | - hir::BinOpKind::Shl, |
139 | | - hir::BinOpKind::Shr, |
140 | | - ], |
141 | | - ) { |
| 67 | + if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn); |
| 68 | + if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; |
| 69 | + let body = cx.tcx.hir().body(body_id); |
| 70 | + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); |
| 71 | + if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn); |
| 72 | + let trait_id = trait_ref.path.res.def_id(); |
| 73 | + if ![binop_trait_id, op_assign_trait_id].contains(&trait_id); |
| 74 | + if let Some(&(_, lint)) = [ |
| 75 | + (&BINOP_TRAITS, SUSPICIOUS_ARITHMETIC_IMPL), |
| 76 | + (&OP_ASSIGN_TRAITS, SUSPICIOUS_OP_ASSIGN_IMPL), |
| 77 | + ] |
| 78 | + .iter() |
| 79 | + .find(|&(ts, _)| ts.iter().any(|&t| Ok(trait_id) == cx.tcx.lang_items().require(t))); |
| 80 | + if count_binops(&body.value) == 1; |
| 81 | + then { |
142 | 82 | span_lint( |
143 | 83 | cx, |
144 | | - SUSPICIOUS_OP_ASSIGN_IMPL, |
| 84 | + lint, |
145 | 85 | binop.span, |
146 | | - &format!("suspicious use of binary operator in `{}` impl", impl_trait), |
| 86 | + &format!("suspicious use of `{}` in `{}` impl", binop.node.as_str(), cx.tcx.item_name(trait_id)), |
147 | 87 | ); |
148 | 88 | } |
149 | 89 | } |
150 | 90 | } |
151 | 91 | } |
152 | 92 |
|
153 | | -fn check_binop( |
154 | | - cx: &LateContext<'_>, |
155 | | - expr: &hir::Expr<'_>, |
156 | | - binop: hir::BinOpKind, |
157 | | - traits: &[&'static str], |
158 | | - expected_ops: &[hir::BinOpKind], |
159 | | -) -> Option<&'static str> { |
160 | | - let mut trait_ids = vec![]; |
161 | | - let [krate, module] = paths::OPS_MODULE; |
162 | | - |
163 | | - for &t in traits { |
164 | | - let path = [krate, module, t]; |
165 | | - if let Some(trait_id) = get_trait_def_id(cx, &path) { |
166 | | - trait_ids.push(trait_id); |
167 | | - } else { |
168 | | - return None; |
169 | | - } |
170 | | - } |
171 | | - |
172 | | - // Get the actually implemented trait |
173 | | - let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); |
174 | | - |
175 | | - if_chain! { |
176 | | - if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn); |
177 | | - if let Some(idx) = trait_ids.iter().position(|&tid| tid == trait_ref.path.res.def_id()); |
178 | | - if binop != expected_ops[idx]; |
179 | | - then{ |
180 | | - return Some(traits[idx]) |
181 | | - } |
182 | | - } |
183 | | - |
184 | | - None |
| 93 | +fn count_binops(expr: &hir::Expr<'_>) -> u32 { |
| 94 | + let mut visitor = BinaryExprVisitor::default(); |
| 95 | + visitor.visit_expr(expr); |
| 96 | + visitor.nb_binops |
185 | 97 | } |
186 | 98 |
|
| 99 | +#[derive(Default)] |
187 | 100 | struct BinaryExprVisitor { |
188 | 101 | nb_binops: u32, |
189 | 102 | } |
|
0 commit comments