1+ use rustc_data_structures:: fx:: FxHashSet ;
12use Context :: * ;
23
34use rustc_hir as hir;
@@ -9,7 +10,7 @@ use rustc_middle::query::Providers;
910use rustc_middle:: ty:: TyCtxt ;
1011use rustc_session:: Session ;
1112use rustc_span:: hygiene:: DesugaringKind ;
12- use rustc_span:: { BytePos , Span } ;
13+ use rustc_span:: { BytePos , Span , DUMMY_SP } ;
1314
1415use crate :: errors:: {
1516 BreakInsideAsyncBlock , BreakInsideClosure , BreakNonLoop , ContinueLabeledBlock , OutsideLoop ,
@@ -24,21 +25,33 @@ enum Context {
2425 Closure ( Span ) ,
2526 AsyncClosure ( Span ) ,
2627 UnlabeledBlock ( Span ) ,
28+ IfUnlabeledBlock ( Span ) ,
2729 LabeledBlock ,
2830 Constant ,
2931}
3032
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 ) ]
3239struct CheckLoopVisitor < ' a , ' tcx > {
3340 sess : & ' a Session ,
3441 tcx : TyCtxt < ' tcx > ,
35- cx : Context ,
42+ cx_stack : Vec < Context > ,
43+ fix_label_spans : FxHashSet < Span > ,
3644}
3745
3846fn check_mod_loops ( tcx : TyCtxt < ' _ > , module_def_id : LocalModDefId ) {
3947 tcx. hir ( ) . visit_item_likes_in_module (
4048 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+ } ,
4255 ) ;
4356}
4457
@@ -82,6 +95,35 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
8295
8396 fn visit_expr ( & mut self , e : & ' hir hir:: Expr < ' hir > ) {
8497 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+ }
85127 hir:: ExprKind :: Loop ( ref b, _, source, _) => {
86128 self . with_context ( Loop ( source) , |v| v. visit_block ( b) ) ;
87129 }
@@ -102,11 +144,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
102144 hir:: ExprKind :: Block ( ref b, Some ( _label) ) => {
103145 self . with_context ( LabeledBlock , |v| v. visit_block ( b) ) ;
104146 }
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 ) => {
106148 self . with_context ( Normal , |v| v. visit_block ( b) ) ;
107149 }
108150 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+ ) =>
110155 {
111156 self . with_context ( UnlabeledBlock ( b. span . shrink_to_lo ( ) ) , |v| v. visit_block ( b) ) ;
112157 }
@@ -179,7 +224,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
179224 Some ( label) => sp_lo. with_hi ( label. ident . span . hi ( ) ) ,
180225 None => sp_lo. shrink_to_lo ( ) ,
181226 } ;
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 ) ;
183228 }
184229 hir:: ExprKind :: Continue ( destination) => {
185230 self . require_label_in_labeled_block ( e. span , & destination, "continue" ) ;
@@ -201,7 +246,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
201246 }
202247 Err ( _) => { }
203248 }
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 )
205250 }
206251 _ => intravisit:: walk_expr ( self , e) ,
207252 }
@@ -213,15 +258,14 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
213258 where
214259 F : FnOnce ( & mut CheckLoopVisitor < ' a , ' hir > ) ,
215260 {
216- let old_cx = self . cx ;
217- self . cx = cx;
261+ self . cx_stack . push ( cx) ;
218262 f ( self ) ;
219- self . cx = old_cx ;
263+ self . cx_stack . pop ( ) ;
220264 }
221265
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 ) {
223267 let is_break = name == "break" ;
224- match self . cx {
268+ match self . cx_stack [ cx_pos ] {
225269 LabeledBlock | Loop ( _) => { }
226270 Closure ( closure_span) => {
227271 self . sess . dcx ( ) . emit_err ( BreakInsideClosure { span, closure_span, name } ) ;
@@ -230,10 +274,18 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
230274 self . sess . dcx ( ) . emit_err ( BreakInsideAsyncBlock { span, closure_span, name } ) ;
231275 }
232276 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+ } ;
234282 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 ) ;
235287 }
236- Normal | Constant | Fn | UnlabeledBlock ( _) => {
288+ Normal | Constant | Fn | UnlabeledBlock ( _) | IfUnlabeledBlock ( _ ) => {
237289 self . sess . dcx ( ) . emit_err ( OutsideLoop { span, name, is_break, suggestion : None } ) ;
238290 }
239291 }
@@ -246,7 +298,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
246298 cf_type : & str ,
247299 ) -> bool {
248300 if !span. is_desugaring ( DesugaringKind :: QuestionMark )
249- && self . cx == LabeledBlock
301+ && * self . cx_stack . last ( ) . unwrap ( ) == LabeledBlock
250302 && label. label . is_none ( )
251303 {
252304 self . sess . dcx ( ) . emit_err ( UnlabeledInLabeledBlock { span, cf_type } ) ;
0 commit comments