Skip to content

Commit d6ba1b9

Browse files
committed
Auto merge of #49719 - mark-i-m:no_sep, r=petrochenkov
Update `?` repetition disambiguation. **Do not merge** (yet) This is a test implementation of some ideas from discussion in #48075 . This PR - disallows `?` repetition from taking a separator, since the separator is never used. - disallows the use of `?` as a separator. This allows patterns like `$(a)?+` to match `+` and `a+` rather than `a?a?a`. This is a _breaking change_, but maybe that's ok? Perhaps a crater run is the right approach? cc @durka @alexreg @nikomatsakis
2 parents 8de5353 + 54bba4c commit d6ba1b9

File tree

4 files changed

+72
-156
lines changed

4 files changed

+72
-156
lines changed

src/libsyntax/ext/tt/quoted.rs

+22-67
Original file line numberDiff line numberDiff line change
@@ -386,72 +386,26 @@ where
386386
{
387387
// We basically look at two token trees here, denoted as #1 and #2 below
388388
let span = match parse_kleene_op(input, span) {
389-
// #1 is a `+` or `*` KleeneOp
390-
//
391-
// `?` is ambiguous: it could be a separator or a Kleene::ZeroOrOne, so we need to look
392-
// ahead one more token to be sure.
393-
Ok(Ok(op)) if op != KleeneOp::ZeroOrOne => return (None, op),
394-
395-
// #1 is `?` token, but it could be a Kleene::ZeroOrOne without a separator or it could
396-
// be a `?` separator followed by any Kleene operator. We need to look ahead 1 token to
397-
// find out which.
398-
Ok(Ok(op)) => {
399-
assert_eq!(op, KleeneOp::ZeroOrOne);
400-
401-
// Lookahead at #2. If it is a KleenOp, then #1 is a separator.
402-
let is_1_sep = if let Some(&tokenstream::TokenTree::Token(_, ref tok2)) = input.peek() {
403-
kleene_op(tok2).is_some()
404-
} else {
405-
false
406-
};
407-
408-
if is_1_sep {
409-
// #1 is a separator and #2 should be a KleepeOp::*
410-
// (N.B. We need to advance the input iterator.)
411-
match parse_kleene_op(input, span) {
412-
// #2 is a KleeneOp (this is the only valid option) :)
413-
Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => {
414-
if !features.macro_at_most_once_rep
415-
&& !attr::contains_name(attrs, "allow_internal_unstable")
416-
{
417-
let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
418-
emit_feature_err(
419-
sess,
420-
"macro_at_most_once_rep",
421-
span,
422-
GateIssue::Language,
423-
explain,
424-
);
425-
}
426-
return (Some(token::Question), op);
427-
}
428-
Ok(Ok(op)) => return (Some(token::Question), op),
429-
430-
// #2 is a random token (this is an error) :(
431-
Ok(Err((_, span))) => span,
432-
433-
// #2 is not even a token at all :(
434-
Err(span) => span,
435-
}
436-
} else {
437-
if !features.macro_at_most_once_rep
438-
&& !attr::contains_name(attrs, "allow_internal_unstable")
439-
{
440-
let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
441-
emit_feature_err(
442-
sess,
443-
"macro_at_most_once_rep",
444-
span,
445-
GateIssue::Language,
446-
explain,
447-
);
448-
}
449-
450-
// #2 is a random tree and #1 is KleeneOp::ZeroOrOne
451-
return (None, op);
389+
// #1 is any KleeneOp (`?`)
390+
Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => {
391+
if !features.macro_at_most_once_rep
392+
&& !attr::contains_name(attrs, "allow_internal_unstable")
393+
{
394+
let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
395+
emit_feature_err(
396+
sess,
397+
"macro_at_most_once_rep",
398+
span,
399+
GateIssue::Language,
400+
explain,
401+
);
452402
}
403+
return (None, op);
453404
}
454405

406+
// #1 is any KleeneOp (`+`, `*`)
407+
Ok(Ok(op)) => return (None, op),
408+
455409
// #1 is a separator followed by #2, a KleeneOp
456410
Ok(Err((tok, span))) => match parse_kleene_op(input, span) {
457411
// #2 is a KleeneOp :D
@@ -467,8 +421,11 @@ where
467421
GateIssue::Language,
468422
explain,
469423
);
424+
} else {
425+
sess.span_diagnostic
426+
.span_err(span, "`?` macro repetition does not allow a separator");
470427
}
471-
return (Some(tok), op);
428+
return (None, op);
472429
}
473430
Ok(Ok(op)) => return (Some(tok), op),
474431

@@ -483,9 +440,7 @@ where
483440
Err(span) => span,
484441
};
485442

486-
if !features.macro_at_most_once_rep
487-
&& !attr::contains_name(attrs, "allow_internal_unstable")
488-
{
443+
if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") {
489444
sess.span_diagnostic
490445
.span_err(span, "expected one of: `*`, `+`, or `?`");
491446
} else {

src/test/run-pass/macro-at-most-once-rep.rs

+6-23
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,13 @@ macro_rules! foo {
3232
} }
3333
}
3434

