@@ -24,6 +24,7 @@ use middle::region;
2424use middle:: subst;
2525use middle:: ty;
2626use std:: fmt;
27+ use std:: mem:: replace;
2728use syntax:: ast;
2829use syntax:: codemap:: Span ;
2930use syntax:: parse:: token:: special_idents;
@@ -70,6 +71,9 @@ struct LifetimeContext<'a> {
7071
7172 // I'm sorry.
7273 trait_ref_hack : bool ,
74+
75+ // List of labels in the function/method currently under analysis.
76+ labels_in_fn : Vec < ( ast:: Ident , Span ) > ,
7377}
7478
7579enum ScopeChain < ' a > {
@@ -97,13 +101,18 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio
97101 scope : & ROOT_SCOPE ,
98102 def_map : def_map,
99103 trait_ref_hack : false ,
104+ labels_in_fn : vec ! [ ] ,
100105 } , krate) ;
101106 sess. abort_if_errors ( ) ;
102107 named_region_map
103108}
104109
105110impl < ' a , ' v > Visitor < ' v > for LifetimeContext < ' a > {
106111 fn visit_item ( & mut self , item : & ast:: Item ) {
112+ // Items save/restore the set of labels. This way innner items
113+ // can freely reuse names, be they loop labels or lifetimes.
114+ let saved = replace ( & mut self . labels_in_fn , vec ! [ ] ) ;
115+
107116 // Items always introduce a new root scope
108117 self . with ( RootScope , |_, this| {
109118 match item. node {
@@ -137,23 +146,26 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
137146 }
138147 }
139148 } ) ;
149+
150+ // Done traversing the item; restore saved set of labels.
151+ replace ( & mut self . labels_in_fn , saved) ;
140152 }
141153
142154 fn visit_fn ( & mut self , fk : visit:: FnKind < ' v > , fd : & ' v ast:: FnDecl ,
143155 b : & ' v ast:: Block , s : Span , _: ast:: NodeId ) {
144156 match fk {
145157 visit:: FkItemFn ( _, generics, _, _, _) => {
146158 self . visit_early_late ( subst:: FnSpace , generics, |this| {
147- visit :: walk_fn ( this , fk, fd, b, s)
159+ this . walk_fn ( fk, fd, b, s)
148160 } )
149161 }
150162 visit:: FkMethod ( _, sig, _) => {
151163 self . visit_early_late ( subst:: FnSpace , & sig. generics , |this| {
152- visit :: walk_fn ( this , fk, fd, b, s)
164+ this . walk_fn ( fk, fd, b, s)
153165 } )
154166 }
155167 visit:: FkFnBlock ( ..) => {
156- visit :: walk_fn ( self , fk, fd, b, s)
168+ self . walk_fn ( fk, fd, b, s)
157169 }
158170 }
159171 }
@@ -190,13 +202,19 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
190202 }
191203
192204 fn visit_trait_item ( & mut self , trait_item : & ast:: TraitItem ) {
205+ // We reset the labels on every trait item, so that different
206+ // methods in an impl can reuse label names.
207+ let saved = replace ( & mut self . labels_in_fn , vec ! [ ] ) ;
208+
193209 if let ast:: MethodTraitItem ( ref sig, None ) = trait_item. node {
194210 self . visit_early_late (
195211 subst:: FnSpace , & sig. generics ,
196212 |this| visit:: walk_trait_item ( this, trait_item) )
197213 } else {
198214 visit:: walk_trait_item ( self , trait_item) ;
199215 }
216+
217+ replace ( & mut self . labels_in_fn , saved) ;
200218 }
201219
202220 fn visit_block ( & mut self , b : & ast:: Block ) {
@@ -286,7 +304,170 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
286304 }
287305}
288306
307+ #[ derive( Copy , Clone , PartialEq ) ]
308+ enum ShadowKind { Label , Lifetime }
309+ struct Original { kind : ShadowKind , span : Span }
310+ struct Shadower { kind : ShadowKind , span : Span }
311+
312+ fn original_label ( span : Span ) -> Original {
313+ Original { kind : ShadowKind :: Label , span : span }
314+ }
315+ fn shadower_label ( span : Span ) -> Shadower {
316+ Shadower { kind : ShadowKind :: Label , span : span }
317+ }
318+ fn original_lifetime ( l : & ast:: Lifetime ) -> Original {
319+ Original { kind : ShadowKind :: Lifetime , span : l. span }
320+ }
321+ fn shadower_lifetime ( l : & ast:: Lifetime ) -> Shadower {
322+ Shadower { kind : ShadowKind :: Lifetime , span : l. span }
323+ }
324+
325+ impl ShadowKind {
326+ fn desc ( & self ) -> & ' static str {
327+ match * self {
328+ ShadowKind :: Label => "label" ,
329+ ShadowKind :: Lifetime => "lifetime" ,
330+ }
331+ }
332+ }
333+
334+ fn signal_shadowing_problem (
335+ sess : & Session , name : ast:: Name , orig : Original , shadower : Shadower ) {
336+ if let ( ShadowKind :: Lifetime , ShadowKind :: Lifetime ) = ( orig. kind , shadower. kind ) {
337+ // lifetime/lifetime shadowing is an error
338+ sess. span_err ( shadower. span ,
339+ & format ! ( "{} name `{}` shadows a \
340+ {} name that is already in scope",
341+ shadower. kind. desc( ) , name, orig. kind. desc( ) ) ) ;
342+ } else {
343+ // shadowing involving a label is only a warning, due to issues with
344+ // labels and lifetimes not being macro-hygienic.
345+ sess. span_warn ( shadower. span ,
346+ & format ! ( "{} name `{}` shadows a \
347+ {} name that is already in scope",
348+ shadower. kind. desc( ) , name, orig. kind. desc( ) ) ) ;
349+ }
350+ sess. span_note ( orig. span ,
351+ & format ! ( "shadowed {} `{}` declared here" ,
352+ orig. kind. desc( ) , name) ) ;
353+ }
354+
355+ // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
356+ // if one of the label shadows a lifetime or another label.
357+ fn extract_labels < ' v , ' a > ( ctxt : & mut LifetimeContext < ' a > , b : & ' v ast:: Block ) {
358+
359+ struct GatherLabels < ' a > {
360+ sess : & ' a Session ,
361+ scope : Scope < ' a > ,
362+ labels_in_fn : & ' a mut Vec < ( ast:: Ident , Span ) > ,
363+ }
364+
365+ let mut gather = GatherLabels {
366+ sess : ctxt. sess ,
367+ scope : ctxt. scope ,
368+ labels_in_fn : & mut ctxt. labels_in_fn ,
369+ } ;
370+ gather. visit_block ( b) ;
371+ return ;
372+
373+ impl < ' v , ' a > Visitor < ' v > for GatherLabels < ' a > {
374+ fn visit_expr ( & mut self , ex : & ' v ast:: Expr ) {
375+ if let Some ( label) = expression_label ( ex) {
376+ for & ( prior, prior_span) in & self . labels_in_fn [ ..] {
377+ // FIXME (#24278): non-hygienic comparision
378+ if label. name == prior. name {
379+ signal_shadowing_problem ( self . sess ,
380+ label. name ,
381+ original_label ( prior_span) ,
382+ shadower_label ( ex. span ) ) ;
383+ }
384+ }
385+
386+ check_if_label_shadows_lifetime ( self . sess ,
387+ self . scope ,
388+ label,
389+ ex. span ) ;
390+
391+ self . labels_in_fn . push ( ( label, ex. span ) ) ;
392+ }
393+ visit:: walk_expr ( self , ex)
394+ }
395+
396+ fn visit_item ( & mut self , _: & ast:: Item ) {
397+ // do not recurse into items defined in the block
398+ }
399+ }
400+
401+ fn expression_label ( ex : & ast:: Expr ) -> Option < ast:: Ident > {
402+ match ex. node {
403+ ast:: ExprWhile ( _, _, Some ( label) ) |
404+ ast:: ExprWhileLet ( _, _, _, Some ( label) ) |
405+ ast:: ExprForLoop ( _, _, _, Some ( label) ) |
406+ ast:: ExprLoop ( _, Some ( label) ) => Some ( label) ,
407+ _ => None ,
408+ }
409+ }
410+
411+ fn check_if_label_shadows_lifetime < ' a > ( sess : & ' a Session ,
412+ mut scope : Scope < ' a > ,
413+ label : ast:: Ident ,
414+ label_span : Span ) {
415+ loop {
416+ match * scope {
417+ BlockScope ( _, s) => { scope = s; }
418+ RootScope => { return ; }
419+
420+ EarlyScope ( _, lifetimes, s) |
421+ LateScope ( lifetimes, s) => {
422+ for lifetime_def in lifetimes {
423+ // FIXME (#24278): non-hygienic comparision
424+ if label. name == lifetime_def. lifetime . name {
425+ signal_shadowing_problem (
426+ sess,
427+ label. name ,
428+ original_lifetime ( & lifetime_def. lifetime ) ,
429+ shadower_label ( label_span) ) ;
430+ return ;
431+ }
432+ }
433+ scope = s;
434+ }
435+ }
436+ }
437+ }
438+ }
439+
289440impl < ' a > LifetimeContext < ' a > {
441+ // This is just like visit::walk_fn, except that it extracts the
442+ // labels of the function body and swaps them in before visiting
443+ // the function body itself.
444+ fn walk_fn < ' b > ( & mut self ,
445+ fk : visit:: FnKind ,
446+ fd : & ast:: FnDecl ,
447+ fb : & ' b ast:: Block ,
448+ _span : Span ) {
449+ match fk {
450+ visit:: FkItemFn ( _, generics, _, _) => {
451+ visit:: walk_fn_decl ( self , fd) ;
452+ self . visit_generics ( generics) ;
453+ }
454+ visit:: FkMethod ( _, sig) => {
455+ visit:: walk_fn_decl ( self , fd) ;
456+ self . visit_generics ( & sig. generics ) ;
457+ self . visit_explicit_self ( & sig. explicit_self ) ;
458+ }
459+ visit:: FkFnBlock ( ..) => {
460+ visit:: walk_fn_decl ( self , fd) ;
461+ }
462+ }
463+
464+ // After inpsecting the decl, add all labels from the body to
465+ // `self.labels_in_fn`.
466+ extract_labels ( self , fb) ;
467+
468+ self . visit_block ( fb) ;
469+ }
470+
290471 fn with < F > ( & mut self , wrap_scope : ScopeChain , f : F ) where
291472 F : FnOnce ( Scope , & mut LifetimeContext ) ,
292473 {
@@ -297,6 +478,7 @@ impl<'a> LifetimeContext<'a> {
297478 scope : & wrap_scope,
298479 def_map : self . def_map ,
299480 trait_ref_hack : self . trait_ref_hack ,
481+ labels_in_fn : self . labels_in_fn . clone ( ) ,
300482 } ;
301483 debug ! ( "entering scope {:?}" , this. scope) ;
302484 f ( self . scope , & mut this) ;
@@ -494,6 +676,17 @@ impl<'a> LifetimeContext<'a> {
494676 mut old_scope : Scope ,
495677 lifetime : & ast:: Lifetime )
496678 {
679+ for & ( label, label_span) in & self . labels_in_fn {
680+ // FIXME (#24278): non-hygienic comparision
681+ if lifetime. name == label. name {
682+ signal_shadowing_problem ( self . sess ,
683+ lifetime. name ,
684+ original_label ( label_span) ,
685+ shadower_lifetime ( & lifetime) ) ;
686+ return ;
687+ }
688+ }
689+
497690 loop {
498691 match * old_scope {
499692 BlockScope ( _, s) => {
@@ -507,15 +700,11 @@ impl<'a> LifetimeContext<'a> {
507700 EarlyScope ( _, lifetimes, s) |
508701 LateScope ( lifetimes, s) => {
509702 if let Some ( ( _, lifetime_def) ) = search_lifetimes ( lifetimes, lifetime) {
510- self . sess . span_err (
511- lifetime. span ,
512- & format ! ( "lifetime name `{}` shadows another \
513- lifetime name that is already in scope",
514- token:: get_name( lifetime. name) ) ) ;
515- self . sess . span_note (
516- lifetime_def. span ,
517- & format ! ( "shadowed lifetime `{}` declared here" ,
518- token:: get_name( lifetime. name) ) ) ;
703+ signal_shadowing_problem (
704+ self . sess ,
705+ lifetime. name ,
706+ original_lifetime ( & lifetime_def) ,
707+ shadower_lifetime ( & lifetime) ) ;
519708 return ;
520709 }
521710
0 commit comments