Skip to content

Commit b60de4b

Browse files
committed
Emit warning when lifetime names are shadowed.
This is not technically a [breaking-change], but it will be soon, so you should update your code. Typically, shadowing is accidental, and the shadowing lifetime can simply be removed. This frequently occurs in constructor patterns: ```rust // Old: impl<'a> SomeStruct<'a> { fn new<'a>(..) -> SomeStruct<'a> { ... } } // Should be: impl<'a> SomeStruct<'a> { fn new(..) -> SomeStruct<'a> { ... } } ``` Otherwise, you should rename the inner lifetime to something else. Note though that lifetime elision frequently applies: ```rust // Old impl<'a> SomeStruct<'a> { fn get<'a>(x: &'a self) -> &'a T { &self.field } } // Should be: impl<'a> SomeStruct<'a> { fn get(x: &self) -> &T { &self.field } } ``
1 parent ef0bc46 commit b60de4b

File tree

2 files changed

+144
-53
lines changed

2 files changed

+144
-53
lines changed

src/librustc/middle/resolve_lifetime.rs

Lines changed: 101 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -90,48 +90,51 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio
9090

9191
impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
9292
fn visit_item(&mut self, item: &ast::Item) {
93-
match item.node {
94-
ast::ItemFn(..) => {
95-
// Fn lifetimes get added in visit_fn below:
96-
self.with(RootScope, |this| visit::walk_item(this, item));
97-
}
98-
ast::ItemMod(..) |
99-
ast::ItemMac(..) |
100-
ast::ItemForeignMod(..) |
101-
ast::ItemStatic(..) |
102-
ast::ItemConst(..) => {
103-
// These sorts of items have no lifetime parameters at all.
104-
self.with(RootScope, |this| visit::walk_item(this, item));
105-
}
106-
ast::ItemTy(_, ref generics) |
107-
ast::ItemEnum(_, ref generics) |
108-
ast::ItemStruct(_, ref generics) |
109-
ast::ItemTrait(_, ref generics, _, _, _) => {
110-
// These kinds of items have only early bound lifetime parameters.
111-
let lifetimes = &generics.lifetimes;
112-
self.with(EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE), |this| {
113-
this.check_lifetime_defs(lifetimes);
93+
// Items always introduce a new root scope
94+
self.with(RootScope, |_, this| {
95+
match item.node {
96+
ast::ItemFn(..) => {
97+
// Fn lifetimes get added in visit_fn below:
11498
visit::walk_item(this, item);
115-
});
116-
}
117-
ast::ItemImpl(_, ref generics, _, _, _) => {
118-
// Impls have both early- and late-bound lifetimes.
119-
self.visit_early_late(subst::TypeSpace, generics, |this| {
120-
this.check_lifetime_defs(&generics.lifetimes);
99+
}
100+
ast::ItemMod(..) |
101+
ast::ItemMac(..) |
102+
ast::ItemForeignMod(..) |
103+
ast::ItemStatic(..) |
104+
ast::ItemConst(..) => {
105+
// These sorts of items have no lifetime parameters at all.
121106
visit::walk_item(this, item);
122-
})
107+
}
108+
ast::ItemTy(_, ref generics) |
109+
ast::ItemEnum(_, ref generics) |
110+
ast::ItemStruct(_, ref generics) |
111+
ast::ItemTrait(_, ref generics, _, _, _) => {
112+
// These kinds of items have only early bound lifetime parameters.
113+
let lifetimes = &generics.lifetimes;
114+
let early_scope = EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE);
115+
this.with(early_scope, |old_scope, this| {
116+
this.check_lifetime_defs(old_scope, lifetimes);
117+
visit::walk_item(this, item);
118+
});
119+
}
120+
ast::ItemImpl(_, ref generics, _, _, _) => {
121+
// Impls have both early- and late-bound lifetimes.
122+
this.visit_early_late(subst::TypeSpace, generics, |this| {
123+
visit::walk_item(this, item);
124+
})
125+
}
123126
}
124-
}
127+
});
125128
}
126129

127130
fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
128131
b: &'v ast::Block, s: Span, _: ast::NodeId) {
129132
match fk {
130133
visit::FkItemFn(_, generics, _, _) |
131134
visit::FkMethod(_, generics, _) => {
132-
self.visit_early_late(
133-
subst::FnSpace, generics,
134-
|this| visit::walk_fn(this, fk, fd, b, s))
135+
self.visit_early_late(subst::FnSpace, generics, |this| {
136+
visit::walk_fn(this, fk, fd, b, s)
137+
})
135138
}
136139
visit::FkFnBlock(..) => {
137140
visit::walk_fn(self, fk, fd, b, s)
@@ -145,8 +148,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
145148
// Careful, the bounds on a closure/proc are *not* within its binder.
146149
visit::walk_ty_param_bounds_helper(self, &c.bounds);
147150
visit::walk_lifetime_decls_helper(self, &c.lifetimes);
148-
self.with(LateScope(&c.lifetimes, self.scope), |this| {
149-
this.check_lifetime_defs(&c.lifetimes);
151+
self.with(LateScope(&c.lifetimes, self.scope), |old_scope, this| {
152+
this.check_lifetime_defs(old_scope, &c.lifetimes);
150153
for argument in c.decl.inputs.iter() {
151154
this.visit_ty(&*argument.ty)
152155
}
@@ -155,10 +158,10 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
155158
}
156159
ast::TyBareFn(ref c) => {
157160
visit::walk_lifetime_decls_helper(self, &c.lifetimes);
158-
self.with(LateScope(&c.lifetimes, self.scope), |this| {
161+
self.with(LateScope(&c.lifetimes, self.scope), |old_scope, this| {
159162
// a bare fn has no bounds, so everything
160163
// contained within is scoped within its binder.
161-
this.check_lifetime_defs(&c.lifetimes);
164+
this.check_lifetime_defs(old_scope, &c.lifetimes);
162165
visit::walk_ty(this, ty);
163166
});
164167
}
@@ -167,7 +170,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
167170
// a trait ref, which introduces a binding scope.
168171
match self.def_map.borrow().get(&id) {
169172
Some(&def::DefTrait(..)) => {
170-
self.with(LateScope(&Vec::new(), self.scope), |this| {
173+
self.with(LateScope(&Vec::new(), self.scope), |_, this| {
171174
this.visit_path(path, id);
172175
});
173176
}
@@ -190,7 +193,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
190193

191194
fn visit_block(&mut self, b: &ast::Block) {
192195
self.with(BlockScope(region::CodeExtent::from_node_id(b.id), self.scope),
193-
|this| visit::walk_block(this, b));
196+
|_, this| visit::walk_block(this, b));
194197
}
195198

196199
fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime) {
@@ -232,8 +235,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
232235
fn visit_poly_trait_ref(&mut self, trait_ref: &ast::PolyTraitRef) {
233236
debug!("visit_poly_trait_ref trait_ref={}", trait_ref);
234237

235-
self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |this| {
236-
this.check_lifetime_defs(&trait_ref.bound_lifetimes);
238+
self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |old_scope, this| {
239+
this.check_lifetime_defs(old_scope, &trait_ref.bound_lifetimes);
237240
for lifetime in trait_ref.bound_lifetimes.iter() {
238241
this.visit_lifetime_def(lifetime);
239242
}
@@ -248,7 +251,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
248251

249252
impl<'a> LifetimeContext<'a> {
250253
fn with<F>(&mut self, wrap_scope: ScopeChain, f: F) where
251-
F: FnOnce(&mut LifetimeContext),
254+
F: FnOnce(Scope, &mut LifetimeContext),
252255
{
253256
let LifetimeContext {sess, ref mut named_region_map, ..} = *self;
254257
let mut this = LifetimeContext {
@@ -258,7 +261,7 @@ impl<'a> LifetimeContext<'a> {
258261
def_map: self.def_map,
259262
};
260263
debug!("entering scope {}", this.scope);
261-
f(&mut this);
264+
f(self.scope, &mut this);
262265
debug!("exiting scope {}", this.scope);
263266
}
264267

@@ -294,9 +297,9 @@ impl<'a> LifetimeContext<'a> {
294297
let (early, late) = generics.lifetimes.clone().partition(
295298
|l| referenced_idents.iter().any(|&i| i == l.lifetime.name));
296299

297-
self.with(EarlyScope(early_space, &early, self.scope), move |this| {
298-
this.with(LateScope(&late, this.scope), move |this| {
299-
this.check_lifetime_defs(&generics.lifetimes);
300+
self.with(EarlyScope(early_space, &early, self.scope), move |old_scope, this| {
301+
this.with(LateScope(&late, this.scope), move |_, this| {
302+
this.check_lifetime_defs(old_scope, &generics.lifetimes);
300303
walk(this);
301304
});
302305
});
@@ -323,7 +326,8 @@ impl<'a> LifetimeContext<'a> {
323326

324327
EarlyScope(space, lifetimes, s) => {
325328
match search_lifetimes(lifetimes, lifetime_ref) {
326-
Some((index, decl_id)) => {
329+
Some((index, lifetime_def)) => {
330+
let decl_id = lifetime_def.id;
327331
let def = DefEarlyBoundRegion(space, index, decl_id);
328332
self.insert_lifetime(lifetime_ref, def);
329333
return;
@@ -336,7 +340,8 @@ impl<'a> LifetimeContext<'a> {
336340

337341
LateScope(lifetimes, s) => {
338342
match search_lifetimes(lifetimes, lifetime_ref) {
339-
Some((_index, decl_id)) => {
343+
Some((_index, lifetime_def)) => {
344+
let decl_id = lifetime_def.id;
340345
let debruijn = ty::DebruijnIndex::new(late_depth + 1);
341346
let def = DefLateBoundRegion(debruijn, decl_id);
342347
self.insert_lifetime(lifetime_ref, def);
@@ -388,8 +393,8 @@ impl<'a> LifetimeContext<'a> {
388393
}
389394

390395
match search_result {
391-
Some((_depth, decl_id)) => {
392-
let def = DefFreeRegion(scope_data, decl_id);
396+
Some((_depth, lifetime)) => {
397+
let def = DefFreeRegion(scope_data, lifetime.id);
393398
self.insert_lifetime(lifetime_ref, def);
394399
}
395400

@@ -407,7 +412,7 @@ impl<'a> LifetimeContext<'a> {
407412
token::get_name(lifetime_ref.name)).as_slice());
408413
}
409414

410-
fn check_lifetime_defs(&mut self, lifetimes: &Vec<ast::LifetimeDef>) {
415+
fn check_lifetime_defs(&mut self, old_scope: Scope, lifetimes: &Vec<ast::LifetimeDef>) {
411416
for i in range(0, lifetimes.len()) {
412417
let lifetime_i = &lifetimes[i];
413418

@@ -422,6 +427,7 @@ impl<'a> LifetimeContext<'a> {
422427
}
423428
}
424429

430+
// It is a hard error to shadow a lifetime within the same scope.
425431
for j in range(i + 1, lifetimes.len()) {
426432
let lifetime_j = &lifetimes[j];
427433

@@ -435,12 +441,54 @@ impl<'a> LifetimeContext<'a> {
435441
}
436442
}
437443

444+
// It is a soft error to shadow a lifetime within a parent scope.
445+
self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime);
446+
438447
for bound in lifetime_i.bounds.iter() {
439448
self.resolve_lifetime_ref(bound);
440449
}
441450
}
442451
}
443452

453+
fn check_lifetime_def_for_shadowing(&self,
454+
mut old_scope: Scope,
455+
lifetime: &ast::Lifetime)
456+
{
457+
loop {
458+
match *old_scope {
459+
BlockScope(_, s) => {
460+
old_scope = s;
461+
}
462+
463+
RootScope => {
464+
return;
465+
}
466+
467+
EarlyScope(_, lifetimes, s) |
468+
LateScope(lifetimes, s) => {
469+
if let Some((_, lifetime_def)) = search_lifetimes(lifetimes, lifetime) {
470+
self.sess.span_warn(
471+
lifetime.span,
472+
format!("lifetime name `{}` shadows another \
473+
lifetime name that is already in scope",
474+
token::get_name(lifetime.name)).as_slice());
475+
self.sess.span_help(
476+
lifetime_def.span,
477+
format!("shadowed lifetime `{}` declared here",
478+
token::get_name(lifetime.name)).as_slice());
479+
self.sess.span_help(
480+
lifetime.span,
481+
"shadowed lifetimes are deprecated \
482+
and will become a hard error before 1.0");
483+
return;
484+
}
485+
486+
old_scope = s;
487+
}
488+
}
489+
}
490+
}
491+
444492
fn insert_lifetime(&mut self,
445493
lifetime_ref: &ast::Lifetime,
446494
def: DefRegion) {
@@ -458,12 +506,12 @@ impl<'a> LifetimeContext<'a> {
458506
}
459507
}
460508

461-
fn search_lifetimes(lifetimes: &Vec<ast::LifetimeDef>,
509+
fn search_lifetimes<'a>(lifetimes: &'a Vec<ast::LifetimeDef>,
462510
lifetime_ref: &ast::Lifetime)
463-
-> Option<(uint, ast::NodeId)> {
511+
-> Option<(uint, &'a ast::Lifetime)> {
464512
for (i, lifetime_decl) in lifetimes.iter().enumerate() {
465513
if lifetime_decl.lifetime.name == lifetime_ref.name {
466-
return Some((i, lifetime_decl.lifetime.id));
514+
return Some((i, &lifetime_decl.lifetime));
467515
}
468516
}
469517
return None;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that shadowed lifetimes generate an error.
12+
13+
struct Foo<'a>(&'a int);
14+
15+
impl<'a> Foo<'a> {
16+
//~^ HELP shadowed lifetime `'a` declared here
17+
fn shadow_in_method<'a>(&'a self) -> &'a int {
18+
//~^ WARNING lifetime name `'a` shadows another lifetime name that is already in scope
19+
//~| HELP deprecated
20+
self.0
21+
}
22+
23+
fn shadow_in_type<'b>(&'b self) -> &'b int {
24+
//~^ HELP shadowed lifetime `'b` declared here
25+
let x: for<'b> fn(&'b int) = panic!();
26+
//~^ WARNING lifetime name `'b` shadows another lifetime name that is already in scope
27+
//~| HELP deprecated
28+
self.0
29+
}
30+
31+
fn not_shadow_in_item<'b>(&'b self) {
32+
struct Bar<'a, 'b>(&'a int, &'b int); // not a shadow, separate item
33+
fn foo<'a, 'b>(x: &'a int, y: &'b int) { } // same
34+
}
35+
}
36+
37+
fn main() {
38+
// intentional error that occurs after `resolve_lifetime` runs,
39+
// just to ensure that this test fails to compile; when shadowed
40+
// lifetimes become either an error or a proper lint, this will
41+
// not be needed.
42+
let x: int = 3u; //~ ERROR mismatched types
43+
}

0 commit comments

Comments
 (0)