35-
macro_rules! baz {
36-
($($a:ident),? ; $num:expr) => { { // comma separator is meaningless for `?`
37-
let mut x = 0;
38-
39-
$(
40-
x += $a;
41-
)?
42-
43-
assert_eq!(x, $num);
44-
} }
45-
}
46-
4735
macro_rules! barplus {
4836
($($a:ident)?+ ; $num:expr) => { {
4937
let mut x = 0;
5038

5139
$(
5240
x += $a;
53-
)+
41+
)?
5442

5543
assert_eq!(x, $num);
5644
} }
@@ -62,7 +50,7 @@ macro_rules! barstar {
6250

6351
$(
6452
x += $a;
65-
)*
53+
)?
6654

6755
assert_eq!(x, $num);
6856
} }
@@ -74,15 +62,10 @@ pub fn main() {
7462
// accept 0 or 1 repetitions
7563
foo!( ; 0);
7664
foo!(a ; 1);
77-
baz!( ; 0);
78-
baz!(a ; 1);
7965

8066
// Make sure using ? as a separator works as before
81-
barplus!(a ; 1);
82-
barplus!(a?a ; 2);
83-
barplus!(a?a?a ; 3);
84-
barstar!( ; 0);
85-
barstar!(a ; 1);
86-
barstar!(a?a ; 2);
87-
barstar!(a?a?a ; 3);
67+
barplus!(+ ; 0);
68+
barplus!(a + ; 1);
69+
barstar!(* ; 0);
70+
barstar!(a * ; 1);
8871
}

src/test/ui/macros/macro-at-most-once-rep-ambig.rs

+15-19
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,26 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// The logic for parsing Kleene operators in macros has a special case to disambiguate `?`.
12-
// Specifically, `$(pat)?` is the ZeroOrOne operator whereas `$(pat)?+` or `$(pat)?*` are the
13-
// ZeroOrMore and OneOrMore operators using `?` as a separator. These tests are intended to
14-
// exercise that logic in the macro parser.
15-
//
16-
// Moreover, we also throw in some tests for using a separator with `?`, which is meaningless but
17-
// included for consistency with `+` and `*`.
18-
//
19-
// This test focuses on error cases.
11+
// Tests the behavior of various Kleene operators in macros with respect to `?` terminals. In
12+
// particular, `?` in the position of a separator and of a Kleene operator is tested.
2013

2114
#![feature(macro_at_most_once_rep)]
2215

16+
// should match `` and `a`
2317
macro_rules! foo {
2418
($(a)?) => {}
2519
}
2620

2721
macro_rules! baz {
28-
($(a),?) => {} // comma separator is meaningless for `?`
22+
($(a),?) => {} //~ ERROR `?` macro repetition does not allow a separator
2923
}
3024

25+
// should match `+` and `a+`
3126
macro_rules! barplus {
3227
($(a)?+) => {}
3328
}
3429

30+
// should match `*` and `a*`
3531
macro_rules! barstar {
3632
($(a)?*) => {}
3733
}
@@ -40,14 +36,14 @@ pub fn main() {
4036
foo!(a?a?a); //~ ERROR no rules expected the token `?`
4137
foo!(a?a); //~ ERROR no rules expected the token `?`
4238
foo!(a?); //~ ERROR no rules expected the token `?`
43-
baz!(a?a?a); //~ ERROR no rules expected the token `?`
44-
baz!(a?a); //~ ERROR no rules expected the token `?`
45-
baz!(a?); //~ ERROR no rules expected the token `?`
46-
baz!(a,); //~ ERROR unexpected end of macro invocation
47-
baz!(a?a?a,); //~ ERROR no rules expected the token `?`
48-
baz!(a?a,); //~ ERROR no rules expected the token `?`
49-
baz!(a?,); //~ ERROR no rules expected the token `?`
5039
barplus!(); //~ ERROR unexpected end of macro invocation
51-
barplus!(a?); //~ ERROR unexpected end of macro invocation
52-
barstar!(a?); //~ ERROR unexpected end of macro invocation
40+
barstar!(); //~ ERROR unexpected end of macro invocation
41+
barplus!(a?); //~ ERROR no rules expected the token `?`
42+
barplus!(a); //~ ERROR unexpected end of macro invocation
43+
barstar!(a?); //~ ERROR no rules expected the token `?`
44+
barstar!(a); //~ ERROR unexpected end of macro invocation
45+
barplus!(+); // ok
46+
barstar!(*); // ok
47+
barplus!(a+); // ok
48+
barstar!(a*); // ok
5349
}
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,62 @@
1+
error: `?` macro repetition does not allow a separator
2+
--> $DIR/macro-at-most-once-rep-ambig.rs:22:10
3+
|
4+
LL | ($(a),?) => {} //~ ERROR `?` macro repetition does not allow a separator
5+
| ^
6+
17
error: no rules expected the token `?`
2-
--> $DIR/macro-at-most-once-rep-ambig.rs:40:11
8+
--> $DIR/macro-at-most-once-rep-ambig.rs:36:11
39
|
410
LL | foo!(a?a?a); //~ ERROR no rules expected the token `?`
511
| ^
612

