@@ -292,58 +292,102 @@ fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) {
292
292
_ => cx. span_bug ( sp, "wrong-structured lhs for follow check" )
293
293
} ;
294
294
295
- check_matcher ( cx, matcher, & Eof ) ;
295
+ check_matcher ( cx, matcher. iter ( ) , & Eof ) ;
296
296
// we don't abort on errors on rejection, the driver will do that for us
297
297
// after parsing/expansion. we can report every error in every macro this way.
298
298
}
299
299
300
- fn check_matcher ( cx : & mut ExtCtxt , matcher : & [ TokenTree ] , follow : & Token ) {
300
+ // returns the last token that was checked, for TtSequence. this gets used later on.
301
+ fn check_matcher < ' a , I > ( cx : & mut ExtCtxt , matcher : I , follow : & Token )
302
+ -> Option < ( Span , Token ) > where I : Iterator < Item =& ' a TokenTree > {
301
303
use print:: pprust:: token_to_string;
302
304
303
- // 1. If there are no tokens in M, accept
304
- if matcher. is_empty ( ) {
305
- return ;
306
- }
305
+ let mut last = None ;
307
306
308
307
// 2. For each token T in M:
309
- let mut tokens = matcher. iter ( ) . peekable ( ) ;
308
+ let mut tokens = matcher. peekable ( ) ;
310
309
while let Some ( token) = tokens. next ( ) {
311
- match * token {
310
+ last = match * token {
312
311
TtToken ( sp, MatchNt ( ref name, ref frag_spec, _, _) ) => {
313
312
// ii. If T is a simple NT, look ahead to the next token T' in
314
313
// M.
315
314
let next_token = match tokens. peek ( ) {
316
315
// If T' closes a complex NT, replace T' with F
317
- Some ( & & TtToken ( _, CloseDelim ( _) ) ) => follow,
318
- Some ( & & TtToken ( _, ref tok) ) => tok,
319
- // T' is any NT (this catches complex NTs, the next
320
- // iteration will die if it's a TtDelimited).
321
- Some ( _) => continue ,
316
+ Some ( & & TtToken ( _, CloseDelim ( _) ) ) => follow. clone ( ) ,
317
+ Some ( & & TtToken ( _, ref tok) ) => tok. clone ( ) ,
318
+ Some ( & & TtSequence ( sp, _) ) => {
319
+ cx. span_err ( sp, format ! ( "`${0}:{1}` is followed by a sequence \
320
+ repetition, which is not allowed for `{1}` \
321
+ fragments", name. as_str( ) , frag_spec. as_str( ) ) [ ] ) ;
322
+ Eof
323
+ } ,
324
+ // die next iteration
325
+ Some ( & & TtDelimited ( _, ref delim) ) => delim. close_token ( ) ,
322
326
// else, we're at the end of the macro or sequence
323
- None => follow
327
+ None => follow. clone ( )
324
328
} ;
325
329
330
+ let tok = if let TtToken ( _, ref tok) = * token { tok } else { unreachable ! ( ) } ;
326
331
// If T' is in the set FOLLOW(NT), continue. Else, reject.
327
- match * next_token {
328
- Eof | MatchNt ( ..) => continue ,
329
- _ if is_in_follow ( cx, next_token, frag_spec. as_str ( ) ) => continue ,
330
- ref tok => cx. span_err ( sp, format ! ( "`${0}:{1}` is followed by `{2}`, which \
331
- is not allowed for `{1}` fragments",
332
- name. as_str( ) , frag_spec. as_str( ) ,
333
- token_to_string( tok) ) [ ] )
332
+ match & next_token {
333
+ & Eof => return Some ( ( sp, tok. clone ( ) ) ) ,
334
+ _ if is_in_follow ( cx, & next_token, frag_spec. as_str ( ) ) => continue ,
335
+ next => {
336
+ cx. span_err ( sp, format ! ( "`${0}:{1}` is followed by `{2}`, which \
337
+ is not allowed for `{1}` fragments",
338
+ name. as_str( ) , frag_spec. as_str( ) ,
339
+ token_to_string( next) ) [ ] ) ;
340
+ continue
341
+ } ,
334
342
}
335
343
} ,
336
- TtSequence ( _ , ref seq) => {
344
+ TtSequence ( sp , ref seq) => {
337
345
// iii. Else, T is a complex NT.
338
346
match seq. separator {
339
347
// If T has the form $(...)U+ or $(...)U* for some token U,
340
348
// run the algorithm on the contents with F set to U. If it
341
349
// accepts, continue, else, reject.
342
- Some ( ref u) => check_matcher ( cx, seq. tts [ ] , u) ,
343
- // If T has the form $(...)+ or $(...)*, run the algorithm
344
- // on the contents with F set to EOF. If it accepts,
345
- // continue, else, reject.
346
- None => check_matcher ( cx, seq. tts [ ] , & Eof )
350
+ Some ( ref u) => {
351
+ let last = check_matcher ( cx, seq. tts . iter ( ) , u) ;
352
+ match last {
353
+ // Since the delimiter isn't required after the last repetition, make
354
+ // sure that the *next* token is sane. This doesn't actually compute
355
+ // the FIRST of the rest of the matcher yet, it only considers single
356
+ // tokens and simple NTs. This is imprecise, but conservatively
357
+ // correct.
358
+ Some ( ( span, tok) ) => {
359
+ let fol = match tokens. peek ( ) {
360
+ Some ( & & TtToken ( _, ref tok) ) => tok. clone ( ) ,
361
+ Some ( & & TtDelimited ( _, ref delim) ) => delim. close_token ( ) ,
362
+ Some ( _) => {
363
+ cx. span_err ( sp, "sequence repetition followed by \
364
+ another sequence repetition, which is not allowed") ;
365
+ Eof
366
+ } ,
367
+ None => Eof
368
+ } ;
369
+ check_matcher ( cx, Some ( & TtToken ( span, tok. clone ( ) ) ) . into_iter ( ) ,
370
+ & fol)
371
+ } ,
372
+ None => last,
373
+ }
374
+ } ,
375
+ // If T has the form $(...)+ or $(...)*, run the algorithm on the contents with
376
+ // F set to the token following the sequence. If it accepts, continue, else,
377
+ // reject.
378
+ None => {
379
+ let fol = match tokens. peek ( ) {
380
+ Some ( & & TtToken ( _, ref tok) ) => tok. clone ( ) ,
381
+ Some ( & & TtDelimited ( _, ref delim) ) => delim. close_token ( ) ,
382
+ Some ( _) => {
383
+ cx. span_err ( sp, "sequence repetition followed by another \
384
+ sequence repetition, which is not allowed") ;
385
+ Eof
386
+ } ,
387
+ None => Eof
388
+ } ;
389
+ check_matcher ( cx, seq. tts . iter ( ) , & fol)
390
+ }
347
391
}
348
392
} ,
349
393
TtToken ( ..) => {
@@ -352,11 +396,12 @@ fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree], follow: &Token) {
352
396
} ,
353
397
TtDelimited ( _, ref tts) => {
354
398
// if we don't pass in that close delimiter, we'll incorrectly consider the matcher
355
- // `{ $foo:ty }` as having a follow that isn't `} `
356
- check_matcher ( cx, tts. tts [ ] , & tts. close_token ( ) )
399
+ // `{ $foo:ty }` as having a follow that isn't `RBrace `
400
+ check_matcher ( cx, tts. tts . iter ( ) , & tts. close_token ( ) )
357
401
}
358
402
}
359
403
}
404
+ last
360
405
}
361
406
362
407
fn is_in_follow ( cx : & ExtCtxt , tok : & Token , frag : & str ) -> bool {
0 commit comments