1
+ use rustc_data_structures:: fx:: FxHashSet ;
1
2
use Context :: * ;
2
3
3
4
use rustc_hir as hir;
@@ -9,7 +10,7 @@ use rustc_middle::query::Providers;
9
10
use rustc_middle:: ty:: TyCtxt ;
10
11
use rustc_session:: Session ;
11
12
use rustc_span:: hygiene:: DesugaringKind ;
12
- use rustc_span:: { BytePos , Span } ;
13
+ use rustc_span:: { BytePos , Span , DUMMY_SP } ;
13
14
14
15
use crate :: errors:: {
15
16
BreakInsideAsyncBlock , BreakInsideClosure , BreakNonLoop , ContinueLabeledBlock , OutsideLoop ,
@@ -24,21 +25,33 @@ enum Context {
24
25
Closure ( Span ) ,
25
26
AsyncClosure ( Span ) ,
26
27
UnlabeledBlock ( Span ) ,
28
+ IfUnlabeledBlock ( Span ) ,
27
29
LabeledBlock ,
28
30
Constant ,
29
31
}
30
32
31
- #[ derive( Copy , Clone ) ]
33
+ /// cx_stack is a stack, used for some situation like in `if` block,
34
+ /// we should not suggest adding `'block` label in `if` block,
35
+ /// we can back to outer block and add label there.
36
+ /// fix_label_spans used for recording spans which has added `'block` label,
37
+ /// this can help avoiding suggesting redundant labels.
38
+ #[ derive( Clone ) ]
32
39
struct CheckLoopVisitor < ' a , ' tcx > {
33
40
sess : & ' a Session ,
34
41
tcx : TyCtxt < ' tcx > ,
35
- cx : Context ,
42
+ cx_stack : Vec < Context > ,
43
+ fix_label_spans : FxHashSet < Span > ,
36
44
}
37
45
38
46
fn check_mod_loops ( tcx : TyCtxt < ' _ > , module_def_id : LocalModDefId ) {
39
47
tcx. hir ( ) . visit_item_likes_in_module (
40
48
module_def_id,
41
- & mut CheckLoopVisitor { sess : tcx. sess , tcx, cx : Normal } ,
49
+ & mut CheckLoopVisitor {
50
+ sess : tcx. sess ,
51
+ tcx,
52
+ cx_stack : vec ! [ Normal ] ,
53
+ fix_label_spans : Default :: default ( ) ,
54
+ } ,
42
55
) ;
43
56
}
44
57
@@ -82,6 +95,35 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
82
95
83
96
fn visit_expr ( & mut self , e : & ' hir hir:: Expr < ' hir > ) {
84
97
match e. kind {
98
+ hir:: ExprKind :: If ( cond, then, else_opt) => {
99
+ self . visit_expr ( cond) ;
100
+ if let hir:: ExprKind :: Block ( ref b, None ) = then. kind
101
+ && matches ! (
102
+ self . cx_stack. last( ) . unwrap( ) ,
103
+ Normal | Constant | UnlabeledBlock ( _)
104
+ )
105
+ {
106
+ self . with_context ( IfUnlabeledBlock ( b. span . shrink_to_lo ( ) ) , |v| {
107
+ v. visit_block ( b)
108
+ } ) ;
109
+ } else {
110
+ self . visit_expr ( then) ;
111
+ }
112
+ if let Some ( else_expr) = else_opt {
113
+ if let hir:: ExprKind :: Block ( ref b, None ) = else_expr. kind
114
+ && matches ! (
115
+ self . cx_stack. last( ) . unwrap( ) ,
116
+ Normal | Constant | UnlabeledBlock ( _)
117
+ )
118
+ {
119
+ self . with_context ( IfUnlabeledBlock ( b. span . shrink_to_lo ( ) ) , |v| {
120
+ v. visit_block ( b)
121
+ } ) ;
122
+ } else {
123
+ self . visit_expr ( then) ;
124
+ }
125
+ }
126
+ }
85
127
hir:: ExprKind :: Loop ( ref b, _, source, _) => {
86
128
self . with_context ( Loop ( source) , |v| v. visit_block ( b) ) ;
87
129
}
@@ -102,11 +144,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
102
144
hir:: ExprKind :: Block ( ref b, Some ( _label) ) => {
103
145
self . with_context ( LabeledBlock , |v| v. visit_block ( b) ) ;
104
146
}
105
- hir:: ExprKind :: Block ( ref b, None ) if matches ! ( self . cx , Fn ) => {
147
+ hir:: ExprKind :: Block ( ref b, None ) if matches ! ( self . cx_stack . last ( ) . unwrap ( ) , Fn ) => {
106
148
self . with_context ( Normal , |v| v. visit_block ( b) ) ;
107
149
}
108
150
hir:: ExprKind :: Block ( ref b, None )
109
- if matches ! ( self . cx, Normal | Constant | UnlabeledBlock ( _) ) =>
151
+ if matches ! (
152
+ self . cx_stack. last( ) . unwrap( ) ,
153
+ Normal | Constant | UnlabeledBlock ( _)
154
+ ) =>
110
155
{
111
156
self . with_context ( UnlabeledBlock ( b. span . shrink_to_lo ( ) ) , |v| v. visit_block ( b) ) ;
112
157
}
@@ -179,7 +224,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
179
224
Some ( label) => sp_lo. with_hi ( label. ident . span . hi ( ) ) ,
180
225
None => sp_lo. shrink_to_lo ( ) ,
181
226
} ;
182
- self . require_break_cx ( "break" , e. span , label_sp) ;
227
+ self . require_break_cx ( "break" , e. span , label_sp, self . cx_stack . len ( ) - 1 ) ;
183
228
}
184
229
hir:: ExprKind :: Continue ( destination) => {
185
230
self . require_label_in_labeled_block ( e. span , & destination, "continue" ) ;
@@ -201,7 +246,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
201
246
}
202
247
Err ( _) => { }
203
248
}
204
- self . require_break_cx ( "continue" , e. span , e. span )
249
+ self . require_break_cx ( "continue" , e. span , e. span , self . cx_stack . len ( ) - 1 )
205
250
}
206
251
_ => intravisit:: walk_expr ( self , e) ,
207
252
}
@@ -213,15 +258,14 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
213
258
where
214
259
F : FnOnce ( & mut CheckLoopVisitor < ' a , ' hir > ) ,
215
260
{
216
- let old_cx = self . cx ;
217
- self . cx = cx;
261
+ self . cx_stack . push ( cx) ;
218
262
f ( self ) ;
219
- self . cx = old_cx ;
263
+ self . cx_stack . pop ( ) ;
220
264
}
221
265
222
- fn require_break_cx ( & self , name : & str , span : Span , break_span : Span ) {
266
+ fn require_break_cx ( & mut self , name : & str , span : Span , break_span : Span , cx_pos : usize ) {
223
267
let is_break = name == "break" ;
224
- match self . cx {
268
+ match self . cx_stack [ cx_pos ] {
225
269
LabeledBlock | Loop ( _) => { }
226
270
Closure ( closure_span) => {
227
271
self . sess . dcx ( ) . emit_err ( BreakInsideClosure { span, closure_span, name } ) ;
@@ -230,10 +274,18 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
230
274
self . sess . dcx ( ) . emit_err ( BreakInsideAsyncBlock { span, closure_span, name } ) ;
231
275
}
232
276
UnlabeledBlock ( block_span) if is_break && block_span. eq_ctxt ( break_span) => {
233
- let suggestion = Some ( OutsideLoopSuggestion { block_span, break_span } ) ;
277
+ let suggestion = if !self . fix_label_spans . contains ( & block_span) {
278
+ Some ( OutsideLoopSuggestion { block_span, break_span } )
279
+ } else {
280
+ Some ( OutsideLoopSuggestion { block_span : DUMMY_SP , break_span } )
281
+ } ;
234
282
self . sess . dcx ( ) . emit_err ( OutsideLoop { span, name, is_break, suggestion } ) ;
283
+ self . fix_label_spans . insert ( block_span) ;
284
+ }
285
+ IfUnlabeledBlock ( _) if is_break => {
286
+ self . require_break_cx ( name, span, break_span, cx_pos - 1 ) ;
235
287
}
236
- Normal | Constant | Fn | UnlabeledBlock ( _) => {
288
+ Normal | Constant | Fn | UnlabeledBlock ( _) | IfUnlabeledBlock ( _ ) => {
237
289
self . sess . dcx ( ) . emit_err ( OutsideLoop { span, name, is_break, suggestion : None } ) ;
238
290
}
239
291
}
@@ -246,7 +298,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
246
298
cf_type : & str ,
247
299
) -> bool {
248
300
if !span. is_desugaring ( DesugaringKind :: QuestionMark )
249
- && self . cx == LabeledBlock
301
+ && * self . cx_stack . last ( ) . unwrap ( ) == LabeledBlock
250
302
&& label. label . is_none ( )
251
303
{
252
304
self . sess . dcx ( ) . emit_err ( UnlabeledInLabeledBlock { span, cf_type } ) ;
0 commit comments