Skip to content

Commit e6e60ef

Browse files
committed
Auto merge of #60932 - Centril:macro-at-most-once-2015, r=mark-i-m
Support ? Kleene macro operator in 2015 Closes #56668. See that issue for rationale and discussion. Crater will be needed (done in #60932 (comment), zero regressions) and then, if all goes well, FCP (in #60932 (comment)).
2 parents 57e13e0 + 3ba82f7 commit e6e60ef

16 files changed

+281
-321
lines changed

src/libsyntax/ext/tt/quoted.rs

+15-189
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::ast::NodeId;
2-
use crate::early_buffered_lints::BufferedEarlyLintId;
32
use crate::ext::tt::macro_parser;
43
use crate::feature_gate::Features;
54
use crate::parse::token::{self, Token, TokenKind};
@@ -250,19 +249,16 @@ pub fn parse(
250249
/// - `sess`: the parsing session. Any errors will be emitted to this session.
251250
/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use
252251
/// unstable features or not.
253-
fn parse_tree<I>(
252+
fn parse_tree(
254253
tree: tokenstream::TokenTree,
255-
trees: &mut Peekable<I>,
254+
trees: &mut Peekable<impl Iterator<Item = tokenstream::TokenTree>>,
256255
expect_matchers: bool,
257256
sess: &ParseSess,
258257
features: &Features,
259258
attrs: &[ast::Attribute],
260259
edition: Edition,
261260
macro_node_id: NodeId,
262-
) -> TokenTree
263-
where
264-
I: Iterator<Item = tokenstream::TokenTree>,
265-
{
261+
) -> TokenTree {
266262
// Depending on what `tree` is, we could be parsing different parts of a macro
267263
match tree {
268264
// `tree` is a `$` token. Look at the next token in `trees`
@@ -287,16 +283,7 @@ where
287283
macro_node_id,
288284
);
289285
// Get the Kleene operator and optional separator
290-
let (separator, op) =
291-
parse_sep_and_kleene_op(
292-
trees,
293-
span.entire(),
294-
sess,
295-
features,
296-
attrs,
297-
edition,
298-
macro_node_id,
299-
);
286+
let (separator, op) = parse_sep_and_kleene_op(trees, span.entire(), sess);
300287
// Count the number of captured "names" (i.e., named metavars)
301288
let name_captures = macro_parser::count_names(&sequence);
302289
TokenTree::Sequence(
@@ -375,10 +362,10 @@ fn kleene_op(token: &Token) -> Option<KleeneOp> {
375362
/// - Ok(Ok((op, span))) if the next token tree is a KleeneOp
376363
/// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp
377364
/// - Err(span) if the next token tree is not a token
378-
fn parse_kleene_op<I>(input: &mut I, span: Span) -> Result<Result<(KleeneOp, Span), Token>, Span>
379-
where
380-
I: Iterator<Item = tokenstream::TokenTree>,
381-
{
365+
fn parse_kleene_op(
366+
input: &mut impl Iterator<Item = tokenstream::TokenTree>,
367+
span: Span,
368+
) -> Result<Result<(KleeneOp, Span), Token>, Span> {
382369
match input.next() {
383370
Some(tokenstream::TokenTree::Token(token)) => match kleene_op(&token) {
384371
Some(op) => Ok(Ok((op, token.span))),
@@ -403,178 +390,20 @@ where
403390
/// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene
404391
/// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an
405392
/// error with the appropriate span is emitted to `sess` and a dummy value is returned.
406-
///
407-
/// N.B., in the 2015 edition, `*` and `+` are the only Kleene operators, and `?` is a separator.
408-
/// In the 2018 edition however, `?` is a Kleene operator, and not a separator.
409-
fn parse_sep_and_kleene_op<I>(
410-
input: &mut Peekable<I>,
411-
span: Span,
412-
sess: &ParseSess,
413-
features: &Features,
414-
attrs: &[ast::Attribute],
415-
edition: Edition,
416-
macro_node_id: NodeId,
417-
) -> (Option<Token>, KleeneOp)
418-
where
419-
I: Iterator<Item = tokenstream::TokenTree>,
420-
{
421-
match edition {
422-
Edition::Edition2015 => parse_sep_and_kleene_op_2015(
423-
input,
424-
span,
425-
sess,
426-
features,
427-
attrs,
428-
macro_node_id,
429-
),
430-
Edition::Edition2018 => parse_sep_and_kleene_op_2018(input, span, sess, features, attrs),
431-
}
432-
}
433-
434-
// `?` is a separator (with a migration warning) and never a KleeneOp.
435-
fn parse_sep_and_kleene_op_2015<I>(
436-
input: &mut Peekable<I>,
437-
span: Span,
438-
sess: &ParseSess,
439-
_features: &Features,
440-
_attrs: &[ast::Attribute],
441-
macro_node_id: NodeId,
442-
) -> (Option<Token>, KleeneOp)
443-
where
444-
I: Iterator<Item = tokenstream::TokenTree>,
445-
{
446-
// We basically look at two token trees here, denoted as #1 and #2 below
447-
let span = match parse_kleene_op(input, span) {
448-
// #1 is a `+` or `*` KleeneOp
449-
//
450-
// `?` is ambiguous: it could be a separator (warning) or a Kleene::ZeroOrOne (error), so
451-
// we need to look ahead one more token to be sure.
452-
Ok(Ok((op, _))) if op != KleeneOp::ZeroOrOne => return (None, op),
453-
454-
// #1 is `?` token, but it could be a Kleene::ZeroOrOne (error in 2015) without a separator
455-
// or it could be a `?` separator followed by any Kleene operator. We need to look ahead 1
456-
// token to find out which.
457-
Ok(Ok((op, op1_span))) => {
458-
assert_eq!(op, KleeneOp::ZeroOrOne);
459-
460-
// Lookahead at #2. If it is a KleenOp, then #1 is a separator.
461-
let is_1_sep = if let Some(tokenstream::TokenTree::Token(tok2)) = input.peek() {
462-
kleene_op(tok2).is_some()
463-
} else {
464-
false
465-
};
466-
467-
if is_1_sep {
468-
// #1 is a separator and #2 should be a KleepeOp.
469-
// (N.B. We need to advance the input iterator.)
470-
match parse_kleene_op(input, span) {
471-
// #2 is `?`, which is not allowed as a Kleene op in 2015 edition,
472-
// but is allowed in the 2018 edition.
473-
Ok(Ok((op, op2_span))) if op == KleeneOp::ZeroOrOne => {
474-
sess.span_diagnostic
475-
.struct_span_err(op2_span, "expected `*` or `+`")
476-
.note("`?` is not a macro repetition operator in the 2015 edition, \
477-
but is accepted in the 2018 edition")
478-
.emit();
479-
480-
// Return a dummy
481-
return (None, KleeneOp::ZeroOrMore);
482-
}
483-
484-
// #2 is a Kleene op, which is the only valid option
485-
Ok(Ok((op, _))) => {
486-
// Warn that `?` as a separator will be deprecated
487-
sess.buffer_lint(
488-
BufferedEarlyLintId::QuestionMarkMacroSep,
489-
op1_span,
490-
macro_node_id,
491-
"using `?` as a separator is deprecated and will be \
492-
a hard error in an upcoming edition",
493-
);
494-
495-
return (Some(Token::new(token::Question, op1_span)), op);
496-
}
497-
498-
// #2 is a random token (this is an error) :(
499-
Ok(Err(_)) => op1_span,
500-
501-
// #2 is not even a token at all :(
502-
Err(_) => op1_span,
503-
}
504-
} else {
505-
// `?` is not allowed as a Kleene op in 2015,
506-
// but is allowed in the 2018 edition
507-
sess.span_diagnostic
508-
.struct_span_err(op1_span, "expected `*` or `+`")
509-
.note("`?` is not a macro repetition operator in the 2015 edition, \
510-
but is accepted in the 2018 edition")
511-
.emit();
512-
513-
// Return a dummy
514-
return (None, KleeneOp::ZeroOrMore);
515-
}
516-
}
517-
518-
// #1 is a separator followed by #2, a KleeneOp
519-
Ok(Err(token)) => match parse_kleene_op(input, token.span) {
520-
// #2 is a `?`, which is not allowed as a Kleene op in 2015 edition,
521-
// but is allowed in the 2018 edition
522-
Ok(Ok((op, op2_span))) if op == KleeneOp::ZeroOrOne => {
523-
sess.span_diagnostic
524-
.struct_span_err(op2_span, "expected `*` or `+`")
525-
.note("`?` is not a macro repetition operator in the 2015 edition, \
526-
but is accepted in the 2018 edition")
527-
.emit();
528-
529-
// Return a dummy
530-
return (None, KleeneOp::ZeroOrMore);
531-
}
532-
533-
// #2 is a KleeneOp :D
534-
Ok(Ok((op, _))) => return (Some(token), op),
535-
536-
// #2 is a random token :(
537-
Ok(Err(token)) => token.span,
538-
539-
// #2 is not a token at all :(
540-
Err(span) => span,
541-
},
542-
543-
// #1 is not a token
544-
Err(span) => span,
545-
};
546-
547-
sess.span_diagnostic.span_err(span, "expected `*` or `+`");
548-
549-
// Return a dummy
550-
(None, KleeneOp::ZeroOrMore)
551-
}
552-
553-
// `?` is a Kleene op, not a separator
554-
fn parse_sep_and_kleene_op_2018<I>(
555-
input: &mut Peekable<I>,
393+
fn parse_sep_and_kleene_op(
394+
input: &mut Peekable<impl Iterator<Item = tokenstream::TokenTree>>,
556395
span: Span,
557396
sess: &ParseSess,
558-
_features: &Features,
559-
_attrs: &[ast::Attribute],
560-
) -> (Option<Token>, KleeneOp)
561-
where
562-
I: Iterator<Item = tokenstream::TokenTree>,
563-
{
397+
) -> (Option<Token>, KleeneOp) {
564398
// We basically look at two token trees here, denoted as #1 and #2 below
565399
let span = match parse_kleene_op(input, span) {
566-
// #1 is a `?` (needs feature gate)
567-
Ok(Ok((op, _op1_span))) if op == KleeneOp::ZeroOrOne => {
568-
return (None, op);
569-
}
570-
571-
// #1 is a `+` or `*` KleeneOp
400+
// #1 is a `?`, `+`, or `*` KleeneOp
572401
Ok(Ok((op, _))) => return (None, op),
573402

574403
// #1 is a separator followed by #2, a KleeneOp
575404
Ok(Err(token)) => match parse_kleene_op(input, token.span) {
576405
// #2 is the `?` Kleene op, which does not take a separator (error)
577-
Ok(Ok((op, _op2_span))) if op == KleeneOp::ZeroOrOne => {
406+
Ok(Ok((KleeneOp::ZeroOrOne, _))) => {
578407
// Error!
579408
sess.span_diagnostic.span_err(
580409
token.span,
@@ -588,11 +417,8 @@ where
588417
// #2 is a KleeneOp :D
589418
Ok(Ok((op, _))) => return (Some(token), op),
590419

591-
// #2 is a random token :(
592-
Ok(Err(token)) => token.span,
593-
594-
// #2 is not a token at all :(
595-
Err(span) => span,
420+
// #2 is a random token or not a token at all :(
421+
Ok(Err(Token { span, .. })) | Err(span) => span,
596422
},
597423

598424
// #1 is not a token
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// run-pass
2+
3+
#![allow(unused_mut)]
4+
5+
// Check that when `?` is followed by what looks like a Kleene operator (?, +, and *)
6+
// then that `?` is not interpreted as a separator. In other words, `$(pat)?+` matches `pat +`
7+
// or `+` but does not match `pat` or `pat ? pat`.
8+
9+
// edition:2015
10+
11+
macro_rules! foo {
12+
// Check for `?`.
13+
($($a:ident)? ? $num:expr) => {
14+
foo!($($a)? ; $num);
15+
};
16+
// Check for `+`.
17+
($($a:ident)? + $num:expr) => {
18+
foo!($($a)? ; $num);
19+
};
20+
// Check for `*`.
21+
($($a:ident)? * $num:expr) => {
22+
foo!($($a)? ; $num);
23+
};
24+
// Check for `;`, not a kleene operator.
25+
($($a:ident)? ; $num:expr) => {
26+
let mut x = 0;
27+
28+
$(
29+
x += $a;
30+
)?
31+
32+
assert_eq!(x, $num);
33+
};
34+
}
35+
36+
pub fn main() {
37+
let a = 1;
38+
39+
// Accept 0 repetitions.
40+
foo!( ; 0);
41+
foo!( + 0);
42+
foo!( * 0);
43+
foo!( ? 0);
44+
45+
// Accept 1 repetition.
46+
foo!(a ; 1);
47+
foo!(a + 1);
48+
foo!(a * 1);
49+
foo!(a ? 1);
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// run-pass
2+
3+
#![allow(unused_mut)]
4+
5+
// Check that when `?` is followed by what looks like a Kleene operator (?, +, and *)
6+
// then that `?` is not interpreted as a separator. In other words, `$(pat)?+` matches `pat +`
7+
// or `+` but does not match `pat` or `pat ? pat`.
8+
9+
// edition:2018
10+
11+
macro_rules! foo {
12+
// Check for `?`.
13+
($($a:ident)? ? $num:expr) => {
14+
foo!($($a)? ; $num);
15+
};
16+
// Check for `+`.
17+
($($a:ident)? + $num:expr) => {
18+
foo!($($a)? ; $num);
19+
};
20+
// Check for `*`.
21+
($($a:ident)? * $num:expr) => {
22+
foo!($($a)? ; $num);
23+
};
24+
// Check for `;`, not a kleene operator.
25+
($($a:ident)? ; $num:expr) => {
26+
let mut x = 0;
27+
28+
$(
29+
x += $a;
30+
)?
31+
32+
assert_eq!(x, $num);
33+
};
34+
}
35+
36+
pub fn main() {
37+
let a = 1;
38+
39+
// Accept 0 repetitions.
40+
foo!( ; 0);
41+
foo!( + 0);
42+
foo!( * 0);
43+
foo!( ? 0);
44+
45+
// Accept 1 repetition.
46+
foo!(a ; 1);
47+
foo!(a + 1);
48+
foo!(a * 1);
49+
foo!(a ? 1);
50+
}

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

-33
This file was deleted.

0 commit comments

Comments
 (0)