|
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