1
1
use Context :: * ;
2
2
3
3
use rustc_hir as hir;
4
- use rustc_hir:: def_id:: LocalModDefId ;
4
+ use rustc_hir:: def_id:: { LocalDefId , LocalModDefId } ;
5
5
use rustc_hir:: intravisit:: { self , Visitor } ;
6
6
use rustc_hir:: { Destination , Movability , Node } ;
7
7
use rustc_middle:: hir:: map:: Map ;
@@ -10,19 +10,21 @@ use rustc_middle::query::Providers;
10
10
use rustc_middle:: ty:: TyCtxt ;
11
11
use rustc_session:: Session ;
12
12
use rustc_span:: hygiene:: DesugaringKind ;
13
- use rustc_span:: Span ;
13
+ use rustc_span:: { BytePos , Span } ;
14
14
15
15
use crate :: errors:: {
16
16
BreakInsideAsyncBlock , BreakInsideClosure , BreakNonLoop , ContinueLabeledBlock , OutsideLoop ,
17
- UnlabeledCfInWhileCondition , UnlabeledInLabeledBlock ,
17
+ OutsideLoopSuggestion , UnlabeledCfInWhileCondition , UnlabeledInLabeledBlock ,
18
18
} ;
19
19
20
20
#[ derive( Clone , Copy , Debug , PartialEq ) ]
21
21
enum Context {
22
22
Normal ,
23
+ Fn ,
23
24
Loop ( hir:: LoopSource ) ,
24
25
Closure ( Span ) ,
25
26
AsyncClosure ( Span ) ,
27
+ UnlabeledBlock ( Span ) ,
26
28
LabeledBlock ,
27
29
Constant ,
28
30
}
@@ -60,6 +62,25 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
60
62
self . with_context ( Constant , |v| intravisit:: walk_inline_const ( v, c) ) ;
61
63
}
62
64
65
+ fn visit_fn (
66
+ & mut self ,
67
+ fk : hir:: intravisit:: FnKind < ' hir > ,
68
+ fd : & ' hir hir:: FnDecl < ' hir > ,
69
+ b : hir:: BodyId ,
70
+ _: Span ,
71
+ id : LocalDefId ,
72
+ ) {
73
+ self . with_context ( Fn , |v| intravisit:: walk_fn ( v, fk, fd, b, id) ) ;
74
+ }
75
+
76
+ fn visit_trait_item ( & mut self , trait_item : & ' hir hir:: TraitItem < ' hir > ) {
77
+ self . with_context ( Fn , |v| intravisit:: walk_trait_item ( v, trait_item) ) ;
78
+ }
79
+
80
+ fn visit_impl_item ( & mut self , impl_item : & ' hir hir:: ImplItem < ' hir > ) {
81
+ self . with_context ( Fn , |v| intravisit:: walk_impl_item ( v, impl_item) ) ;
82
+ }
83
+
63
84
fn visit_expr ( & mut self , e : & ' hir hir:: Expr < ' hir > ) {
64
85
match e. kind {
65
86
hir:: ExprKind :: Loop ( ref b, _, source, _) => {
@@ -83,6 +104,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
83
104
hir:: ExprKind :: Block ( ref b, Some ( _label) ) => {
84
105
self . with_context ( LabeledBlock , |v| v. visit_block ( & b) ) ;
85
106
}
107
+ hir:: ExprKind :: Block ( ref b, None ) if matches ! ( self . cx, Fn ) => {
108
+ self . with_context ( Normal , |v| v. visit_block ( & b) ) ;
109
+ }
110
+ hir:: ExprKind :: Block ( ref b, None )
111
+ if matches ! ( self . cx, Normal | Constant | UnlabeledBlock ( _) ) =>
112
+ {
113
+ self . with_context ( UnlabeledBlock ( b. span . shrink_to_lo ( ) ) , |v| v. visit_block ( & b) ) ;
114
+ }
86
115
hir:: ExprKind :: Break ( break_label, ref opt_expr) => {
87
116
if let Some ( e) = opt_expr {
88
117
self . visit_expr ( e) ;
@@ -147,7 +176,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
147
176
}
148
177
}
149
178
150
- self . require_break_cx ( "break" , e. span ) ;
179
+ let sp_lo = e. span . with_lo ( e. span . lo ( ) + BytePos ( "break" . len ( ) as u32 ) ) ;
180
+ let label_sp = match break_label. label {
181
+ Some ( label) => sp_lo. with_hi ( label. ident . span . hi ( ) ) ,
182
+ None => sp_lo. shrink_to_lo ( ) ,
183
+ } ;
184
+ self . require_break_cx ( "break" , e. span , label_sp) ;
151
185
}
152
186
hir:: ExprKind :: Continue ( destination) => {
153
187
self . require_label_in_labeled_block ( e. span , & destination, "continue" ) ;
@@ -169,7 +203,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
169
203
}
170
204
Err ( _) => { }
171
205
}
172
- self . require_break_cx ( "continue" , e. span )
206
+ self . require_break_cx ( "continue" , e. span , e . span )
173
207
}
174
208
_ => intravisit:: walk_expr ( self , e) ,
175
209
}
@@ -187,7 +221,8 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
187
221
self . cx = old_cx;
188
222
}
189
223
190
- fn require_break_cx ( & self , name : & str , span : Span ) {
224
+ fn require_break_cx ( & self , name : & str , span : Span , break_span : Span ) {
225
+ let is_break = name == "break" ;
191
226
match self . cx {
192
227
LabeledBlock | Loop ( _) => { }
193
228
Closure ( closure_span) => {
@@ -196,8 +231,12 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
196
231
AsyncClosure ( closure_span) => {
197
232
self . sess . emit_err ( BreakInsideAsyncBlock { span, closure_span, name } ) ;
198
233
}
199
- Normal | Constant => {
200
- self . sess . emit_err ( OutsideLoop { span, name, is_break : name == "break" } ) ;
234
+ UnlabeledBlock ( block_span) if is_break => {
235
+ let suggestion = Some ( OutsideLoopSuggestion { block_span, break_span } ) ;
236
+ self . sess . emit_err ( OutsideLoop { span, name, is_break, suggestion } ) ;
237
+ }
238
+ Normal | Constant | Fn | UnlabeledBlock ( _) => {
239
+ self . sess . emit_err ( OutsideLoop { span, name, is_break, suggestion : None } ) ;
201
240
}
202
241
}
203
242
}
0 commit comments