@@ -299,7 +299,7 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
299299 } ;
300300
301301 for lhs in & lhses {
302- check_lhs_nt_follows ( cx, lhs, def . span ) ;
302+ valid &= check_lhs_nt_follows ( cx, lhs) ;
303303 }
304304
305305 let rhses = match * * argument_map. get ( & rhs_nm. name ) . unwrap ( ) {
@@ -330,19 +330,19 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
330330// why is this here? because of https://github.com/rust-lang/rust/issues/27774
331331fn ref_slice < A > ( s : & A ) -> & [ A ] { use std:: slice:: from_raw_parts; unsafe { from_raw_parts ( s, 1 ) } }
332332
333- fn check_lhs_nt_follows ( cx : & mut ExtCtxt , lhs : & TokenTree , sp : Span ) {
333+ fn check_lhs_nt_follows ( cx : & mut ExtCtxt , lhs : & TokenTree ) -> bool {
334334 // lhs is going to be like TokenTree::Delimited(...), where the
335335 // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
336336 match lhs {
337- & TokenTree :: Delimited ( _, ref tts) => {
338- check_matcher ( cx, & tts . tts ) ;
339- } ,
340- tt @ & TokenTree :: Sequence ( .. ) => {
341- check_matcher ( cx , ref_slice ( tt ) ) ;
342- } ,
343- _ => cx . span_err ( sp , "invalid macro matcher; matchers must be contained \
344- in balanced delimiters or a repetition indicator" )
345- } ;
337+ & TokenTree :: Delimited ( _, ref tts) => check_matcher ( cx , & tts . tts ) ,
338+ tt @ & TokenTree :: Sequence ( .. ) => check_matcher ( cx, ref_slice ( tt ) ) ,
339+ _ => {
340+ cx . span_err ( lhs . get_span ( ) ,
341+ "invalid macro matcher; matchers must be contained \
342+ in balanced delimiters or a repetition indicator" ) ;
343+ false
344+ }
345+ }
346346 // we don't abort on errors on rejection, the driver will do that for us
347347 // after parsing/expansion. we can report every error in every macro this way.
348348}
@@ -364,28 +364,33 @@ struct OnFail {
364364 action : OnFailAction ,
365365}
366366
367- #[ derive( Copy , Clone , Debug ) ]
367+ #[ derive( Copy , Clone , Debug , PartialEq ) ]
368368enum OnFailAction { Warn , Error , DoNothing }
369369
370370impl OnFail {
371371 fn warn ( ) -> OnFail { OnFail { saw_failure : false , action : OnFailAction :: Warn } }
372372 fn error ( ) -> OnFail { OnFail { saw_failure : false , action : OnFailAction :: Error } }
373373 fn do_nothing ( ) -> OnFail { OnFail { saw_failure : false , action : OnFailAction :: DoNothing } }
374- fn react ( & mut self , cx : & mut ExtCtxt , sp : Span , msg : & str ) {
374+ fn react ( & mut self , cx : & mut ExtCtxt , sp : Span , msg : & str , help : Option < & str > ) {
375375 match self . action {
376376 OnFailAction :: DoNothing => { }
377- OnFailAction :: Error => cx. span_err ( sp, msg) ,
377+ OnFailAction :: Error => {
378+ let mut err = cx. struct_span_err ( sp, msg) ;
379+ if let Some ( msg) = help { err. span_help ( sp, msg) ; }
380+ err. emit ( ) ;
381+ }
378382 OnFailAction :: Warn => {
379- cx. struct_span_warn ( sp, msg)
380- . span_note ( sp, "The above warning will be a hard error in the next release." )
383+ let mut warn = cx. struct_span_warn ( sp, msg) ;
384+ if let Some ( msg) = help { warn. span_help ( sp, msg) ; }
385+ warn. span_note ( sp, "The above warning will be a hard error in the next release." )
381386 . emit ( ) ;
382387 }
383388 } ;
384389 self . saw_failure = true ;
385390 }
386391}
387392
388- fn check_matcher ( cx : & mut ExtCtxt , matcher : & [ TokenTree ] ) {
393+ fn check_matcher ( cx : & mut ExtCtxt , matcher : & [ TokenTree ] ) -> bool {
389394 // Issue 30450: when we are through a warning cycle, we can just
390395 // error on all failure conditions (and remove check_matcher_old).
391396
@@ -400,6 +405,9 @@ fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree]) {
400405 OnFail :: warn ( )
401406 } ;
402407 check_matcher_new ( cx, matcher, & mut on_fail) ;
408+ // matcher is valid if the new pass didn't see any error,
409+ // or if errors were considered warnings
410+ on_fail. action != OnFailAction :: Error || !on_fail. saw_failure
403411}
404412
405413// returns the last token that was checked, for TokenTree::Sequence.
@@ -435,11 +443,11 @@ fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fai
435443 // sequence, which may itself be a sequence,
436444 // and so on).
437445 on_fail. react ( cx, sp,
438- & format ! ( "`${0}:{1}` is followed by a \
439- sequence repetition, which is not \
440- allowed for `{1}` fragments",
441- name, frag_spec)
442- ) ;
446+ & format ! ( "`${0}:{1}` is followed by a \
447+ sequence repetition, which is not \
448+ allowed for `{1}` fragments",
449+ name, frag_spec) ,
450+ None ) ;
443451 Eof
444452 } ,
445453 // die next iteration
@@ -456,8 +464,10 @@ fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fai
456464
457465 // If T' is in the set FOLLOW(NT), continue. Else, reject.
458466 match ( & next_token, is_in_follow ( cx, & next_token, & frag_spec. name . as_str ( ) ) ) {
459- ( _, Err ( msg) ) => {
460- on_fail. react ( cx, sp, & msg) ;
467+ ( _, Err ( ( msg, _) ) ) => {
468+ // no need for help message, those messages
469+ // are never emitted anyway...
470+ on_fail. react ( cx, sp, & msg, None ) ;
461471 continue
462472 }
463473 ( & Eof , _) => return Some ( ( sp, tok. clone ( ) ) ) ,
@@ -466,7 +476,7 @@ fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fai
466476 on_fail. react ( cx, sp, & format ! ( "`${0}:{1}` is followed by `{2}`, which \
467477 is not allowed for `{1}` fragments",
468478 name, frag_spec,
469- token_to_string( next) ) ) ;
479+ token_to_string( next) ) , None ) ;
470480 continue
471481 } ,
472482 }
@@ -494,7 +504,8 @@ fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fai
494504 delim. close_token ( ) ,
495505 Some ( _) => {
496506 on_fail. react ( cx, sp, "sequence repetition followed by \
497- another sequence repetition, which is not allowed") ;
507+ another sequence repetition, which is not allowed",
508+ None ) ;
498509 Eof
499510 } ,
500511 None => Eof
@@ -514,7 +525,7 @@ fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fai
514525 Some ( & & TokenTree :: Delimited ( _, ref delim) ) => delim. close_token ( ) ,
515526 Some ( _) => {
516527 on_fail. react ( cx, sp, "sequence repetition followed by another \
517- sequence repetition, which is not allowed") ;
528+ sequence repetition, which is not allowed", None ) ;
518529 Eof
519530 } ,
520531 None => Eof
@@ -810,7 +821,11 @@ fn check_matcher_core(cx: &mut ExtCtxt,
810821 TokenTree :: Token ( sp, ref tok) => {
811822 let can_be_followed_by_any;
812823 if let Err ( bad_frag) = has_legal_fragment_specifier ( tok) {
813- on_fail. react ( cx, sp, & format ! ( "invalid fragment specifier `{}`" , bad_frag) ) ;
824+ on_fail. react ( cx, sp,
825+ & format ! ( "invalid fragment specifier `{}`" , bad_frag) ,
826+ Some ( "valid fragment specifiers are `ident`, `block`, \
827+ `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
828+ and `item`") ) ;
814829 // (This eliminates false positives and duplicates
815830 // from error messages.)
816831 can_be_followed_by_any = true ;
@@ -884,8 +899,8 @@ fn check_matcher_core(cx: &mut ExtCtxt,
884899 if let MatchNt ( ref name, ref frag_spec) = * t {
885900 for & ( sp, ref next_token) in & suffix_first. tokens {
886901 match is_in_follow ( cx, next_token, & frag_spec. name . as_str ( ) ) {
887- Err ( msg) => {
888- on_fail. react ( cx, sp, & msg) ;
902+ Err ( ( msg, help ) ) => {
903+ on_fail. react ( cx, sp, & msg, Some ( help ) ) ;
889904 // don't bother reporting every source of
890905 // conflict for a particular element of `last`.
891906 continue ' each_last;
@@ -907,7 +922,9 @@ fn check_matcher_core(cx: &mut ExtCtxt,
907922 name=name,
908923 frag=frag_spec,
909924 next=token_to_string( next_token) ,
910- may_be=may_be) ) ;
925+ may_be=may_be) ,
926+ None
927+ ) ;
911928 }
912929 }
913930 }
@@ -978,7 +995,7 @@ fn can_be_followed_by_any(frag: &str) -> bool {
978995/// break macros that were relying on that binary operator as a
979996/// separator.
980997// when changing this do not forget to update doc/book/macros.md!
981- fn is_in_follow ( _: & ExtCtxt , tok : & Token , frag : & str ) -> Result < bool , String > {
998+ fn is_in_follow ( _: & ExtCtxt , tok : & Token , frag : & str ) -> Result < bool , ( String , & ' static str ) > {
982999 if let & CloseDelim ( _) = tok {
9831000 // closing a token tree can never be matched by any fragment;
9841001 // iow, we always require that `(` and `)` match, etc.
@@ -1027,7 +1044,10 @@ fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result<bool, String> {
10271044 // harmless
10281045 Ok ( true )
10291046 } ,
1030- _ => Err ( format ! ( "invalid fragment specifier `{}`" , frag) )
1047+ _ => Err ( ( format ! ( "invalid fragment specifier `{}`" , frag) ,
1048+ "valid fragment specifiers are `ident`, `block`, \
1049+ `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
1050+ and `item`") )
10311051 }
10321052 }
10331053}
0 commit comments