@@ -7,6 +7,8 @@ use rustc_macros::{Decodable, Encodable};
77use rustc_session:: parse:: ParseSess ;
88use rustc_span:: { Ident , Span , Symbol } ;
99
10+ use crate :: errors;
11+
1012pub ( crate ) const RAW_IDENT_ERR : & str = "`${concat(..)}` currently does not support raw identifiers" ;
1113pub ( crate ) const UNSUPPORTED_CONCAT_ELEM_ERR : & str = "expected identifier or string literal" ;
1214
@@ -40,11 +42,32 @@ impl MetaVarExpr {
4042 ) -> PResult < ' psess , MetaVarExpr > {
4143 let mut iter = input. iter ( ) ;
4244 let ident = parse_ident ( & mut iter, psess, outer_span) ?;
43- let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , args) ) = iter. next ( ) else {
44- let msg = "meta-variable expression parameter must be wrapped in parentheses" ;
45- return Err ( psess. dcx ( ) . struct_span_err ( ident. span , msg) ) ;
45+ let next = iter. next ( ) ;
46+ let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , args) ) = next else {
47+ // No `()`; wrong or no delimiters. Point at a problematic span or a place to
48+ // add parens if it makes sense.
49+ let ( unexpected_span, insert_span) = match next {
50+ Some ( TokenTree :: Delimited ( ..) ) => ( None , None ) ,
51+ Some ( tt) => ( Some ( tt. span ( ) ) , None ) ,
52+ None => ( None , Some ( ident. span . shrink_to_hi ( ) ) ) ,
53+ } ;
54+ let err =
55+ errors:: MveMissingParen { ident_span : ident. span , unexpected_span, insert_span } ;
56+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
4657 } ;
47- check_trailing_token ( & mut iter, psess) ?;
58+
59+ // Ensure there are no trailing tokens in the braces, e.g. `${foo() extra}`
60+ if iter. peek ( ) . is_some ( ) {
61+ let span = iter_span ( & iter) . expect ( "checked is_some above" ) ;
62+ let err = errors:: MveExtraTokens {
63+ span,
64+ ident_span : ident. span ,
65+ extra_count : iter. count ( ) ,
66+ ..Default :: default ( )
67+ } ;
68+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
69+ }
70+
4871 let mut iter = args. iter ( ) ;
4972 let rslt = match ident. as_str ( ) {
5073 "concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -56,18 +79,14 @@ impl MetaVarExpr {
5679 "index" => MetaVarExpr :: Index ( parse_depth ( & mut iter, psess, ident. span ) ?) ,
5780 "len" => MetaVarExpr :: Len ( parse_depth ( & mut iter, psess, ident. span ) ?) ,
5881 _ => {
59- let err_msg = "unrecognized meta-variable expression" ;
60- let mut err = psess. dcx ( ) . struct_span_err ( ident. span , err_msg) ;
61- err. span_suggestion (
62- ident. span ,
63- "supported expressions are count, ignore, index and len" ,
64- "" ,
65- Applicability :: MachineApplicable ,
66- ) ;
67- return Err ( err) ;
82+ let err = errors:: MveUnrecognizedExpr {
83+ span : ident. span ,
84+ valid_expr_list : "`count`, `ignore`, `index`, `len`, and `concat`" ,
85+ } ;
86+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
6887 }
6988 } ;
70- check_trailing_token ( & mut iter, psess) ?;
89+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
7190 Ok ( rslt)
7291 }
7392
@@ -87,20 +106,51 @@ impl MetaVarExpr {
87106 }
88107}
89108
90- // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
91- fn check_trailing_token < ' psess > (
109+ /// Checks if there are any remaining tokens (for example, `${ignore($valid, extra)}`) and create
110+ /// a diag with the correct arg count if so.
111+ fn check_trailing_tokens < ' psess > (
92112 iter : & mut TokenStreamIter < ' _ > ,
93113 psess : & ' psess ParseSess ,
114+ ident : Ident ,
94115) -> PResult < ' psess , ( ) > {
95- if let Some ( tt) = iter. next ( ) {
96- let mut diag = psess
97- . dcx ( )
98- . struct_span_err ( tt. span ( ) , format ! ( "unexpected token: {}" , pprust:: tt_to_string( tt) ) ) ;
99- diag. span_note ( tt. span ( ) , "meta-variable expression must not have trailing tokens" ) ;
100- Err ( diag)
101- } else {
102- Ok ( ( ) )
116+ if iter. peek ( ) . is_none ( ) {
117+ // All tokens consumed, as expected
118+ return Ok ( ( ) ) ;
103119 }
120+
121+ // `None` for max indicates the arg count must be exact, `Some` indicates a range is accepted.
122+ let ( min_or_exact_args, max_args) = match ident. as_str ( ) {
123+ "concat" => panic ! ( "concat takes unlimited tokens but didn't eat them all" ) ,
124+ "ignore" => ( 1 , None ) ,
125+ // 1 or 2 args
126+ "count" => ( 1 , Some ( 2 ) ) ,
127+ // 0 or 1 arg
128+ "index" => ( 0 , Some ( 1 ) ) ,
129+ "len" => ( 0 , Some ( 1 ) ) ,
130+ other => unreachable ! ( "unknown MVEs should be rejected earlier (got `{other}`)" ) ,
131+ } ;
132+
133+ let err = errors:: MveExtraTokens {
134+ span : iter_span ( iter) . expect ( "checked is_none above" ) ,
135+ ident_span : ident. span ,
136+ extra_count : iter. count ( ) ,
137+
138+ exact_args_note : if max_args. is_some ( ) { None } else { Some ( ( ) ) } ,
139+ range_args_note : if max_args. is_some ( ) { Some ( ( ) ) } else { None } ,
140+ min_or_exact_args,
141+ max_args : max_args. unwrap_or_default ( ) ,
142+ name : ident. to_string ( ) ,
143+ } ;
144+ Err ( psess. dcx ( ) . create_err ( err) )
145+ }
146+
147+ /// Returns a span encompassing all tokens in the iterator if there is at least one item.
148+ fn iter_span ( iter : & TokenStreamIter < ' _ > ) -> Option < Span > {
149+ let mut iter = iter. clone ( ) ; // cloning is cheap
150+ let first_sp = iter. next ( ) ?. span ( ) ;
151+ let last_sp = iter. last ( ) . map ( TokenTree :: span) . unwrap_or ( first_sp) ;
152+ let span = first_sp. with_hi ( last_sp. hi ( ) ) ;
153+ Some ( span)
104154}
105155
106156/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments