]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::span_lint; |
136023e0 | 2 | use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; |
f20569fa XL |
3 | use if_chain::if_chain; |
4 | use rustc_hir as hir; | |
5 | use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; | |
6 | use rustc_lint::{LateContext, LateLintPass}; | |
7 | use rustc_middle::hir::map::Map; | |
8 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
9 | ||
10 | declare_clippy_lint! { | |
11 | /// **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g. | |
12 | /// subtracting elements in an Add impl. | |
13 | /// | |
14 | /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended. | |
15 | /// | |
16 | /// **Known problems:** None. | |
17 | /// | |
18 | /// **Example:** | |
19 | /// ```ignore | |
20 | /// impl Add for Foo { | |
21 | /// type Output = Foo; | |
22 | /// | |
23 | /// fn add(self, other: Foo) -> Foo { | |
24 | /// Foo(self.0 - other.0) | |
25 | /// } | |
26 | /// } | |
27 | /// ``` | |
28 | pub SUSPICIOUS_ARITHMETIC_IMPL, | |
136023e0 | 29 | suspicious, |
f20569fa XL |
30 | "suspicious use of operators in impl of arithmetic trait" |
31 | } | |
32 | ||
33 | declare_clippy_lint! { | |
34 | /// **What it does:** Lints for suspicious operations in impls of OpAssign, e.g. | |
35 | /// subtracting elements in an AddAssign impl. | |
36 | /// | |
37 | /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended. | |
38 | /// | |
39 | /// **Known problems:** None. | |
40 | /// | |
41 | /// **Example:** | |
42 | /// ```ignore | |
43 | /// impl AddAssign for Foo { | |
44 | /// fn add_assign(&mut self, other: Foo) { | |
45 | /// *self = *self - other; | |
46 | /// } | |
47 | /// } | |
48 | /// ``` | |
49 | pub SUSPICIOUS_OP_ASSIGN_IMPL, | |
136023e0 | 50 | suspicious, |
f20569fa XL |
51 | "suspicious use of operators in impl of OpAssign trait" |
52 | } | |
53 | ||
54 | declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]); | |
55 | ||
56 | impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { | |
57 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { | |
136023e0 XL |
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); | |
f20569fa XL |
63 | |
64 | // Check for more than one binary operation in the implemented function | |
65 | // Linting when multiple operations are involved can result in false positives | |
cdc7bbd5 | 66 | let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); |
136023e0 XL |
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 { | |
f20569fa XL |
82 | span_lint( |
83 | cx, | |
136023e0 | 84 | lint, |
f20569fa | 85 | binop.span, |
136023e0 | 86 | &format!("suspicious use of `{}` in `{}` impl", binop.node.as_str(), cx.tcx.item_name(trait_id)), |
f20569fa XL |
87 | ); |
88 | } | |
89 | } | |
90 | } | |
91 | } | |
92 | ||
136023e0 XL |
93 | fn count_binops(expr: &hir::Expr<'_>) -> u32 { |
94 | let mut visitor = BinaryExprVisitor::default(); | |
95 | visitor.visit_expr(expr); | |
96 | visitor.nb_binops | |
f20569fa XL |
97 | } |
98 | ||
136023e0 | 99 | #[derive(Default)] |
f20569fa XL |
100 | struct BinaryExprVisitor { |
101 | nb_binops: u32, | |
102 | } | |
103 | ||
104 | impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { | |
105 | type Map = Map<'tcx>; | |
106 | ||
107 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { | |
108 | match expr.kind { | |
109 | hir::ExprKind::Binary(..) | |
110 | | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _) | |
111 | | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, | |
112 | _ => {}, | |
113 | } | |
114 | ||
115 | walk_expr(self, expr); | |
116 | } | |
117 | ||
118 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { | |
119 | NestedVisitorMap::None | |
120 | } | |
121 | } |