]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | use rustc::hir::*; |
2 | use rustc::lint::*; | |
3 | use rustc::ty; | |
4 | use utils::{differing_macro_contexts, match_type, paths, snippet, span_lint_and_then, walk_ptrs_ty, SpanlessEq}; | |
5 | use utils::sugg::Sugg; | |
6 | ||
7 | /// **What it does:** Checks for manual swapping. | |
8 | /// | |
9 | /// **Why is this bad?** The `std::mem::swap` function exposes the intent better | |
10 | /// without deinitializing or copying either variable. | |
11 | /// | |
12 | /// **Known problems:** None. | |
13 | /// | |
14 | /// **Example:** | |
15 | /// ```rust,ignore | |
16 | /// let t = b; | |
17 | /// b = a; | |
18 | /// a = t; | |
19 | /// ``` | |
20 | declare_lint! { | |
21 | pub MANUAL_SWAP, | |
22 | Warn, | |
23 | "manual swap of two variables" | |
24 | } | |
25 | ||
26 | /// **What it does:** Checks for `foo = bar; bar = foo` sequences. | |
27 | /// | |
28 | /// **Why is this bad?** This looks like a failed attempt to swap. | |
29 | /// | |
30 | /// **Known problems:** None. | |
31 | /// | |
32 | /// **Example:** | |
33 | /// ```rust,ignore | |
34 | /// a = b; | |
35 | /// b = a; | |
36 | /// ``` | |
37 | declare_lint! { | |
38 | pub ALMOST_SWAPPED, | |
39 | Warn, | |
40 | "`foo = bar; bar = foo` sequence" | |
41 | } | |
42 | ||
43 | #[derive(Copy, Clone)] | |
44 | pub struct Swap; | |
45 | ||
46 | impl LintPass for Swap { | |
47 | fn get_lints(&self) -> LintArray { | |
48 | lint_array![MANUAL_SWAP, ALMOST_SWAPPED] | |
49 | } | |
50 | } | |
51 | ||
52 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap { | |
53 | fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) { | |
54 | check_manual_swap(cx, block); | |
55 | check_suspicious_swap(cx, block); | |
56 | } | |
57 | } | |
58 | ||
59 | /// Implementation of the `MANUAL_SWAP` lint. | |
60 | fn check_manual_swap(cx: &LateContext, block: &Block) { | |
61 | for w in block.stmts.windows(3) { | |
abe05a73 | 62 | if_chain! { |
ea8adc8c | 63 | // let t = foo(); |
abe05a73 XL |
64 | if let StmtDecl(ref tmp, _) = w[0].node; |
65 | if let DeclLocal(ref tmp) = tmp.node; | |
66 | if let Some(ref tmp_init) = tmp.init; | |
67 | if let PatKind::Binding(_, _, ref tmp_name, None) = tmp.pat.node; | |
ea8adc8c XL |
68 | |
69 | // foo() = bar(); | |
abe05a73 XL |
70 | if let StmtSemi(ref first, _) = w[1].node; |
71 | if let ExprAssign(ref lhs1, ref rhs1) = first.node; | |
ea8adc8c XL |
72 | |
73 | // bar() = t; | |
abe05a73 XL |
74 | if let StmtSemi(ref second, _) = w[2].node; |
75 | if let ExprAssign(ref lhs2, ref rhs2) = second.node; | |
76 | if let ExprPath(QPath::Resolved(None, ref rhs2)) = rhs2.node; | |
77 | if rhs2.segments.len() == 1; | |
78 | ||
79 | if tmp_name.node.as_str() == rhs2.segments[0].name.as_str(); | |
80 | if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1); | |
81 | if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2); | |
82 | then { | |
83 | fn check_for_slice<'a>( | |
84 | cx: &LateContext, | |
85 | lhs1: &'a Expr, | |
86 | lhs2: &'a Expr, | |
87 | ) -> Option<(&'a Expr, &'a Expr, &'a Expr)> { | |
88 | if let ExprIndex(ref lhs1, ref idx1) = lhs1.node { | |
89 | if let ExprIndex(ref lhs2, ref idx2) = lhs2.node { | |
90 | if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) { | |
91 | let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1)); | |
92 | ||
93 | if matches!(ty.sty, ty::TySlice(_)) || | |
94 | matches!(ty.sty, ty::TyArray(_, _)) || | |
95 | match_type(cx, ty, &paths::VEC) || | |
96 | match_type(cx, ty, &paths::VEC_DEQUE) { | |
97 | return Some((lhs1, idx1, idx2)); | |
98 | } | |
ea8adc8c XL |
99 | } |
100 | } | |
101 | } | |
ea8adc8c | 102 | |
abe05a73 XL |
103 | None |
104 | } | |
ea8adc8c | 105 | |
abe05a73 XL |
106 | let (replace, what, sugg) = if let Some((slice, idx1, idx2)) = check_for_slice(cx, lhs1, lhs2) { |
107 | if let Some(slice) = Sugg::hir_opt(cx, slice) { | |
108 | (false, | |
109 | format!(" elements of `{}`", slice), | |
110 | format!("{}.swap({}, {})", | |
111 | slice.maybe_par(), | |
112 | snippet(cx, idx1.span, ".."), | |
113 | snippet(cx, idx2.span, ".."))) | |
114 | } else { | |
115 | (false, "".to_owned(), "".to_owned()) | |
116 | } | |
117 | } else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) { | |
118 | (true, format!(" `{}` and `{}`", first, second), | |
119 | format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr())) | |
ea8adc8c | 120 | } else { |
abe05a73 XL |
121 | (true, "".to_owned(), "".to_owned()) |
122 | }; | |
123 | ||
124 | let span = w[0].span.to(second.span); | |
125 | ||
126 | span_lint_and_then(cx, | |
127 | MANUAL_SWAP, | |
128 | span, | |
129 | &format!("this looks like you are swapping{} manually", what), | |
130 | |db| { | |
131 | if !sugg.is_empty() { | |
132 | db.span_suggestion(span, "try", sugg); | |
133 | ||
134 | if replace { | |
135 | db.note("or maybe you should use `std::mem::replace`?"); | |
136 | } | |
ea8adc8c | 137 | } |
abe05a73 XL |
138 | }); |
139 | } | |
140 | } | |
ea8adc8c XL |
141 | } |
142 | } | |
143 | ||
144 | /// Implementation of the `ALMOST_SWAPPED` lint. | |
145 | fn check_suspicious_swap(cx: &LateContext, block: &Block) { | |
146 | for w in block.stmts.windows(2) { | |
abe05a73 XL |
147 | if_chain! { |
148 | if let StmtSemi(ref first, _) = w[0].node; | |
149 | if let StmtSemi(ref second, _) = w[1].node; | |
150 | if !differing_macro_contexts(first.span, second.span); | |
151 | if let ExprAssign(ref lhs0, ref rhs0) = first.node; | |
152 | if let ExprAssign(ref lhs1, ref rhs1) = second.node; | |
153 | if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1); | |
154 | if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0); | |
155 | then { | |
156 | let lhs0 = Sugg::hir_opt(cx, lhs0); | |
157 | let rhs0 = Sugg::hir_opt(cx, rhs0); | |
158 | let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) { | |
159 | ( | |
160 | format!(" `{}` and `{}`", first, second), | |
161 | first.mut_addr().to_string(), | |
162 | second.mut_addr().to_string(), | |
163 | ) | |
164 | } else { | |
165 | ("".to_owned(), "".to_owned(), "".to_owned()) | |
166 | }; | |
167 | ||
168 | let span = first.span.to(second.span); | |
169 | ||
170 | span_lint_and_then(cx, | |
171 | ALMOST_SWAPPED, | |
172 | span, | |
173 | &format!("this looks like you are trying to swap{}", what), | |
174 | |db| { | |
175 | if !what.is_empty() { | |
176 | db.span_suggestion(span, "try", | |
177 | format!("std::mem::swap({}, {})", lhs, rhs)); | |
178 | db.note("or maybe you should use `std::mem::replace`?"); | |
179 | } | |
180 | }); | |
181 | } | |
182 | } | |
ea8adc8c XL |
183 | } |
184 | } |