1
- use clippy_utils:: diagnostics:: { span_lint , span_lint_hir } ;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_sugg , span_lint_hir_and_then } ;
2
2
use clippy_utils:: higher;
3
- use rustc_hir:: { self as hir, AmbigArg , intravisit} ;
3
+ use clippy_utils:: source:: snippet_with_applicability;
4
+ use clippy_utils:: sugg:: Sugg ;
5
+ use rustc_data_structures:: fx:: FxHashSet ;
6
+ use rustc_errors:: Applicability ;
7
+ use rustc_hir:: { self as hir, AmbigArg , BorrowKind , Expr , ExprKind , HirId , Mutability , TyKind , intravisit} ;
4
8
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
5
9
use rustc_middle:: ty;
6
- use rustc_session:: declare_lint_pass ;
10
+ use rustc_session:: impl_lint_pass ;
7
11
8
12
declare_clippy_lint ! {
9
13
/// ### What it does
10
14
/// Checks for instances of `mut mut` references.
11
15
///
12
16
/// ### Why is this bad?
13
- /// Multiple `mut`s don't add anything meaningful to the
14
- /// source. This is either a copy'n'paste error, or it shows a fundamental
15
- /// misunderstanding of references.
17
+ /// This is usually just a typo or a misunderstanding of how references work.
16
18
///
17
19
/// ### Example
18
20
/// ```no_run
19
- /// # let mut y = 1;
20
- /// let x = &mut &mut y;
21
+ /// let x = &mut &mut 1;
22
+ ///
23
+ /// let mut x = &mut 1;
24
+ /// let y = &mut x;
25
+ ///
26
+ /// fn foo(x: &mut &mut u32) {}
27
+ /// ```
28
+ /// Use instead
29
+ /// ```no_run
30
+ /// let x = &mut 1;
31
+ ///
32
+ /// let mut x = &mut 1;
33
+ /// let y = &mut *x; // reborrow
34
+ ///
35
+ /// fn foo(x: &mut u32) {}
21
36
/// ```
22
37
#[ clippy:: version = "pre 1.29.0" ]
23
38
pub MUT_MUT ,
24
39
pedantic,
25
- "usage of double-mut refs, e.g., `&mut &mut ...`"
40
+ "usage of double mut- refs, e.g., `&mut &mut ...`"
26
41
}
27
42
28
- declare_lint_pass ! ( MutMut => [ MUT_MUT ] ) ;
43
+ impl_lint_pass ! ( MutMut => [ MUT_MUT ] ) ;
44
+
45
+ #[ derive( Default ) ]
46
+ pub ( crate ) struct MutMut {
47
+ seen_tys : FxHashSet < HirId > ,
48
+ }
29
49
30
50
impl < ' tcx > LateLintPass < ' tcx > for MutMut {
31
51
fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx hir:: Block < ' _ > ) {
32
52
intravisit:: walk_block ( & mut MutVisitor { cx } , block) ;
33
53
}
34
54
35
55
fn check_ty ( & mut self , cx : & LateContext < ' tcx > , ty : & ' tcx hir:: Ty < ' _ , AmbigArg > ) {
36
- if let hir :: TyKind :: Ref ( _, mty) = ty. kind
37
- && mty. mutbl == hir :: Mutability :: Mut
38
- && let hir :: TyKind :: Ref ( _, mty ) = mty. ty . kind
39
- && mty . mutbl == hir :: Mutability :: Mut
56
+ if let TyKind :: Ref ( _, mty) = ty. kind
57
+ && mty. mutbl == Mutability :: Mut
58
+ && let TyKind :: Ref ( _, mty2 ) = mty. ty . kind
59
+ && mty2 . mutbl == Mutability :: Mut
40
60
&& !ty. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
41
61
{
42
- span_lint (
62
+ if self . seen_tys . contains ( & ty. hir_id ) {
63
+ // we have 2+ `&mut`s, e.g., `&mut &mut &mut x`
64
+ // and we have already flagged on the outermost `&mut &mut (&mut x)`,
65
+ // so don't flag the inner `&mut &mut (x)`
66
+ return ;
67
+ }
68
+
69
+ // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off
70
+ // all extra ones at once
71
+ let ( mut t, mut t2) = ( mty. ty , mty2. ty ) ;
72
+ let mut many_muts = false ;
73
+ loop {
74
+ // this should allow us to remember all the nested types, so that the `contains`
75
+ // above fails faster
76
+ self . seen_tys . insert ( t. hir_id ) ;
77
+ if let TyKind :: Ref ( _, next) = t2. kind
78
+ && next. mutbl == Mutability :: Mut
79
+ {
80
+ ( t, t2) = ( t2, next. ty ) ;
81
+ many_muts = true ;
82
+ } else {
83
+ break ;
84
+ }
85
+ }
86
+
87
+ let mut applicability = Applicability :: MaybeIncorrect ;
88
+ let sugg = snippet_with_applicability ( cx. sess ( ) , t. span , ".." , & mut applicability) ;
89
+ let suffix = if many_muts { "s" } else { "" } ;
90
+ span_lint_and_sugg (
43
91
cx,
44
92
MUT_MUT ,
45
93
ty. span ,
46
- "generally you want to avoid `&mut &mut _` if possible" ,
94
+ "a type of form `&mut &mut _`" ,
95
+ format ! ( "remove the extra `&mut`{suffix}" ) ,
96
+ sugg. to_string ( ) ,
97
+ applicability,
47
98
) ;
48
99
}
49
100
}
@@ -54,7 +105,7 @@ pub struct MutVisitor<'a, 'tcx> {
54
105
}
55
106
56
107
impl < ' tcx > intravisit:: Visitor < ' tcx > for MutVisitor < ' _ , ' tcx > {
57
- fn visit_expr ( & mut self , expr : & ' tcx hir :: Expr < ' _ > ) {
108
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' _ > ) {
58
109
if expr. span . in_external_macro ( self . cx . sess ( ) . source_map ( ) ) {
59
110
return ;
60
111
}
@@ -68,24 +119,60 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> {
68
119
// Let's ignore the generated code.
69
120
intravisit:: walk_expr ( self , arg) ;
70
121
intravisit:: walk_expr ( self , body) ;
71
- } else if let hir:: ExprKind :: AddrOf ( hir:: BorrowKind :: Ref , hir:: Mutability :: Mut , e) = expr. kind {
72
- if let hir:: ExprKind :: AddrOf ( hir:: BorrowKind :: Ref , hir:: Mutability :: Mut , _) = e. kind {
73
- span_lint_hir (
122
+ } else if let ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mut , e) = expr. kind {
123
+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mut , e2) = e. kind {
124
+ if !expr. span . eq_ctxt ( e. span ) {
125
+ return ;
126
+ }
127
+
128
+ // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off
129
+ // all extra ones at once
130
+ let ( mut e, mut e2) = ( e, e2) ;
131
+ let mut many_muts = false ;
132
+ loop {
133
+ if !e. span . eq_ctxt ( e2. span ) {
134
+ return ;
135
+ }
136
+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mut , next) = e2. kind {
137
+ ( e, e2) = ( e2, next) ;
138
+ many_muts = true ;
139
+ } else {
140
+ break ;
141
+ }
142
+ }
143
+
144
+ let mut applicability = Applicability :: MaybeIncorrect ;
145
+ let sugg = Sugg :: hir_with_applicability ( self . cx , e, ".." , & mut applicability) ;
146
+ let suffix = if many_muts { "s" } else { "" } ;
147
+ span_lint_hir_and_then (
74
148
self . cx ,
75
149
MUT_MUT ,
76
150
expr. hir_id ,
77
151
expr. span ,
78
- "generally you want to avoid `&mut &mut _` if possible" ,
152
+ "an expression of form `&mut &mut _`" ,
153
+ |diag| {
154
+ diag. span_suggestion (
155
+ expr. span ,
156
+ format ! ( "remove the extra `&mut`{suffix}" ) ,
157
+ sugg,
158
+ applicability,
159
+ ) ;
160
+ } ,
79
161
) ;
80
- } else if let ty:: Ref ( _, ty, hir :: Mutability :: Mut ) = self . cx . typeck_results ( ) . expr_ty ( e) . kind ( )
162
+ } else if let ty:: Ref ( _, ty, Mutability :: Mut ) = self . cx . typeck_results ( ) . expr_ty ( e) . kind ( )
81
163
&& ty. peel_refs ( ) . is_sized ( self . cx . tcx , self . cx . typing_env ( ) )
82
164
{
83
- span_lint_hir (
165
+ let mut applicability = Applicability :: MaybeIncorrect ;
166
+ let sugg = Sugg :: hir_with_applicability ( self . cx , e, ".." , & mut applicability) . mut_addr_deref ( ) ;
167
+ span_lint_hir_and_then (
84
168
self . cx ,
85
169
MUT_MUT ,
86
170
expr. hir_id ,
87
171
expr. span ,
88
- "this expression mutably borrows a mutable reference. Consider reborrowing" ,
172
+ "this expression mutably borrows a mutable reference" ,
173
+ |diag| {
174
+ diag. span_suggestion ( expr. span , "reborrow instead" , sugg, applicability) ;
175
+ } ,
89
176
) ;
90
177
}
91
178
}
0 commit comments