@@ -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