@@ -24,6 +24,7 @@ use middle::region;
24
24
use middle:: subst;
25
25
use middle:: ty;
26
26
use std:: fmt;
27
+ use std:: mem:: replace;
27
28
use syntax:: ast;
28
29
use syntax:: codemap:: Span ;
29
30
use syntax:: parse:: token:: special_idents;
@@ -70,6 +71,9 @@ struct LifetimeContext<'a> {
70
71
71
72
// I'm sorry.
72
73
trait_ref_hack : bool ,
74
+
75
+ // List of labels in the function/method currently under analysis.
76
+ labels_in_fn : Vec < ( ast:: Ident , Span ) > ,
73
77
}
74
78
75
79
enum ScopeChain < ' a > {
@@ -97,13 +101,18 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio
97
101
scope : & ROOT_SCOPE ,
98
102
def_map : def_map,
99
103
trait_ref_hack : false ,
104
+ labels_in_fn : vec ! [ ] ,
100
105
} , krate) ;
101
106
sess. abort_if_errors ( ) ;
102
107
named_region_map
103
108
}
104
109
105
110
impl < ' a , ' v > Visitor < ' v > for LifetimeContext < ' a > {
106
111
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
+
107
116
// Items always introduce a new root scope
108
117
self . with ( RootScope , |_, this| {
109
118
match item. node {
@@ -137,23 +146,26 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
137
146
}
138
147
}
139
148
} ) ;
149
+
150
+ // Done traversing the item; restore saved set of labels.
151
+ replace ( & mut self . labels_in_fn , saved) ;
140
152
}
141
153
142
154
fn visit_fn ( & mut self , fk : visit:: FnKind < ' v > , fd : & ' v ast:: FnDecl ,
143
155
b : & ' v ast:: Block , s : Span , _: ast:: NodeId ) {
144
156
match fk {
145
157
visit:: FkItemFn ( _, generics, _, _, _) => {
146
158
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)
148
160
} )
149
161
}
150
162
visit:: FkMethod ( _, sig, _) => {
151
163
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)
153
165
} )
154
166
}
155
167
visit:: FkFnBlock ( ..) => {
156
- visit :: walk_fn ( self , fk, fd, b, s)
168
+ self . walk_fn ( fk, fd, b, s)
157
169
}
158
170
}
159
171
}
@@ -190,13 +202,19 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
190
202
}
191
203
192
204
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
+
193
209
if let ast:: MethodTraitItem ( ref sig, None ) = trait_item. node {
194
210
self . visit_early_late (
195
211
subst:: FnSpace , & sig. generics ,
196
212
|this| visit:: walk_trait_item ( this, trait_item) )
197
213
} else {
198
214
visit:: walk_trait_item ( self , trait_item) ;
199
215
}
216
+
217
+ replace ( & mut self . labels_in_fn , saved) ;
200
218
}
201
219
202
220
fn visit_block ( & mut self , b : & ast:: Block ) {
@@ -286,7 +304,170 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
286
304
}
287
305
}
288
306
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
+
289
440
impl < ' 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
+
290
471
fn with < F > ( & mut self , wrap_scope : ScopeChain , f : F ) where
291
472
F : FnOnce ( Scope , & mut LifetimeContext ) ,
292
473
{
@@ -297,6 +478,7 @@ impl<'a> LifetimeContext<'a> {
297
478
scope : & wrap_scope,
298
479
def_map : self . def_map ,
299
480
trait_ref_hack : self . trait_ref_hack ,
481
+ labels_in_fn : self . labels_in_fn . clone ( ) ,
300
482
} ;
301
483
debug ! ( "entering scope {:?}" , this. scope) ;
302
484
f ( self . scope , & mut this) ;
@@ -494,6 +676,17 @@ impl<'a> LifetimeContext<'a> {
494
676
mut old_scope : Scope ,
495
677
lifetime : & ast:: Lifetime )
496
678
{
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
+
497
690
loop {
498
691
match * old_scope {
499
692
BlockScope ( _, s) => {
@@ -507,15 +700,11 @@ impl<'a> LifetimeContext<'a> {
507
700
EarlyScope ( _, lifetimes, s) |
508
701
LateScope ( lifetimes, s) => {
509
702
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) ) ;
519
708
return ;
520
709
}
521
710
0 commit comments