From 760879bc88b2884275b59fc38e0c5b1a8632e4cd Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 18 Jan 2018 18:41:09 -0600 Subject: [PATCH 01/18] Allow `?` as a KleeneOp in the macro parser --- src/libsyntax/ext/tt/quoted.rs | 130 +++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 46 deletions(-) diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index c55dfaba8f6b2..f8ae8726bc1e6 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -16,6 +16,7 @@ use symbol::keywords; use syntax_pos::{BytePos, Span, DUMMY_SP}; use tokenstream; +use std::iter::Peekable; use std::rc::Rc; /// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note @@ -78,6 +79,7 @@ pub enum KleeneOp { ZeroOrMore, /// Kleene plus (`+`) for one or more repetitions OneOrMore, + ZeroOrOne, } /// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)` @@ -183,7 +185,7 @@ pub fn parse( // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming // additional trees if need be. - let mut trees = input.trees(); + let mut trees = input.trees().peekable(); while let Some(tree) = trees.next() { let tree = parse_tree(tree, &mut trees, expect_matchers, sess); @@ -321,6 +323,34 @@ where } } +/// Takes a token and returns `Some(KleeneOp)` if the token is `+` `*` or `?`. Otherwise, return +/// `None`. +fn kleene_op(token: &token::Token) -> Option { + match *token { + token::BinOp(token::Star) => Some(KleeneOp::ZeroOrMore), + token::BinOp(token::Plus) => Some(KleeneOp::OneOrMore), + token::Question => Some(KleeneOp::ZeroOrOne), + _ => None, + } +} + +/// Parse the next token tree of the input looking for a KleeneOp. Returns +/// +/// - Ok(Ok(op)) if the next token tree is a KleeneOp +/// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp +/// - Err(span) if the next token tree is not a token +fn parse_kleene_op(input: &mut I, span: Span) -> Result, Span> + where I: Iterator, +{ + match input.next() { + Some(tokenstream::TokenTree::Token(span, tok)) => match kleene_op(&tok) { + Some(op) => Ok(Ok(op)), + None => Ok(Err((tok, span))), + } + tree => Err(tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span)), + } +} + /// Attempt to parse a single Kleene star, possibly with a separator. /// /// For example, in a pattern such as `$(a),*`, `a` is the pattern to be repeated, `,` is the @@ -333,56 +363,64 @@ where /// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene /// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an /// error with the appropriate span is emitted to `sess` and a dummy value is returned. -fn parse_sep_and_kleene_op( - input: &mut I, - span: Span, - sess: &ParseSess, -) -> (Option, KleeneOp) -where - I: Iterator, +fn parse_sep_and_kleene_op(input: &mut Peekable, span: Span, sess: &ParseSess) + -> (Option, KleeneOp) + where I: Iterator, { - fn kleene_op(token: &token::Token) -> Option { - match *token { - token::BinOp(token::Star) => Some(KleeneOp::ZeroOrMore), - token::BinOp(token::Plus) => Some(KleeneOp::OneOrMore), - _ => None, + // We basically look at two token trees here, denoted as #1 and #2 below + let span = match parse_kleene_op(input, span) { + // #1 is a `+` or `*` KleeneOp + // + // `?` is ambiguous: it could be a separator or a Kleene::ZeroOrOne, so we need to look + // ahead one more token to be sure. + Ok(Ok(op)) if op != KleeneOp::ZeroOrOne => return (None, op), + + // #1 is `?` token, but it could be a Kleene::ZeroOrOne without a separator or it could + // be a `?` separator followed by any Kleene operator. We need to look ahead 1 token to + // find out which. + Ok(Ok(op)) => { + // Lookahead at #2. If it is a KleenOp, then #1 is a separator. + let is_1_sep = if let Some(&tokenstream::TokenTree::Token(_, ref tok2)) = input.peek() { + kleene_op(tok2).is_some() + } else { + false + }; + + if is_1_sep { + // #1 is a separator and #2 should be a KleepeOp::* + // (N.B. We need to advance the input iterator.) + match parse_kleene_op(input, span) { + // #2 is a KleeneOp (this is the only valid option) :) + Ok(Ok(op)) => return (Some(token::Question), op), + + // #2 is a random token (this is an error) :( + Ok(Err((_, span))) => span, + + // #2 is not even a token at all :( + Err(span) => span, + } + } else { + // #2 is a random tree and #1 is KleeneOp::ZeroOrOne + return (None, op); + } } - } - // We attempt to look at the next two token trees in `input`. I will call the first #1 and the - // second #2. If #1 and #2 don't match a valid KleeneOp with/without separator, that is an - // error, and we should emit an error on the most specific span possible. - let span = match input.next() { - // #1 is a token - Some(tokenstream::TokenTree::Token(span, tok)) => match kleene_op(&tok) { - // #1 is a KleeneOp with no separator - Some(op) => return (None, op), - - // #1 is not a KleeneOp, but may be a separator... need to look at #2 - None => match input.next() { - // #2 is a token - Some(tokenstream::TokenTree::Token(span, tok2)) => match kleene_op(&tok2) { - // #2 is a KleeneOp, so #1 must be a separator - Some(op) => return (Some(tok), op), - - // #2 is not a KleeneOp... error - None => span, - }, - - // #2 is not a token at all... error - tree => tree.as_ref() - .map(tokenstream::TokenTree::span) - .unwrap_or(span), - }, - }, + // #1 is a separator followed by #2, a KleeneOp + Ok(Err((tok, span))) => match parse_kleene_op(input, span) { + // #2 is a KleeneOp :D + Ok(Ok(op)) => return (Some(tok), op), + + // #2 is a random token :( + Ok(Err((_, span))) => span, + + // #2 is not a token at all :( + Err(span) => span, + } - // #1 is not a token at all... error - tree => tree.as_ref() - .map(tokenstream::TokenTree::span) - .unwrap_or(span), + // #1 is not a token + Err(span) => span, }; - // Error... - sess.span_diagnostic.span_err(span, "expected `*` or `+`"); + sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`"); (None, KleeneOp::ZeroOrMore) } From bb8110c1fcc33117fbf8bed985c0f472b3816bc3 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 18 Jan 2018 19:30:15 -0600 Subject: [PATCH 02/18] Update the macro parser to allow at most once repetitions for `?` Kleene --- src/libsyntax/ext/tt/macro_parser.rs | 38 ++++++++++++++++------------ 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 1a9849ca5307d..88144b1960980 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -486,8 +486,8 @@ fn inner_parse_loop( match item.top_elts.get_tt(idx) { // Need to descend into a sequence TokenTree::Sequence(sp, seq) => { - if seq.op == quoted::KleeneOp::ZeroOrMore { - // Examine the case where there are 0 matches of this sequence + // Examine the case where there are 0 matches of this sequence + if seq.op == quoted::KleeneOp::ZeroOrMore || seq.op == quoted::KleeneOp::ZeroOrOne { let mut new_item = item.clone(); new_item.match_cur += seq.num_captures; new_item.idx += 1; @@ -497,20 +497,26 @@ fn inner_parse_loop( cur_items.push(new_item); } - // Examine the case where there is at least one match of this sequence - let matches = create_matches(item.matches.len()); - cur_items.push(Box::new(MatcherPos { - stack: vec![], - sep: seq.separator.clone(), - idx: 0, - matches, - match_lo: item.match_cur, - match_cur: item.match_cur, - match_hi: item.match_cur + seq.num_captures, - up: Some(item), - sp_lo: sp.lo(), - top_elts: Tt(TokenTree::Sequence(sp, seq)), - })); + // For ZeroOrMore and OneOrMore, we want to examine the case were there is at + // least one match. For ZeroOrOne, we only want the case where there is exactly + // one match. + if (seq.op == quoted::KleeneOp::ZeroOrOne && seq.num_captures == 1) || + seq.op != quoted::KleeneOp::ZeroOrOne { + + let matches = create_matches(item.matches.len()); + cur_items.push(Box::new(MatcherPos { + stack: vec![], + sep: seq.separator.clone(), + idx: 0, + matches, + match_lo: item.match_cur, + match_cur: item.match_cur, + match_hi: item.match_cur + seq.num_captures, + up: Some(item), + sp_lo: sp.lo(), + top_elts: Tt(TokenTree::Sequence(sp, seq)), + })); + } } // We need to match a metavar (but the identifier is invalid)... this is an error From c33649cd8effaf3e1301ad2d81b053bd90764e32 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 18 Jan 2018 21:17:01 -0600 Subject: [PATCH 03/18] Run rustfmt on quoted.rs --- src/libsyntax/ext/tt/quoted.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index f8ae8726bc1e6..13764ebfca1e1 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -241,7 +241,7 @@ pub fn parse( /// - `sess`: the parsing session. Any errors will be emitted to this session. fn parse_tree( tree: tokenstream::TokenTree, - trees: &mut I, + trees: &mut Peekable, expect_matchers: bool, sess: &ParseSess, ) -> TokenTree @@ -339,15 +339,21 @@ fn kleene_op(token: &token::Token) -> Option { /// - Ok(Ok(op)) if the next token tree is a KleeneOp /// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp /// - Err(span) if the next token tree is not a token -fn parse_kleene_op(input: &mut I, span: Span) -> Result, Span> - where I: Iterator, +fn parse_kleene_op( + input: &mut I, + span: Span, +) -> Result, Span> +where + I: Iterator, { match input.next() { Some(tokenstream::TokenTree::Token(span, tok)) => match kleene_op(&tok) { Some(op) => Ok(Ok(op)), None => Ok(Err((tok, span))), - } - tree => Err(tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span)), + }, + tree => Err(tree.as_ref() + .map(tokenstream::TokenTree::span) + .unwrap_or(span)), } } @@ -363,9 +369,13 @@ fn parse_kleene_op(input: &mut I, span: Span) -> Result(input: &mut Peekable, span: Span, sess: &ParseSess) - -> (Option, KleeneOp) - where I: Iterator, +fn parse_sep_and_kleene_op( + input: &mut Peekable, + span: Span, + sess: &ParseSess, +) -> (Option, KleeneOp) +where + I: Iterator, { // We basically look at two token trees here, denoted as #1 and #2 below let span = match parse_kleene_op(input, span) { @@ -415,12 +425,13 @@ fn parse_sep_and_kleene_op(input: &mut Peekable, span: Span, sess: &ParseS // #2 is not a token at all :( Err(span) => span, - } + }, // #1 is not a token Err(span) => span, }; - sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`"); + sess.span_diagnostic + .span_err(span, "expected one of: `*`, `+`, or `?`"); (None, KleeneOp::ZeroOrMore) } From 711f71cfa909808334d6b5d9024e8745ab62b19d Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 18 Jan 2018 21:17:27 -0600 Subject: [PATCH 04/18] Add a couple of tests --- .../macro-at-most-once-rep-ambig.rs | 26 +++++++++++++++++++ src/test/run-pass/macro-at-most-once-rep.rs | 25 ++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/test/compile-fail/macro-at-most-once-rep-ambig.rs create mode 100644 src/test/run-pass/macro-at-most-once-rep.rs diff --git a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs new file mode 100644 index 0000000000000..b0a6ec145e16a --- /dev/null +++ b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs @@ -0,0 +1,26 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! foo { + ($(a)?) => {} +} + +macro_rules! bar { + ($(a)?+) => {} +} + +pub fn main() { + foo!(a?a?a); //~ ERROR no rules expected the token `?` + foo!(a?a); //~ ERROR no rules expected the token `?` + foo!(a?); //~ ERROR no rules expected the token `?` + bar!(); //~ ERROR no rules expected the token `)` + bar!(a?); //~ ERROR no rules expected the token `?` +} + diff --git a/src/test/run-pass/macro-at-most-once-rep.rs b/src/test/run-pass/macro-at-most-once-rep.rs new file mode 100644 index 0000000000000..fa1f90bf6ef98 --- /dev/null +++ b/src/test/run-pass/macro-at-most-once-rep.rs @@ -0,0 +1,25 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! foo { + ($(a)?) => {} +} + +macro_rules! bar { + ($(a)?+) => {} +} + +pub fn main() { + foo!(); + foo!(a); + bar!(a); + bar!(a?a); + bar!(a?a?a); +} From 5ac48ec82623433bc6dbf62ce8c101eeec12648d Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 18 Jan 2018 21:18:04 -0600 Subject: [PATCH 05/18] Run rustfmt on macro_parser.rs --- src/libsyntax/ext/tt/macro_parser.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 88144b1960980..e067bbecce28f 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -487,7 +487,9 @@ fn inner_parse_loop( // Need to descend into a sequence TokenTree::Sequence(sp, seq) => { // Examine the case where there are 0 matches of this sequence - if seq.op == quoted::KleeneOp::ZeroOrMore || seq.op == quoted::KleeneOp::ZeroOrOne { + if seq.op == quoted::KleeneOp::ZeroOrMore + || seq.op == quoted::KleeneOp::ZeroOrOne + { let mut new_item = item.clone(); new_item.match_cur += seq.num_captures; new_item.idx += 1; @@ -500,9 +502,9 @@ fn inner_parse_loop( // For ZeroOrMore and OneOrMore, we want to examine the case were there is at // least one match. For ZeroOrOne, we only want the case where there is exactly // one match. - if (seq.op == quoted::KleeneOp::ZeroOrOne && seq.num_captures == 1) || - seq.op != quoted::KleeneOp::ZeroOrOne { - + if (seq.op == quoted::KleeneOp::ZeroOrOne && seq.num_captures == 1) + || seq.op != quoted::KleeneOp::ZeroOrOne + { let matches = create_matches(item.matches.len()); cur_items.push(Box::new(MatcherPos { stack: vec![], From f59b821944e52c1158ad921f8b2be1be54039942 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 25 Jan 2018 12:45:27 -0600 Subject: [PATCH 06/18] Attempted fix for `?` kleene op --- src/libsyntax/ext/tt/macro_parser.rs | 44 +++++++++++++--------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index e067bbecce28f..b35beedfff7d4 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -181,6 +181,8 @@ struct MatcherPos { match_hi: usize, // Specifically used if we are matching a repetition. If we aren't both should be `None`. + /// The KleeneOp of this sequence if we are in a repetition. + seq_op: Option, /// The separator if we are in a repetition sep: Option, /// The "parent" matcher position if we are in a repetition. That is, the matcher position just @@ -263,6 +265,7 @@ fn initial_matcher_pos(ms: Vec, lo: BytePos) -> Box { stack: vec![], // Haven't descended into any sequences, so both of these are `None`. + seq_op: None, sep: None, up: None, }) @@ -464,10 +467,11 @@ fn inner_parse_loop( item.idx += 1; next_items.push(item); } - } + } // We don't need a separator. Move the "dot" back to the beginning of the matcher - // and try to match again. - else { + // and try to match again UNLESS we are only allowed to have _one_ repetition. + else if item.seq_op != Some(quoted::KleeneOp::ZeroOrOne) { + // we don't need a separator item.match_cur = item.match_lo; item.idx = 0; cur_items.push(item); @@ -499,26 +503,20 @@ fn inner_parse_loop( cur_items.push(new_item); } - // For ZeroOrMore and OneOrMore, we want to examine the case were there is at - // least one match. For ZeroOrOne, we only want the case where there is exactly - // one match. - if (seq.op == quoted::KleeneOp::ZeroOrOne && seq.num_captures == 1) - || seq.op != quoted::KleeneOp::ZeroOrOne - { - let matches = create_matches(item.matches.len()); - cur_items.push(Box::new(MatcherPos { - stack: vec![], - sep: seq.separator.clone(), - idx: 0, - matches, - match_lo: item.match_cur, - match_cur: item.match_cur, - match_hi: item.match_cur + seq.num_captures, - up: Some(item), - sp_lo: sp.lo(), - top_elts: Tt(TokenTree::Sequence(sp, seq)), - })); - } + let matches = create_matches(item.matches.len()); + cur_items.push(Box::new(MatcherPos { + stack: vec![], + sep: seq.separator.clone(), + seq_op: Some(seq.op), + idx: 0, + matches, + match_lo: item.match_cur, + match_cur: item.match_cur, + match_hi: item.match_cur + seq.num_captures, + up: Some(item), + sp_lo: sp.lo(), + top_elts: Tt(TokenTree::Sequence(sp, seq)), + })); } // We need to match a metavar (but the identifier is invalid)... this is an error From 51ef7393ef3e55e22cd385bd89dfa2c1b95a659b Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 25 Jan 2018 15:23:43 -0600 Subject: [PATCH 07/18] Fix typo in error message + update tests --- src/libsyntax/ext/tt/quoted.rs | 2 +- src/test/compile-fail/issue-39388.rs | 2 +- .../compile-fail/macro-at-most-once-rep-ambig.rs | 14 ++++++++++++-- src/test/run-pass/macro-at-most-once-rep.rs | 6 ++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 13764ebfca1e1..670d3614604b7 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -432,6 +432,6 @@ where }; sess.span_diagnostic - .span_err(span, "expected one of: `*`, `+`, or `?`"); + .span_err(span, "expected one of: `*`, `+`, or `?`"); (None, KleeneOp::ZeroOrMore) } diff --git a/src/test/compile-fail/issue-39388.rs b/src/test/compile-fail/issue-39388.rs index 15eef429eab97..3fbbab62d24cc 100644 --- a/src/test/compile-fail/issue-39388.rs +++ b/src/test/compile-fail/issue-39388.rs @@ -11,7 +11,7 @@ #![allow(unused_macros)] macro_rules! assign { - (($($a:tt)*) = ($($b:tt))*) => { //~ ERROR expected `*` or `+` + (($($a:tt)*) = ($($b:tt))*) => { //~ ERROR 14:22: 14:29: expected one of: `*`, `+`, or `?` $($a)* = $($b)* } } diff --git a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs index b0a6ec145e16a..89ca30840a985 100644 --- a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs +++ b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs @@ -12,6 +12,10 @@ macro_rules! foo { ($(a)?) => {} } +macro_rules! baz { + ($(a),?) => {} // comma separator is meaningless for `?` +} + macro_rules! bar { ($(a)?+) => {} } @@ -20,7 +24,13 @@ pub fn main() { foo!(a?a?a); //~ ERROR no rules expected the token `?` foo!(a?a); //~ ERROR no rules expected the token `?` foo!(a?); //~ ERROR no rules expected the token `?` - bar!(); //~ ERROR no rules expected the token `)` + baz!(a?a?a); //~ ERROR no rules expected the token `?` + baz!(a?a); //~ ERROR no rules expected the token `?` + baz!(a?); //~ ERROR no rules expected the token `?` + baz!(a,); //~ ERROR no rules expected the token `,` + baz!(a?a?a,); //~ ERROR no rules expected the token `?` + baz!(a?a,); //~ ERROR no rules expected the token `?` + baz!(a?,); //~ ERROR no rules expected the token `?` + bar!(); //~ ERROR unexpected end of macro invocation bar!(a?); //~ ERROR no rules expected the token `?` } - diff --git a/src/test/run-pass/macro-at-most-once-rep.rs b/src/test/run-pass/macro-at-most-once-rep.rs index fa1f90bf6ef98..823f42c454427 100644 --- a/src/test/run-pass/macro-at-most-once-rep.rs +++ b/src/test/run-pass/macro-at-most-once-rep.rs @@ -12,6 +12,10 @@ macro_rules! foo { ($(a)?) => {} } +macro_rules! baz { + ($(a),?) => {} // comma separator is meaningless for `?` +} + macro_rules! bar { ($(a)?+) => {} } @@ -19,6 +23,8 @@ macro_rules! bar { pub fn main() { foo!(); foo!(a); + baz!(); + baz!(a); bar!(a); bar!(a?a); bar!(a?a?a); From 4897a05ebf862f694f8b276e6c540ba30af4326a Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 25 Jan 2018 15:31:40 -0600 Subject: [PATCH 08/18] Fix a couple of tests --- src/test/compile-fail/macro-at-most-once-rep-ambig.rs | 4 ++-- src/test/parse-fail/issue-33569.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs index 89ca30840a985..c745568f1caa2 100644 --- a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs +++ b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs @@ -27,10 +27,10 @@ pub fn main() { baz!(a?a?a); //~ ERROR no rules expected the token `?` baz!(a?a); //~ ERROR no rules expected the token `?` baz!(a?); //~ ERROR no rules expected the token `?` - baz!(a,); //~ ERROR no rules expected the token `,` + baz!(a,); //~ ERROR unexpected end of macro invocation baz!(a?a?a,); //~ ERROR no rules expected the token `?` baz!(a?a,); //~ ERROR no rules expected the token `?` baz!(a?,); //~ ERROR no rules expected the token `?` bar!(); //~ ERROR unexpected end of macro invocation - bar!(a?); //~ ERROR no rules expected the token `?` + bar!(a?); //~ ERROR unexpected end of macro invocation } diff --git a/src/test/parse-fail/issue-33569.rs b/src/test/parse-fail/issue-33569.rs index 15d491719a6d5..af90d0a83c926 100644 --- a/src/test/parse-fail/issue-33569.rs +++ b/src/test/parse-fail/issue-33569.rs @@ -13,7 +13,7 @@ macro_rules! foo { { $+ } => { //~ ERROR expected identifier, found `+` //~^ ERROR missing fragment specifier - $(x)(y) //~ ERROR expected `*` or `+` + $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?` } } From 3c15405c2571e03226b98f2d6eddec51967f0a18 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Fri, 26 Jan 2018 16:16:43 -0600 Subject: [PATCH 09/18] Add feature gate + tests --- src/libsyntax/ext/tt/macro_rules.rs | 6 +- src/libsyntax/ext/tt/quoted.rs | 72 ++++++++++++++++--- src/libsyntax/feature_gate.rs | 8 +++ .../ui/feature-gate-macro_at_most_once_rep.rs | 19 +++++ ...feature-gate-macro_at_most_once_rep.stderr | 11 +++ 5 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/feature-gate-macro_at_most_once_rep.rs create mode 100644 src/test/ui/feature-gate-macro_at_most_once_rep.stderr diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 9efb4faa63535..5254c751e6b62 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -237,7 +237,8 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) 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).pop().unwrap(); + let tt = quoted::parse(tt.clone().into(), true, sess, features, &def.attrs) + .pop().unwrap(); valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); return tt; } @@ -253,7 +254,8 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) s.iter().map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return quoted::parse(tt.clone().into(), false, sess).pop().unwrap(); + return quoted::parse(tt.clone().into(), false, sess, features, &def.attrs) + .pop().unwrap(); } } sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 670d3614604b7..8e05a6ccc4704 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -8,14 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast; +use {ast, attr}; use ext::tt::macro_parser; +use feature_gate::{self, emit_feature_err, Features, GateIssue}; use parse::{token, ParseSess}; use print::pprust; use symbol::keywords; use syntax_pos::{BytePos, Span, DUMMY_SP}; use tokenstream; +use std::cell::RefCell; use std::iter::Peekable; use std::rc::Rc; @@ -179,6 +181,8 @@ pub fn parse( input: tokenstream::TokenStream, expect_matchers: bool, sess: &ParseSess, + features: &RefCell, + attrs: &[ast::Attribute], ) -> Vec { // Will contain the final collection of `self::TokenTree` let mut result = Vec::new(); @@ -187,10 +191,9 @@ pub fn parse( // additional trees if need be. let mut trees = input.trees().peekable(); while let Some(tree) = trees.next() { - let tree = parse_tree(tree, &mut trees, expect_matchers, sess); - // 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); match tree { TokenTree::MetaVar(start_sp, ident) if expect_matchers => { let span = match trees.next() { @@ -244,6 +247,8 @@ fn parse_tree( trees: &mut Peekable, expect_matchers: bool, sess: &ParseSess, + features: &RefCell, + attrs: &[ast::Attribute], ) -> TokenTree where I: Iterator, @@ -262,9 +267,9 @@ where sess.span_diagnostic.span_err(span, &msg); } // Parse the contents of the sequence itself - let sequence = parse(delimited.tts.into(), expect_matchers, sess); + 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); + 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( @@ -317,7 +322,7 @@ where span, Rc::new(Delimited { delim: delimited.delim, - tts: parse(delimited.tts.into(), expect_matchers, sess), + tts: parse(delimited.tts.into(), expect_matchers, sess, features, attrs), }), ), } @@ -373,6 +378,8 @@ fn parse_sep_and_kleene_op( input: &mut Peekable, span: Span, sess: &ParseSess, + features: &RefCell, + attrs: &[ast::Attribute], ) -> (Option, KleeneOp) where I: Iterator, @@ -401,6 +408,21 @@ where // (N.B. We need to advance the input iterator.) match parse_kleene_op(input, span) { // #2 is a KleeneOp (this is the only valid option) :) + Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => { + if !features.borrow().macro_at_most_once_rep + && !attr::contains_name(attrs, "allow_internal_unstable") + { + let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; + emit_feature_err( + sess, + "macro_at_most_once_rep", + span, + GateIssue::Language, + explain, + ); + } + return (Some(token::Question), op); + } Ok(Ok(op)) => return (Some(token::Question), op), // #2 is a random token (this is an error) :( @@ -410,6 +432,19 @@ where Err(span) => span, } } else { + if !features.borrow().macro_at_most_once_rep + && !attr::contains_name(attrs, "allow_internal_unstable") + { + let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; + emit_feature_err( + sess, + "macro_at_most_once_rep", + span, + GateIssue::Language, + explain, + ); + } + // #2 is a random tree and #1 is KleeneOp::ZeroOrOne return (None, op); } @@ -418,6 +453,21 @@ where // #1 is a separator followed by #2, a KleeneOp Ok(Err((tok, span))) => match parse_kleene_op(input, span) { // #2 is a KleeneOp :D + Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => { + if !features.borrow().macro_at_most_once_rep + && !attr::contains_name(attrs, "allow_internal_unstable") + { + let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; + emit_feature_err( + sess, + "macro_at_most_once_rep", + span, + GateIssue::Language, + explain, + ); + } + return (Some(tok), op); + } Ok(Ok(op)) => return (Some(tok), op), // #2 is a random token :( @@ -431,7 +481,13 @@ where Err(span) => span, }; - sess.span_diagnostic - .span_err(span, "expected one of: `*`, `+`, or `?`"); + if !features.borrow().macro_at_most_once_rep + && !attr::contains_name(attrs, "allow_internal_unstable") + { + sess.span_diagnostic + .span_err(span, "expected one of: `*`, `+`, or `?`"); + } else { + sess.span_diagnostic.span_err(span, "expected `*` or `+`"); + } (None, KleeneOp::ZeroOrMore) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 9a2560b04583d..9358511018a93 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -452,6 +452,11 @@ declare_features! ( // Allows `#[repr(transparent)]` attribute on newtype structs (active, repr_transparent, "1.25.0", Some(43036)), + + // Use `?` as the Kleene "at most one" operator + // FIXME(mark-i-m): make sure we use the correct issue number when there is + // a tracking issue... + (active, macro_at_most_once_rep, "1.25.0", None), ); declare_features! ( @@ -1250,6 +1255,9 @@ pub const EXPLAIN_PLACEMENT_IN: &'static str = pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str = "Unsized tuple coercion is not stable enough for use and is subject to change"; +pub const EXPLAIN_MACRO_AT_MOST_ONCE_REP: &'static str = + "Using the `?` macro Kleene operator for \"at most one\" repetition is unstable"; + struct PostExpansionVisitor<'a> { context: &'a Context<'a>, } diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.rs b/src/test/ui/feature-gate-macro_at_most_once_rep.rs new file mode 100644 index 0000000000000..13b2a8e217a42 --- /dev/null +++ b/src/test/ui/feature-gate-macro_at_most_once_rep.rs @@ -0,0 +1,19 @@ +// Copyright 2017 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. + +// Test that the MSP430 interrupt ABI cannot be used when msp430_interrupt +// feature gate is not used. + +macro_rules! m { ($(a)?) => {} } +//~^ ERROR Using the `?` macro Kleene operator for "at most one" repetition is unstable + +fn main() { + m!(); +} diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.stderr b/src/test/ui/feature-gate-macro_at_most_once_rep.stderr new file mode 100644 index 0000000000000..f470399ffefa5 --- /dev/null +++ b/src/test/ui/feature-gate-macro_at_most_once_rep.stderr @@ -0,0 +1,11 @@ +error[E0658]: Using the `?` macro Kleene operator for "at most one" repetition is unstable + --> $DIR/feature-gate-macro_at_most_once_rep.rs:14:19 + | +14 | macro_rules! m { ($(a)?) => {} } + | ^^^^^ + | + = help: add #![feature(macro_at_most_once_rep)] to the crate attributes to enable + +error: aborting due to previous error + + From 5c4b4fe4d6b528e1500fc75154b9c701645ac274 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Fri, 26 Jan 2018 16:34:26 -0600 Subject: [PATCH 10/18] Corrected ui feature gate test --- src/test/ui/feature-gate-macro_at_most_once_rep.stderr | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.stderr b/src/test/ui/feature-gate-macro_at_most_once_rep.stderr index f470399ffefa5..515c6243e668b 100644 --- a/src/test/ui/feature-gate-macro_at_most_once_rep.stderr +++ b/src/test/ui/feature-gate-macro_at_most_once_rep.stderr @@ -1,11 +1,10 @@ error[E0658]: Using the `?` macro Kleene operator for "at most one" repetition is unstable - --> $DIR/feature-gate-macro_at_most_once_rep.rs:14:19 + --> $DIR/feature-gate-macro_at_most_once_rep.rs:14:20 | 14 | macro_rules! m { ($(a)?) => {} } - | ^^^^^ + | ^^^ | = help: add #![feature(macro_at_most_once_rep)] to the crate attributes to enable error: aborting due to previous error - From bd98a935587ad988b5780b75021b1a45d0f508d7 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Fri, 26 Jan 2018 17:08:50 -0600 Subject: [PATCH 11/18] Fix more tests --- src/test/compile-fail/macro-at-most-once-rep-ambig.rs | 2 ++ .../auxiliary/procedural_mbe_matching.rs | 9 ++++++++- src/test/run-pass/macro-at-most-once-rep.rs | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs index c745568f1caa2..6886a02cb9210 100644 --- a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs +++ b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(macro_at_most_once_rep)] + macro_rules! foo { ($(a)?) => {} } 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 b5d6ff595afd5..9ebc438ad5a00 100644 --- a/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs +++ b/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs @@ -18,6 +18,7 @@ extern crate syntax_pos; extern crate rustc; extern crate rustc_plugin; +use syntax::feature_gate::Features; use syntax::parse::token::{NtExpr, NtPat}; use syntax::ast::{Ident, Pat}; use syntax::tokenstream::{TokenTree}; @@ -31,11 +32,17 @@ use syntax::ptr::P; use syntax_pos::Span; use rustc_plugin::Registry; +use std::cell::RefCell; + fn expand_mbe_matches(cx: &mut ExtCtxt, _: Span, args: &[TokenTree]) -> Box { let mbe_matcher = quote_tokens!(cx, $$matched:expr, $$($$pat:pat)|+); - let mbe_matcher = quoted::parse(mbe_matcher.into_iter().collect(), true, cx.parse_sess); + let mbe_matcher = quoted::parse(mbe_matcher.into_iter().collect(), + true, + cx.parse_sess, + &RefCell::new(Features::new()), + &[]); let map = match TokenTree::parse(cx, &mbe_matcher, args.iter().cloned().collect()) { Success(map) => map, Failure(_, tok) => { diff --git a/src/test/run-pass/macro-at-most-once-rep.rs b/src/test/run-pass/macro-at-most-once-rep.rs index 823f42c454427..ecfa92d5a738b 100644 --- a/src/test/run-pass/macro-at-most-once-rep.rs +++ b/src/test/run-pass/macro-at-most-once-rep.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(macro_at_most_once_rep)] + macro_rules! foo { ($(a)?) => {} } From 6943430e6d0ff11db4d99544cef7d480b15385e5 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Fri, 26 Jan 2018 17:26:09 -0600 Subject: [PATCH 12/18] Add ? to unstable book --- .../language-features/macro-at-most-once-rep.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/macro-at-most-once-rep.md diff --git a/src/doc/unstable-book/src/language-features/macro-at-most-once-rep.md b/src/doc/unstable-book/src/language-features/macro-at-most-once-rep.md new file mode 100644 index 0000000000000..dbaf91b6e78b2 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/macro-at-most-once-rep.md @@ -0,0 +1,17 @@ +# `macro_at_most_once_rep` + +The tracking issue for this feature is: TODO(mark-i-m) + +With this feature gate enabled, one can use `?` as a Kleene operator meaning "0 +or 1 repetitions" in a macro definition. Previously only `+` and `*` were allowed. + +For example: +```rust +macro_rules! foo { + (something $(,)?) // `?` indicates `,` is "optional"... + => {} +} +``` + +------------------------ + From 3859eca85134d518278f172fb26483e098381047 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Mon, 29 Jan 2018 16:26:11 -0600 Subject: [PATCH 13/18] Improved tests + typo fixes + assert --- src/libsyntax/ext/tt/quoted.rs | 2 + src/test/compile-fail/issue-39388.rs | 2 +- .../macro-at-most-once-rep-ambig.rs | 21 ++++- src/test/run-pass/macro-at-most-once-rep.rs | 77 ++++++++++++++++--- .../ui/feature-gate-macro_at_most_once_rep.rs | 4 +- 5 files changed, 89 insertions(+), 17 deletions(-) diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 8e05a6ccc4704..bde1010b523ab 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -396,6 +396,8 @@ where // be a `?` separator followed by any Kleene operator. We need to look ahead 1 token to // find out which. Ok(Ok(op)) => { + assert_eq!(op, KleeneOp::ZeroOrOne); + // Lookahead at #2. If it is a KleenOp, then #1 is a separator. let is_1_sep = if let Some(&tokenstream::TokenTree::Token(_, ref tok2)) = input.peek() { kleene_op(tok2).is_some() diff --git a/src/test/compile-fail/issue-39388.rs b/src/test/compile-fail/issue-39388.rs index 3fbbab62d24cc..6da049374086a 100644 --- a/src/test/compile-fail/issue-39388.rs +++ b/src/test/compile-fail/issue-39388.rs @@ -11,7 +11,7 @@ #![allow(unused_macros)] macro_rules! assign { - (($($a:tt)*) = ($($b:tt))*) => { //~ ERROR 14:22: 14:29: expected one of: `*`, `+`, or `?` + (($($a:tt)*) = ($($b:tt))*) => { //~ ERROR expected one of: `*`, `+`, or `?` $($a)* = $($b)* } } diff --git a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs index 6886a02cb9210..a5660f8b41f8d 100644 --- a/src/test/compile-fail/macro-at-most-once-rep-ambig.rs +++ b/src/test/compile-fail/macro-at-most-once-rep-ambig.rs @@ -8,6 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// The logic for parsing Kleene operators in macros has a special case to disambiguate `?`. +// Specifically, `$(pat)?` is the ZeroOrOne operator whereas `$(pat)?+` or `$(pat)?*` are the +// ZeroOrMore and OneOrMore operators using `?` as a separator. These tests are intended to +// exercise that logic in the macro parser. +// +// Moreover, we also throw in some tests for using a separator with `?`, which is meaningless but +// included for consistency with `+` and `*`. +// +// This test focuses on error cases. + #![feature(macro_at_most_once_rep)] macro_rules! foo { @@ -18,10 +28,14 @@ macro_rules! baz { ($(a),?) => {} // comma separator is meaningless for `?` } -macro_rules! bar { +macro_rules! barplus { ($(a)?+) => {} } +macro_rules! barstar { + ($(a)?*) => {} +} + pub fn main() { foo!(a?a?a); //~ ERROR no rules expected the token `?` foo!(a?a); //~ ERROR no rules expected the token `?` @@ -33,6 +47,7 @@ pub fn main() { baz!(a?a?a,); //~ ERROR no rules expected the token `?` baz!(a?a,); //~ ERROR no rules expected the token `?` baz!(a?,); //~ ERROR no rules expected the token `?` - bar!(); //~ ERROR unexpected end of macro invocation - bar!(a?); //~ ERROR unexpected end of macro invocation + barplus!(); //~ ERROR unexpected end of macro invocation + barplus!(a?); //~ ERROR unexpected end of macro invocation + barstar!(a?); //~ ERROR unexpected end of macro invocation } diff --git a/src/test/run-pass/macro-at-most-once-rep.rs b/src/test/run-pass/macro-at-most-once-rep.rs index ecfa92d5a738b..b7e942f938321 100644 --- a/src/test/run-pass/macro-at-most-once-rep.rs +++ b/src/test/run-pass/macro-at-most-once-rep.rs @@ -8,26 +8,81 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// The logic for parsing Kleene operators in macros has a special case to disambiguate `?`. +// Specifically, `$(pat)?` is the ZeroOrOne operator whereas `$(pat)?+` or `$(pat)?*` are the +// ZeroOrMore and OneOrMore operators using `?` as a separator. These tests are intended to +// exercise that logic in the macro parser. +// +// Moreover, we also throw in some tests for using a separator with `?`, which is meaningless but +// included for consistency with `+` and `*`. +// +// This test focuses on non-error cases and making sure the correct number of repetitions happen. + #![feature(macro_at_most_once_rep)] macro_rules! foo { - ($(a)?) => {} + ($($a:ident)? ; $num:expr) => { { + let mut x = 0; + + $( + x += $a; + )? + + assert_eq!(x, $num); + } } } macro_rules! baz { - ($(a),?) => {} // comma separator is meaningless for `?` + ($($a:ident),? ; $num:expr) => { { // comma separator is meaningless for `?` + let mut x = 0; + + $( + x += $a; + )? + + assert_eq!(x, $num); + } } } -macro_rules! bar { - ($(a)?+) => {} +macro_rules! barplus { + ($($a:ident)?+ ; $num:expr) => { { + let mut x = 0; + + $( + x += $a; + )+ + + assert_eq!(x, $num); + } } +} + +macro_rules! barstar { + ($($a:ident)?* ; $num:expr) => { { + let mut x = 0; + + $( + x += $a; + )* + + assert_eq!(x, $num); + } } } pub fn main() { - foo!(); - foo!(a); - baz!(); - baz!(a); - bar!(a); - bar!(a?a); - bar!(a?a?a); + let a = 1; + + // accept 0 or 1 repetitions + foo!( ; 0); + foo!(a ; 1); + baz!( ; 0); + baz!(a ; 1); + + // Make sure using ? as a separator works as before + barplus!(a ; 1); + barplus!(a?a ; 2); + barplus!(a?a?a ; 3); + barstar!( ; 0); + barstar!(a ; 1); + barstar!(a?a ; 2); + barstar!(a?a?a ; 3); } diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.rs b/src/test/ui/feature-gate-macro_at_most_once_rep.rs index 13b2a8e217a42..19f5aca5730e1 100644 --- a/src/test/ui/feature-gate-macro_at_most_once_rep.rs +++ b/src/test/ui/feature-gate-macro_at_most_once_rep.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test that the MSP430 interrupt ABI cannot be used when msp430_interrupt -// feature gate is not used. +// Test that `?` macro Kleene operator can not be used when the `macro_at_most_once_rep` feature +// gate is not used. macro_rules! m { ($(a)?) => {} } //~^ ERROR Using the `?` macro Kleene operator for "at most one" repetition is unstable From 786b2ca1556de6d589bfb7d07e4a98e8fdcc6498 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 30 Jan 2018 12:45:35 -0600 Subject: [PATCH 14/18] Fix trailing whitespace --- src/libsyntax/ext/tt/macro_parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index b35beedfff7d4..2a3b96ebcb52b 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -467,7 +467,7 @@ fn inner_parse_loop( item.idx += 1; next_items.push(item); } - } + } // We don't need a separator. Move the "dot" back to the beginning of the matcher // and try to match again UNLESS we are only allowed to have _one_ repetition. else if item.seq_op != Some(quoted::KleeneOp::ZeroOrOne) { From 549534e438e40439aa43e6580fe31509ea31376f Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Tue, 30 Jan 2018 16:20:46 -0600 Subject: [PATCH 15/18] Update a few comments --- src/libsyntax/ext/tt/macro_parser.rs | 1 - src/libsyntax/ext/tt/quoted.rs | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 2a3b96ebcb52b..0621f728e2a9d 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -471,7 +471,6 @@ fn inner_parse_loop( // We don't need a separator. Move the "dot" back to the beginning of the matcher // and try to match again UNLESS we are only allowed to have _one_ repetition. else if item.seq_op != Some(quoted::KleeneOp::ZeroOrOne) { - // we don't need a separator item.match_cur = item.match_lo; item.idx = 0; cur_items.push(item); diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index bde1010b523ab..982b60b81e47e 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -173,6 +173,8 @@ impl TokenTree { /// `ident` are "matchers". They are not present in the body of a macro rule -- just in the /// pattern, so we pass a parameter to indicate whether to expect them or not. /// - `sess`: the parsing session. Any errors will be emitted to this session. +/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use +/// unstable features or not. /// /// # Returns /// @@ -242,6 +244,8 @@ pub fn parse( /// converting `tree` /// - `expect_matchers`: same as for `parse` (see above). /// - `sess`: the parsing session. Any errors will be emitted to this session. +/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use +/// unstable features or not. fn parse_tree( tree: tokenstream::TokenTree, trees: &mut Peekable, From 4cf3b65714726d2c3022717e108943590807b4a2 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 8 Feb 2018 18:40:00 -0600 Subject: [PATCH 16/18] Use the right tracking issue --- src/libsyntax/feature_gate.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 9358511018a93..382ee3525ec2c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -454,9 +454,7 @@ declare_features! ( (active, repr_transparent, "1.25.0", Some(43036)), // Use `?` as the Kleene "at most one" operator - // FIXME(mark-i-m): make sure we use the correct issue number when there is - // a tracking issue... - (active, macro_at_most_once_rep, "1.25.0", None), + (active, macro_at_most_once_rep, "1.25.0", Some(48075)), ); declare_features! ( From 1bd086283b0fab7ecda60f451df870aa4e21d00b Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 8 Feb 2018 22:00:51 -0600 Subject: [PATCH 17/18] Update feature gate test --- src/test/ui/feature-gate-macro_at_most_once_rep.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.rs b/src/test/ui/feature-gate-macro_at_most_once_rep.rs index 19f5aca5730e1..27257eec277c1 100644 --- a/src/test/ui/feature-gate-macro_at_most_once_rep.rs +++ b/src/test/ui/feature-gate-macro_at_most_once_rep.rs @@ -12,7 +12,7 @@ // gate is not used. macro_rules! m { ($(a)?) => {} } -//~^ ERROR Using the `?` macro Kleene operator for "at most one" repetition is unstable +//~^ ERROR Using the `?` macro Kleene operator for "at most one" repetition is unstable (see issue #48075) fn main() { m!(); From b92e542ddd41affaf6fb5d1267b8e8dfc03089a5 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 8 Feb 2018 23:00:38 -0600 Subject: [PATCH 18/18] Fix the test --- src/test/ui/feature-gate-macro_at_most_once_rep.rs | 2 +- src/test/ui/feature-gate-macro_at_most_once_rep.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.rs b/src/test/ui/feature-gate-macro_at_most_once_rep.rs index 27257eec277c1..19f5aca5730e1 100644 --- a/src/test/ui/feature-gate-macro_at_most_once_rep.rs +++ b/src/test/ui/feature-gate-macro_at_most_once_rep.rs @@ -12,7 +12,7 @@ // gate is not used. macro_rules! m { ($(a)?) => {} } -//~^ ERROR Using the `?` macro Kleene operator for "at most one" repetition is unstable (see issue #48075) +//~^ ERROR Using the `?` macro Kleene operator for "at most one" repetition is unstable fn main() { m!(); diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.stderr b/src/test/ui/feature-gate-macro_at_most_once_rep.stderr index 515c6243e668b..02dbab07bdecc 100644 --- a/src/test/ui/feature-gate-macro_at_most_once_rep.stderr +++ b/src/test/ui/feature-gate-macro_at_most_once_rep.stderr @@ -1,4 +1,4 @@ -error[E0658]: Using the `?` macro Kleene operator for "at most one" repetition is unstable +error[E0658]: Using the `?` macro Kleene operator for "at most one" repetition is unstable (see issue #48075) --> $DIR/feature-gate-macro_at_most_once_rep.rs:14:20 | 14 | macro_rules! m { ($(a)?) => {} }