From 93af9038733f173698befc0928726819cb47bdb7 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sun, 4 Feb 2018 03:20:03 +0000 Subject: [PATCH 01/10] Implemented hygiene opt-out for idents in expansion of declarative macros. Prefixing idents with the `#` (pound) character indicates that their syntax context should be that of the call site rather than the definition site. --- src/libsyntax/ext/expand.rs | 2 +- src/libsyntax/ext/tt/macro_rules.rs | 2 +- src/libsyntax/ext/tt/transcribe.rs | 22 +++++++++++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 584b9455a93ad..00dc3c7e5eee0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -677,7 +677,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { IdentTT(ref expander, tt_span, allow_internal_unstable) => { if ident.name == keywords::Invalid.name() { self.cx.span_err(path.span, - &format!("macro {}! expects an ident argument", path)); + &format!("macro {}! expects an ident argument", path)); self.cx.trace_macros_diag(); kind.dummy(span) } else { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index ffe68289d5224..f7ce876b0df69 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -121,7 +121,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, }; let rhs_spans = rhs.iter().map(|t| t.span()).collect::>(); - // rhs has holes ( `$id` and `$(...)` that need filled) + // rhs has holes ( `$id` and `$(...)` which need to be filled) let mut tts = transcribe(cx, Some(named_matches), rhs); // Replace all the tokens for the corresponding positions in the macro, to maintain diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index 1cdb6b0e5c902..33c4363d1ea74 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -75,6 +75,7 @@ pub fn transcribe(cx: &ExtCtxt, let mut repeats = Vec::new(); let mut result: Vec = Vec::new(); let mut result_stack = Vec::new(); + let mut prev_token: Option = None; loop { let tree = if let Some(tree) = stack.last_mut().unwrap().next() { @@ -116,7 +117,7 @@ pub fn transcribe(cx: &ExtCtxt, continue }; - match tree { + match tree.clone() { quoted::TokenTree::Sequence(sp, seq) => { // FIXME(pcwalton): Bad copy. match lockstep_iter_size("ed::TokenTree::Sequence(sp, seq.clone()), @@ -178,11 +179,26 @@ pub fn transcribe(cx: &ExtCtxt, result_stack.push(mem::replace(&mut result, Vec::new())); } quoted::TokenTree::Token(sp, tok) => { - let mut marker = Marker(cx.current_expansion.mark); - result.push(noop_fold_tt(TokenTree::Token(sp, tok), &mut marker).into()) + // Check if token is ident with opt-out hygiene (pound-sign prefix). + if let (Some(token::Pound), token::Ident(ident, is_raw)) = (prev_token, tok.clone()) { + let call_site = cx.call_site(); + let sp = sp.with_ctxt(call_site.ctxt().apply_mark(cx.current_expansion.mark)); + let ident = Ident { ctxt: call_site.ctxt(), ..ident }; + result.pop(); + result.push(TokenTree::Token(sp, token::Ident(ident, is_raw)).into()); + } else { + let mut marker = Marker(cx.current_expansion.mark); + result.push(noop_fold_tt(TokenTree::Token(sp, tok), &mut marker).into()); + } } quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"), } + + prev_token = if let quoted::TokenTree::Token(_, tok) = tree { + Some(tok) + } else { + None + }; } } From 4739674c776f5382040743c9fb2cbc88aa6e3a32 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 6 Feb 2018 01:47:47 +0000 Subject: [PATCH 02/10] Do parsing of `#` prefix properly. --- src/libsyntax/ext/tt/macro_parser.rs | 15 ++- src/libsyntax/ext/tt/macro_rules.rs | 23 ++++- src/libsyntax/ext/tt/quoted.rs | 139 +++++++++++++++++---------- src/libsyntax/ext/tt/transcribe.rs | 34 +++---- 4 files changed, 136 insertions(+), 75 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 71634ada89458..08f0753d8a2dc 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -230,6 +230,7 @@ pub fn count_names(ms: &[TokenTree]) -> usize { TokenTree::MetaVar(..) => 0, TokenTree::MetaVarDecl(..) => 1, TokenTree::Token(..) => 0, + TokenTree::IdentToken(..) => 0, } }) } @@ -332,7 +333,9 @@ fn nameize>( } } } - TokenTree::MetaVar(..) | TokenTree::Token(..) => (), + TokenTree::MetaVar(..) | + TokenTree::Token(..) | + TokenTree::IdentToken(..) => (), } Ok(()) @@ -557,11 +560,19 @@ fn inner_parse_loop( next_items.push(item); } + // We just matched an ident token. We can just advance the parser. + TokenTree::IdentToken(_, i, _) if token_name_eq(&token::Ident(i, false), token) => { + item.idx += 1; + next_items.push(item); + } + // There was another token that was not `token`... This means we can't add any // rules. NOTE that this is not necessarily an error unless _all_ items in // `cur_items` end up doing this. There may still be some other matchers that do // end up working out. - TokenTree::Token(..) | TokenTree::MetaVar(..) => {} + TokenTree::Token(..) | + TokenTree::IdentToken(..) | + TokenTree::MetaVar(..) => {} } } } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index f7ce876b0df69..f8867db74dcd9 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -328,7 +328,10 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool { use self::quoted::TokenTree; for tt in tts { match *tt { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (), + TokenTree::Token(..) | + TokenTree::IdentToken(..) | + TokenTree::MetaVar(..) | + TokenTree::MetaVarDecl(..) => (), TokenTree::Delimited(_, ref del) => if !check_lhs_no_empty_seq(sess, &del.tts) { return false; }, @@ -410,7 +413,10 @@ impl FirstSets { let mut first = TokenSet::empty(); for tt in tts.iter().rev() { match *tt { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { + TokenTree::Token(..) | + TokenTree::IdentToken(..) | + TokenTree::MetaVar(..) | + TokenTree::MetaVarDecl(..) => { first.replace_with(tt.clone()); } TokenTree::Delimited(span, ref delimited) => { @@ -470,7 +476,10 @@ impl FirstSets { for tt in tts.iter() { assert!(first.maybe_empty); match *tt { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { + TokenTree::Token(..) | + TokenTree::IdentToken(..) | + TokenTree::MetaVar(..) | + TokenTree::MetaVarDecl(..) => { first.add_one(tt.clone()); return first; } @@ -641,7 +650,10 @@ fn check_matcher_core(sess: &ParseSess, // First, update `last` so that it corresponds to the set // of NT tokens that might end the sequence `... token`. match *token { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { + TokenTree::Token(..) | + TokenTree::IdentToken(..) | + TokenTree::MetaVar(..) | + TokenTree::MetaVarDecl(..) => { let can_be_followed_by_any; if let Err(bad_frag) = has_legal_fragment_specifier(sess, features, attrs, token) { let msg = format!("invalid fragment specifier `{}`", bad_frag); @@ -932,7 +944,8 @@ fn is_legal_fragment_specifier(sess: &ParseSess, fn quoted_tt_to_string(tt: "ed::TokenTree) -> String { match *tt { quoted::TokenTree::Token(_, ref tok) => ::print::pprust::token_to_string(tok), - quoted::TokenTree::MetaVar(_, name) => format!("${}", name), + quoted::TokenTree::IdentToken(_, ident, _) => ::print::pprust::token_to_string(&token::Ident(ident, false)), + quoted::TokenTree::MetaVar(_, name, _) => format!("${}", name), quoted::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind), _ => panic!("unexpected quoted::TokenTree::{{Sequence or Delimited}} \ in follow set checker"), diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 77c6afa1c64a6..975ac281f71b4 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -80,6 +80,7 @@ pub enum KleeneOp { ZeroOrMore, /// Kleene plus (`+`) for one or more repetitions OneOrMore, + /// Question mark (`?`) for zero or one repetitions ZeroOrOne, } @@ -88,11 +89,12 @@ pub enum KleeneOp { #[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub enum TokenTree { Token(Span, token::Token), + IdentToken(Span, ast::Ident, bool /* escape hygiene */), Delimited(Span, Lrc), /// A kleene-style repetition sequence Sequence(Span, Lrc), /// E.g. `$var` - MetaVar(Span, ast::Ident), + MetaVar(Span, ast::Ident, bool /* escape hygiene */), /// E.g. `$var:expr`. This is only used in the left hand side of MBE macros. MetaVarDecl( Span, @@ -151,7 +153,8 @@ impl TokenTree { pub fn span(&self) -> Span { match *self { TokenTree::Token(sp, _) - | TokenTree::MetaVar(sp, _) + | TokenTree::IdentToken(sp, _, _) + | TokenTree::MetaVar(sp, _, _) | TokenTree::MetaVarDecl(sp, _, _) | TokenTree::Delimited(sp, _) | TokenTree::Sequence(sp, _) => sp, @@ -196,7 +199,7 @@ pub fn parse( // parse out the matcher (i.e. in `$id:ident` this would parse the `:` and `ident`). let tree = parse_tree(tree, &mut trees, expect_matchers, sess, features, attrs); match tree { - TokenTree::MetaVar(start_sp, ident) if expect_matchers => { + TokenTree::MetaVar(start_sp, ident, _) if expect_matchers => { let span = match trees.next() { Some(tokenstream::TokenTree::Token(span, token::Colon)) => match trees.next() { Some(tokenstream::TokenTree::Token(end_sp, ref tok)) => match tok.ident() { @@ -258,60 +261,30 @@ where { // Depending on what `tree` is, we could be parsing different parts of a macro match tree { - // `tree` is a `$` token. Look at the next token in `trees` - tokenstream::TokenTree::Token(span, token::Dollar) => match trees.next() { - // `tree` is followed by a delimited set of token trees. This indicates the beginning - // of a repetition sequence in the macro (e.g. `$(pat)*`). - Some(tokenstream::TokenTree::Delimited(span, delimited)) => { - // Must have `(` not `{` or `[` - if delimited.delim != token::Paren { - let tok = pprust::token_to_string(&token::OpenDelim(delimited.delim)); - let msg = format!("expected `(`, found `{}`", tok); - sess.span_diagnostic.span_err(span, &msg); + // `tree` is `#` token. Look at the next token in `trees`. + tokenstream::TokenTree::Token(span, token::Pound) => match trees.peek() { + Some(tokenstream::TokenTree::Token(_, token::Dollar)) => { + if let tokenstream::TokenTree::Token(span, token::Dollar) = trees.next().unwrap() { + parse_meta_var(true, span, trees, expect_matchers, sess, features, attrs) + } else { + unreachable!(); } - // Parse the contents of the sequence itself - let sequence = parse(delimited.tts.into(), expect_matchers, sess, features, attrs); - // Get the Kleene operator and optional separator - let (separator, op) = parse_sep_and_kleene_op(trees, span, sess, features, attrs); - // Count the number of captured "names" (i.e. named metavars) - let name_captures = macro_parser::count_names(&sequence); - TokenTree::Sequence( - span, - Lrc::new(SequenceRepetition { - tts: sequence, - separator, - op, - num_captures: name_captures, - }), - ) } - // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` special - // metavariable that names the crate of the invokation. - Some(tokenstream::TokenTree::Token(ident_span, ref token)) if token.is_ident() => { - let (ident, is_raw) = token.ident().unwrap(); - let span = ident_span.with_lo(span.lo()); - if ident.name == keywords::Crate.name() && !is_raw { - let ident = ast::Ident::new(keywords::DollarCrate.name(), ident.span); - TokenTree::Token(span, token::Ident(ident, is_raw)) + Some(tokenstream::TokenTree::Token(_, token::Ident(_, _))) => { + if let tokenstream::TokenTree::Token(span, token::Ident(ident, _)) = trees.next().unwrap() { + TokenTree::IdentToken(span, ident, true) } else { - TokenTree::MetaVar(span, ident) + unreachable!(); } } - // `tree` is followed by a random token. This is an error. - Some(tokenstream::TokenTree::Token(span, tok)) => { - let msg = format!( - "expected identifier, found `{}`", - pprust::token_to_string(&tok) - ); - sess.span_diagnostic.span_err(span, &msg); - TokenTree::MetaVar(span, keywords::Invalid.ident()) - } + _ => TokenTree::Token(span, token::Pound), + } - // There are no more tokens. Just return the `$` we already have. - None => TokenTree::Token(span, token::Dollar), - }, + // `tree` is a `$` token. Look at the next token in `trees`. + tokenstream::TokenTree::Token(span, token::Dollar) => + parse_meta_var(false, span, trees, expect_matchers, sess, features, attrs), // `tree` is an arbitrary token. Keep it. tokenstream::TokenTree::Token(span, tok) => TokenTree::Token(span, tok), @@ -328,6 +301,74 @@ where } } +/// Attempt to parse a single meta variable or meta variable sequence. +fn parse_meta_var( + escape_hygiene: bool, + span: Span, + trees: &mut Peekable, + expect_matchers: bool, + sess: &ParseSess, + features: &Features, + attrs: &[ast::Attribute], +) -> TokenTree +where + I: Iterator, +{ + match trees.next() { + // `tree` is followed by a delimited set of token trees. This indicates the beginning + // of a repetition sequence in the macro (e.g. `$(pat)*`). + Some(tokenstream::TokenTree::Delimited(span, delimited)) => { + // Must have `(` not `{` or `[` + if delimited.delim != token::Paren { + let tok = pprust::token_to_string(&token::OpenDelim(delimited.delim)); + let msg = format!("expected `(`, found `{}`", tok); + sess.span_diagnostic.span_err(span, &msg); + } + // Parse the contents of the sequence itself + let sequence = parse(delimited.tts.into(), expect_matchers, sess, features, attrs); + // Get the Kleene operator and optional separator + let (separator, op) = parse_sep_and_kleene_op(trees, span, sess, features, attrs); + // Count the number of captured "names" (i.e. named metavars) + let name_captures = macro_parser::count_names(&sequence); + TokenTree::Sequence( + span, + Lrc::new(SequenceRepetition { + tts: sequence, + separator, + op, + num_captures: name_captures, + }), + ) + } + + // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` special + // metavariable that names the crate of the invokation. + Some(tokenstream::TokenTree::Token(ident_span, ref token)) if token.is_ident() => { + let (ident, _) = token.ident().unwrap(); + let span = ident_span.with_lo(span.lo()); + if ident.name == keywords::Crate.name() && !is_raw { + let ident = ast::Ident::new(keywords::DollarCrate.name(), ident.span); + TokenTree::Token(span, token::Ident(ident, is_raw)) + } else { + TokenTree::MetaVar(span, ident, escape_hygiene) + } + } + + // `tree` is followed by an arbitrary token. This is an error. + Some(tokenstream::TokenTree::Token(span, tok)) => { + let msg = format!( + "expected identifier, found `{}`", + pprust::token_to_string(&tok) + ); + sess.span_diagnostic.span_err(span, &msg); + TokenTree::MetaVar(span, keywords::Invalid.ident(), escape_hygiene) + } + + // There are no more tokens. Just return the `$` we already have. + None => TokenTree::Token(span, token::Dollar), + } +} + /// Takes a token and returns `Some(KleeneOp)` if the token is `+` `*` or `?`. Otherwise, return /// `None`. fn kleene_op(token: &token::Token) -> Option { diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index 33c4363d1ea74..edec515405523 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -75,7 +75,6 @@ pub fn transcribe(cx: &ExtCtxt, let mut repeats = Vec::new(); let mut result: Vec = Vec::new(); let mut result_stack = Vec::new(); - let mut prev_token: Option = None; loop { let tree = if let Some(tree) = stack.last_mut().unwrap().next() { @@ -151,7 +150,7 @@ pub fn transcribe(cx: &ExtCtxt, } } // FIXME #2887: think about span stuff here - quoted::TokenTree::MetaVar(mut sp, ident) => { + quoted::TokenTree::MetaVar(mut sp, ident, _escape_hygiene) => { if let Some(cur_matched) = lookup_cur_matched(ident, &interpolations, &repeats) { if let MatchedNonterminal(ref nt) = *cur_matched { if let NtTT(ref tt) = **nt { @@ -179,26 +178,22 @@ pub fn transcribe(cx: &ExtCtxt, result_stack.push(mem::replace(&mut result, Vec::new())); } quoted::TokenTree::Token(sp, tok) => { - // Check if token is ident with opt-out hygiene (pound-sign prefix). - if let (Some(token::Pound), token::Ident(ident, is_raw)) = (prev_token, tok.clone()) { - let call_site = cx.call_site(); - let sp = sp.with_ctxt(call_site.ctxt().apply_mark(cx.current_expansion.mark)); - let ident = Ident { ctxt: call_site.ctxt(), ..ident }; - result.pop(); - result.push(TokenTree::Token(sp, token::Ident(ident, is_raw)).into()); + let mut marker = Marker(cx.current_expansion.mark); + result.push(noop_fold_tt(TokenTree::Token(sp, tok), &mut marker).into()) + } + quoted::TokenTree::IdentToken(sp, ident, escape_hygiene) => { + let sp_ctxt = if escape_hygiene { cx.call_site() } else { sp }.ctxt(); + let sp = sp.with_ctxt(sp_ctxt.apply_mark(cx.current_expansion.mark)); + let ident_ctxt = if escape_hygiene { + cx.call_site().ctxt() } else { - let mut marker = Marker(cx.current_expansion.mark); - result.push(noop_fold_tt(TokenTree::Token(sp, tok), &mut marker).into()); - } + ident.ctxt.apply_mark(cx.current_expansion.mark) + }; + let ident = Ident { ctxt: ident_ctxt, ..ident }; + result.push(TokenTree::Token(sp, token::Ident(ident, false)).into()); } quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"), } - - prev_token = if let quoted::TokenTree::Token(_, tok) = tree { - Some(tok) - } else { - None - }; } } @@ -265,7 +260,7 @@ fn lockstep_iter_size(tree: "ed::TokenTree, size + lockstep_iter_size(tt, interpolations, repeats) }) }, - TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => + TokenTree::MetaVar(_, name, _) | TokenTree::MetaVarDecl(_, name, _) => match lookup_cur_matched(name, interpolations, repeats) { Some(matched) => match *matched { MatchedNonterminal(_) => LockstepIterSize::Unconstrained, @@ -274,5 +269,6 @@ fn lockstep_iter_size(tree: "ed::TokenTree, _ => LockstepIterSize::Unconstrained }, TokenTree::Token(..) => LockstepIterSize::Unconstrained, + TokenTree::IdentToken(..) => LockstepIterSize::Unconstrained, } } From 2750e655af966963be82ced97b7654d048188ad2 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 6 Feb 2018 17:00:02 +0000 Subject: [PATCH 03/10] Disable hygiene opt-out syntax for legacy `macro_rules`. --- src/libsyntax/ext/tt/macro_rules.rs | 4 ++-- src/libsyntax/ext/tt/quoted.rs | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index f8867db74dcd9..ae4246d62adf0 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -237,7 +237,7 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax s.iter().map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = quoted::parse(tt.clone().into(), true, sess, features, &def.attrs) + let tt = quoted::parse(tt.clone().into(), false, true, sess, features, &def.attrs) .pop().unwrap(); valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); return tt; @@ -254,7 +254,7 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax s.iter().map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return quoted::parse(tt.clone().into(), false, sess, features, &def.attrs) + return quoted::parse(tt.clone().into(), !body.legacy, false, sess, features, &def.attrs) .pop().unwrap(); } } diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 975ac281f71b4..a4474e856552d 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -183,6 +183,7 @@ impl TokenTree { /// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`. pub fn parse( input: tokenstream::TokenStream, + hygiene_optout: bool, expect_matchers: bool, sess: &ParseSess, features: &Features, @@ -197,7 +198,7 @@ pub fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e. in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess, features, attrs); + let tree = parse_tree(tree, &mut trees, hygiene_optout, expect_matchers, sess, features, attrs); match tree { TokenTree::MetaVar(start_sp, ident, _) if expect_matchers => { let span = match trees.next() { @@ -251,6 +252,7 @@ pub fn parse( fn parse_tree( tree: tokenstream::TokenTree, trees: &mut Peekable, + hygiene_optout: bool, expect_matchers: bool, sess: &ParseSess, features: &Features, @@ -261,11 +263,11 @@ where { // Depending on what `tree` is, we could be parsing different parts of a macro match tree { - // `tree` is `#` token. Look at the next token in `trees`. - tokenstream::TokenTree::Token(span, token::Pound) => match trees.peek() { + // `tree` is `#` token and hygiene opt-out syntax is on. Look at the next token in `trees`. + tokenstream::TokenTree::Token(span, token::Pound) if hygiene_optout => match trees.peek() { Some(tokenstream::TokenTree::Token(_, token::Dollar)) => { if let tokenstream::TokenTree::Token(span, token::Dollar) = trees.next().unwrap() { - parse_meta_var(true, span, trees, expect_matchers, sess, features, attrs) + parse_meta_var(true, span, trees, hygiene_optout, expect_matchers, sess, features, attrs) } else { unreachable!(); } @@ -284,7 +286,7 @@ where // `tree` is a `$` token. Look at the next token in `trees`. tokenstream::TokenTree::Token(span, token::Dollar) => - parse_meta_var(false, span, trees, expect_matchers, sess, features, attrs), + parse_meta_var(false, span, trees, hygiene_optout, expect_matchers, sess, features, attrs), // `tree` is an arbitrary token. Keep it. tokenstream::TokenTree::Token(span, tok) => TokenTree::Token(span, tok), @@ -295,7 +297,7 @@ where span, Lrc::new(Delimited { delim: delimited.delim, - tts: parse(delimited.tts.into(), expect_matchers, sess, features, attrs), + tts: parse(delimited.tts.into(), hygiene_optout, expect_matchers, sess, features, attrs), }), ), } @@ -306,6 +308,7 @@ fn parse_meta_var( escape_hygiene: bool, span: Span, trees: &mut Peekable, + hygiene_optout: bool, expect_matchers: bool, sess: &ParseSess, features: &Features, @@ -325,7 +328,7 @@ where sess.span_diagnostic.span_err(span, &msg); } // Parse the contents of the sequence itself - let sequence = parse(delimited.tts.into(), expect_matchers, sess, features, attrs); + let sequence = parse(delimited.tts.into(), hygiene_optout, expect_matchers, sess, features, attrs); // Get the Kleene operator and optional separator let (separator, op) = parse_sep_and_kleene_op(trees, span, sess, features, attrs); // Count the number of captured "names" (i.e. named metavars) From d397378bfc14f57bcbb9688d1433673db4a35a45 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 6 Feb 2018 23:05:40 +0000 Subject: [PATCH 04/10] Added support for `#` opt-out syntax on lifetimes. --- src/libsyntax/ext/tt/macro_parser.rs | 15 ++------ src/libsyntax/ext/tt/macro_rules.rs | 27 ++++++-------- src/libsyntax/ext/tt/quoted.rs | 56 ++++++++++++++++++---------- src/libsyntax/ext/tt/transcribe.rs | 34 +++++++++++------ 4 files changed, 72 insertions(+), 60 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 08f0753d8a2dc..0e731ccf3a93d 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -230,7 +230,6 @@ pub fn count_names(ms: &[TokenTree]) -> usize { TokenTree::MetaVar(..) => 0, TokenTree::MetaVarDecl(..) => 1, TokenTree::Token(..) => 0, - TokenTree::IdentToken(..) => 0, } }) } @@ -334,8 +333,7 @@ fn nameize>( } } TokenTree::MetaVar(..) | - TokenTree::Token(..) | - TokenTree::IdentToken(..) => (), + TokenTree::Token(..) => (), } Ok(()) @@ -543,7 +541,7 @@ fn inner_parse_loop( // // At the beginning of the loop, if we reach the end of the delimited submatcher, // we pop the stack to backtrack out of the descent. - seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, DocComment(..)) => { + seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, DocComment(..), _) => { let lower_elts = mem::replace(&mut item.top_elts, Tt(seq)); let idx = item.idx; item.stack.push(MatcherTtFrame { @@ -555,13 +553,7 @@ fn inner_parse_loop( } // We just matched a normal token. We can just advance the parser. - TokenTree::Token(_, ref t) if token_name_eq(t, token) => { - item.idx += 1; - next_items.push(item); - } - - // We just matched an ident token. We can just advance the parser. - TokenTree::IdentToken(_, i, _) if token_name_eq(&token::Ident(i, false), token) => { + TokenTree::Token(_, ref t, _) if token_name_eq(t, token) => { item.idx += 1; next_items.push(item); } @@ -571,7 +563,6 @@ fn inner_parse_loop( // `cur_items` end up doing this. There may still be some other matchers that do // end up working out. TokenTree::Token(..) | - TokenTree::IdentToken(..) | TokenTree::MetaVar(..) => {} } } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index ae4246d62adf0..85deef9ade8e2 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -202,7 +202,7 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax quoted::TokenTree::Sequence(DUMMY_SP, Lrc::new(quoted::SequenceRepetition { tts: vec![ quoted::TokenTree::MetaVarDecl(DUMMY_SP, lhs_nm, ast::Ident::from_str("tt")), - quoted::TokenTree::Token(DUMMY_SP, token::FatArrow), + quoted::TokenTree::Token(DUMMY_SP, token::FatArrow, false), quoted::TokenTree::MetaVarDecl(DUMMY_SP, rhs_nm, ast::Ident::from_str("tt")), ], separator: Some(if body.legacy { token::Semi } else { token::Comma }), @@ -211,7 +211,7 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax })), // to phase into semicolon-termination instead of semicolon-separation quoted::TokenTree::Sequence(DUMMY_SP, Lrc::new(quoted::SequenceRepetition { - tts: vec![quoted::TokenTree::Token(DUMMY_SP, token::Semi)], + tts: vec![quoted::TokenTree::Token(DUMMY_SP, token::Semi, false)], separator: None, op: quoted::KleeneOp::ZeroOrMore, num_captures: 0 @@ -329,7 +329,6 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool { for tt in tts { match *tt { TokenTree::Token(..) | - TokenTree::IdentToken(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (), TokenTree::Delimited(_, ref del) => if !check_lhs_no_empty_seq(sess, &del.tts) { @@ -414,7 +413,6 @@ impl FirstSets { for tt in tts.iter().rev() { match *tt { TokenTree::Token(..) | - TokenTree::IdentToken(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { first.replace_with(tt.clone()); @@ -446,7 +444,7 @@ impl FirstSets { if let (Some(ref sep), true) = (seq_rep.separator.clone(), subfirst.maybe_empty) { - first.add_one_maybe(TokenTree::Token(sp, sep.clone())); + first.add_one_maybe(TokenTree::Token(sp, sep.clone(), false)); } // Reverse scan: Sequence comes before `first`. @@ -477,7 +475,6 @@ impl FirstSets { assert!(first.maybe_empty); match *tt { TokenTree::Token(..) | - TokenTree::IdentToken(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { first.add_one(tt.clone()); @@ -496,7 +493,7 @@ impl FirstSets { if let (Some(ref sep), true) = (seq_rep.separator.clone(), subfirst.maybe_empty) { - first.add_one_maybe(TokenTree::Token(sp, sep.clone())); + first.add_one_maybe(TokenTree::Token(sp, sep.clone(), false)); } assert!(first.maybe_empty); @@ -651,7 +648,6 @@ fn check_matcher_core(sess: &ParseSess, // of NT tokens that might end the sequence `... token`. match *token { TokenTree::Token(..) | - TokenTree::IdentToken(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { let can_be_followed_by_any; @@ -704,7 +700,7 @@ fn check_matcher_core(sess: &ParseSess, let mut new; let my_suffix = if let Some(ref u) = seq_rep.separator { new = suffix_first.clone(); - new.add_one_maybe(TokenTree::Token(sp, u.clone())); + new.add_one_maybe(TokenTree::Token(sp, u.clone(), false)); &new } else { &suffix_first @@ -817,7 +813,7 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool { fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result { use self::quoted::TokenTree; - if let TokenTree::Token(_, token::CloseDelim(_)) = *tok { + if let TokenTree::Token(_, token::CloseDelim(_), _) = *tok { // closing a token tree can never be matched by any fragment; // iow, we always require that `(` and `)` match, etc. Ok(true) @@ -834,14 +830,14 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result match *tok { - TokenTree::Token(_, ref tok) => match *tok { + TokenTree::Token(_, ref tok, _) => match *tok { FatArrow | Comma | Semi => Ok(true), _ => Ok(false) }, _ => Ok(false), }, "pat" => match *tok { - TokenTree::Token(_, ref tok) => match *tok { + TokenTree::Token(_, ref tok, _) => match *tok { FatArrow | Comma | Eq | BinOp(token::Or) => Ok(true), Ident(i, false) if i.name == "if" || i.name == "in" => Ok(true), _ => Ok(false) @@ -849,7 +845,7 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result Ok(false), }, "path" | "ty" => match *tok { - TokenTree::Token(_, ref tok) => match *tok { + TokenTree::Token(_, ref tok, _) => match *tok { OpenDelim(token::DelimToken::Brace) | OpenDelim(token::DelimToken::Bracket) | Comma | FatArrow | Colon | Eq | Gt | Semi | BinOp(token::Or) => Ok(true), Ident(i, false) if i.name == "as" || i.name == "where" => Ok(true), @@ -870,7 +866,7 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result { // Explicitly disallow `priv`, on the off chance it comes back. match *tok { - TokenTree::Token(_, ref tok) => match *tok { + TokenTree::Token(_, ref tok, _) => match *tok { Comma => Ok(true), Ident(i, is_raw) if is_raw || i.name != "priv" => Ok(true), ref tok => Ok(tok.can_begin_type()) @@ -943,8 +939,7 @@ fn is_legal_fragment_specifier(sess: &ParseSess, fn quoted_tt_to_string(tt: "ed::TokenTree) -> String { match *tt { - quoted::TokenTree::Token(_, ref tok) => ::print::pprust::token_to_string(tok), - quoted::TokenTree::IdentToken(_, ident, _) => ::print::pprust::token_to_string(&token::Ident(ident, false)), + quoted::TokenTree::Token(_, ref tok, _) => ::print::pprust::token_to_string(tok), quoted::TokenTree::MetaVar(_, name, _) => format!("${}", name), quoted::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind), _ => panic!("unexpected quoted::TokenTree::{{Sequence or Delimited}} \ diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index a4474e856552d..7059f613445ad 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -46,7 +46,7 @@ impl Delimited { } else { span.with_lo(span.lo() + BytePos(self.delim.len() as u32)) }; - TokenTree::Token(open_span, self.open_token()) + TokenTree::Token(open_span, self.open_token(), false) } /// Return a `self::TokenTree` with a `Span` corresponding to the closing delimiter. @@ -56,7 +56,7 @@ impl Delimited { } else { span.with_lo(span.hi() - BytePos(self.delim.len() as u32)) }; - TokenTree::Token(close_span, self.close_token()) + TokenTree::Token(close_span, self.close_token(), false) } } @@ -88,13 +88,20 @@ pub enum KleeneOp { /// are "first-class" token trees. Useful for parsing macros. #[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub enum TokenTree { - Token(Span, token::Token), - IdentToken(Span, ast::Ident, bool /* escape hygiene */), + Token( + Span, + token::Token, + bool, /* escape hygiene */ + ), Delimited(Span, Lrc), - /// A kleene-style repetition sequence + /// A Kleene-style repetition sequence Sequence(Span, Lrc), /// E.g. `$var` - MetaVar(Span, ast::Ident, bool /* escape hygiene */), + MetaVar( + Span, + ast::Ident, + bool, /* escape hygiene */ + ), /// E.g. `$var:expr`. This is only used in the left hand side of MBE macros. MetaVarDecl( Span, @@ -152,12 +159,11 @@ impl TokenTree { /// Retrieve the `TokenTree`'s span. pub fn span(&self) -> Span { match *self { - TokenTree::Token(sp, _) - | TokenTree::IdentToken(sp, _, _) - | TokenTree::MetaVar(sp, _, _) - | TokenTree::MetaVarDecl(sp, _, _) - | TokenTree::Delimited(sp, _) - | TokenTree::Sequence(sp, _) => sp, + TokenTree::Token(sp, _, _) | + TokenTree::MetaVar(sp, _, _) | + TokenTree::MetaVarDecl(sp, _, _) | + TokenTree::Delimited(sp, _) | + TokenTree::Sequence(sp, _) => sp, } } } @@ -273,15 +279,25 @@ where } } - Some(tokenstream::TokenTree::Token(_, token::Ident(_, _))) => { - if let tokenstream::TokenTree::Token(span, token::Ident(ident, _)) = trees.next().unwrap() { - TokenTree::IdentToken(span, ident, true) + Some(tokenstream::TokenTree::Token(_, token::Ident(..))) => { + if let tokenstream::TokenTree::Token(span, tok @ token::Ident(..)) = + trees.next().unwrap() { + TokenTree::Token(span, tok, true) + } else { + unreachable!(); + } + } + + Some(tokenstream::TokenTree::Token(_, token::Lifetime(..))) => { + if let tokenstream::TokenTree::Token(span, tok @ token::Lifetime(..)) = + trees.next().unwrap() { + TokenTree::Token(span, tok, true) } else { unreachable!(); } } - _ => TokenTree::Token(span, token::Pound), + _ => TokenTree::Token(span, token::Pound, false), } // `tree` is a `$` token. Look at the next token in `trees`. @@ -289,7 +305,7 @@ where parse_meta_var(false, span, trees, hygiene_optout, expect_matchers, sess, features, attrs), // `tree` is an arbitrary token. Keep it. - tokenstream::TokenTree::Token(span, tok) => TokenTree::Token(span, tok), + tokenstream::TokenTree::Token(span, tok) => TokenTree::Token(span, tok, false), // `tree` is the beginning of a delimited set of tokens (e.g. `(` or `{`). We need to // descend into the delimited set and further parse it. @@ -347,11 +363,11 @@ where // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` special // metavariable that names the crate of the invokation. Some(tokenstream::TokenTree::Token(ident_span, ref token)) if token.is_ident() => { - let (ident, _) = token.ident().unwrap(); + let (ident, is_raw) = token.ident().unwrap(); let span = ident_span.with_lo(span.lo()); if ident.name == keywords::Crate.name() && !is_raw { let ident = ast::Ident::new(keywords::DollarCrate.name(), ident.span); - TokenTree::Token(span, token::Ident(ident, is_raw)) + TokenTree::Token(span, token::Ident(ident, is_raw), escape_hygiene) } else { TokenTree::MetaVar(span, ident, escape_hygiene) } @@ -368,7 +384,7 @@ where } // There are no more tokens. Just return the `$` we already have. - None => TokenTree::Token(span, token::Dollar), + None => TokenTree::Token(span, token::Dollar, false), } } diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index edec515405523..65df6ee1ac1cc 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -177,20 +177,31 @@ pub fn transcribe(cx: &ExtCtxt, stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span }); result_stack.push(mem::replace(&mut result, Vec::new())); } - quoted::TokenTree::Token(sp, tok) => { - let mut marker = Marker(cx.current_expansion.mark); - result.push(noop_fold_tt(TokenTree::Token(sp, tok), &mut marker).into()) - } - quoted::TokenTree::IdentToken(sp, ident, escape_hygiene) => { + quoted::TokenTree::Token(sp, tok, escape_hygiene) => { let sp_ctxt = if escape_hygiene { cx.call_site() } else { sp }.ctxt(); let sp = sp.with_ctxt(sp_ctxt.apply_mark(cx.current_expansion.mark)); - let ident_ctxt = if escape_hygiene { - cx.call_site().ctxt() - } else { - ident.ctxt.apply_mark(cx.current_expansion.mark) + + let update_ident_ctxt = |ident: Ident| { + let ident_ctxt = if escape_hygiene { + cx.call_site().ctxt() + } else { + ident.ctxt.apply_mark(cx.current_expansion.mark) + }; + Ident { ctxt: ident_ctxt, ..ident } }; - let ident = Ident { ctxt: ident_ctxt, ..ident }; - result.push(TokenTree::Token(sp, token::Ident(ident, false)).into()); + + let result_tok = match tok { + token::Ident(ident, is_raw) => + TokenTree::Token(sp, token::Ident(update_ident_ctxt(ident), is_raw)), + token::Lifetime(ident) => + TokenTree::Token(sp, token::Lifetime(ident)), + _ => { + let mut marker = Marker(cx.current_expansion.mark); + noop_fold_tt(TokenTree::Token(sp, tok), &mut marker) + } + }; + + result.push(result_tok.into()); } quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"), } @@ -269,6 +280,5 @@ fn lockstep_iter_size(tree: "ed::TokenTree, _ => LockstepIterSize::Unconstrained }, TokenTree::Token(..) => LockstepIterSize::Unconstrained, - TokenTree::IdentToken(..) => LockstepIterSize::Unconstrained, } } From 7206157760e89f4e731d1fc92b9dedfee6b471f4 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Wed, 7 Feb 2018 01:43:26 +0000 Subject: [PATCH 05/10] Added run-pass and compile-fail tests. --- src/test/compile-fail/hygiene-optout-1.rs | 23 ++++++++++++++++ src/test/compile-fail/hygiene-optout-2.rs | 23 ++++++++++++++++ src/test/compile-fail/hygiene-optout-3.rs | 26 +++++++++++++++++++ src/test/run-pass/hygiene-optout-1.rs | 22 ++++++++++++++++ src/test/run-pass/hygiene-optout-2.rs | 22 ++++++++++++++++ src/test/run-pass/hygiene-optout-lifetimes.rs | 22 ++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 src/test/compile-fail/hygiene-optout-1.rs create mode 100644 src/test/compile-fail/hygiene-optout-2.rs create mode 100644 src/test/compile-fail/hygiene-optout-3.rs create mode 100644 src/test/run-pass/hygiene-optout-1.rs create mode 100644 src/test/run-pass/hygiene-optout-2.rs create mode 100644 src/test/run-pass/hygiene-optout-lifetimes.rs diff --git a/src/test/compile-fail/hygiene-optout-1.rs b/src/test/compile-fail/hygiene-optout-1.rs new file mode 100644 index 0000000000000..8b05dfd49119b --- /dev/null +++ b/src/test/compile-fail/hygiene-optout-1.rs @@ -0,0 +1,23 @@ +// Copyright 2018 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(decl_macro)] + +macro m($mod_name:ident) { + pub mod $mod_name { + pub const BAR: u32 = 123; + } +} + +fn main() { + m!(foo); + let _ = foo::BAR; + //~^ ERROR cannot find value `BAR` in module `foo` +} diff --git a/src/test/compile-fail/hygiene-optout-2.rs b/src/test/compile-fail/hygiene-optout-2.rs new file mode 100644 index 0000000000000..996f763faacdc --- /dev/null +++ b/src/test/compile-fail/hygiene-optout-2.rs @@ -0,0 +1,23 @@ +// Copyright 2018 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(decl_macro)] + +macro m() { + pub mod #foo { + pub const BAR: u32 = 123; + } +} + +fn main() { + m!(); + let _ = foo::BAR; + //~^ ERROR cannot find value `BAR` in module `foo` +} diff --git a/src/test/compile-fail/hygiene-optout-3.rs b/src/test/compile-fail/hygiene-optout-3.rs new file mode 100644 index 0000000000000..db2cc1365d938 --- /dev/null +++ b/src/test/compile-fail/hygiene-optout-3.rs @@ -0,0 +1,26 @@ +// Copyright 2018 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(decl_macro)] + +macro m_helper() { + struct #S; +} + +macro m() { + m_helper!(); + let s = S; +} + +fn main() { + m!(); + let s = S; + //~^ ERROR cannot find value `S` in this scope +} diff --git a/src/test/run-pass/hygiene-optout-1.rs b/src/test/run-pass/hygiene-optout-1.rs new file mode 100644 index 0000000000000..155900d5fdfbc --- /dev/null +++ b/src/test/run-pass/hygiene-optout-1.rs @@ -0,0 +1,22 @@ +// Copyright 2018 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(decl_macro)] + +macro m($mod_name:ident) { + pub mod #$mod_name { + pub const #BAR: u32 = 123; + } +} + +fn main() { + m!(foo); + assert_eq!(123, foo::BAR); +} diff --git a/src/test/run-pass/hygiene-optout-2.rs b/src/test/run-pass/hygiene-optout-2.rs new file mode 100644 index 0000000000000..89ddd4d003c5c --- /dev/null +++ b/src/test/run-pass/hygiene-optout-2.rs @@ -0,0 +1,22 @@ +// Copyright 2018 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(decl_macro)] + +macro m() { + pub mod #foo { + pub const #BAR: u32 = 123; + } +} + +fn main() { + m!(); + assert_eq!(123, foo::BAR); +} diff --git a/src/test/run-pass/hygiene-optout-lifetimes.rs b/src/test/run-pass/hygiene-optout-lifetimes.rs new file mode 100644 index 0000000000000..e6e5bf90c46ed --- /dev/null +++ b/src/test/run-pass/hygiene-optout-lifetimes.rs @@ -0,0 +1,22 @@ +// Copyright 2018 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(decl_macro)] + +use std::marker::PhantomData; + +macro m($lifetime:tt) { + pub struct #Foo<$lifetime>(PhantomData<&#'a ()>); +} + +fn main() { + m!('a); + let _ = Foo(Default::default()); +} From ad54054ed625f1456a181724400d980479b2c6cf Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Wed, 7 Feb 2018 01:46:21 +0000 Subject: [PATCH 06/10] Added missing argument to call to `quoted::parse`. --- src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs b/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs index fd8f7b9e384f3..012c5f09340bd 100644 --- a/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs +++ b/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs @@ -39,6 +39,7 @@ fn expand_mbe_matches(cx: &mut ExtCtxt, _: Span, args: &[TokenTree]) let mbe_matcher = quote_tokens!(cx, $$matched:expr, $$($$pat:pat)|+); let mbe_matcher = quoted::parse(mbe_matcher.into_iter().collect(), + false, true, cx.parse_sess, &Features::new(), From 8c000de2b7911c1dd8ea51ff4d0e9f86573c7c94 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sat, 7 Apr 2018 00:30:25 +0100 Subject: [PATCH 07/10] Wrapped long lines of code. --- src/libsyntax/ext/tt/macro_rules.rs | 14 ++++++++-- src/libsyntax/ext/tt/quoted.rs | 42 +++++++++++++++++++++++++---- src/libsyntax/ext/tt/transcribe.rs | 10 +++---- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 85deef9ade8e2..665e10c59bbdc 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -237,7 +237,12 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax s.iter().map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = quoted::parse(tt.clone().into(), false, true, sess, features, &def.attrs) + let tt = quoted::parse(tt.clone().into(), + false, + true, + sess, + features, + &def.attrs) .pop().unwrap(); valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); return tt; @@ -254,7 +259,12 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax s.iter().map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return quoted::parse(tt.clone().into(), !body.legacy, false, sess, features, &def.attrs) + return quoted::parse(tt.clone().into(), + !body.legacy, + false, + sess, + features, + &def.attrs) .pop().unwrap(); } } diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 7059f613445ad..76be567d5e094 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -204,7 +204,13 @@ pub fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e. in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, hygiene_optout, expect_matchers, sess, features, attrs); + let tree = parse_tree(tree, + &mut trees, + hygiene_optout, + expect_matchers, + sess, + features, + attrs); match tree { TokenTree::MetaVar(start_sp, ident, _) if expect_matchers => { let span = match trees.next() { @@ -273,7 +279,14 @@ where tokenstream::TokenTree::Token(span, token::Pound) if hygiene_optout => match trees.peek() { Some(tokenstream::TokenTree::Token(_, token::Dollar)) => { if let tokenstream::TokenTree::Token(span, token::Dollar) = trees.next().unwrap() { - parse_meta_var(true, span, trees, hygiene_optout, expect_matchers, sess, features, attrs) + parse_meta_var(true, + span, + trees, + hygiene_optout, + expect_matchers, + sess, + features, + attrs) } else { unreachable!(); } @@ -302,7 +315,14 @@ where // `tree` is a `$` token. Look at the next token in `trees`. tokenstream::TokenTree::Token(span, token::Dollar) => - parse_meta_var(false, span, trees, hygiene_optout, expect_matchers, sess, features, attrs), + parse_meta_var(false, + span, + trees, + hygiene_optout, + expect_matchers, + sess, + features, + attrs), // `tree` is an arbitrary token. Keep it. tokenstream::TokenTree::Token(span, tok) => TokenTree::Token(span, tok, false), @@ -313,7 +333,12 @@ where span, Lrc::new(Delimited { delim: delimited.delim, - tts: parse(delimited.tts.into(), hygiene_optout, expect_matchers, sess, features, attrs), + tts: parse(delimited.tts.into(), + hygiene_optout, + expect_matchers, + sess, + features, + attrs), }), ), } @@ -344,7 +369,14 @@ where sess.span_diagnostic.span_err(span, &msg); } // Parse the contents of the sequence itself - let sequence = parse(delimited.tts.into(), hygiene_optout, expect_matchers, sess, features, attrs); + let sequence = parse( + delimited.tts.into(), + hygiene_optout, + expect_matchers, + sess, + features, + attrs + ); // Get the Kleene operator and optional separator let (separator, op) = parse_sep_and_kleene_op(trees, span, sess, features, attrs); // Count the number of captured "names" (i.e. named metavars) diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index 65df6ee1ac1cc..c4d8905716053 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -182,19 +182,19 @@ pub fn transcribe(cx: &ExtCtxt, let sp = sp.with_ctxt(sp_ctxt.apply_mark(cx.current_expansion.mark)); let update_ident_ctxt = |ident: Ident| { - let ident_ctxt = if escape_hygiene { - cx.call_site().ctxt() + let new_span = if escape_hygiene { + cx.call_site() } else { - ident.ctxt.apply_mark(cx.current_expansion.mark) + ident.span.apply_mark(cx.current_expansion.mark) }; - Ident { ctxt: ident_ctxt, ..ident } + Ident::new(ident.name, new_span) }; let result_tok = match tok { token::Ident(ident, is_raw) => TokenTree::Token(sp, token::Ident(update_ident_ctxt(ident), is_raw)), token::Lifetime(ident) => - TokenTree::Token(sp, token::Lifetime(ident)), + TokenTree::Token(sp, token::Lifetime()), _ => { let mut marker = Marker(cx.current_expansion.mark); noop_fold_tt(TokenTree::Token(sp, tok), &mut marker) From e9991a066c25c40c1fd33a4f54e32822efb80265 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sun, 6 May 2018 22:13:01 +0100 Subject: [PATCH 08/10] Fixed bug with modification of `SyntaxContext` for escaped idents. --- src/libsyntax/ext/tt/macro_rules.rs | 14 ++++--- src/libsyntax/ext/tt/quoted.rs | 50 +++++++++++------------ src/libsyntax/ext/tt/transcribe.rs | 49 +++++++++++----------- src/test/compile-fail/hygiene-optout-4.rs | 24 +++++++++++ 4 files changed, 82 insertions(+), 55 deletions(-) create mode 100644 src/test/compile-fail/hygiene-optout-4.rs diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 665e10c59bbdc..78d8f4e69cc15 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -202,7 +202,7 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax quoted::TokenTree::Sequence(DUMMY_SP, Lrc::new(quoted::SequenceRepetition { tts: vec![ quoted::TokenTree::MetaVarDecl(DUMMY_SP, lhs_nm, ast::Ident::from_str("tt")), - quoted::TokenTree::Token(DUMMY_SP, token::FatArrow, false), + quoted::TokenTree::Token(DUMMY_SP, token::FatArrow, quoted::TokenHygiene::DefSite), quoted::TokenTree::MetaVarDecl(DUMMY_SP, rhs_nm, ast::Ident::from_str("tt")), ], separator: Some(if body.legacy { token::Semi } else { token::Comma }), @@ -211,7 +211,8 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax })), // to phase into semicolon-termination instead of semicolon-separation quoted::TokenTree::Sequence(DUMMY_SP, Lrc::new(quoted::SequenceRepetition { - tts: vec![quoted::TokenTree::Token(DUMMY_SP, token::Semi, false)], + tts: vec![quoted::TokenTree::Token(DUMMY_SP, token::Semi, + quoted::TokenHygiene::DefSite)], separator: None, op: quoted::KleeneOp::ZeroOrMore, num_captures: 0 @@ -454,7 +455,8 @@ impl FirstSets { if let (Some(ref sep), true) = (seq_rep.separator.clone(), subfirst.maybe_empty) { - first.add_one_maybe(TokenTree::Token(sp, sep.clone(), false)); + first.add_one_maybe(TokenTree::Token(sp, sep.clone(), + quoted::TokenHygiene::DefSite)); } // Reverse scan: Sequence comes before `first`. @@ -503,7 +505,8 @@ impl FirstSets { if let (Some(ref sep), true) = (seq_rep.separator.clone(), subfirst.maybe_empty) { - first.add_one_maybe(TokenTree::Token(sp, sep.clone(), false)); + first.add_one_maybe(TokenTree::Token(sp, sep.clone(), + quoted::TokenHygiene::DefSite)); } assert!(first.maybe_empty); @@ -710,7 +713,8 @@ fn check_matcher_core(sess: &ParseSess, let mut new; let my_suffix = if let Some(ref u) = seq_rep.separator { new = suffix_first.clone(); - new.add_one_maybe(TokenTree::Token(sp, u.clone(), false)); + new.add_one_maybe(TokenTree::Token(sp, u.clone(), + quoted::TokenHygiene::DefSite)); &new } else { &suffix_first diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 76be567d5e094..147c7e2f8af96 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -46,7 +46,7 @@ impl Delimited { } else { span.with_lo(span.lo() + BytePos(self.delim.len() as u32)) }; - TokenTree::Token(open_span, self.open_token(), false) + TokenTree::Token(open_span, self.open_token(), TokenHygiene::DefSite) } /// Return a `self::TokenTree` with a `Span` corresponding to the closing delimiter. @@ -56,7 +56,7 @@ impl Delimited { } else { span.with_lo(span.hi() - BytePos(self.delim.len() as u32)) }; - TokenTree::Token(close_span, self.close_token(), false) + TokenTree::Token(close_span, self.close_token(), TokenHygiene::DefSite) } } @@ -88,20 +88,12 @@ pub enum KleeneOp { /// are "first-class" token trees. Useful for parsing macros. #[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub enum TokenTree { - Token( - Span, - token::Token, - bool, /* escape hygiene */ - ), + Token(Span, token::Token, TokenHygiene), Delimited(Span, Lrc), /// A Kleene-style repetition sequence Sequence(Span, Lrc), /// E.g. `$var` - MetaVar( - Span, - ast::Ident, - bool, /* escape hygiene */ - ), + MetaVar(Span, ast::Ident, TokenHygiene), /// E.g. `$var:expr`. This is only used in the left hand side of MBE macros. MetaVarDecl( Span, @@ -168,6 +160,13 @@ impl TokenTree { } } +/// Syntaxt context to apply to a token when invoking a macro. +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +pub enum TokenHygiene { + DefSite, + CallSite, +} + /// Takes a `tokenstream::TokenStream` and returns a `Vec`. Specifically, this /// takes a generic `TokenStream`, such as is used in the rest of the compiler, and returns a /// collection of `TokenTree` for use in parsing a macro. @@ -279,8 +278,8 @@ where tokenstream::TokenTree::Token(span, token::Pound) if hygiene_optout => match trees.peek() { Some(tokenstream::TokenTree::Token(_, token::Dollar)) => { if let tokenstream::TokenTree::Token(span, token::Dollar) = trees.next().unwrap() { - parse_meta_var(true, - span, + parse_meta_var(span, + TokenHygiene::CallSite, trees, hygiene_optout, expect_matchers, @@ -295,7 +294,7 @@ where Some(tokenstream::TokenTree::Token(_, token::Ident(..))) => { if let tokenstream::TokenTree::Token(span, tok @ token::Ident(..)) = trees.next().unwrap() { - TokenTree::Token(span, tok, true) + TokenTree::Token(span, tok, TokenHygiene::CallSite) } else { unreachable!(); } @@ -304,19 +303,19 @@ where Some(tokenstream::TokenTree::Token(_, token::Lifetime(..))) => { if let tokenstream::TokenTree::Token(span, tok @ token::Lifetime(..)) = trees.next().unwrap() { - TokenTree::Token(span, tok, true) + TokenTree::Token(span, tok, TokenHygiene::CallSite) } else { unreachable!(); } } - _ => TokenTree::Token(span, token::Pound, false), + _ => TokenTree::Token(span, token::Pound, TokenHygiene::DefSite), } // `tree` is a `$` token. Look at the next token in `trees`. tokenstream::TokenTree::Token(span, token::Dollar) => - parse_meta_var(false, - span, + parse_meta_var(span, + TokenHygiene::DefSite, trees, hygiene_optout, expect_matchers, @@ -325,7 +324,8 @@ where attrs), // `tree` is an arbitrary token. Keep it. - tokenstream::TokenTree::Token(span, tok) => TokenTree::Token(span, tok, false), + tokenstream::TokenTree::Token(span, tok) => + TokenTree::Token(span, tok, TokenHygiene::DefSite), // `tree` is the beginning of a delimited set of tokens (e.g. `(` or `{`). We need to // descend into the delimited set and further parse it. @@ -346,8 +346,8 @@ where /// Attempt to parse a single meta variable or meta variable sequence. fn parse_meta_var( - escape_hygiene: bool, span: Span, + token_hygiene: TokenHygiene, trees: &mut Peekable, hygiene_optout: bool, expect_matchers: bool, @@ -399,9 +399,9 @@ where let span = ident_span.with_lo(span.lo()); if ident.name == keywords::Crate.name() && !is_raw { let ident = ast::Ident::new(keywords::DollarCrate.name(), ident.span); - TokenTree::Token(span, token::Ident(ident, is_raw), escape_hygiene) + TokenTree::Token(span, token::Ident(ident, is_raw), token_hygiene) } else { - TokenTree::MetaVar(span, ident, escape_hygiene) + TokenTree::MetaVar(span, ident, token_hygiene) } } @@ -412,11 +412,11 @@ where pprust::token_to_string(&tok) ); sess.span_diagnostic.span_err(span, &msg); - TokenTree::MetaVar(span, keywords::Invalid.ident(), escape_hygiene) + TokenTree::MetaVar(span, keywords::Invalid.ident(), token_hygiene) } // There are no more tokens. Just return the `$` we already have. - None => TokenTree::Token(span, token::Dollar, false), + None => TokenTree::Token(span, token::Dollar, TokenHygiene::DefSite), } } diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index c4d8905716053..1672333609f69 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -13,9 +13,9 @@ use ext::base::ExtCtxt; use ext::expand::Marker; use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal}; use ext::tt::quoted; -use fold::noop_fold_tt; +use fold::{Folder, noop_fold_tt}; use parse::token::{self, Token, NtTT}; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::{Span, SyntaxContext, DUMMY_SP}; use tokenstream::{TokenStream, TokenTree, Delimited}; use util::small_vector::SmallVector; @@ -177,31 +177,15 @@ pub fn transcribe(cx: &ExtCtxt, stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span }); result_stack.push(mem::replace(&mut result, Vec::new())); } - quoted::TokenTree::Token(sp, tok, escape_hygiene) => { - let sp_ctxt = if escape_hygiene { cx.call_site() } else { sp }.ctxt(); - let sp = sp.with_ctxt(sp_ctxt.apply_mark(cx.current_expansion.mark)); - - let update_ident_ctxt = |ident: Ident| { - let new_span = if escape_hygiene { - cx.call_site() - } else { - ident.span.apply_mark(cx.current_expansion.mark) - }; - Ident::new(ident.name, new_span) - }; - - let result_tok = match tok { - token::Ident(ident, is_raw) => - TokenTree::Token(sp, token::Ident(update_ident_ctxt(ident), is_raw)), - token::Lifetime(ident) => - TokenTree::Token(sp, token::Lifetime()), - _ => { - let mut marker = Marker(cx.current_expansion.mark); - noop_fold_tt(TokenTree::Token(sp, tok), &mut marker) - } + quoted::TokenTree::Token(sp, tok, hygiene) => { + let mut new_tok = match hygiene { + quoted::TokenHygiene::DefSite => noop_fold_tt(TokenTree::Token(sp, tok), + &mut Marker(cx.current_expansion.mark)), + quoted::TokenHygiene::CallSite => noop_fold_tt(TokenTree::Token(sp, tok), + &mut Escaper(cx.call_site().ctxt())), }; - result.push(result_tok.into()); + result.push(new_tok.into()); } quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"), } @@ -282,3 +266,18 @@ fn lockstep_iter_size(tree: "ed::TokenTree, TokenTree::Token(..) => LockstepIterSize::Unconstrained, } } + +// An Escaper escapes the syntax context with the given syntax context. +#[derive(Debug)] +pub struct Escaper(pub SyntaxContext); + +impl Folder for Escaper { + fn fold_ident(&mut self, mut ident: Ident) -> Ident { + ident.span = ident.span.with_ctxt(self.0); + ident + } + + fn new_span(&mut self, span: Span) -> Span { + span.with_ctxt(self.0) + } +} diff --git a/src/test/compile-fail/hygiene-optout-4.rs b/src/test/compile-fail/hygiene-optout-4.rs new file mode 100644 index 0000000000000..2401c9d8e60eb --- /dev/null +++ b/src/test/compile-fail/hygiene-optout-4.rs @@ -0,0 +1,24 @@ +// Copyright 2018 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(decl_macro)] + +macro m() { + struct #S; + + impl S { + //~^ ERROR cannot find type `S` in this scope + fn f() {} + } +} + +fn main() { + m!(); +} From 61c2a4d8ff168f65e7e010862a65022d76184cd5 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Wed, 9 May 2018 02:11:13 +0100 Subject: [PATCH 09/10] Added compile-fail test for `$#metavar` hygiene opt-out syntax. We want this to fail for the time being, per . --- src/test/compile-fail/hygiene-optout-5.rs | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/compile-fail/hygiene-optout-5.rs diff --git a/src/test/compile-fail/hygiene-optout-5.rs b/src/test/compile-fail/hygiene-optout-5.rs new file mode 100644 index 0000000000000..bd526d3fd4890 --- /dev/null +++ b/src/test/compile-fail/hygiene-optout-5.rs @@ -0,0 +1,24 @@ +// Copyright 2018 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(decl_macro)] + +macro m($mod_name:ident) { + pub mod $#mod_name { + //~^ ERROR expected identifier, found `#` + //~| ERROR unknown macro variable `` + //~| ERROR expected identifier, found reserved identifier `` + //~| ERROR expected one of `;` or `{`, found `mod_name` + } +} + +fn main() { + m!(foo); +} From c7f158b286fb56665057c5defb869112f08c0660 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Wed, 9 May 2018 02:45:46 +0100 Subject: [PATCH 10/10] Added feature gate (`macro_hygiene_optout`). --- src/libsyntax/ext/tt/macro_rules.rs | 2 +- src/libsyntax/feature_gate.rs | 3 +++ .../feature-gate-macro-hygiene-optout.rs | 22 +++++++++++++++++++ src/test/compile-fail/hygiene-optout-1.rs | 1 + src/test/compile-fail/hygiene-optout-2.rs | 1 + src/test/compile-fail/hygiene-optout-3.rs | 1 + src/test/compile-fail/hygiene-optout-4.rs | 1 + src/test/compile-fail/hygiene-optout-5.rs | 1 + src/test/run-pass/hygiene-optout-1.rs | 7 +++--- src/test/run-pass/hygiene-optout-2.rs | 7 +++--- src/test/run-pass/hygiene-optout-lifetimes.rs | 1 + 11 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-macro-hygiene-optout.rs diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 78d8f4e69cc15..a29d1dd0e5f8b 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -261,7 +261,7 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { return quoted::parse(tt.clone().into(), - !body.legacy, + !body.legacy && features.macro_hygiene_optout, false, sess, features, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d8db76a95ff38..e54877a636ae0 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -305,6 +305,9 @@ declare_features! ( // Declarative macros 2.0 (`macro`). (active, decl_macro, "1.17.0", Some(39412), None), + // Hygiene opt-out (escaping) for macros 2.0 using `#ident` syntax. + (active, macro_hygiene_optout, "1.27.0", None, None), + // Allows #[link(kind="static-nobundle"...] (active, static_nobundle, "1.16.0", Some(37403), None), diff --git a/src/test/compile-fail/feature-gate-macro-hygiene-optout.rs b/src/test/compile-fail/feature-gate-macro-hygiene-optout.rs new file mode 100644 index 0000000000000..69184a7121be5 --- /dev/null +++ b/src/test/compile-fail/feature-gate-macro-hygiene-optout.rs @@ -0,0 +1,22 @@ +// Copyright 2018 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(decl_macro)] + +macro m() { + pub mod #foo { + //~^ ERROR expected identifier, found `#` + pub const #BAR: u32 = 123; + } +} + +fn main() { + m!(); +} diff --git a/src/test/compile-fail/hygiene-optout-1.rs b/src/test/compile-fail/hygiene-optout-1.rs index 8b05dfd49119b..8fdc9161ec552 100644 --- a/src/test/compile-fail/hygiene-optout-1.rs +++ b/src/test/compile-fail/hygiene-optout-1.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] macro m($mod_name:ident) { pub mod $mod_name { diff --git a/src/test/compile-fail/hygiene-optout-2.rs b/src/test/compile-fail/hygiene-optout-2.rs index 996f763faacdc..ff348ce4c1b66 100644 --- a/src/test/compile-fail/hygiene-optout-2.rs +++ b/src/test/compile-fail/hygiene-optout-2.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] macro m() { pub mod #foo { diff --git a/src/test/compile-fail/hygiene-optout-3.rs b/src/test/compile-fail/hygiene-optout-3.rs index db2cc1365d938..b2d3860682d75 100644 --- a/src/test/compile-fail/hygiene-optout-3.rs +++ b/src/test/compile-fail/hygiene-optout-3.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] macro m_helper() { struct #S; diff --git a/src/test/compile-fail/hygiene-optout-4.rs b/src/test/compile-fail/hygiene-optout-4.rs index 2401c9d8e60eb..e494f95163869 100644 --- a/src/test/compile-fail/hygiene-optout-4.rs +++ b/src/test/compile-fail/hygiene-optout-4.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] macro m() { struct #S; diff --git a/src/test/compile-fail/hygiene-optout-5.rs b/src/test/compile-fail/hygiene-optout-5.rs index bd526d3fd4890..10d3f9858c0be 100644 --- a/src/test/compile-fail/hygiene-optout-5.rs +++ b/src/test/compile-fail/hygiene-optout-5.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] macro m($mod_name:ident) { pub mod $#mod_name { diff --git a/src/test/run-pass/hygiene-optout-1.rs b/src/test/run-pass/hygiene-optout-1.rs index 155900d5fdfbc..ddadc9e569f89 100644 --- a/src/test/run-pass/hygiene-optout-1.rs +++ b/src/test/run-pass/hygiene-optout-1.rs @@ -9,14 +9,15 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] -macro m($mod_name:ident) { - pub mod #$mod_name { +macro m() { + pub mod #foo { pub const #BAR: u32 = 123; } } fn main() { - m!(foo); + m!(); assert_eq!(123, foo::BAR); } diff --git a/src/test/run-pass/hygiene-optout-2.rs b/src/test/run-pass/hygiene-optout-2.rs index 89ddd4d003c5c..a46cfca2bf008 100644 --- a/src/test/run-pass/hygiene-optout-2.rs +++ b/src/test/run-pass/hygiene-optout-2.rs @@ -9,14 +9,15 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] -macro m() { - pub mod #foo { +macro m($mod_name:ident) { + pub mod #$mod_name { pub const #BAR: u32 = 123; } } fn main() { - m!(); + m!(foo); assert_eq!(123, foo::BAR); } diff --git a/src/test/run-pass/hygiene-optout-lifetimes.rs b/src/test/run-pass/hygiene-optout-lifetimes.rs index e6e5bf90c46ed..55f4dfb5e6b9b 100644 --- a/src/test/run-pass/hygiene-optout-lifetimes.rs +++ b/src/test/run-pass/hygiene-optout-lifetimes.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(decl_macro)] +#![feature(macro_hygiene_optout)] use std::marker::PhantomData;