1
- // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1
+ // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2
2
// file at the top-level directory of this distribution and at
3
3
// http://rust-lang.org/COPYRIGHT.
4
4
//
8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use ast:: { Ident , TtDelimited , TtSequence , TtToken } ;
11
+ use ast:: { TokenTree , TtDelimited , TtSequence , TtToken } ;
12
12
use ast;
13
13
use codemap:: { Span , DUMMY_SP } ;
14
14
use ext:: base:: { ExtCtxt , MacResult , SyntaxExtension } ;
@@ -19,8 +19,8 @@ use ext::tt::macro_parser::{parse, parse_or_else};
19
19
use parse:: lexer:: new_tt_reader;
20
20
use parse:: parser:: Parser ;
21
21
use parse:: attr:: ParserAttr ;
22
- use parse:: token:: { special_idents, gensym_ident} ;
23
- use parse:: token:: { MatchNt , NtTT } ;
22
+ use parse:: token:: { special_idents, gensym_ident, NtTT , Token } ;
23
+ use parse:: token:: Token :: * ;
24
24
use parse:: token;
25
25
use print;
26
26
use ptr:: P ;
@@ -109,8 +109,8 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
109
109
}
110
110
111
111
struct MacroRulesMacroExpander {
112
- name : Ident ,
113
- imported_from : Option < Ident > ,
112
+ name : ast :: Ident ,
113
+ imported_from : Option < ast :: Ident > ,
114
114
lhses : Vec < Rc < NamedMatch > > ,
115
115
rhses : Vec < Rc < NamedMatch > > ,
116
116
}
@@ -134,8 +134,8 @@ impl TTMacroExpander for MacroRulesMacroExpander {
134
134
/// Given `lhses` and `rhses`, this is the new macro we create
135
135
fn generic_extension < ' cx > ( cx : & ' cx ExtCtxt ,
136
136
sp : Span ,
137
- name : Ident ,
138
- imported_from : Option < Ident > ,
137
+ name : ast :: Ident ,
138
+ imported_from : Option < ast :: Ident > ,
139
139
arg : & [ ast:: TokenTree ] ,
140
140
lhses : & [ Rc < NamedMatch > ] ,
141
141
rhses : & [ Rc < NamedMatch > ] )
@@ -260,6 +260,10 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
260
260
_ => cx. span_bug ( def. span , "wrong-structured lhs" )
261
261
} ;
262
262
263
+ for lhs in lhses. iter ( ) {
264
+ check_lhs_nt_follows ( cx, & * * lhs, def. span ) ;
265
+ }
266
+
263
267
let rhses = match * argument_map[ rhs_nm] {
264
268
MatchedSeq ( ref s, _) => /* FIXME (#2543) */ ( * s) . clone ( ) ,
265
269
_ => cx. span_bug ( def. span , "wrong-structured rhs" )
@@ -274,3 +278,131 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
274
278
275
279
NormalTT ( exp, Some ( def. span ) )
276
280
}
281
+
282
+ fn check_lhs_nt_follows ( cx : & mut ExtCtxt , lhs : & NamedMatch , sp : Span ) {
283
+ // lhs is going to be like MatchedNonterminal(NtTT(TtDelimited(...))), where
284
+ // the entire lhs is those tts.
285
+ // if ever we get box/deref patterns, this could turn into an `if let
286
+ // &MatchedNonterminal(NtTT(box TtDelimited(...))) = lhs`
287
+ let matcher = match lhs {
288
+ & MatchedNonterminal ( NtTT ( ref inner) ) => match & * * inner {
289
+ & TtDelimited ( _, ref tts) => tts. tts [ ] ,
290
+ _ => cx. span_bug ( sp, "wrong-structured lhs for follow check" )
291
+ } ,
292
+ _ => cx. span_bug ( sp, "wrong-structured lhs for follow check" )
293
+ } ;
294
+
295
+ check_matcher ( cx, matcher, & Eof ) ;
296
+ // we don't abort on errors on rejection, the driver will do that for us
297
+ // after parsing/expansion. we can report every error in every macro this way.
298
+ }
299
+
300
+ fn check_matcher ( cx : & mut ExtCtxt , matcher : & [ TokenTree ] , follow : & Token ) {
301
+ use print:: pprust:: token_to_string;
302
+
303
+ // 1. If there are no tokens in M, accept
304
+ if matcher. is_empty ( ) {
305
+ return ;
306
+ }
307
+
308
+ // 2. For each token T in M:
309
+ let mut tokens = matcher. iter ( ) . peekable ( ) ;
310
+ while let Some ( token) = tokens. next ( ) {
311
+ match * token {
312
+ TtToken ( sp, MatchNt ( ref name, ref frag_spec, _, _) ) => {
313
+ // ii. If T is a simple NT, look ahead to the next token T' in
314
+ // M.
315
+ let next_token = match tokens. peek ( ) {
316
+ // 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 ,
322
+ // else, we're at the end of the macro or sequence
323
+ None => follow
324
+ } ;
325
+
326
+ // 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) ) [ ] )
334
+ }
335
+ } ,
336
+ TtSequence ( _, ref seq) => {
337
+ // iii. Else, T is a complex NT.
338
+ match seq. separator {
339
+ // If T has the form $(...)U+ or $(...)U* for some token U,
340
+ // run the algorithm on the contents with F set to U. If it
341
+ // 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 )
347
+ }
348
+ } ,
349
+ TtToken ( ..) => {
350
+ // i. If T is not an NT, continue.
351
+ continue
352
+ } ,
353
+ TtDelimited ( _, ref tts) => {
354
+ // 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 ( ) )
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ fn is_in_follow ( cx : & ExtCtxt , tok : & Token , frag : & str ) -> bool {
363
+ if let & CloseDelim ( _) = tok {
364
+ return true ;
365
+ }
366
+
367
+ match frag {
368
+ "item" => {
369
+ // since items *must* be followed by either a `;` or a `}`, we can
370
+ // accept anything after them
371
+ true
372
+ } ,
373
+ "block" => {
374
+ // anything can follow block, the braces provide a easy boundary to
375
+ // maintain
376
+ true
377
+ } ,
378
+ "stmt" | "expr" => {
379
+ match * tok {
380
+ Comma | Semi => true ,
381
+ _ => false
382
+ }
383
+ } ,
384
+ "pat" => {
385
+ match * tok {
386
+ FatArrow | Comma | Eq => true ,
387
+ _ => false
388
+ }
389
+ } ,
390
+ "path" | "ty" => {
391
+ match * tok {
392
+ Comma | RArrow | Colon | Eq | Gt => true ,
393
+ Ident ( i, _) if i. as_str ( ) == "as" => true ,
394
+ _ => false
395
+ }
396
+ } ,
397
+ "ident" => {
398
+ // being a single token, idents are harmless
399
+ true
400
+ } ,
401
+ "meta" | "tt" => {
402
+ // being either a single token or a delimited sequence, tt is
403
+ // harmless
404
+ true
405
+ } ,
406
+ _ => cx. bug ( format ! ( "unrecognized builtin nonterminal {}" , frag) [ ] ) ,
407
+ }
408
+ }
0 commit comments