]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/swap.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / swap.rs
CommitLineData
94222f64 1use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
cdc7bbd5
XL
2use clippy_utils::source::snippet_with_applicability;
3use clippy_utils::sugg::Sugg;
4use clippy_utils::ty::is_type_diagnostic_item;
5099ac24 5use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core};
f20569fa
XL
6use if_chain::if_chain;
7use rustc_errors::Applicability;
94222f64 8use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
f20569fa
XL
9use rustc_lint::{LateContext, LateLintPass};
10use rustc_middle::ty;
11use rustc_session::{declare_lint_pass, declare_tool_lint};
94222f64
XL
12use rustc_span::source_map::Spanned;
13use rustc_span::{sym, Span};
f20569fa
XL
14
15declare_clippy_lint! {
94222f64
XL
16 /// ### What it does
17 /// Checks for manual swapping.
f20569fa 18 ///
94222f64
XL
19 /// ### Why is this bad?
20 /// The `std::mem::swap` function exposes the intent better
f20569fa
XL
21 /// without deinitializing or copying either variable.
22 ///
94222f64 23 /// ### Example
f20569fa
XL
24 /// ```rust
25 /// let mut a = 42;
26 /// let mut b = 1337;
27 ///
28 /// let t = b;
29 /// b = a;
30 /// a = t;
31 /// ```
32 /// Use std::mem::swap():
33 /// ```rust
34 /// let mut a = 1;
35 /// let mut b = 2;
36 /// std::mem::swap(&mut a, &mut b);
37 /// ```
a2a8927a 38 #[clippy::version = "pre 1.29.0"]
f20569fa
XL
39 pub MANUAL_SWAP,
40 complexity,
41 "manual swap of two variables"
42}
43
44declare_clippy_lint! {
94222f64
XL
45 /// ### What it does
46 /// Checks for `foo = bar; bar = foo` sequences.
f20569fa 47 ///
94222f64
XL
48 /// ### Why is this bad?
49 /// This looks like a failed attempt to swap.
f20569fa 50 ///
94222f64 51 /// ### Example
f20569fa
XL
52 /// ```rust
53 /// # let mut a = 1;
54 /// # let mut b = 2;
55 /// a = b;
56 /// b = a;
57 /// ```
58 /// If swapping is intended, use `swap()` instead:
59 /// ```rust
60 /// # let mut a = 1;
61 /// # let mut b = 2;
62 /// std::mem::swap(&mut a, &mut b);
63 /// ```
a2a8927a 64 #[clippy::version = "pre 1.29.0"]
f20569fa
XL
65 pub ALMOST_SWAPPED,
66 correctness,
67 "`foo = bar; bar = foo` sequence"
68}
69
70declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]);
71
72impl<'tcx> LateLintPass<'tcx> for Swap {
73 fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
74 check_manual_swap(cx, block);
75 check_suspicious_swap(cx, block);
94222f64 76 check_xor_swap(cx, block);
f20569fa
XL
77 }
78}
79
94222f64
XL
80fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
81 let mut applicability = Applicability::MachineApplicable;
82
83 if !can_mut_borrow_both(cx, e1, e2) {
84 if let ExprKind::Index(lhs1, idx1) = e1.kind {
85 if let ExprKind::Index(lhs2, idx2) = e2.kind {
86 if eq_expr_value(cx, lhs1, lhs2) {
87 let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
88
89 if matches!(ty.kind(), ty::Slice(_))
90 || matches!(ty.kind(), ty::Array(_, _))
c295e0f8
XL
91 || is_type_diagnostic_item(cx, ty, sym::Vec)
92 || is_type_diagnostic_item(cx, ty, sym::VecDeque)
94222f64
XL
93 {
94 let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
95 span_lint_and_sugg(
96 cx,
97 MANUAL_SWAP,
98 span,
99 &format!("this looks like you are swapping elements of `{}` manually", slice),
100 "try",
101 format!(
102 "{}.swap({}, {})",
103 slice.maybe_par(),
104 snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
105 snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
106 ),
107 applicability,
108 );
109 }
110 }
111 }
112 }
113 return;
114 }
115
116 let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
117 let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
a2a8927a
XL
118 let Some(sugg) = std_or_core(cx) else { return };
119
94222f64
XL
120 span_lint_and_then(
121 cx,
122 MANUAL_SWAP,
123 span,
124 &format!("this looks like you are swapping `{}` and `{}` manually", first, second),
125 |diag| {
126 diag.span_suggestion(
127 span,
128 "try",
a2a8927a 129 format!("{}::mem::swap({}, {})", sugg, first.mut_addr(), second.mut_addr()),
94222f64
XL
130 applicability,
131 );
132 if !is_xor_based {
a2a8927a 133 diag.note(&format!("or maybe you should use `{}::mem::replace`?", sugg));
94222f64
XL
134 }
135 },
136 );
137}
138
f20569fa
XL
139/// Implementation of the `MANUAL_SWAP` lint.
140fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
141 for w in block.stmts.windows(3) {
142 if_chain! {
143 // let t = foo();
cdc7bbd5
XL
144 if let StmtKind::Local(tmp) = w[0].kind;
145 if let Some(tmp_init) = tmp.init;
f20569fa
XL
146 if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
147
148 // foo() = bar();
cdc7bbd5
XL
149 if let StmtKind::Semi(first) = w[1].kind;
150 if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
f20569fa
XL
151
152 // bar() = t;
cdc7bbd5
XL
153 if let StmtKind::Semi(second) = w[2].kind;
154 if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
155 if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
f20569fa
XL
156 if rhs2.segments.len() == 1;
157
158 if ident.name == rhs2.segments[0].ident.name;
159 if eq_expr_value(cx, tmp_init, lhs1);
160 if eq_expr_value(cx, rhs1, lhs2);
161 then {
f20569fa 162 let span = w[0].span.to(second.span);
94222f64 163 generate_swap_warning(cx, lhs1, lhs2, span, false);
f20569fa
XL
164 }
165 }
166 }
167}
168
f20569fa
XL
169/// Implementation of the `ALMOST_SWAPPED` lint.
170fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
171 for w in block.stmts.windows(2) {
172 if_chain! {
cdc7bbd5
XL
173 if let StmtKind::Semi(first) = w[0].kind;
174 if let StmtKind::Semi(second) = w[1].kind;
5099ac24 175 if first.span.ctxt() == second.span.ctxt();
cdc7bbd5
XL
176 if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
177 if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
f20569fa
XL
178 if eq_expr_value(cx, lhs0, rhs1);
179 if eq_expr_value(cx, lhs1, rhs0);
180 then {
181 let lhs0 = Sugg::hir_opt(cx, lhs0);
182 let rhs0 = Sugg::hir_opt(cx, rhs0);
183 let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
184 (
185 format!(" `{}` and `{}`", first, second),
186 first.mut_addr().to_string(),
187 second.mut_addr().to_string(),
188 )
189 } else {
190 (String::new(), String::new(), String::new())
191 };
192
193 let span = first.span.to(second.span);
a2a8927a 194 let Some(sugg) = std_or_core(cx) else { return };
f20569fa
XL
195
196 span_lint_and_then(cx,
a2a8927a
XL
197 ALMOST_SWAPPED,
198 span,
199 &format!("this looks like you are trying to swap{}", what),
200 |diag| {
201 if !what.is_empty() {
202 diag.span_suggestion(
203 span,
204 "try",
205 format!(
206 "{}::mem::swap({}, {})",
207 sugg,
208 lhs,
209 rhs,
210 ),
211 Applicability::MaybeIncorrect,
212 );
213 diag.note(
214 &format!("or maybe you should use `{}::mem::replace`?", sugg)
215 );
216 }
217 });
f20569fa
XL
218 }
219 }
220 }
221}
94222f64
XL
222
223/// Implementation of the xor case for `MANUAL_SWAP` lint.
224fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
225 for window in block.stmts.windows(3) {
226 if_chain! {
227 if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
228 if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
229 if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
230 if eq_expr_value(cx, lhs0, rhs1);
231 if eq_expr_value(cx, lhs2, rhs1);
232 if eq_expr_value(cx, lhs1, rhs0);
233 if eq_expr_value(cx, lhs1, rhs2);
234 then {
235 let span = window[0].span.to(window[2].span);
236 generate_swap_warning(cx, lhs0, rhs0, span, true);
237 }
238 };
239 }
240}
241
c295e0f8 242/// Returns the lhs and rhs of an xor assignment statement.
94222f64
XL
243fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
244 if let StmtKind::Semi(expr) = stmt.kind {
245 if let ExprKind::AssignOp(
246 Spanned {
247 node: BinOpKind::BitXor,
248 ..
249 },
250 lhs,
251 rhs,
252 ) = expr.kind
253 {
254 return Some((lhs, rhs));
255 }
256 }
257 None
258}