1- use clippy_utils:: diagnostics:: { span_lint , span_lint_hir } ;
1+ use clippy_utils:: diagnostics:: { span_lint_and_sugg , span_lint_hir_and_then } ;
22use 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} ;
48use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
59use rustc_middle:: ty;
6- use rustc_session:: declare_lint_pass ;
10+ use rustc_session:: impl_lint_pass ;
711
812declare_clippy_lint ! {
913 /// ### What it does
1014 /// Checks for instances of `mut mut` references.
1115 ///
1216 /// ### 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.
1618 ///
1719 /// ### Example
1820 /// ```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) {}
2136 /// ```
2237 #[ clippy:: version = "pre 1.29.0" ]
2338 pub MUT_MUT ,
2439 pedantic,
25- "usage of double-mut refs, e.g., `&mut &mut ...`"
40+ "usage of double mut- refs, e.g., `&mut &mut ...`"
2641}
2742
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+ }
2949
3050impl < ' tcx > LateLintPass < ' tcx > for MutMut {
3151 fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx hir:: Block < ' _ > ) {
3252 intravisit:: walk_block ( & mut MutVisitor { cx } , block) ;
3353 }
3454
3555 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
4060 && !ty. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
4161 {
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 (
4391 cx,
4492 MUT_MUT ,
4593 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,
4798 ) ;
4899 }
49100 }
@@ -54,7 +105,7 @@ pub struct MutVisitor<'a, 'tcx> {
54105}
55106
56107impl < ' 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 < ' _ > ) {
58109 if expr. span . in_external_macro ( self . cx . sess ( ) . source_map ( ) ) {
59110 return ;
60111 }
@@ -68,24 +119,60 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> {
68119 // Let's ignore the generated code.
69120 intravisit:: walk_expr ( self , arg) ;
70121 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 (
74148 self . cx ,
75149 MUT_MUT ,
76150 expr. hir_id ,
77151 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+ } ,
79161 ) ;
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 ( )
81163 && ty. peel_refs ( ) . is_sized ( self . cx . tcx , self . cx . typing_env ( ) )
82164 {
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 (
84168 self . cx ,
85169 MUT_MUT ,
86170 expr. hir_id ,
87171 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+ } ,
89176 ) ;
90177 }
91178 }
0 commit comments