1 use clippy_utils
::diagnostics
::span_lint
;
2 use clippy_utils
::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}
;
3 use if_chain
::if_chain
;
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}
;
10 declare_clippy_lint
! {
12 /// Lints for suspicious operations in impls of arithmetic operators, e.g.
13 /// subtracting elements in an Add impl.
15 /// ### Why is this bad?
16 /// This is probably a typo or copy-and-paste error and not intended.
20 /// impl Add for Foo {
21 /// type Output = Foo;
23 /// fn add(self, other: Foo) -> Foo {
24 /// Foo(self.0 - other.0)
28 pub SUSPICIOUS_ARITHMETIC_IMPL
,
30 "suspicious use of operators in impl of arithmetic trait"
33 declare_clippy_lint
! {
35 /// Lints for suspicious operations in impls of OpAssign, e.g.
36 /// subtracting elements in an AddAssign impl.
38 /// ### Why is this bad?
39 /// This is probably a typo or copy-and-paste error and not intended.
43 /// impl AddAssign for Foo {
44 /// fn add_assign(&mut self, other: Foo) {
45 /// *self = *self - other;
49 pub SUSPICIOUS_OP_ASSIGN_IMPL
,
51 "suspicious use of operators in impl of OpAssign trait"
54 declare_lint_pass
!(SuspiciousImpl
=> [SUSPICIOUS_ARITHMETIC_IMPL
, SUSPICIOUS_OP_ASSIGN_IMPL
]);
56 impl<'tcx
> LateLintPass
<'tcx
> for SuspiciousImpl
{
57 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx hir
::Expr
<'_
>) {
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
);
64 // Check for more than one binary operation in the implemented function
65 // Linting when multiple operations are involved can result in false positives
66 let parent_fn
= cx
.tcx
.hir().get_parent_item(expr
.hir_id
);
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
),
79 .find(|&(ts
, _
)| ts
.iter().any(|&t
| Ok(trait_id
) == cx
.tcx
.lang_items().require(t
)));
80 if count_binops(&body
.value
) == 1;
86 &format
!("suspicious use of `{}` in `{}` impl", binop
.node
.as_str(), cx
.tcx
.item_name(trait_id
)),
93 fn count_binops(expr
: &hir
::Expr
<'_
>) -> u32 {
94 let mut visitor
= BinaryExprVisitor
::default();
95 visitor
.visit_expr(expr
);
100 struct BinaryExprVisitor
{
104 impl<'tcx
> Visitor
<'tcx
> for BinaryExprVisitor
{
105 type Map
= Map
<'tcx
>;
107 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
<'_
>) {
109 hir
::ExprKind
::Binary(..)
110 | hir
::ExprKind
::Unary(hir
::UnOp
::Not
| hir
::UnOp
::Neg
, _
)
111 | hir
::ExprKind
::AssignOp(..) => self.nb_binops
+= 1,
115 walk_expr(self, expr
);
118 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
119 NestedVisitorMap
::None