4 use utils
::{differing_macro_contexts, match_type, paths, snippet, span_lint_and_then, walk_ptrs_ty, SpanlessEq}
;
7 /// **What it does:** Checks for manual swapping.
9 /// **Why is this bad?** The `std::mem::swap` function exposes the intent better
10 /// without deinitializing or copying either variable.
12 /// **Known problems:** None.
23 "manual swap of two variables"
26 /// **What it does:** Checks for `foo = bar; bar = foo` sequences.
28 /// **Why is this bad?** This looks like a failed attempt to swap.
30 /// **Known problems:** None.
40 "`foo = bar; bar = foo` sequence"
43 #[derive(Copy, Clone)]
46 impl LintPass
for Swap
{
47 fn get_lints(&self) -> LintArray
{
48 lint_array
![MANUAL_SWAP
, ALMOST_SWAPPED
]
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
);
59 /// Implementation of the `MANUAL_SWAP` lint.
60 fn check_manual_swap(cx
: &LateContext
, block
: &Block
) {
61 for w
in block
.stmts
.windows(3) {
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
;
70 if let StmtSemi(ref first
, _
) = w
[1].node
;
71 if let ExprAssign(ref lhs1
, ref rhs1
) = first
.node
;
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;
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
);
83 fn check_for_slice
<'a
>(
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
));
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
));
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
) {
109 format
!(" elements of `{}`", slice
),
110 format
!("{}.swap({}, {})",
112 snippet(cx
, idx1
.span
, ".."),
113 snippet(cx
, idx2
.span
, "..")))
115 (false, "".to_owned(), "".to_owned())
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()))
121 (true, "".to_owned(), "".to_owned())
124 let span
= w
[0].span
.to(second
.span
);
126 span_lint_and_then(cx
,
129 &format
!("this looks like you are swapping{} manually", what
),
131 if !sugg
.is_empty() {
132 db
.span_suggestion(span
, "try", sugg
);
135 db
.note("or maybe you should use `std::mem::replace`?");
144 /// Implementation of the `ALMOST_SWAPPED` lint.
145 fn check_suspicious_swap(cx
: &LateContext
, block
: &Block
) {
146 for w
in block
.stmts
.windows(2) {
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
);
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
) {
160 format
!(" `{}` and `{}`", first
, second
),
161 first
.mut_addr().to_string(),
162 second
.mut_addr().to_string(),
165 ("".to_owned(), "".to_owned(), "".to_owned())
168 let span
= first
.span
.to(second
.span
);
170 span_lint_and_then(cx
,
173 &format
!("this looks like you are trying to swap{}", what
),
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`?");