From dd240707999216a64e7914c2290cb59c54d9c60c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 3 Apr 2015 02:51:38 +0200 Subject: [PATCH 1/3] Check for shadowing between lifetimes and loop labels in function bodies. Note: this Warns rather than error on shadowing problems involving labels. We took this more conservative option mostly due to issues with hygiene being broken for labels and/or lifetimes. Add FIXME regarding non-hygienic comparison. --- src/librustc/middle/resolve_lifetime.rs | 213 ++++++++++++++++++++++-- 1 file changed, 201 insertions(+), 12 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index a9da92a768fe6..6f40e17855a78 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -24,6 +24,7 @@ use middle::region; use middle::subst; use middle::ty; use std::fmt; +use std::mem::replace; use syntax::ast; use syntax::codemap::Span; use syntax::parse::token::special_idents; @@ -70,6 +71,9 @@ struct LifetimeContext<'a> { // I'm sorry. trait_ref_hack: bool, + + // List of labels in the function/method currently under analysis. + labels_in_fn: Vec<(ast::Ident, Span)>, } enum ScopeChain<'a> { @@ -97,6 +101,7 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio scope: &ROOT_SCOPE, def_map: def_map, trait_ref_hack: false, + labels_in_fn: vec![], }, krate); sess.abort_if_errors(); named_region_map @@ -104,6 +109,10 @@ pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegio impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { fn visit_item(&mut self, item: &ast::Item) { + // Items save/restore the set of labels. This way innner items + // can freely reuse names, be they loop labels or lifetimes. + let saved = replace(&mut self.labels_in_fn, vec![]); + // Items always introduce a new root scope self.with(RootScope, |_, this| { match item.node { @@ -137,6 +146,9 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { } } }); + + // Done traversing the item; restore saved set of labels. + replace(&mut self.labels_in_fn, saved); } fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, @@ -144,16 +156,16 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { match fk { visit::FkItemFn(_, generics, _, _, _) => { self.visit_early_late(subst::FnSpace, generics, |this| { - visit::walk_fn(this, fk, fd, b, s) + this.walk_fn(fk, fd, b, s) }) } visit::FkMethod(_, sig, _) => { self.visit_early_late(subst::FnSpace, &sig.generics, |this| { - visit::walk_fn(this, fk, fd, b, s) + this.walk_fn(fk, fd, b, s) }) } visit::FkFnBlock(..) => { - visit::walk_fn(self, fk, fd, b, s) + self.walk_fn(fk, fd, b, s) } } } @@ -190,6 +202,10 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { } fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { + // We reset the labels on every trait item, so that different + // methods in an impl can reuse label names. + let saved = replace(&mut self.labels_in_fn, vec![]); + if let ast::MethodTraitItem(ref sig, None) = trait_item.node { self.visit_early_late( subst::FnSpace, &sig.generics, @@ -197,6 +213,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { } else { visit::walk_trait_item(self, trait_item); } + + replace(&mut self.labels_in_fn, saved); } fn visit_block(&mut self, b: &ast::Block) { @@ -286,7 +304,170 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { } } +#[derive(Copy, Clone, PartialEq)] +enum ShadowKind { Label, Lifetime } +struct Original { kind: ShadowKind, span: Span } +struct Shadower { kind: ShadowKind, span: Span } + +fn original_label(span: Span) -> Original { + Original { kind: ShadowKind::Label, span: span } +} +fn shadower_label(span: Span) -> Shadower { + Shadower { kind: ShadowKind::Label, span: span } +} +fn original_lifetime(l: &ast::Lifetime) -> Original { + Original { kind: ShadowKind::Lifetime, span: l.span } +} +fn shadower_lifetime(l: &ast::Lifetime) -> Shadower { + Shadower { kind: ShadowKind::Lifetime, span: l.span } +} + +impl ShadowKind { + fn desc(&self) -> &'static str { + match *self { + ShadowKind::Label => "label", + ShadowKind::Lifetime => "lifetime", + } + } +} + +fn signal_shadowing_problem( + sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) { + if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) { + // lifetime/lifetime shadowing is an error + sess.span_err(shadower.span, + &format!("{} name `{}` shadows a \ + {} name that is already in scope", + shadower.kind.desc(), name, orig.kind.desc())); + } else { + // shadowing involving a label is only a warning, due to issues with + // labels and lifetimes not being macro-hygienic. + sess.span_warn(shadower.span, + &format!("{} name `{}` shadows a \ + {} name that is already in scope", + shadower.kind.desc(), name, orig.kind.desc())); + } + sess.span_note(orig.span, + &format!("shadowed {} `{}` declared here", + orig.kind.desc(), name)); +} + +// Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning +// if one of the label shadows a lifetime or another label. +fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v ast::Block) { + + struct GatherLabels<'a> { + sess: &'a Session, + scope: Scope<'a>, + labels_in_fn: &'a mut Vec<(ast::Ident, Span)>, + } + + let mut gather = GatherLabels { + sess: ctxt.sess, + scope: ctxt.scope, + labels_in_fn: &mut ctxt.labels_in_fn, + }; + gather.visit_block(b); + return; + + impl<'v, 'a> Visitor<'v> for GatherLabels<'a> { + fn visit_expr(&mut self, ex: &'v ast::Expr) { + if let Some(label) = expression_label(ex) { + for &(prior, prior_span) in &self.labels_in_fn[..] { + // FIXME (#24278): non-hygienic comparision + if label.name == prior.name { + signal_shadowing_problem(self.sess, + label.name, + original_label(prior_span), + shadower_label(ex.span)); + } + } + + check_if_label_shadows_lifetime(self.sess, + self.scope, + label, + ex.span); + + self.labels_in_fn.push((label, ex.span)); + } + visit::walk_expr(self, ex) + } + + fn visit_item(&mut self, _: &ast::Item) { + // do not recurse into items defined in the block + } + } + + fn expression_label(ex: &ast::Expr) -> Option { + match ex.node { + ast::ExprWhile(_, _, Some(label)) | + ast::ExprWhileLet(_, _, _, Some(label)) | + ast::ExprForLoop(_, _, _, Some(label)) | + ast::ExprLoop(_, Some(label)) => Some(label), + _ => None, + } + } + + fn check_if_label_shadows_lifetime<'a>(sess: &'a Session, + mut scope: Scope<'a>, + label: ast::Ident, + label_span: Span) { + loop { + match *scope { + BlockScope(_, s) => { scope = s; } + RootScope => { return; } + + EarlyScope(_, lifetimes, s) | + LateScope(lifetimes, s) => { + for lifetime_def in lifetimes { + // FIXME (#24278): non-hygienic comparision + if label.name == lifetime_def.lifetime.name { + signal_shadowing_problem( + sess, + label.name, + original_lifetime(&lifetime_def.lifetime), + shadower_label(label_span)); + return; + } + } + scope = s; + } + } + } + } +} + impl<'a> LifetimeContext<'a> { + // This is just like visit::walk_fn, except that it extracts the + // labels of the function body and swaps them in before visiting + // the function body itself. + fn walk_fn<'b>(&mut self, + fk: visit::FnKind, + fd: &ast::FnDecl, + fb: &'b ast::Block, + _span: Span) { + match fk { + visit::FkItemFn(_, generics, _, _, _) => { + visit::walk_fn_decl(self, fd); + self.visit_generics(generics); + } + visit::FkMethod(_, sig, _) => { + visit::walk_fn_decl(self, fd); + self.visit_generics(&sig.generics); + self.visit_explicit_self(&sig.explicit_self); + } + visit::FkFnBlock(..) => { + visit::walk_fn_decl(self, fd); + } + } + + // After inpsecting the decl, add all labels from the body to + // `self.labels_in_fn`. + extract_labels(self, fb); + + self.visit_block(fb); + } + fn with(&mut self, wrap_scope: ScopeChain, f: F) where F: FnOnce(Scope, &mut LifetimeContext), { @@ -297,6 +478,7 @@ impl<'a> LifetimeContext<'a> { scope: &wrap_scope, def_map: self.def_map, trait_ref_hack: self.trait_ref_hack, + labels_in_fn: self.labels_in_fn.clone(), }; debug!("entering scope {:?}", this.scope); f(self.scope, &mut this); @@ -494,6 +676,17 @@ impl<'a> LifetimeContext<'a> { mut old_scope: Scope, lifetime: &ast::Lifetime) { + for &(label, label_span) in &self.labels_in_fn { + // FIXME (#24278): non-hygienic comparision + if lifetime.name == label.name { + signal_shadowing_problem(self.sess, + lifetime.name, + original_label(label_span), + shadower_lifetime(&lifetime)); + return; + } + } + loop { match *old_scope { BlockScope(_, s) => { @@ -507,15 +700,11 @@ impl<'a> LifetimeContext<'a> { EarlyScope(_, lifetimes, s) | LateScope(lifetimes, s) => { if let Some((_, lifetime_def)) = search_lifetimes(lifetimes, lifetime) { - self.sess.span_err( - lifetime.span, - &format!("lifetime name `{}` shadows another \ - lifetime name that is already in scope", - token::get_name(lifetime.name))); - self.sess.span_note( - lifetime_def.span, - &format!("shadowed lifetime `{}` declared here", - token::get_name(lifetime.name))); + signal_shadowing_problem( + self.sess, + lifetime.name, + original_lifetime(&lifetime_def), + shadower_lifetime(&lifetime)); return; } From ccc9f5eb846aa4be48b080eed810e26d2f96bdde Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 8 Apr 2015 18:37:47 +0200 Subject: [PATCH 2/3] Tests for shadowing between lifetimes and loop labels within function bodies. --- .../compile-fail/loop-labeled-break-value.rs | 2 +- .../loops-reject-duplicate-labels-2.rs | 51 ++++++++ .../loops-reject-duplicate-labels.rs | 60 +++++++++ ...loops-reject-labels-shadowing-lifetimes.rs | 120 ++++++++++++++++++ .../loops-reject-lifetime-shadowing-label.rs | 41 ++++++ src/test/compile-fail/shadowed-lifetime.rs | 4 +- 6 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/loops-reject-duplicate-labels-2.rs create mode 100644 src/test/compile-fail/loops-reject-duplicate-labels.rs create mode 100644 src/test/compile-fail/loops-reject-labels-shadowing-lifetimes.rs create mode 100644 src/test/compile-fail/loops-reject-lifetime-shadowing-label.rs diff --git a/src/test/compile-fail/loop-labeled-break-value.rs b/src/test/compile-fail/loop-labeled-break-value.rs index e1ae3ae464f98..f0792c145d2a2 100644 --- a/src/test/compile-fail/loop-labeled-break-value.rs +++ b/src/test/compile-fail/loop-labeled-break-value.rs @@ -16,6 +16,6 @@ fn main() { let _: i32 = 'inner: loop { break 'inner }; //~ ERROR mismatched types } loop { - let _: i32 = 'inner: loop { loop { break 'inner } }; //~ ERROR mismatched types + let _: i32 = 'inner2: loop { loop { break 'inner2 } }; //~ ERROR mismatched types } } diff --git a/src/test/compile-fail/loops-reject-duplicate-labels-2.rs b/src/test/compile-fail/loops-reject-duplicate-labels-2.rs new file mode 100644 index 0000000000000..68627ecaa718f --- /dev/null +++ b/src/test/compile-fail/loops-reject-duplicate-labels-2.rs @@ -0,0 +1,51 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +// ignore-tidy-linelength + +// Issue #21633: reject duplicate loop labels in function bodies. +// +// This is testing the generalization (to the whole function body) +// discussed here: +// http://internals.rust-lang.org/t/psa-rejecting-duplicate-loop-labels/1833 + +pub fn foo() { + { 'fl: for _ in 0..10 { break; } } //~ NOTE shadowed label `'fl` declared here + { 'fl: loop { break; } } //~ WARN label name `'fl` shadows a label name that is already in scope + + { 'lf: loop { break; } } //~ NOTE shadowed label `'lf` declared here + { 'lf: for _ in 0..10 { break; } } //~ WARN label name `'lf` shadows a label name that is already in scope + + { 'wl: while 2 > 1 { break; } } //~ NOTE shadowed label `'wl` declared here + { 'wl: loop { break; } } //~ WARN label name `'wl` shadows a label name that is already in scope + + { 'lw: loop { break; } } //~ NOTE shadowed label `'lw` declared here + { 'lw: while 2 > 1 { break; } } //~ WARN label name `'lw` shadows a label name that is already in scope + + { 'fw: for _ in 0..10 { break; } } //~ NOTE shadowed label `'fw` declared here + { 'fw: while 2 > 1 { break; } } //~ WARN label name `'fw` shadows a label name that is already in scope + + { 'wf: while 2 > 1 { break; } } //~ NOTE shadowed label `'wf` declared here + { 'wf: for _ in 0..10 { break; } } //~ WARN label name `'wf` shadows a label name that is already in scope + + { 'tl: while let Some(_) = None:: { break; } } //~ NOTE shadowed label `'tl` declared here + { 'tl: loop { break; } } //~ WARN label name `'tl` shadows a label name that is already in scope + + { 'lt: loop { break; } } //~ NOTE shadowed label `'lt` declared here + { 'lt: while let Some(_) = None:: { break; } } + //~^ WARN label name `'lt` shadows a label name that is already in scope +} + +#[rustc_error] +pub fn main() { //~ ERROR compilation successful + foo(); +} diff --git a/src/test/compile-fail/loops-reject-duplicate-labels.rs b/src/test/compile-fail/loops-reject-duplicate-labels.rs new file mode 100644 index 0000000000000..15446bf642d4d --- /dev/null +++ b/src/test/compile-fail/loops-reject-duplicate-labels.rs @@ -0,0 +1,60 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +// ignore-tidy-linelength + +// Issue #21633: reject duplicate loop labels in function bodies. +// This is testing the exact cases that are in the issue description. + +fn foo() { + 'fl: for _ in 0..10 { break; } //~ NOTE shadowed label `'fl` declared here + 'fl: loop { break; } //~ WARN label name `'fl` shadows a label name that is already in scope + + 'lf: loop { break; } //~ NOTE shadowed label `'lf` declared here + 'lf: for _ in 0..10 { break; } //~ WARN label name `'lf` shadows a label name that is already in scope + + 'wl: while 2 > 1 { break; } //~ NOTE shadowed label `'wl` declared here + 'wl: loop { break; } //~ WARN label name `'wl` shadows a label name that is already in scope + + 'lw: loop { break; } //~ NOTE shadowed label `'lw` declared here + 'lw: while 2 > 1 { break; } //~ WARN label name `'lw` shadows a label name that is already in scope + + 'fw: for _ in 0..10 { break; } //~ NOTE shadowed label `'fw` declared here + 'fw: while 2 > 1 { break; } //~ WARN label name `'fw` shadows a label name that is already in scope + + 'wf: while 2 > 1 { break; } //~ NOTE shadowed label `'wf` declared here + 'wf: for _ in 0..10 { break; } //~ WARN label name `'wf` shadows a label name that is already in scope + + 'tl: while let Some(_) = None:: { break; } //~ NOTE shadowed label `'tl` declared here + 'tl: loop { break; } //~ WARN label name `'tl` shadows a label name that is already in scope + + 'lt: loop { break; } //~ NOTE shadowed label `'lt` declared here + 'lt: while let Some(_) = None:: { break; } + //~^ WARN label name `'lt` shadows a label name that is already in scope +} + +// Note however that it is okay for the same label to be reused in +// different methods of one impl, as illustrated here. + +struct S; +impl S { + fn m1(&self) { 'okay: loop { break 'okay; } } + fn m2(&self) { 'okay: loop { break 'okay; } } +} + +#[rustc_error] +pub fn main() { //~ ERROR compilation successful + let s = S; + s.m1(); + s.m2(); + foo(); +} diff --git a/src/test/compile-fail/loops-reject-labels-shadowing-lifetimes.rs b/src/test/compile-fail/loops-reject-labels-shadowing-lifetimes.rs new file mode 100644 index 0000000000000..bbdd0774ed936 --- /dev/null +++ b/src/test/compile-fail/loops-reject-labels-shadowing-lifetimes.rs @@ -0,0 +1,120 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Issue #21633: reject duplicate loop labels in function bodies. +// This is testing interaction between lifetime-params and labels. + +#![feature(rustc_attrs)] + +#![allow(dead_code, unused_variables)] + +fn foo() { + fn foo<'a>() { //~ NOTE shadowed lifetime `'a` declared here + 'a: loop { break 'a; } + //~^ WARN label name `'a` shadows a lifetime name that is already in scope + } + + struct Struct<'b, 'c> { _f: &'b i8, _g: &'c i8 } + enum Enum<'d, 'e> { A(&'d i8), B(&'e i8) } + + impl<'d, 'e> Struct<'d, 'e> { + fn meth_okay() { + 'a: loop { break 'a; } + 'b: loop { break 'b; } + 'c: loop { break 'c; } + } + } + + impl <'d, 'e> Enum<'d, 'e> { + fn meth_okay() { + 'a: loop { break 'a; } + 'b: loop { break 'b; } + 'c: loop { break 'c; } + } + } + + impl<'bad, 'c> Struct<'bad, 'c> { //~ NOTE shadowed lifetime `'bad` declared here + fn meth_bad(&self) { + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + + impl<'b, 'bad> Struct<'b, 'bad> { //~ NOTE shadowed lifetime `'bad` declared here + fn meth_bad2(&self) { + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + + impl<'b, 'c> Struct<'b, 'c> { + fn meth_bad3<'bad>(x: &'bad i8) { //~ NOTE shadowed lifetime `'bad` declared here + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + + fn meth_bad4<'a,'bad>(x: &'a i8, y: &'bad i8) { + //~^ NOTE shadowed lifetime `'bad` declared here + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + + impl <'bad, 'e> Enum<'bad, 'e> { //~ NOTE shadowed lifetime `'bad` declared here + fn meth_bad(&self) { + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + impl <'d, 'bad> Enum<'d, 'bad> { //~ NOTE shadowed lifetime `'bad` declared here + fn meth_bad2(&self) { + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + impl <'d, 'e> Enum<'d, 'e> { + fn meth_bad3<'bad>(x: &'bad i8) { //~ NOTE shadowed lifetime `'bad` declared here + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + + fn meth_bad4<'a,'bad>(x: &'bad i8) { //~ NOTE shadowed lifetime `'bad` declared here + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + + trait HasDefaultMethod1<'bad> { //~ NOTE shadowed lifetime `'bad` declared here + fn meth_okay() { + 'c: loop { break 'c; } + } + fn meth_bad(&self) { + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + trait HasDefaultMethod2<'a,'bad> { //~ NOTE shadowed lifetime `'bad` declared here + fn meth_bad(&self) { + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } + trait HasDefaultMethod3<'a,'b> { + fn meth_bad<'bad>(&self) { //~ NOTE shadowed lifetime `'bad` declared here + 'bad: loop { break 'bad; } + //~^ WARN label name `'bad` shadows a lifetime name that is already in scope + } + } +} + +#[rustc_error] +pub fn main() { //~ ERROR compilation successful + foo(); +} diff --git a/src/test/compile-fail/loops-reject-lifetime-shadowing-label.rs b/src/test/compile-fail/loops-reject-lifetime-shadowing-label.rs new file mode 100644 index 0000000000000..2344d251c9a69 --- /dev/null +++ b/src/test/compile-fail/loops-reject-lifetime-shadowing-label.rs @@ -0,0 +1,41 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +#![allow(dead_code, unused_variables)] + +// Issue #21633: reject duplicate loop labels in function bodies. +// +// Test rejection of lifetimes in *expressions* that shadow loop labels. + +fn foo() { + // Reusing lifetime `'a` in function item is okay. + fn foo<'a>(x: &'a i8) -> i8 { *x } + + // So is reusing `'a` in struct item + struct S1<'a> { x: &'a i8 } impl<'a> S1<'a> { fn m(&self) {} } + // and a method item + struct S2; impl S2 { fn m<'a>(&self) {} } + + let z = 3_i8; + + 'a: loop { //~ NOTE shadowed label `'a` declared here + let b = Box::new(|x: &i8| *x) as Box Fn(&'a i8) -> i8>; + //~^ WARN lifetime name `'a` shadows a label name that is already in scope + assert_eq!((*b)(&z), z); + break 'a; + } +} + +#[rustc_error] +pub fn main() { //~ ERROR compilation successful + foo(); +} diff --git a/src/test/compile-fail/shadowed-lifetime.rs b/src/test/compile-fail/shadowed-lifetime.rs index 110b1a0d90c22..8cbab5f830809 100644 --- a/src/test/compile-fail/shadowed-lifetime.rs +++ b/src/test/compile-fail/shadowed-lifetime.rs @@ -15,14 +15,14 @@ struct Foo<'a>(&'a isize); impl<'a> Foo<'a> { //~^ NOTE shadowed lifetime `'a` declared here fn shadow_in_method<'a>(&'a self) -> &'a isize { - //~^ ERROR lifetime name `'a` shadows another lifetime name that is already in scope + //~^ ERROR lifetime name `'a` shadows a lifetime name that is already in scope self.0 } fn shadow_in_type<'b>(&'b self) -> &'b isize { //~^ NOTE shadowed lifetime `'b` declared here let x: for<'b> fn(&'b isize) = panic!(); - //~^ ERROR lifetime name `'b` shadows another lifetime name that is already in scope + //~^ ERROR lifetime name `'b` shadows a lifetime name that is already in scope self.0 } From 2b3cd40f829e256bf54729e7fd0a3b8ceb4c2925 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 21 Apr 2015 09:58:06 +0200 Subject: [PATCH 3/3] add notes clarifying introduction of warnings for a pair of run-pass tests. --- src/test/run-pass/hygienic-labels-in-let.rs | 8 ++++++++ src/test/run-pass/hygienic-labels.rs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/test/run-pass/hygienic-labels-in-let.rs b/src/test/run-pass/hygienic-labels-in-let.rs index 589d6e1581bde..5b45f1e0d3928 100644 --- a/src/test/run-pass/hygienic-labels-in-let.rs +++ b/src/test/run-pass/hygienic-labels-in-let.rs @@ -10,6 +10,14 @@ // ignore-pretty: pprust doesn't print hygiene output +// Test that labels injected by macros do not break hygiene. This +// checks cases where the macros invocations are under the rhs of a +// let statement. + +// Issue #24278: The label/lifetime shadowing checker from #24162 +// conservatively ignores hygiene, and thus issues warnings that are +// both true- and false-positives for this test. + macro_rules! loop_x { ($e: expr) => { // $e shouldn't be able to interact with this 'x diff --git a/src/test/run-pass/hygienic-labels.rs b/src/test/run-pass/hygienic-labels.rs index df72a5410a2b2..a5882f022920f 100644 --- a/src/test/run-pass/hygienic-labels.rs +++ b/src/test/run-pass/hygienic-labels.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that labels injected by macros do not break hygiene. + +// Issue #24278: The label/lifetime shadowing checker from #24162 +// conservatively ignores hygiene, and thus issues warnings that are +// both true- and false-positives for this test. macro_rules! loop_x { ($e: expr) => {