1
Fork 0

Remember the span of the Kleene operator in macros

This is needed for having complete error messages where reporting macro variable
errors. Here is what they would look like:

error: meta-variable repeats with different kleene operator
  --> $DIR/issue-61053-different-kleene.rs:3:57
   |
LL |     ( $( $i:ident = $($j:ident),+ );* ) => { $( $( $i = $j; )* )* };
   |                                 - expected repetition   ^^   - conflicting repetition
This commit is contained in:
Julien Cretin 2019-05-29 20:29:51 +02:00
parent 527dce7137
commit 82abc0db81
4 changed files with 33 additions and 21 deletions

View file

@ -557,8 +557,8 @@ fn inner_parse_loop<'root, 'tt>(
// implicitly disallowing OneOrMore from having 0 matches here. Thus, that will // implicitly disallowing OneOrMore from having 0 matches here. Thus, that will
// result in a "no rules expected token" error by virtue of this matcher not // result in a "no rules expected token" error by virtue of this matcher not
// working. // working.
if seq.op == quoted::KleeneOp::ZeroOrMore if seq.kleene.op == quoted::KleeneOp::ZeroOrMore
|| seq.op == quoted::KleeneOp::ZeroOrOne || seq.kleene.op == quoted::KleeneOp::ZeroOrOne
{ {
let mut new_item = item.clone(); let mut new_item = item.clone();
new_item.match_cur += seq.num_captures; new_item.match_cur += seq.num_captures;
@ -573,7 +573,7 @@ fn inner_parse_loop<'root, 'tt>(
cur_items.push(MatcherPosHandle::Box(Box::new(MatcherPos { cur_items.push(MatcherPosHandle::Box(Box::new(MatcherPos {
stack: smallvec![], stack: smallvec![],
sep: seq.separator.clone(), sep: seq.separator.clone(),
seq_op: Some(seq.op), seq_op: Some(seq.kleene.op),
idx: 0, idx: 0,
matches, matches,
match_lo: item.match_cur, match_lo: item.match_cur,

View file

@ -273,7 +273,7 @@ pub fn compile(
if body.legacy { token::Semi } else { token::Comma }, if body.legacy { token::Semi } else { token::Comma },
def.span, def.span,
)), )),
op: quoted::KleeneOp::OneOrMore, kleene: quoted::KleeneToken::new(quoted::KleeneOp::OneOrMore, def.span),
num_captures: 2, num_captures: 2,
}), }),
), ),
@ -283,7 +283,7 @@ pub fn compile(
Lrc::new(quoted::SequenceRepetition { Lrc::new(quoted::SequenceRepetition {
tts: vec![quoted::TokenTree::token(token::Semi, def.span)], tts: vec![quoted::TokenTree::token(token::Semi, def.span)],
separator: None, separator: None,
op: quoted::KleeneOp::ZeroOrMore, kleene: quoted::KleeneToken::new(quoted::KleeneOp::ZeroOrMore, def.span),
num_captures: 0, num_captures: 0,
}), }),
), ),
@ -477,8 +477,8 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
&& seq.tts.iter().all(|seq_tt| match *seq_tt { && seq.tts.iter().all(|seq_tt| match *seq_tt {
TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis, TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis,
TokenTree::Sequence(_, ref sub_seq) => { TokenTree::Sequence(_, ref sub_seq) => {
sub_seq.op == quoted::KleeneOp::ZeroOrMore sub_seq.kleene.op == quoted::KleeneOp::ZeroOrMore
|| sub_seq.op == quoted::KleeneOp::ZeroOrOne || sub_seq.kleene.op == quoted::KleeneOp::ZeroOrOne
} }
_ => false, _ => false,
}) })
@ -628,8 +628,8 @@ impl FirstSets {
// Reverse scan: Sequence comes before `first`. // Reverse scan: Sequence comes before `first`.
if subfirst.maybe_empty if subfirst.maybe_empty
|| seq_rep.op == quoted::KleeneOp::ZeroOrMore || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore
|| seq_rep.op == quoted::KleeneOp::ZeroOrOne || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne
{ {
// If sequence is potentially empty, then // If sequence is potentially empty, then
// union them (preserving first emptiness). // union them (preserving first emptiness).
@ -677,8 +677,8 @@ impl FirstSets {
assert!(first.maybe_empty); assert!(first.maybe_empty);
first.add_all(subfirst); first.add_all(subfirst);
if subfirst.maybe_empty if subfirst.maybe_empty
|| seq_rep.op == quoted::KleeneOp::ZeroOrMore || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore
|| seq_rep.op == quoted::KleeneOp::ZeroOrOne || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne
{ {
// continue scanning for more first // continue scanning for more first
// tokens, but also make sure we // tokens, but also make sure we

View file

@ -50,11 +50,23 @@ pub struct SequenceRepetition {
/// The optional separator /// The optional separator
pub separator: Option<Token>, pub separator: Option<Token>,
/// Whether the sequence can be repeated zero (*), or one or more times (+) /// Whether the sequence can be repeated zero (*), or one or more times (+)
pub op: KleeneOp, pub kleene: KleeneToken,
/// The number of `Match`s that appear in the sequence (and subsequences) /// The number of `Match`s that appear in the sequence (and subsequences)
pub num_captures: usize, pub num_captures: usize,
} }
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)]
pub struct KleeneToken {
pub span: Span,
pub op: KleeneOp,
}
impl KleeneToken {
pub fn new(op: KleeneOp, span: Span) -> KleeneToken {
KleeneToken { span, op }
}
}
/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star) /// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
/// for token sequences. /// for token sequences.
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@ -273,7 +285,7 @@ fn parse_tree(
macro_node_id, macro_node_id,
); );
// Get the Kleene operator and optional separator // Get the Kleene operator and optional separator
let (separator, op) = parse_sep_and_kleene_op(trees, span.entire(), sess); let (separator, kleene) = parse_sep_and_kleene_op(trees, span.entire(), sess);
// Count the number of captured "names" (i.e., named metavars) // Count the number of captured "names" (i.e., named metavars)
let name_captures = macro_parser::count_names(&sequence); let name_captures = macro_parser::count_names(&sequence);
TokenTree::Sequence( TokenTree::Sequence(
@ -281,7 +293,7 @@ fn parse_tree(
Lrc::new(SequenceRepetition { Lrc::new(SequenceRepetition {
tts: sequence, tts: sequence,
separator, separator,
op, kleene,
num_captures: name_captures, num_captures: name_captures,
}), }),
) )
@ -379,16 +391,16 @@ fn parse_sep_and_kleene_op(
input: &mut Peekable<impl Iterator<Item = tokenstream::TokenTree>>, input: &mut Peekable<impl Iterator<Item = tokenstream::TokenTree>>,
span: Span, span: Span,
sess: &ParseSess, sess: &ParseSess,
) -> (Option<Token>, KleeneOp) { ) -> (Option<Token>, KleeneToken) {
// 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 a `?`, `+`, or `*` KleeneOp
Ok(Ok((op, _))) => return (None, op), Ok(Ok((op, span))) => return (None, KleeneToken::new(op, span)),
// #1 is a separator followed by #2, a KleeneOp // #1 is a separator followed by #2, a KleeneOp
Ok(Err(token)) => match parse_kleene_op(input, token.span) { Ok(Err(token)) => match parse_kleene_op(input, token.span) {
// #2 is the `?` Kleene op, which does not take a separator (error) // #2 is the `?` Kleene op, which does not take a separator (error)
Ok(Ok((KleeneOp::ZeroOrOne, _))) => { Ok(Ok((KleeneOp::ZeroOrOne, span))) => {
// Error! // Error!
sess.span_diagnostic.span_err( sess.span_diagnostic.span_err(
token.span, token.span,
@ -396,11 +408,11 @@ fn parse_sep_and_kleene_op(
); );
// Return a dummy // Return a dummy
return (None, KleeneOp::ZeroOrMore); return (None, KleeneToken::new(KleeneOp::ZeroOrMore, span));
} }
// #2 is a KleeneOp :D // #2 is a KleeneOp :D
Ok(Ok((op, _))) => return (Some(token), op), Ok(Ok((op, span))) => return (Some(token), KleeneToken::new(op, span)),
// #2 is a random token or not a token at all :( // #2 is a random token or not a token at all :(
Ok(Err(Token { span, .. })) | Err(span) => span, Ok(Err(Token { span, .. })) | Err(span) => span,
@ -414,5 +426,5 @@ fn parse_sep_and_kleene_op(
sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`"); sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`");
// Return a dummy // Return a dummy
(None, KleeneOp::ZeroOrMore) (None, KleeneToken::new(KleeneOp::ZeroOrMore, span))
} }

View file

@ -183,7 +183,7 @@ pub fn transcribe(
// Is the repetition empty? // Is the repetition empty?
if len == 0 { if len == 0 {
if seq.op == quoted::KleeneOp::OneOrMore { if seq.kleene.op == quoted::KleeneOp::OneOrMore {
// FIXME: this really ought to be caught at macro definition // FIXME: this really ought to be caught at macro definition
// time... It happens when the Kleene operator in the matcher and // time... It happens when the Kleene operator in the matcher and
// the body for the same meta-variable do not match. // the body for the same meta-variable do not match.