@@ -13,7 +13,7 @@ use rustc::session::Session;
13
13
14
14
use rustc:: hir:: map:: Map ;
15
15
use rustc:: hir:: intravisit:: { self , Visitor , NestedVisitorMap } ;
16
- use rustc:: hir;
16
+ use rustc:: hir:: { self , Destination } ;
17
17
use syntax:: ast;
18
18
use syntax_pos:: Span ;
19
19
@@ -39,6 +39,7 @@ enum Context {
39
39
Normal ,
40
40
Loop ( LoopKind ) ,
41
41
Closure ,
42
+ LabeledBlock ,
42
43
}
43
44
44
45
#[ derive( Copy , Clone ) ]
@@ -84,7 +85,16 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
84
85
hir:: ExprClosure ( .., b, _, _) => {
85
86
self . with_context ( Closure , |v| v. visit_nested_body ( b) ) ;
86
87
}
88
+ hir:: ExprBlock ( ref b, Some ( _label) ) => {
89
+ self . with_context ( LabeledBlock , |v| v. visit_block ( & b) ) ;
90
+ }
87
91
hir:: ExprBreak ( label, ref opt_expr) => {
92
+ if self . require_label_in_labeled_block ( e. span , & label, "break" ) {
93
+ // If we emitted an error about an unlabeled break in a labeled
94
+ // block, we don't need any further checking for this break any more
95
+ return ;
96
+ }
97
+
88
98
let loop_id = match label. target_id . into ( ) {
89
99
Ok ( loop_id) => loop_id,
90
100
Err ( hir:: LoopIdError :: OutsideLoopScope ) => ast:: DUMMY_NODE_ID ,
@@ -94,6 +104,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
94
104
} ,
95
105
Err ( hir:: LoopIdError :: UnresolvedLabel ) => ast:: DUMMY_NODE_ID ,
96
106
} ;
107
+
97
108
if loop_id != ast:: DUMMY_NODE_ID {
98
109
match self . hir_map . find ( loop_id) . unwrap ( ) {
99
110
hir:: map:: NodeBlock ( _) => return ,
@@ -113,13 +124,15 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
113
124
} )
114
125
} ;
115
126
match loop_kind {
116
- None | Some ( LoopKind :: Loop ( hir:: LoopSource :: Loop ) ) => ( ) ,
127
+ None |
128
+ Some ( LoopKind :: Loop ( hir:: LoopSource :: Loop ) ) => ( ) ,
117
129
Some ( kind) => {
118
130
struct_span_err ! ( self . sess, e. span, E0571 ,
119
131
"`break` with value from a `{}` loop" ,
120
132
kind. name( ) )
121
133
. span_label ( e. span ,
122
- "can only break with a value inside `loop`" )
134
+ "can only break with a value inside \
135
+ `loop` or breakable block")
123
136
. span_suggestion ( e. span ,
124
137
& format ! ( "instead, use `break` on its own \
125
138
without a value inside this `{}` loop",
@@ -130,13 +143,29 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
130
143
}
131
144
}
132
145
133
- self . require_loop ( "break" , e. span ) ;
146
+ self . require_break_cx ( "break" , e. span ) ;
134
147
}
135
148
hir:: ExprAgain ( label) => {
136
- if let Err ( hir:: LoopIdError :: UnlabeledCfInWhileCondition ) = label. target_id {
137
- self . emit_unlabled_cf_in_while_condition ( e. span , "continue" ) ;
149
+ self . require_label_in_labeled_block ( e. span , & label, "continue" ) ;
150
+
151
+ match label. target_id {
152
+ Ok ( loop_id) => {
153
+ if let hir:: map:: NodeBlock ( block) = self . hir_map . find ( loop_id) . unwrap ( ) {
154
+ struct_span_err ! ( self . sess, e. span, E0696 ,
155
+ "`continue` pointing to a labeled block" )
156
+ . span_label ( e. span ,
157
+ "labeled blocks cannot be `continue`'d" )
158
+ . span_note ( block. span ,
159
+ "labeled block the continue points to" )
160
+ . emit ( ) ;
161
+ }
162
+ }
163
+ Err ( hir:: LoopIdError :: UnlabeledCfInWhileCondition ) => {
164
+ self . emit_unlabled_cf_in_while_condition ( e. span , "continue" ) ;
165
+ }
166
+ _ => { }
138
167
}
139
- self . require_loop ( "continue" , e. span )
168
+ self . require_break_cx ( "continue" , e. span )
140
169
} ,
141
170
_ => intravisit:: walk_expr ( self , e) ,
142
171
}
@@ -153,8 +182,9 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
153
182
self . cx = old_cx;
154
183
}
155
184
156
- fn require_loop ( & self , name : & str , span : Span ) {
185
+ fn require_break_cx ( & self , name : & str , span : Span ) {
157
186
match self . cx {
187
+ LabeledBlock |
158
188
Loop ( _) => { }
159
189
Closure => {
160
190
struct_span_err ! ( self . sess, span, E0267 , "`{}` inside of a closure" , name)
@@ -169,6 +199,22 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
169
199
}
170
200
}
171
201
202
+ fn require_label_in_labeled_block ( & mut self , span : Span , label : & Destination , cf_type : & str )
203
+ -> bool
204
+ {
205
+ if self . cx == LabeledBlock {
206
+ if label. label . is_none ( ) {
207
+ struct_span_err ! ( self . sess, span, E0695 ,
208
+ "unlabeled `{}` inside of a labeled block" , cf_type)
209
+ . span_label ( span,
210
+ format ! ( "`{}` statements that would diverge to or through \
211
+ a labeled block need to bear a label", cf_type) )
212
+ . emit ( ) ;
213
+ return true ;
214
+ }
215
+ }
216
+ return false ;
217
+ }
172
218
fn emit_unlabled_cf_in_while_condition ( & mut self , span : Span , cf_type : & str ) {
173
219
struct_span_err ! ( self . sess, span, E0590 ,
174
220
"`break` or `continue` with no label in the condition of a `while` loop" )
0 commit comments