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
// result in a "no rules expected token" error by virtue of this matcher not
// working.
if seq.op == quoted::KleeneOp::ZeroOrMore
|| seq.op == quoted::KleeneOp::ZeroOrOne
if seq.kleene.op == quoted::KleeneOp::ZeroOrMore
|| seq.kleene.op == quoted::KleeneOp::ZeroOrOne
{
let mut new_item = item.clone();
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 {
stack: smallvec![],
sep: seq.separator.clone(),
seq_op: Some(seq.op),
seq_op: Some(seq.kleene.op),
idx: 0,
matches,
match_lo: item.match_cur,

View file

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

View file

@ -50,11 +50,23 @@ pub struct SequenceRepetition {
/// The optional separator
pub separator: Option<Token>,
/// 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)
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)
/// for token sequences.
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@ -273,7 +285,7 @@ fn parse_tree(
macro_node_id,
);
// 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)
let name_captures = macro_parser::count_names(&sequence);
TokenTree::Sequence(
@ -281,7 +293,7 @@ fn parse_tree(
Lrc::new(SequenceRepetition {
tts: sequence,
separator,
op,
kleene,
num_captures: name_captures,
}),
)
@ -379,16 +391,16 @@ fn parse_sep_and_kleene_op(
input: &mut Peekable<impl Iterator<Item = tokenstream::TokenTree>>,
span: Span,
sess: &ParseSess,
) -> (Option<Token>, KleeneOp) {
) -> (Option<Token>, KleeneToken) {
// 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
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
Ok(Err(token)) => match parse_kleene_op(input, token.span) {
// #2 is the `?` Kleene op, which does not take a separator (error)
Ok(Ok((KleeneOp::ZeroOrOne, _))) => {
Ok(Ok((KleeneOp::ZeroOrOne, span))) => {
// Error!
sess.span_diagnostic.span_err(
token.span,
@ -396,11 +408,11 @@ fn parse_sep_and_kleene_op(
);
// Return a dummy
return (None, KleeneOp::ZeroOrMore);
return (None, KleeneToken::new(KleeneOp::ZeroOrMore, span));
}
// #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 :(
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 `?`");
// 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?
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
// time... It happens when the Kleene operator in the matcher and
// the body for the same meta-variable do not match.