1
1
use super :: MUT_RANGE_BOUND ;
2
- use clippy_utils:: diagnostics:: span_lint ;
3
- use clippy_utils:: { higher, path_to_local} ;
2
+ use clippy_utils:: diagnostics:: span_lint_and_note ;
3
+ use clippy_utils:: { get_enclosing_block , higher, path_to_local} ;
4
4
use if_chain:: if_chain;
5
- use rustc_hir:: { BindingAnnotation , Expr , HirId , Node , PatKind } ;
5
+ use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
6
+ use rustc_hir:: { BindingAnnotation , Expr , ExprKind , HirId , Node , PatKind } ;
6
7
use rustc_infer:: infer:: TyCtxtInferExt ;
7
8
use rustc_lint:: LateContext ;
9
+ use rustc_middle:: hir:: map:: Map ;
8
10
use rustc_middle:: { mir:: FakeReadCause , ty} ;
9
11
use rustc_span:: source_map:: Span ;
10
12
use rustc_typeck:: expr_use_visitor:: { Delegate , ExprUseVisitor , PlaceBase , PlaceWithHirId } ;
11
13
12
14
pub ( super ) fn check ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > , body : & Expr < ' _ > ) {
13
- if let Some ( higher:: Range {
14
- start : Some ( start) ,
15
- end : Some ( end) ,
16
- ..
17
- } ) = higher:: Range :: hir ( arg)
18
- {
19
- let mut_ids = vec ! [ check_for_mutability( cx, start) , check_for_mutability( cx, end) ] ;
20
- if mut_ids[ 0 ] . is_some ( ) || mut_ids[ 1 ] . is_some ( ) {
21
- let ( span_low, span_high) = check_for_mutation ( cx, body, & mut_ids) ;
15
+ if_chain ! {
16
+ if let Some ( higher:: Range {
17
+ start: Some ( start) ,
18
+ end: Some ( end) ,
19
+ ..
20
+ } ) = higher:: Range :: hir( arg) ;
21
+ let ( mut_id_start, mut_id_end) = ( check_for_mutability( cx, start) , check_for_mutability( cx, end) ) ;
22
+ if mut_id_start. is_some( ) || mut_id_end. is_some( ) ;
23
+ then {
24
+ let ( span_low, span_high) = check_for_mutation( cx, body, mut_id_start, mut_id_end) ;
22
25
mut_warn_with_span( cx, span_low) ;
23
26
mut_warn_with_span( cx, span_high) ;
24
27
}
@@ -27,11 +30,13 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
27
30
28
31
fn mut_warn_with_span ( cx : & LateContext < ' _ > , span : Option < Span > ) {
29
32
if let Some ( sp) = span {
30
- span_lint (
33
+ span_lint_and_note (
31
34
cx,
32
35
MUT_RANGE_BOUND ,
33
36
sp,
34
- "attempt to mutate range bound within loop; note that the range of the loop is unchanged" ,
37
+ "attempt to mutate range bound within loop" ,
38
+ None ,
39
+ "the range of the loop is unchanged" ,
35
40
) ;
36
41
}
37
42
}
@@ -51,12 +56,13 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId>
51
56
fn check_for_mutation < ' tcx > (
52
57
cx : & LateContext < ' tcx > ,
53
58
body : & Expr < ' _ > ,
54
- bound_ids : & [ Option < HirId > ] ,
59
+ bound_id_start : Option < HirId > ,
60
+ bound_id_end : Option < HirId > ,
55
61
) -> ( Option < Span > , Option < Span > ) {
56
62
let mut delegate = MutatePairDelegate {
57
63
cx,
58
- hir_id_low : bound_ids [ 0 ] ,
59
- hir_id_high : bound_ids [ 1 ] ,
64
+ hir_id_low : bound_id_start ,
65
+ hir_id_high : bound_id_end ,
60
66
span_low : None ,
61
67
span_high : None ,
62
68
} ;
@@ -70,6 +76,7 @@ fn check_for_mutation<'tcx>(
70
76
)
71
77
. walk_expr ( body) ;
72
78
} ) ;
79
+
73
80
delegate. mutation_span ( )
74
81
}
75
82
@@ -87,10 +94,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
87
94
fn borrow ( & mut self , cmt : & PlaceWithHirId < ' tcx > , diag_expr_id : HirId , bk : ty:: BorrowKind ) {
88
95
if let ty:: BorrowKind :: MutBorrow = bk {
89
96
if let PlaceBase :: Local ( id) = cmt. place . base {
90
- if Some ( id) == self . hir_id_low {
97
+ if Some ( id) == self . hir_id_low && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
91
98
self . span_low = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
92
99
}
93
- if Some ( id) == self . hir_id_high {
100
+ if Some ( id) == self . hir_id_high && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
94
101
self . span_high = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
95
102
}
96
103
}
@@ -99,10 +106,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
99
106
100
107
fn mutate ( & mut self , cmt : & PlaceWithHirId < ' tcx > , diag_expr_id : HirId ) {
101
108
if let PlaceBase :: Local ( id) = cmt. place . base {
102
- if Some ( id) == self . hir_id_low {
109
+ if Some ( id) == self . hir_id_low && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
103
110
self . span_low = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
104
111
}
105
- if Some ( id) == self . hir_id_high {
112
+ if Some ( id) == self . hir_id_high && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
106
113
self . span_high = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
107
114
}
108
115
}
@@ -116,3 +123,52 @@ impl MutatePairDelegate<'_, '_> {
116
123
( self . span_low , self . span_high )
117
124
}
118
125
}
126
+
127
+ struct BreakAfterExprVisitor {
128
+ hir_id : HirId ,
129
+ past_expr : bool ,
130
+ past_candidate : bool ,
131
+ break_after_expr : bool ,
132
+ }
133
+
134
+ impl BreakAfterExprVisitor {
135
+ pub fn is_found ( cx : & LateContext < ' _ > , hir_id : HirId ) -> bool {
136
+ let mut visitor = BreakAfterExprVisitor {
137
+ hir_id,
138
+ past_expr : false ,
139
+ past_candidate : false ,
140
+ break_after_expr : false ,
141
+ } ;
142
+
143
+ get_enclosing_block ( cx, hir_id) . map_or ( false , |block| {
144
+ visitor. visit_block ( block) ;
145
+ visitor. break_after_expr
146
+ } )
147
+ }
148
+ }
149
+
150
+ impl intravisit:: Visitor < ' tcx > for BreakAfterExprVisitor {
151
+ type Map = Map < ' tcx > ;
152
+
153
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
154
+ NestedVisitorMap :: None
155
+ }
156
+
157
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
158
+ if self . past_candidate {
159
+ return ;
160
+ }
161
+
162
+ if expr. hir_id == self . hir_id {
163
+ self . past_expr = true ;
164
+ } else if self . past_expr {
165
+ if matches ! ( & expr. kind, ExprKind :: Break ( ..) ) {
166
+ self . break_after_expr = true ;
167
+ }
168
+
169
+ self . past_candidate = true ;
170
+ } else {
171
+ intravisit:: walk_expr ( self , expr) ;
172
+ }
173
+ }
174
+ }
0 commit comments