@@ -12,7 +12,7 @@ use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
1212use  rustc_ast_pretty:: pprust; 
1313use  rustc_attr_parsing:: { AttributeKind ,  find_attr} ; 
1414use  rustc_data_structures:: fx:: { FxHashMap ,  FxIndexMap } ; 
15- use  rustc_errors:: { Applicability ,  ErrorGuaranteed } ; 
15+ use  rustc_errors:: { Applicability ,  Diag ,   ErrorGuaranteed } ; 
1616use  rustc_feature:: Features ; 
1717use  rustc_hir as  hir; 
1818use  rustc_lint_defs:: BuiltinLintDiag ; 
@@ -27,19 +27,18 @@ use rustc_span::hygiene::Transparency;
2727use  rustc_span:: { Ident ,  MacroRulesNormalizedIdent ,  Span ,  kw,  sym} ; 
2828use  tracing:: { debug,  instrument,  trace,  trace_span} ; 
2929
30- use  super :: diagnostics; 
3130use  super :: macro_parser:: { NamedMatches ,  NamedParseResult } ; 
31+ use  super :: { SequenceRepetition ,  diagnostics} ; 
3232use  crate :: base:: { 
3333    DummyResult ,  ExpandResult ,  ExtCtxt ,  MacResult ,  MacroExpanderResult ,  SyntaxExtension , 
3434    SyntaxExtensionKind ,  TTMacroExpander , 
3535} ; 
3636use  crate :: expand:: { AstFragment ,  AstFragmentKind ,  ensure_complete_parse,  parse_ast_fragment} ; 
37- use  crate :: mbe; 
3837use  crate :: mbe:: diagnostics:: { annotate_doc_comment,  parse_failure_msg} ; 
39- use  crate :: mbe:: macro_check; 
4038use  crate :: mbe:: macro_parser:: NamedMatch :: * ; 
4139use  crate :: mbe:: macro_parser:: { Error ,  ErrorReported ,  Failure ,  MatcherLoc ,  Success ,  TtParser } ; 
4240use  crate :: mbe:: transcribe:: transcribe; 
41+ use  crate :: mbe:: { self ,  KleeneOp ,  macro_check} ; 
4342
4443pub ( crate )  struct  ParserAnyMacro < ' a >  { 
4544    parser :  Parser < ' a > , 
@@ -640,6 +639,37 @@ fn is_empty_token_tree(sess: &Session, seq: &mbe::SequenceRepetition) -> bool {
640639    } 
641640} 
642641
642+ /// Checks if a `vis` nonterminal fragment is unnecessarily wrapped in an optional repetition. 
643+ /// 
644+ /// When a `vis` fragment (which can already be empty) is wrapped in `$(...)?`, 
645+ /// this suggests removing the redundant repetition syntax since it provides no additional benefit. 
646+ fn  check_redundant_vis_repetition ( 
647+     err :  & mut  Diag < ' _ > , 
648+     sess :  & Session , 
649+     seq :  & SequenceRepetition , 
650+     span :  & DelimSpan , 
651+ )  { 
652+     let  is_zero_or_one:  bool  = seq. kleene . op  == KleeneOp :: ZeroOrOne ; 
653+     let  is_vis = seq. tts . first ( ) . map_or ( false ,  |tt| { 
654+         matches ! ( tt,  mbe:: TokenTree :: MetaVarDecl ( _,  _,  Some ( NonterminalKind :: Vis ) ) ) 
655+     } ) ; 
656+ 
657+     if  is_vis && is_zero_or_one { 
658+         err. note ( "a `vis` fragment can already be empty" ) ; 
659+         err. multipart_suggestion ( 
660+             "remove the `$(` and `)?`" , 
661+             vec ! [ 
662+                 ( 
663+                     sess. source_map( ) . span_extend_to_prev_char_before( span. open,  '$' ,  true ) , 
664+                     "" . to_string( ) , 
665+                 ) , 
666+                 ( span. close. with_hi( seq. kleene. span. hi( ) ) ,  "" . to_string( ) ) , 
667+             ] , 
668+             Applicability :: MaybeIncorrect , 
669+         ) ; 
670+     } 
671+ } 
672+ 
643673/// Checks that the lhs contains no repetition which could match an empty token 
644674/// tree, because then the matcher would hang indefinitely. 
645675fn  check_lhs_no_empty_seq ( sess :  & Session ,  tts :  & [ mbe:: TokenTree ] )  -> Result < ( ) ,  ErrorGuaranteed >  { 
@@ -654,8 +684,10 @@ fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> Result<(),
654684            TokenTree :: Sequence ( span,  seq)  => { 
655685                if  is_empty_token_tree ( sess,  seq)  { 
656686                    let  sp = span. entire ( ) ; 
657-                     let  guar = sess. dcx ( ) . span_err ( sp,  "repetition matches empty token tree" ) ; 
658-                     return  Err ( guar) ; 
687+                     let  mut  err =
688+                         sess. dcx ( ) . struct_span_err ( sp,  "repetition matches empty token tree" ) ; 
689+                     check_redundant_vis_repetition ( & mut  err,  sess,  seq,  span) ; 
690+                     return  Err ( err. emit ( ) ) ; 
659691                } 
660692                check_lhs_no_empty_seq ( sess,  & seq. tts ) ?
661693            } 
0 commit comments