No separator for ?
. No ?
as a separator.
This commit is contained in:
parent
56714acc5e
commit
b2ed17b5ea
@ -389,72 +389,26 @@ fn parse_sep_and_kleene_op<I>(
|
|||||||
{
|
{
|
||||||
// We basically look at two token trees here, denoted as #1 and #2 below
|
// We basically look at two token trees here, denoted as #1 and #2 below
|
||||||
let span = match parse_kleene_op(input, span) {
|
let span = match parse_kleene_op(input, span) {
|
||||||
// #1 is a `+` or `*` KleeneOp
|
// #1 is any KleeneOp (`?`)
|
||||||
//
|
Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => {
|
||||||
// `?` is ambiguous: it could be a separator or a Kleene::ZeroOrOne, so we need to look
|
if !features.macro_at_most_once_rep
|
||||||
// ahead one more token to be sure.
|
&& !attr::contains_name(attrs, "allow_internal_unstable")
|
||||||
Ok(Ok(op)) if op != KleeneOp::ZeroOrOne => return (None, op),
|
{
|
||||||
|
let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
|
||||||
// #1 is `?` token, but it could be a Kleene::ZeroOrOne without a separator or it could
|
emit_feature_err(
|
||||||
// be a `?` separator followed by any Kleene operator. We need to look ahead 1 token to
|
sess,
|
||||||
// find out which.
|
"macro_at_most_once_rep",
|
||||||
Ok(Ok(op)) => {
|
span,
|
||||||
assert_eq!(op, KleeneOp::ZeroOrOne);
|
GateIssue::Language,
|
||||||
|
explain,
|
||||||
// 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)) if op == KleeneOp::ZeroOrOne => {
|
|
||||||
if !features.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) :(
|
|
||||||
Ok(Err((_, span))) => span,
|
|
||||||
|
|
||||||
// #2 is not even a token at all :(
|
|
||||||
Err(span) => span,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !features.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);
|
|
||||||
}
|
}
|
||||||
|
return (None, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #1 is any KleeneOp (`+`, `*`)
|
||||||
|
Ok(Ok(op)) => return (None, op),
|
||||||
|
|
||||||
// #1 is a separator followed by #2, a KleeneOp
|
// #1 is a separator followed by #2, a KleeneOp
|
||||||
Ok(Err((tok, span))) => match parse_kleene_op(input, span) {
|
Ok(Err((tok, span))) => match parse_kleene_op(input, span) {
|
||||||
// #2 is a KleeneOp :D
|
// #2 is a KleeneOp :D
|
||||||
@ -470,8 +424,11 @@ fn parse_sep_and_kleene_op<I>(
|
|||||||
GateIssue::Language,
|
GateIssue::Language,
|
||||||
explain,
|
explain,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
sess.span_diagnostic
|
||||||
|
.span_err(span, "`?` macro repetition does not allow a separator");
|
||||||
}
|
}
|
||||||
return (Some(tok), op);
|
return (None, op);
|
||||||
}
|
}
|
||||||
Ok(Ok(op)) => return (Some(tok), op),
|
Ok(Ok(op)) => return (Some(tok), op),
|
||||||
|
|
||||||
@ -486,9 +443,7 @@ fn parse_sep_and_kleene_op<I>(
|
|||||||
Err(span) => span,
|
Err(span) => span,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !features.macro_at_most_once_rep
|
if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") {
|
||||||
&& !attr::contains_name(attrs, "allow_internal_unstable")
|
|
||||||
{
|
|
||||||
sess.span_diagnostic
|
sess.span_diagnostic
|
||||||
.span_err(span, "expected one of: `*`, `+`, or `?`");
|
.span_err(span, "expected one of: `*`, `+`, or `?`");
|
||||||
} else {
|
} else {
|
||||||
|
@ -32,25 +32,13 @@ macro_rules! foo {
|
|||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! baz {
|
|
||||||
($($a:ident),? ; $num:expr) => { { // comma separator is meaningless for `?`
|
|
||||||
let mut x = 0;
|
|
||||||
|
|
||||||
$(
|
|
||||||
x += $a;
|
|
||||||
)?
|
|
||||||
|
|
||||||
assert_eq!(x, $num);
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! barplus {
|
macro_rules! barplus {
|
||||||
($($a:ident)?+ ; $num:expr) => { {
|
($($a:ident)?+ ; $num:expr) => { {
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
|
|
||||||
$(
|
$(
|
||||||
x += $a;
|
x += $a;
|
||||||
)+
|
)?
|
||||||
|
|
||||||
assert_eq!(x, $num);
|
assert_eq!(x, $num);
|
||||||
} }
|
} }
|
||||||
@ -62,7 +50,7 @@ macro_rules! barstar {
|
|||||||
|
|
||||||
$(
|
$(
|
||||||
x += $a;
|
x += $a;
|
||||||
)*
|
)?
|
||||||
|
|
||||||
assert_eq!(x, $num);
|
assert_eq!(x, $num);
|
||||||
} }
|
} }
|
||||||
@ -74,15 +62,10 @@ pub fn main() {
|
|||||||
// accept 0 or 1 repetitions
|
// accept 0 or 1 repetitions
|
||||||
foo!( ; 0);
|
foo!( ; 0);
|
||||||
foo!(a ; 1);
|
foo!(a ; 1);
|
||||||
baz!( ; 0);
|
|
||||||
baz!(a ; 1);
|
|
||||||
|
|
||||||
// Make sure using ? as a separator works as before
|
// Make sure using ? as a separator works as before
|
||||||
barplus!(a ; 1);
|
barplus!(+ ; 0);
|
||||||
barplus!(a?a ; 2);
|
barplus!(a + ; 1);
|
||||||
barplus!(a?a?a ; 3);
|
barstar!(* ; 0);
|
||||||
barstar!( ; 0);
|
barstar!(a * ; 1);
|
||||||
barstar!(a ; 1);
|
|
||||||
barstar!(a?a ; 2);
|
|
||||||
barstar!(a?a?a ; 3);
|
|
||||||
}
|
}
|
||||||
|
@ -8,30 +8,26 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// The logic for parsing Kleene operators in macros has a special case to disambiguate `?`.
|
// Tests the behavior of various Kleene operators in macros with respect to `?` terminals. In
|
||||||
// Specifically, `$(pat)?` is the ZeroOrOne operator whereas `$(pat)?+` or `$(pat)?*` are the
|
// particular, `?` in the position of a separator and of a Kleene operator is tested.
|
||||||
// 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)]
|
#![feature(macro_at_most_once_rep)]
|
||||||
|
|
||||||
|
// should match `` and `a`
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
($(a)?) => {}
|
($(a)?) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! baz {
|
macro_rules! baz {
|
||||||
($(a),?) => {} // comma separator is meaningless for `?`
|
($(a),?) => {} //~ ERROR `?` macro repetition does not allow a separator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should match `+` and `a+`
|
||||||
macro_rules! barplus {
|
macro_rules! barplus {
|
||||||
($(a)?+) => {}
|
($(a)?+) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should match `*` and `a*`
|
||||||
macro_rules! barstar {
|
macro_rules! barstar {
|
||||||
($(a)?*) => {}
|
($(a)?*) => {}
|
||||||
}
|
}
|
||||||
@ -40,14 +36,14 @@ pub fn main() {
|
|||||||
foo!(a?a?a); //~ ERROR no rules expected the token `?`
|
foo!(a?a?a); //~ ERROR no rules expected the token `?`
|
||||||
foo!(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 `?`
|
foo!(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 `?`
|
|
||||||
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 `?`
|
|
||||||
barplus!(); //~ ERROR unexpected end of macro invocation
|
barplus!(); //~ ERROR unexpected end of macro invocation
|
||||||
barplus!(a?); //~ ERROR unexpected end of macro invocation
|
barstar!(); //~ ERROR unexpected end of macro invocation
|
||||||
barstar!(a?); //~ ERROR unexpected end of macro invocation
|
barplus!(a?); //~ ERROR no rules expected the token `?`
|
||||||
|
barplus!(a); //~ ERROR unexpected end of macro invocation
|
||||||
|
barstar!(a?); //~ ERROR no rules expected the token `?`
|
||||||
|
barstar!(a); //~ ERROR unexpected end of macro invocation
|
||||||
|
barplus!(+); // ok
|
||||||
|
barstar!(*); // ok
|
||||||
|
barplus!(a); // ok
|
||||||
|
barstar!(a*); // ok
|
||||||
}
|
}
|
||||||
|
@ -1,80 +1,62 @@
|
|||||||
|
error: `?` macro repetition does not allow a separator
|
||||||
|
--> $DIR/macro-at-most-once-rep-ambig.rs:29:10
|
||||||
|
|
|
||||||
|
LL | ($(a),?) => {} //~ ERROR `?` macro repetition does not allow a separator
|
||||||
|
| ^
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
error: no rules expected the token `?`
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:40:11
|
--> $DIR/macro-at-most-once-rep-ambig.rs:43:11
|
||||||
|
|
|
|
||||||
LL | foo!(a?a?a); //~ ERROR no rules expected the token `?`
|
LL | foo!(a?a?a); //~ ERROR no rules expected the token `?`
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
error: no rules expected the token `?`
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:41:11
|
--> $DIR/macro-at-most-once-rep-ambig.rs:44:11
|
||||||
|
|
|
|
||||||
LL | foo!(a?a); //~ ERROR no rules expected the token `?`
|
LL | foo!(a?a); //~ ERROR no rules expected the token `?`
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
error: no rules expected the token `?`
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:42:11
|
--> $DIR/macro-at-most-once-rep-ambig.rs:45:11
|
||||||
|
|
|
|
||||||
LL | foo!(a?); //~ ERROR no rules expected the token `?`
|
LL | foo!(a?); //~ ERROR no rules expected the token `?`
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:43:11
|
|
||||||
|
|
|
||||||
LL | baz!(a?a?a); //~ ERROR no rules expected the token `?`
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:44:11
|
|
||||||
|
|
|
||||||
LL | baz!(a?a); //~ ERROR no rules expected the token `?`
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:45:11
|
|
||||||
|
|
|
||||||
LL | baz!(a?); //~ ERROR no rules expected the token `?`
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: unexpected end of macro invocation
|
error: unexpected end of macro invocation
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:46:11
|
--> $DIR/macro-at-most-once-rep-ambig.rs:46:5
|
||||||
|
|
|
||||||
LL | baz!(a,); //~ ERROR unexpected end of macro invocation
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:47:11
|
|
||||||
|
|
|
||||||
LL | baz!(a?a?a,); //~ ERROR no rules expected the token `?`
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:48:11
|
|
||||||
|
|
|
||||||
LL | baz!(a?a,); //~ ERROR no rules expected the token `?`
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: no rules expected the token `?`
|
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:49:11
|
|
||||||
|
|
|
||||||
LL | baz!(a?,); //~ ERROR no rules expected the token `?`
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: unexpected end of macro invocation
|
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:50:5
|
|
||||||
|
|
|
|
||||||
LL | barplus!(); //~ ERROR unexpected end of macro invocation
|
LL | barplus!(); //~ ERROR unexpected end of macro invocation
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
error: unexpected end of macro invocation
|
error: unexpected end of macro invocation
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:51:15
|
--> $DIR/macro-at-most-once-rep-ambig.rs:47:5
|
||||||
|
|
|
|
||||||
LL | barplus!(a?); //~ ERROR unexpected end of macro invocation
|
LL | barstar!(); //~ ERROR unexpected end of macro invocation
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: no rules expected the token `?`
|
||||||
|
--> $DIR/macro-at-most-once-rep-ambig.rs:48:15
|
||||||
|
|
|
||||||
|
LL | barplus!(a?); //~ ERROR no rules expected the token `?`
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: unexpected end of macro invocation
|
error: unexpected end of macro invocation
|
||||||
--> $DIR/macro-at-most-once-rep-ambig.rs:52:15
|
--> $DIR/macro-at-most-once-rep-ambig.rs:49:14
|
||||||
|
|
|
|
||||||
LL | barstar!(a?); //~ ERROR unexpected end of macro invocation
|
LL | barplus!(a); //~ ERROR unexpected end of macro invocation
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: no rules expected the token `?`
|
||||||
|
--> $DIR/macro-at-most-once-rep-ambig.rs:50:15
|
||||||
|
|
|
||||||
|
LL | barstar!(a?); //~ ERROR no rules expected the token `?`
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: aborting due to 13 previous errors
|
error: unexpected end of macro invocation
|
||||||
|
--> $DIR/macro-at-most-once-rep-ambig.rs:51:14
|
||||||
|
|
|
||||||
|
LL | barstar!(a); //~ ERROR unexpected end of macro invocation
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to 10 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user