librustc: Fix the issue with labels shadowing variable names by making
the leading quote part of the identifier for the purposes of hygiene. This adopts @jbclements' solution to #14539. I'm not sure if this is a breaking change or not. Closes #12512. [breaking-change]
This commit is contained in:
parent
e7f11f20e5
commit
2ed4734873
17 changed files with 159 additions and 89 deletions
|
@ -165,7 +165,7 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> {
|
|||
fn visit_lifetime_ref(&mut self,
|
||||
lifetime_ref: &ast::Lifetime,
|
||||
scope: Scope<'a>) {
|
||||
if lifetime_ref.name == special_idents::statik.name {
|
||||
if lifetime_ref.name == special_idents::static_lifetime.name {
|
||||
self.insert_lifetime(lifetime_ref, DefStaticRegion);
|
||||
return;
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ impl<'a> LifetimeContext<'a> {
|
|||
lifetime_ref: &ast::Lifetime) {
|
||||
self.sess.span_err(
|
||||
lifetime_ref.span,
|
||||
format!("use of undeclared lifetime name `'{}`",
|
||||
format!("use of undeclared lifetime name `{}`",
|
||||
token::get_name(lifetime_ref.name)).as_slice());
|
||||
}
|
||||
|
||||
|
@ -338,7 +338,7 @@ impl<'a> LifetimeContext<'a> {
|
|||
for i in range(0, lifetimes.len()) {
|
||||
let lifetime_i = lifetimes.get(i);
|
||||
|
||||
let special_idents = [special_idents::statik];
|
||||
let special_idents = [special_idents::static_lifetime];
|
||||
for lifetime in lifetimes.iter() {
|
||||
if special_idents.iter().any(|&i| i.name == lifetime.name) {
|
||||
self.sess.span_err(
|
||||
|
@ -354,7 +354,7 @@ impl<'a> LifetimeContext<'a> {
|
|||
if lifetime_i.name == lifetime_j.name {
|
||||
self.sess.span_err(
|
||||
lifetime_j.span,
|
||||
format!("lifetime name `'{}` declared twice in \
|
||||
format!("lifetime name `{}` declared twice in \
|
||||
the same scope",
|
||||
token::get_name(lifetime_j.name)).as_slice());
|
||||
}
|
||||
|
|
|
@ -1505,7 +1505,8 @@ impl LifeGiver {
|
|||
fn give_lifetime(&self) -> ast::Lifetime {
|
||||
let mut lifetime;
|
||||
loop {
|
||||
let s = num_to_str(self.counter.get());
|
||||
let mut s = String::from_str("'");
|
||||
s.push_str(num_to_str(self.counter.get()).as_slice());
|
||||
if !self.taken.contains(&s) {
|
||||
lifetime = name_to_dummy_lifetime(
|
||||
token::str_to_ident(s.as_slice()).name);
|
||||
|
|
|
@ -162,7 +162,7 @@ pub fn bound_region_to_str(cx: &ctxt,
|
|||
|
||||
match br {
|
||||
BrNamed(_, name) => {
|
||||
format!("{}'{}{}", prefix, token::get_name(name), space_str)
|
||||
format!("{}{}{}", prefix, token::get_name(name), space_str)
|
||||
}
|
||||
BrAnon(_) => prefix.to_string(),
|
||||
BrFresh(_) => prefix.to_string(),
|
||||
|
|
|
@ -85,6 +85,7 @@ pub trait AstBuilder {
|
|||
typ: P<ast::Ty>,
|
||||
ex: Gc<ast::Expr>)
|
||||
-> Gc<ast::Stmt>;
|
||||
fn stmt_item(&self, sp: Span, item: Gc<ast::Item>) -> Gc<ast::Stmt>;
|
||||
|
||||
// blocks
|
||||
fn block(&self, span: Span, stmts: Vec<Gc<ast::Stmt>>,
|
||||
|
@ -239,6 +240,14 @@ pub trait AstBuilder {
|
|||
vi: Vec<ast::ViewItem>,
|
||||
items: Vec<Gc<ast::Item>>) -> Gc<ast::Item>;
|
||||
|
||||
fn item_static(&self,
|
||||
span: Span,
|
||||
name: Ident,
|
||||
ty: P<ast::Ty>,
|
||||
mutbl: ast::Mutability,
|
||||
expr: Gc<ast::Expr>)
|
||||
-> Gc<ast::Item>;
|
||||
|
||||
fn item_ty_poly(&self,
|
||||
span: Span,
|
||||
name: Ident,
|
||||
|
@ -484,11 +493,19 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
|||
box(GC) respan(sp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))
|
||||
}
|
||||
|
||||
fn block(&self, span: Span, stmts: Vec<Gc<ast::Stmt>>,
|
||||
expr: Option<Gc<Expr>>) -> P<ast::Block> {
|
||||
fn block(&self,
|
||||
span: Span,
|
||||
stmts: Vec<Gc<ast::Stmt>>,
|
||||
expr: Option<Gc<Expr>>)
|
||||
-> P<ast::Block> {
|
||||
self.block_all(span, Vec::new(), stmts, expr)
|
||||
}
|
||||
|
||||
fn stmt_item(&self, sp: Span, item: Gc<ast::Item>) -> Gc<ast::Stmt> {
|
||||
let decl = respan(sp, ast::DeclItem(item));
|
||||
box(GC) respan(sp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))
|
||||
}
|
||||
|
||||
fn block_expr(&self, expr: Gc<ast::Expr>) -> P<ast::Block> {
|
||||
self.block_all(expr.span, Vec::new(), Vec::new(), Some(expr))
|
||||
}
|
||||
|
@ -942,6 +959,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
fn item_static(&self,
|
||||
span: Span,
|
||||
name: Ident,
|
||||
ty: P<ast::Ty>,
|
||||
mutbl: ast::Mutability,
|
||||
expr: Gc<ast::Expr>)
|
||||
-> Gc<ast::Item> {
|
||||
self.item(span, name, Vec::new(), ast::ItemStatic(ty, mutbl, expr))
|
||||
}
|
||||
|
||||
fn item_ty_poly(&self, span: Span, name: Ident, ty: P<ast::Ty>,
|
||||
generics: Generics) -> Gc<ast::Item> {
|
||||
self.item(span, name, Vec::new(), ast::ItemTy(ty, generics))
|
||||
|
|
|
@ -94,6 +94,18 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
|||
}
|
||||
|
||||
let e = cx.expr_vec_slice(sp, bytes);
|
||||
let e = quote_expr!(cx, { static BYTES: &'static [u8] = $e; BYTES});
|
||||
let ty = cx.ty(sp, ast::TyVec(cx.ty_ident(sp, cx.ident_of("u8"))));
|
||||
let lifetime = cx.lifetime(sp, cx.ident_of("'static").name);
|
||||
let item = cx.item_static(sp,
|
||||
cx.ident_of("BYTES"),
|
||||
cx.ty_rptr(sp,
|
||||
ty,
|
||||
Some(lifetime),
|
||||
ast::MutImmutable),
|
||||
ast::MutImmutable,
|
||||
e);
|
||||
let e = cx.expr_block(cx.block(sp,
|
||||
vec!(cx.stmt_item(sp, item)),
|
||||
Some(cx.expr_ident(sp, cx.ident_of("BYTES")))));
|
||||
MacExpr::new(e)
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
|||
cx.ident_of("str")),
|
||||
Some(cx.lifetime(sp,
|
||||
cx.ident_of(
|
||||
"static").name)),
|
||||
"'static").name)),
|
||||
ast::MutImmutable))))
|
||||
}
|
||||
Some(s) => {
|
||||
|
|
|
@ -465,7 +465,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
|||
self.ecx.ident_of("rt"),
|
||||
self.ecx.ident_of("Piece")),
|
||||
vec!(self.ecx.lifetime(self.fmtsp,
|
||||
self.ecx.ident_of("static").name)),
|
||||
self.ecx.ident_of("'static").name)),
|
||||
Vec::new()
|
||||
), None);
|
||||
let ty = ast::TyFixedLengthVec(
|
||||
|
|
|
@ -757,18 +757,33 @@ impl<'a> StringReader<'a> {
|
|||
while ident_continue(self.curr) {
|
||||
self.bump();
|
||||
}
|
||||
|
||||
// Include the leading `'` in the real identifier, for macro
|
||||
// expansion purposes. See #12512 for the gory details of why
|
||||
// this is necessary.
|
||||
let ident = self.with_str_from(start, |lifetime_name| {
|
||||
str_to_ident(format!("'{}", lifetime_name).as_slice())
|
||||
});
|
||||
|
||||
// Conjure up a "keyword checking ident" to make sure that
|
||||
// the lifetime name is not a keyword.
|
||||
let keyword_checking_ident =
|
||||
self.with_str_from(start, |lifetime_name| {
|
||||
str_to_ident(lifetime_name)
|
||||
});
|
||||
let tok = &token::IDENT(ident, false);
|
||||
|
||||
if token::is_keyword(token::keywords::Self, tok) {
|
||||
self.err_span(start, self.last_pos,
|
||||
let keyword_checking_token =
|
||||
&token::IDENT(keyword_checking_ident, false);
|
||||
if token::is_keyword(token::keywords::Self,
|
||||
keyword_checking_token) {
|
||||
self.err_span(start,
|
||||
self.last_pos,
|
||||
"invalid lifetime name: 'self \
|
||||
is no longer a special lifetime");
|
||||
} else if token::is_any_keyword(tok) &&
|
||||
!token::is_keyword(token::keywords::Static, tok) {
|
||||
self.err_span(start, self.last_pos,
|
||||
} else if token::is_any_keyword(keyword_checking_token) &&
|
||||
!token::is_keyword(token::keywords::Static,
|
||||
keyword_checking_token) {
|
||||
self.err_span(start,
|
||||
self.last_pos,
|
||||
"invalid lifetime name");
|
||||
}
|
||||
return token::LIFETIME(ident);
|
||||
|
@ -1128,7 +1143,7 @@ mod test {
|
|||
|
||||
#[test] fn lifetime_name() {
|
||||
assert_eq!(setup(&mk_sh(), "'abc".to_string()).next_token().tok,
|
||||
token::LIFETIME(token::str_to_ident("abc")));
|
||||
token::LIFETIME(token::str_to_ident("'abc")));
|
||||
}
|
||||
|
||||
#[test] fn raw_string() {
|
||||
|
|
|
@ -3452,7 +3452,7 @@ impl<'a> Parser<'a> {
|
|||
match self.token {
|
||||
token::LIFETIME(lifetime) => {
|
||||
let lifetime_interned_string = token::get_ident(lifetime);
|
||||
if lifetime_interned_string.equiv(&("static")) {
|
||||
if lifetime_interned_string.equiv(&("'static")) {
|
||||
result.push(StaticRegionTyParamBound);
|
||||
if allow_any_lifetime && ret_lifetime.is_none() {
|
||||
ret_lifetime = Some(ast::Lifetime {
|
||||
|
|
|
@ -232,7 +232,7 @@ pub fn to_str(t: &Token) -> String {
|
|||
/* Name components */
|
||||
IDENT(s, _) => get_ident(s).get().to_string(),
|
||||
LIFETIME(s) => {
|
||||
(format!("'{}", get_ident(s))).to_string()
|
||||
(format!("{}", get_ident(s))).to_string()
|
||||
}
|
||||
UNDERSCORE => "_".to_string(),
|
||||
|
||||
|
@ -433,71 +433,72 @@ declare_special_idents_and_keywords! {
|
|||
(0, invalid, "");
|
||||
(super::SELF_KEYWORD_NAME, self_, "self");
|
||||
(super::STATIC_KEYWORD_NAME, statik, "static");
|
||||
(3, static_lifetime, "'static");
|
||||
|
||||
// for matcher NTs
|
||||
(3, tt, "tt");
|
||||
(4, matchers, "matchers");
|
||||
(4, tt, "tt");
|
||||
(5, matchers, "matchers");
|
||||
|
||||
// outside of libsyntax
|
||||
(5, clownshoe_abi, "__rust_abi");
|
||||
(6, opaque, "<opaque>");
|
||||
(7, unnamed_field, "<unnamed_field>");
|
||||
(8, type_self, "Self");
|
||||
(6, clownshoe_abi, "__rust_abi");
|
||||
(7, opaque, "<opaque>");
|
||||
(8, unnamed_field, "<unnamed_field>");
|
||||
(9, type_self, "Self");
|
||||
}
|
||||
|
||||
pub mod keywords {
|
||||
// These ones are variants of the Keyword enum
|
||||
|
||||
'strict:
|
||||
(9, As, "as");
|
||||
(10, Break, "break");
|
||||
(11, Crate, "crate");
|
||||
(12, Else, "else");
|
||||
(13, Enum, "enum");
|
||||
(14, Extern, "extern");
|
||||
(15, False, "false");
|
||||
(16, Fn, "fn");
|
||||
(17, For, "for");
|
||||
(18, If, "if");
|
||||
(19, Impl, "impl");
|
||||
(20, In, "in");
|
||||
(21, Let, "let");
|
||||
(22, Loop, "loop");
|
||||
(23, Match, "match");
|
||||
(24, Mod, "mod");
|
||||
(25, Mut, "mut");
|
||||
(26, Once, "once");
|
||||
(27, Pub, "pub");
|
||||
(28, Ref, "ref");
|
||||
(29, Return, "return");
|
||||
(10, As, "as");
|
||||
(11, Break, "break");
|
||||
(12, Crate, "crate");
|
||||
(13, Else, "else");
|
||||
(14, Enum, "enum");
|
||||
(15, Extern, "extern");
|
||||
(16, False, "false");
|
||||
(17, Fn, "fn");
|
||||
(18, For, "for");
|
||||
(19, If, "if");
|
||||
(20, Impl, "impl");
|
||||
(21, In, "in");
|
||||
(22, Let, "let");
|
||||
(23, Loop, "loop");
|
||||
(24, Match, "match");
|
||||
(25, Mod, "mod");
|
||||
(26, Mut, "mut");
|
||||
(27, Once, "once");
|
||||
(28, Pub, "pub");
|
||||
(29, Ref, "ref");
|
||||
(30, Return, "return");
|
||||
// Static and Self are also special idents (prefill de-dupes)
|
||||
(super::STATIC_KEYWORD_NAME, Static, "static");
|
||||
(super::SELF_KEYWORD_NAME, Self, "self");
|
||||
(30, Struct, "struct");
|
||||
(31, Super, "super");
|
||||
(32, True, "true");
|
||||
(33, Trait, "trait");
|
||||
(34, Type, "type");
|
||||
(35, Unsafe, "unsafe");
|
||||
(36, Use, "use");
|
||||
(37, Virtual, "virtual");
|
||||
(38, While, "while");
|
||||
(39, Continue, "continue");
|
||||
(40, Proc, "proc");
|
||||
(41, Box, "box");
|
||||
(31, Struct, "struct");
|
||||
(32, Super, "super");
|
||||
(33, True, "true");
|
||||
(34, Trait, "trait");
|
||||
(35, Type, "type");
|
||||
(36, Unsafe, "unsafe");
|
||||
(37, Use, "use");
|
||||
(38, Virtual, "virtual");
|
||||
(39, While, "while");
|
||||
(40, Continue, "continue");
|
||||
(41, Proc, "proc");
|
||||
(42, Box, "box");
|
||||
|
||||
'reserved:
|
||||
(42, Alignof, "alignof");
|
||||
(43, Be, "be");
|
||||
(44, Const, "const");
|
||||
(45, Offsetof, "offsetof");
|
||||
(46, Priv, "priv");
|
||||
(47, Pure, "pure");
|
||||
(48, Sizeof, "sizeof");
|
||||
(49, Typeof, "typeof");
|
||||
(50, Unsized, "unsized");
|
||||
(51, Yield, "yield");
|
||||
(52, Do, "do");
|
||||
(43, Alignof, "alignof");
|
||||
(44, Be, "be");
|
||||
(45, Const, "const");
|
||||
(46, Offsetof, "offsetof");
|
||||
(47, Priv, "priv");
|
||||
(48, Pure, "pure");
|
||||
(49, Sizeof, "sizeof");
|
||||
(50, Typeof, "typeof");
|
||||
(51, Unsized, "unsized");
|
||||
(52, Yield, "yield");
|
||||
(53, Do, "do");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
// except according to those terms.
|
||||
|
||||
use abi;
|
||||
use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound,
|
||||
TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided};
|
||||
use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound};
|
||||
use ast::{TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided};
|
||||
use ast;
|
||||
use ast_util;
|
||||
use owned_slice::OwnedSlice;
|
||||
|
@ -1325,7 +1325,6 @@ impl<'a> State<'a> {
|
|||
}
|
||||
ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => {
|
||||
for ident in opt_ident.iter() {
|
||||
try!(word(&mut self.s, "'"));
|
||||
try!(self.print_ident(*ident));
|
||||
try!(self.word_space(":"));
|
||||
}
|
||||
|
@ -1339,7 +1338,6 @@ impl<'a> State<'a> {
|
|||
}
|
||||
ast::ExprLoop(ref blk, opt_ident) => {
|
||||
for ident in opt_ident.iter() {
|
||||
try!(word(&mut self.s, "'"));
|
||||
try!(self.print_ident(*ident));
|
||||
try!(self.word_space(":"));
|
||||
}
|
||||
|
@ -1504,7 +1502,6 @@ impl<'a> State<'a> {
|
|||
try!(word(&mut self.s, "break"));
|
||||
try!(space(&mut self.s));
|
||||
for ident in opt_ident.iter() {
|
||||
try!(word(&mut self.s, "'"));
|
||||
try!(self.print_ident(*ident));
|
||||
try!(space(&mut self.s));
|
||||
}
|
||||
|
@ -1513,7 +1510,6 @@ impl<'a> State<'a> {
|
|||
try!(word(&mut self.s, "continue"));
|
||||
try!(space(&mut self.s));
|
||||
for ident in opt_ident.iter() {
|
||||
try!(word(&mut self.s, "'"));
|
||||
try!(self.print_ident(*ident));
|
||||
try!(space(&mut self.s))
|
||||
}
|
||||
|
@ -1943,7 +1939,7 @@ impl<'a> State<'a> {
|
|||
match *region {
|
||||
Some(ref lt) => {
|
||||
let token = token::get_name(lt.name);
|
||||
if token.get() != "static" {
|
||||
if token.get() != "'static" {
|
||||
try!(self.nbsp());
|
||||
first = false;
|
||||
try!(self.print_lifetime(lt));
|
||||
|
@ -1988,7 +1984,6 @@ impl<'a> State<'a> {
|
|||
|
||||
pub fn print_lifetime(&mut self,
|
||||
lifetime: &ast::Lifetime) -> IoResult<()> {
|
||||
try!(word(&mut self.s, "'"));
|
||||
self.print_name(lifetime.name)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,5 +15,5 @@ macro_rules! foo {
|
|||
}
|
||||
|
||||
pub fn main() {
|
||||
'x: loop { foo!() } //~ ERROR use of undeclared label `x`
|
||||
'x: loop { foo!() } //~ ERROR use of undeclared label `'x`
|
||||
}
|
||||
|
|
|
@ -15,5 +15,5 @@ macro_rules! foo {
|
|||
}
|
||||
|
||||
pub fn main() {
|
||||
foo!(break 'x); //~ ERROR use of undeclared label `x`
|
||||
foo!(break 'x); //~ ERROR use of undeclared label `'x`
|
||||
}
|
||||
|
|
|
@ -16,6 +16,6 @@ macro_rules! foo {
|
|||
|
||||
pub fn main() {
|
||||
'x: for _ in range(0,1) {
|
||||
foo!() //~ ERROR use of undeclared label `x`
|
||||
foo!() //~ ERROR use of undeclared label `'x`
|
||||
};
|
||||
}
|
||||
|
|
|
@ -15,5 +15,5 @@ macro_rules! foo {
|
|||
}
|
||||
|
||||
pub fn main() {
|
||||
foo!(break 'x); //~ ERROR use of undeclared label `x`
|
||||
foo!(break 'x); //~ ERROR use of undeclared label `'x`
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `static`
|
||||
struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `'static`
|
||||
x: &'static int
|
||||
}
|
||||
|
||||
|
|
19
src/test/run-pass/loop-label-shadowing.rs
Normal file
19
src/test/run-pass/loop-label-shadowing.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2012 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Issue #12512.
|
||||
|
||||
fn main() {
|
||||
let mut foo = Vec::new();
|
||||
'foo: for i in [1, 2, 3].iter() {
|
||||
foo.push(i);
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue