@@ -12,7 +12,7 @@ use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
12
12
use rustc_ast_pretty:: pprust;
13
13
use rustc_attr_parsing:: { AttributeKind , find_attr} ;
14
14
use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
15
- use rustc_errors:: { Applicability , ErrorGuaranteed } ;
15
+ use rustc_errors:: { Applicability , Diag , ErrorGuaranteed } ;
16
16
use rustc_feature:: Features ;
17
17
use rustc_hir as hir;
18
18
use rustc_lint_defs:: BuiltinLintDiag ;
@@ -27,19 +27,18 @@ use rustc_span::hygiene::Transparency;
27
27
use rustc_span:: { Ident , MacroRulesNormalizedIdent , Span , kw, sym} ;
28
28
use tracing:: { debug, instrument, trace, trace_span} ;
29
29
30
- use super :: diagnostics;
31
30
use super :: macro_parser:: { NamedMatches , NamedParseResult } ;
31
+ use super :: { SequenceRepetition , diagnostics} ;
32
32
use crate :: base:: {
33
33
DummyResult , ExpandResult , ExtCtxt , MacResult , MacroExpanderResult , SyntaxExtension ,
34
34
SyntaxExtensionKind , TTMacroExpander ,
35
35
} ;
36
36
use crate :: expand:: { AstFragment , AstFragmentKind , ensure_complete_parse, parse_ast_fragment} ;
37
- use crate :: mbe;
38
37
use crate :: mbe:: diagnostics:: { annotate_doc_comment, parse_failure_msg} ;
39
- use crate :: mbe:: macro_check;
40
38
use crate :: mbe:: macro_parser:: NamedMatch :: * ;
41
39
use crate :: mbe:: macro_parser:: { Error , ErrorReported , Failure , MatcherLoc , Success , TtParser } ;
42
40
use crate :: mbe:: transcribe:: transcribe;
41
+ use crate :: mbe:: { self , KleeneOp , macro_check} ;
43
42
44
43
pub ( crate ) struct ParserAnyMacro < ' a > {
45
44
parser : Parser < ' a > ,
@@ -640,6 +639,37 @@ fn is_empty_token_tree(sess: &Session, seq: &mbe::SequenceRepetition) -> bool {
640
639
}
641
640
}
642
641
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
+
643
673
/// Checks that the lhs contains no repetition which could match an empty token
644
674
/// tree, because then the matcher would hang indefinitely.
645
675
fn 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<(),
654
684
TokenTree :: Sequence ( span, seq) => {
655
685
if is_empty_token_tree ( sess, seq) {
656
686
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 ( ) ) ;
659
691
}
660
692
check_lhs_no_empty_seq ( sess, & seq. tts ) ?
661
693
}
0 commit comments