@@ -12,8 +12,18 @@ use crate::errors::{self, MveExpectedIdentContext};
1212pub ( crate ) const RAW_IDENT_ERR : & str = "`${concat(..)}` currently does not support raw identifiers" ;
1313pub ( crate ) const UNSUPPORTED_CONCAT_ELEM_ERR : & str = "expected identifier or string literal" ;
1414
15+ /// List of the below list for diagnostics.
1516const VALID_METAVAR_EXPR_NAMES : & str = "`count`, `ignore`, `index`, `len`, and `concat`" ;
1617
18+ /// Map from expression names to the maximum arg count.
19+ const EXPR_NAME_ARG_MAP : & [ ( & str , Option < usize > ) ] = & [
20+ ( "concat" , None ) ,
21+ ( "count" , Some ( 2 ) ) ,
22+ ( "ignore" , Some ( 1 ) ) ,
23+ ( "index" , Some ( 2 ) ) ,
24+ ( "len" , Some ( 2 ) ) ,
25+ ] ;
26+
1727/// A meta-variable expression, for expansions based on properties of meta-variables.
1828#[ derive( Debug , PartialEq , Encodable , Decodable ) ]
1929pub ( crate ) enum MetaVarExpr {
@@ -49,11 +59,26 @@ impl MetaVarExpr {
4959 outer_span,
5060 MveExpectedIdentContext :: ExprName { valid_expr_list : VALID_METAVAR_EXPR_NAMES } ,
5161 ) ?;
52- let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , args) ) = iter. next ( ) else {
53- let msg = "meta-variable expression parameter must be wrapped in parentheses" ;
54- return Err ( psess. dcx ( ) . struct_span_err ( ident. span , msg) ) ;
62+
63+ let next = iter. next ( ) ;
64+ let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , args) ) = next else {
65+ // No `()`; wrong or no delimiters
66+ let ( span, insert_span) = match next {
67+ Some ( TokenTree :: Delimited ( delim, ..) ) => ( delim. open , None ) ,
68+ Some ( tt) => ( tt. span ( ) , Some ( ident. span . shrink_to_hi ( ) ) ) ,
69+ None => ( ident. span . shrink_to_hi ( ) , Some ( ident. span . shrink_to_hi ( ) ) ) ,
70+ } ;
71+ let err = errors:: MveMissingParen { span, insert_span } ;
72+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
5573 } ;
56- check_trailing_token ( & mut iter, psess) ?;
74+
75+ // Ensure there are no other tokens in the
76+ if iter. peek ( ) . is_some ( ) {
77+ let span = iter_span ( & iter) . expect ( "checked is_some above" ) ;
78+ let err = errors:: MveExtraTokensInBraces { span } ;
79+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
80+ }
81+
5782 let mut iter = args. iter ( ) ;
5883 let rslt = match ident. as_str ( ) {
5984 "concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -78,7 +103,7 @@ impl MetaVarExpr {
78103 return Err ( err) ;
79104 }
80105 } ;
81- check_trailing_token ( & mut iter, psess) ?;
106+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
82107 Ok ( rslt)
83108 }
84109
@@ -98,20 +123,44 @@ impl MetaVarExpr {
98123 }
99124}
100125
101- // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
102- fn check_trailing_token < ' psess > (
126+ /// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
127+ fn check_trailing_tokens < ' psess > (
103128 iter : & mut TokenStreamIter < ' _ > ,
104129 psess : & ' psess ParseSess ,
130+ ident : Ident ,
105131) -> PResult < ' psess , ( ) > {
106- if let Some ( tt) = iter. next ( ) {
107- let mut diag = psess
108- . dcx ( )
109- . struct_span_err ( tt. span ( ) , format ! ( "unexpected token: {}" , pprust:: tt_to_string( tt) ) ) ;
110- diag. span_note ( tt. span ( ) , "meta-variable expression must not have trailing tokens" ) ;
111- Err ( diag)
112- } else {
113- Ok ( ( ) )
132+ if iter. peek ( ) . is_none ( ) {
133+ // All tokens used, no problem
134+ return Ok ( ( ) ) ;
114135 }
136+
137+ let ( name, max) = EXPR_NAME_ARG_MAP
138+ . iter ( )
139+ . find ( |( name, _) | * name == ident. as_str ( ) )
140+ . expect ( "called with an invalid name" ) ;
141+
142+ let Some ( max) = * max else {
143+ // For expressions like `concat`, all tokens should be consumed already
144+ panic ! ( "{name} takes unlimited tokens but didn't eat them all" ) ;
145+ } ;
146+
147+ let err = errors:: MveExtraTokensInExpr {
148+ span : iter_span ( iter) . expect ( "checked is_none above" ) ,
149+ ident_span : ident. span ,
150+ count : iter. count ( ) ,
151+ max,
152+ name,
153+ } ;
154+ Err ( psess. dcx ( ) . create_err ( err) )
155+ }
156+
157+ /// Returns a span encompassing all tokens in the iterator if there is at least one item.
158+ fn iter_span ( iter : & TokenStreamIter < ' _ > ) -> Option < Span > {
159+ let mut iter = iter. clone ( ) ; // cloning is cheap
160+ let first_sp = iter. next ( ) ?. span ( ) ;
161+ let last_sp = iter. last ( ) . map ( TokenTree :: span) . unwrap_or ( first_sp) ;
162+ let span = first_sp. with_hi ( last_sp. hi ( ) ) ;
163+ Some ( span)
115164}
116165
117166/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments