1- // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1+ // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
22// file at the top-level directory of this distribution and at
33// http://rust-lang.org/COPYRIGHT.
44//
88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11- use ast:: { Ident , TtDelimited , TtSequence , TtToken } ;
11+ use ast:: { TokenTree , TtDelimited , TtSequence , TtToken } ;
1212use ast;
1313use codemap:: { Span , DUMMY_SP } ;
1414use ext:: base:: { ExtCtxt , MacResult , SyntaxExtension } ;
@@ -19,8 +19,8 @@ use ext::tt::macro_parser::{parse, parse_or_else};
1919use parse:: lexer:: new_tt_reader;
2020use parse:: parser:: Parser ;
2121use 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 :: * ;
2424use parse:: token;
2525use print;
2626use ptr:: P ;
@@ -109,8 +109,8 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
109109}
110110
111111struct MacroRulesMacroExpander {
112- name : Ident ,
113- imported_from : Option < Ident > ,
112+ name : ast :: Ident ,
113+ imported_from : Option < ast :: Ident > ,
114114 lhses : Vec < Rc < NamedMatch > > ,
115115 rhses : Vec < Rc < NamedMatch > > ,
116116}
@@ -134,8 +134,8 @@ impl TTMacroExpander for MacroRulesMacroExpander {
134134/// Given `lhses` and `rhses`, this is the new macro we create
135135fn generic_extension < ' cx > ( cx : & ' cx ExtCtxt ,
136136 sp : Span ,
137- name : Ident ,
138- imported_from : Option < Ident > ,
137+ name : ast :: Ident ,
138+ imported_from : Option < ast :: Ident > ,
139139 arg : & [ ast:: TokenTree ] ,
140140 lhses : & [ Rc < NamedMatch > ] ,
141141 rhses : & [ Rc < NamedMatch > ] )
@@ -260,6 +260,10 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
260260 _ => cx. span_bug ( def. span , "wrong-structured lhs" )
261261 } ;
262262
263+ for lhs in lhses. iter ( ) {
264+ check_lhs_nt_follows ( cx, & * * lhs, def. span ) ;
265+ }
266+
263267 let rhses = match * argument_map[ rhs_nm] {
264268 MatchedSeq ( ref s, _) => /* FIXME (#2543) */ ( * s) . clone ( ) ,
265269 _ => cx. span_bug ( def. span , "wrong-structured rhs" )
@@ -274,3 +278,131 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
274278
275279 NormalTT ( exp, Some ( def. span ) )
276280}
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