Auto merge of #35453 - jseyfried:hygienize_metavariables, r=nrc

macros: Make metavariables hygienic

This PR makes metavariables hygienic. For example, consider:
```rust
macro_rules! foo {
    ($x:tt) => { // Suppose that this token tree argument is always a metavariable.
        macro_rules! bar { ($x:expr, $y:expr) => { ($x, $y) } }
    }
}

fn main() {
    foo!($z); // This currently compiles.
    foo!($y); // This is an error today but compiles after this PR.
}
```
Today, the `macro_rules! bar { ... }` definition is only valid when the metavariable passed to `foo` is not `$y` (since it unhygienically conflicts with the `$y` in the definition of `bar`) or `$x` (c.f. #35450).

After this PR, the definition of `bar` is always valid (and `bar!(a, b)` always expands to `(a, b)` as expected).

This can break code that was allowed in #34925 (landed two weeks ago). For example,
```rust
macro_rules! outer {
    ($t:tt) => {
        macro_rules! inner { ($i:item) => { $t } }
    }
}

outer!($i); // This `$i` should not interact with the `$i` in the definition of `inner!`.
inner!(fn main() {}); // After this PR, this is an error ("unknown macro variable `i`").
```

Due to the severe limitations on nested `macro_rules!` before #34925, this is not a breaking change for stable/beta.

Fixes #35450.

r? @nrc
This commit is contained in:
bors 2016-08-13 23:37:11 -07:00 committed by GitHub
commit eec30ea657
6 changed files with 41 additions and 14 deletions

View File

@ -79,7 +79,7 @@ pub use self::ParseResult::*;
use self::TokenTreeOrTokenTreeVec::*;
use ast;
use ast::{Name, Ident};
use ast::Ident;
use syntax_pos::{self, BytePos, mk_sp, Span};
use codemap::Spanned;
use errors::FatalError;
@ -202,9 +202,9 @@ pub enum NamedMatch {
}
pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
-> ParseResult<HashMap<Name, Rc<NamedMatch>>> {
-> ParseResult<HashMap<Ident, Rc<NamedMatch>>> {
fn n_rec(p_s: &ParseSess, m: &TokenTree, res: &[Rc<NamedMatch>],
ret_val: &mut HashMap<Name, Rc<NamedMatch>>, idx: &mut usize)
ret_val: &mut HashMap<Ident, Rc<NamedMatch>>, idx: &mut usize)
-> Result<(), (syntax_pos::Span, String)> {
match *m {
TokenTree::Sequence(_, ref seq) => {
@ -218,7 +218,7 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
}
}
TokenTree::Token(sp, MatchNt(bind_name, _)) => {
match ret_val.entry(bind_name.name) {
match ret_val.entry(bind_name) {
Vacant(spot) => {
spot.insert(res[*idx].clone());
*idx += 1;
@ -257,7 +257,7 @@ pub enum ParseResult<T> {
Error(syntax_pos::Span, String)
}
pub type NamedParseResult = ParseResult<HashMap<Name, Rc<NamedMatch>>>;
pub type NamedParseResult = ParseResult<HashMap<Ident, Rc<NamedMatch>>>;
pub type PositionalParseResult = ParseResult<Vec<Rc<NamedMatch>>>;
/// Perform a token equality check, ignoring syntax context (that is, an

View File

@ -302,7 +302,7 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
let mut valid = true;
// Extract the arguments:
let lhses = match **argument_map.get(&lhs_nm.name).unwrap() {
let lhses = match **argument_map.get(&lhs_nm).unwrap() {
MatchedSeq(ref s, _) => {
s.iter().map(|m| match **m {
MatchedNonterminal(NtTT(ref tt)) => {
@ -315,7 +315,7 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
_ => cx.span_bug(def.span, "wrong-structured lhs")
};
let rhses = match **argument_map.get(&rhs_nm.name).unwrap() {
let rhses = match **argument_map.get(&rhs_nm).unwrap() {
MatchedSeq(ref s, _) => {
s.iter().map(|m| match **m {
MatchedNonterminal(NtTT(ref tt)) => (**tt).clone(),

View File

@ -9,7 +9,7 @@
// except according to those terms.
use self::LockstepIterSize::*;
use ast::{Ident, Name};
use ast::Ident;
use syntax_pos::{Span, DUMMY_SP};
use errors::{Handler, DiagnosticBuilder};
use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
@ -38,7 +38,7 @@ pub struct TtReader<'a> {
/// the unzipped tree:
stack: Vec<TtFrame>,
/* for MBE-style macro transcription */
interpolations: HashMap<Name, Rc<NamedMatch>>,
interpolations: HashMap<Ident, Rc<NamedMatch>>,
imported_from: Option<Ident>,
// Some => return imported_from as the next token
@ -57,7 +57,7 @@ pub struct TtReader<'a> {
/// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can
/// (and should) be None.
pub fn new_tt_reader(sp_diag: &Handler,
interp: Option<HashMap<Name, Rc<NamedMatch>>>,
interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
imported_from: Option<Ident>,
src: Vec<tokenstream::TokenTree>)
-> TtReader {
@ -71,7 +71,7 @@ pub fn new_tt_reader(sp_diag: &Handler,
/// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can
/// (and should) be None.
pub fn new_tt_reader_with_doc_flag(sp_diag: &Handler,
interp: Option<HashMap<Name, Rc<NamedMatch>>>,
interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
imported_from: Option<Ident>,
src: Vec<tokenstream::TokenTree>,
desugar_doc_comments: bool)
@ -119,7 +119,7 @@ fn lookup_cur_matched_by_matched(r: &TtReader, start: Rc<NamedMatch>) -> Rc<Name
}
fn lookup_cur_matched(r: &TtReader, name: Ident) -> Option<Rc<NamedMatch>> {
let matched_opt = r.interpolations.get(&name.name).cloned();
let matched_opt = r.interpolations.get(&name).cloned();
matched_opt.map(|s| lookup_cur_matched_by_matched(r, s))
}

View File

@ -0,0 +1,16 @@
// Copyright 2016 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.
macro_rules! m { ($t:tt) => { $t } }
fn main() {
m!($t); //~ ERROR unknown macro variable
//~| ERROR expected expression
}

View File

@ -16,5 +16,16 @@ macro_rules! foo {
foo!(Box);
macro_rules! bar {
($x:tt) => {
macro_rules! baz {
($x:tt, $y:tt) => { ($x, $y) }
}
}
}
#[rustc_error]
fn main() {} //~ ERROR compilation successful
fn main() { //~ ERROR compilation successful
bar!($y);
let _: (i8, i16) = baz!(0i8, 0i16);
}

View File

@ -36,7 +36,7 @@ fn expand_mbe_matches(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
let mac_expr = match TokenTree::parse(cx, &mbe_matcher[..], args) {
Success(map) => {
match (&*map[&str_to_ident("matched").name], &*map[&str_to_ident("pat").name]) {
match (&*map[&str_to_ident("matched")], &*map[&str_to_ident("pat")]) {
(&MatchedNonterminal(NtExpr(ref matched_expr)),
&MatchedSeq(ref pats, seq_sp)) => {
let pats: Vec<P<Pat>> = pats.iter().map(|pat_nt|