]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/operators/op_ref.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / operators / op_ref.rs
CommitLineData
064997fb
FG
1use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
2use clippy_utils::get_enclosing_block;
3use clippy_utils::source::snippet;
4use clippy_utils::ty::{implements_trait, is_copy};
5use if_chain::if_chain;
6use rustc_errors::Applicability;
add651ee
FG
7use rustc_hir::def::Res;
8use rustc_hir::def_id::DefId;
9use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
064997fb
FG
10use rustc_lint::LateContext;
11use rustc_middle::ty::{self, Ty};
12
13use super::OP_REF;
14
15#[expect(clippy::similar_names, clippy::too_many_lines)]
16pub(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
178fn 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 204fn 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}