]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; |
2 | use clippy_utils::get_enclosing_block; | |
3 | use clippy_utils::source::snippet; | |
4 | use clippy_utils::ty::{implements_trait, is_copy}; | |
5 | use if_chain::if_chain; | |
6 | use rustc_errors::Applicability; | |
add651ee FG |
7 | use rustc_hir::def::Res; |
8 | use rustc_hir::def_id::DefId; | |
9 | use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind}; | |
064997fb FG |
10 | use rustc_lint::LateContext; |
11 | use rustc_middle::ty::{self, Ty}; | |
12 | ||
13 | use super::OP_REF; | |
14 | ||
15 | #[expect(clippy::similar_names, clippy::too_many_lines)] | |
16 | pub(crate) fn check<'tcx>( | |
17 | cx: &LateContext<'tcx>, | |
18 | e: &'tcx Expr<'_>, | |
19 | op: BinOpKind, | |
20 | left: &'tcx Expr<'_>, | |
21 | right: &'tcx Expr<'_>, | |
22 | ) { | |
23 | let (trait_id, requires_ref) = match op { | |
24 | BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false), | |
25 | BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false), | |
26 | BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false), | |
27 | BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false), | |
28 | BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false), | |
29 | // don't lint short circuiting ops | |
30 | BinOpKind::And | BinOpKind::Or => return, | |
31 | BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false), | |
32 | BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false), | |
33 | BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false), | |
34 | BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false), | |
35 | BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false), | |
36 | BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true), | |
37 | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => { | |
38 | (cx.tcx.lang_items().partial_ord_trait(), true) | |
39 | }, | |
40 | }; | |
41 | if let Some(trait_id) = trait_id { | |
42 | match (&left.kind, &right.kind) { | |
43 | // do not suggest to dereference literals | |
44 | (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, | |
45 | // &foo == &bar | |
46 | (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { | |
47 | let lty = cx.typeck_results().expr_ty(l); | |
48 | let rty = cx.typeck_results().expr_ty(r); | |
49 | let lcpy = is_copy(cx, lty); | |
50 | let rcpy = is_copy(cx, rty); | |
51 | if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { | |
52 | if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) | |
53 | || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) | |
54 | { | |
55 | return; // Don't lint | |
56 | } | |
57 | } | |
58 | // either operator autorefs or both args are copyable | |
59 | if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) { | |
60 | span_lint_and_then( | |
61 | cx, | |
62 | OP_REF, | |
63 | e.span, | |
64 | "needlessly taken reference of both operands", | |
65 | |diag| { | |
66 | let lsnip = snippet(cx, l.span, "...").to_string(); | |
67 | let rsnip = snippet(cx, r.span, "...").to_string(); | |
68 | multispan_sugg( | |
69 | diag, | |
70 | "use the values directly", | |
71 | vec![(left.span, lsnip), (right.span, rsnip)], | |
72 | ); | |
73 | }, | |
74 | ); | |
75 | } else if lcpy | |
76 | && !rcpy | |
77 | && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()]) | |
78 | { | |
79 | span_lint_and_then( | |
80 | cx, | |
81 | OP_REF, | |
82 | e.span, | |
83 | "needlessly taken reference of left operand", | |
84 | |diag| { | |
85 | let lsnip = snippet(cx, l.span, "...").to_string(); | |
86 | diag.span_suggestion( | |
87 | left.span, | |
88 | "use the left value directly", | |
89 | lsnip, | |
90 | Applicability::MaybeIncorrect, // FIXME #2597 | |
91 | ); | |
92 | }, | |
93 | ); | |
94 | } else if !lcpy | |
95 | && rcpy | |
96 | && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) | |
97 | { | |
98 | span_lint_and_then( | |
99 | cx, | |
100 | OP_REF, | |
101 | e.span, | |
102 | "needlessly taken reference of right operand", | |
103 | |diag| { | |
104 | let rsnip = snippet(cx, r.span, "...").to_string(); | |
105 | diag.span_suggestion( | |
106 | right.span, | |
107 | "use the right value directly", | |
108 | rsnip, | |
109 | Applicability::MaybeIncorrect, // FIXME #2597 | |
110 | ); | |
111 | }, | |
112 | ); | |
113 | } | |
114 | }, | |
115 | // &foo == bar | |
116 | (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => { | |
117 | let lty = cx.typeck_results().expr_ty(l); | |
118 | if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { | |
119 | let rty = cx.typeck_results().expr_ty(right); | |
120 | if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) | |
121 | || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) | |
122 | { | |
123 | return; // Don't lint | |
124 | } | |
125 | } | |
126 | let lcpy = is_copy(cx, lty); | |
127 | if (requires_ref || lcpy) | |
128 | && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()]) | |
129 | { | |
130 | span_lint_and_then( | |
131 | cx, | |
132 | OP_REF, | |
133 | e.span, | |
134 | "needlessly taken reference of left operand", | |
135 | |diag| { | |
136 | let lsnip = snippet(cx, l.span, "...").to_string(); | |
137 | diag.span_suggestion( | |
138 | left.span, | |
139 | "use the left value directly", | |
140 | lsnip, | |
141 | Applicability::MaybeIncorrect, // FIXME #2597 | |
142 | ); | |
143 | }, | |
144 | ); | |
145 | } | |
146 | }, | |
147 | // foo == &bar | |
148 | (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { | |
149 | let rty = cx.typeck_results().expr_ty(r); | |
150 | if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { | |
151 | let lty = cx.typeck_results().expr_ty(left); | |
152 | if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) | |
153 | || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) | |
154 | { | |
155 | return; // Don't lint | |
156 | } | |
157 | } | |
158 | let rcpy = is_copy(cx, rty); | |
159 | if (requires_ref || rcpy) | |
160 | && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) | |
161 | { | |
162 | span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| { | |
163 | let rsnip = snippet(cx, r.span, "...").to_string(); | |
164 | diag.span_suggestion( | |
165 | right.span, | |
166 | "use the right value directly", | |
167 | rsnip, | |
168 | Applicability::MaybeIncorrect, // FIXME #2597 | |
169 | ); | |
170 | }); | |
171 | } | |
172 | }, | |
173 | _ => {}, | |
174 | } | |
175 | } | |
176 | } | |
177 | ||
178 | fn in_impl<'tcx>( | |
179 | cx: &LateContext<'tcx>, | |
180 | e: &'tcx Expr<'_>, | |
181 | bin_op: DefId, | |
182 | ) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> { | |
183 | if_chain! { | |
184 | if let Some(block) = get_enclosing_block(cx, e.hir_id); | |
185 | if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()); | |
186 | let item = cx.tcx.hir().expect_item(impl_def_id.expect_local()); | |
187 | if let ItemKind::Impl(item) = &item.kind; | |
188 | if let Some(of_trait) = &item.of_trait; | |
189 | if let Some(seg) = of_trait.path.segments.last(); | |
f2b60f7d | 190 | if let Res::Def(_, trait_id) = seg.res; |
064997fb FG |
191 | if trait_id == bin_op; |
192 | if let Some(generic_args) = seg.args; | |
193 | if let Some(GenericArg::Type(other_ty)) = generic_args.args.last(); | |
194 | ||
195 | then { | |
196 | Some((item.self_ty, other_ty)) | |
197 | } | |
198 | else { | |
199 | None | |
200 | } | |
201 | } | |
202 | } | |
203 | ||
487cf647 | 204 | fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { |
064997fb FG |
205 | if_chain! { |
206 | if let ty::Adt(adt_def, _) = middle_ty.kind(); | |
207 | if let Some(local_did) = adt_def.did().as_local(); | |
208 | let item = cx.tcx.hir().expect_item(local_did); | |
2b03887a | 209 | let middle_ty_id = item.owner_id.to_def_id(); |
064997fb FG |
210 | if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; |
211 | if let Res::Def(_, hir_ty_id) = path.res; | |
212 | ||
213 | then { | |
214 | hir_ty_id == middle_ty_id | |
215 | } | |
216 | else { | |
217 | false | |
218 | } | |
219 | } | |
220 | } |