713
error: no rules expected the token `?`
8-
--> $DIR/macro-at-most-once-rep-ambig.rs:41:11
14+
--> $DIR/macro-at-most-once-rep-ambig.rs:37:11
915
|
1016
LL | foo!(a?a); //~ ERROR no rules expected the token `?`
1117
| ^
1218

1319
error: no rules expected the token `?`
14-
--> $DIR/macro-at-most-once-rep-ambig.rs:42:11
20+
--> $DIR/macro-at-most-once-rep-ambig.rs:38:11
1521
|
1622
LL | foo!(a?); //~ ERROR no rules expected the token `?`
1723
| ^
1824

19-
error: no rules expected the token `?`
20-
--> $DIR/macro-at-most-once-rep-ambig.rs:43:11
21-
|
22-
LL | baz!(a?a?a); //~ ERROR no rules expected the token `?`
23-
| ^
24-
25-
error: no rules expected the token `?`
26-
--> $DIR/macro-at-most-once-rep-ambig.rs:44:11
27-
|
28-
LL | baz!(a?a); //~ ERROR no rules expected the token `?`
29-
| ^
30-
31-
error: no rules expected the token `?`
32-
--> $DIR/macro-at-most-once-rep-ambig.rs:45:11
33-
|
34-
LL | baz!(a?); //~ ERROR no rules expected the token `?`
35-
| ^
36-
3725
error: unexpected end of macro invocation
38-
--> $DIR/macro-at-most-once-rep-ambig.rs:46:11
39-
|
40-
LL | baz!(a,); //~ ERROR unexpected end of macro invocation
41-
| ^
42-
43-
error: no rules expected the token `?`
44-
--> $DIR/macro-at-most-once-rep-ambig.rs:47:11
26+
--> $DIR/macro-at-most-once-rep-ambig.rs:39:5
4527
|
46-
LL | baz!(a?a?a,); //~ ERROR no rules expected the token `?`
47-
| ^
28+
LL | barplus!(); //~ ERROR unexpected end of macro invocation
29+
| ^^^^^^^^^^^
4830

49-
error: no rules expected the token `?`
50-
--> $DIR/macro-at-most-once-rep-ambig.rs:48:11
31+
error: unexpected end of macro invocation
32+
--> $DIR/macro-at-most-once-rep-ambig.rs:40:5
5133
|
52-
LL | baz!(a?a,); //~ ERROR no rules expected the token `?`
53-
| ^
34+
LL | barstar!(); //~ ERROR unexpected end of macro invocation
35+
| ^^^^^^^^^^^
5436

5537
error: no rules expected the token `?`
56-
--> $DIR/macro-at-most-once-rep-ambig.rs:49:11
38+
--> $DIR/macro-at-most-once-rep-ambig.rs:41:15
5739
|
58-
LL | baz!(a?,); //~ ERROR no rules expected the token `?`
59-
| ^
40+
LL | barplus!(a?); //~ ERROR no rules expected the token `?`
41+
| ^
6042

6143
error: unexpected end of macro invocation
62-
--> $DIR/macro-at-most-once-rep-ambig.rs:50:5
44+
--> $DIR/macro-at-most-once-rep-ambig.rs:42:14
6345
|
64-
LL | barplus!(); //~ ERROR unexpected end of macro invocation
65-
| ^^^^^^^^^^^
46+
LL | barplus!(a); //~ ERROR unexpected end of macro invocation
47+
| ^
6648

67-
error: unexpected end of macro invocation
68-
--> $DIR/macro-at-most-once-rep-ambig.rs:51:15
49+
error: no rules expected the token `?`
50+
--> $DIR/macro-at-most-once-rep-ambig.rs:43:15
6951
|
70-
LL | barplus!(a?); //~ ERROR unexpected end of macro invocation
52+
LL | barstar!(a?); //~ ERROR no rules expected the token `?`
7153
| ^
7254

7355
error: unexpected end of macro invocation
74-
--> $DIR/macro-at-most-once-rep-ambig.rs:52:15
56+
--> $DIR/macro-at-most-once-rep-ambig.rs:44:14
7557
|
76-
LL | barstar!(a?); //~ ERROR unexpected end of macro invocation
77-
| ^
58+
LL | barstar!(a); //~ ERROR unexpected end of macro invocation
59+
| ^
7860

79-
error: aborting due to 13 previous errors
61+
error: aborting due to 10 previous errors
8062

0 commit comments

Comments
 (0)