@@ -7,9 +7,20 @@ 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
15+ /// Map from expression names to the maximum arg count.
16+ const EXPR_NAME_ARG_MAP : & [ ( & str , Option < usize > ) ] = & [
17+ ( "concat" , None ) ,
18+ ( "count" , Some ( 2 ) ) ,
19+ ( "ignore" , Some ( 1 ) ) ,
20+ ( "index" , Some ( 2 ) ) ,
21+ ( "len" , Some ( 2 ) ) ,
22+ ] ;
23+
1324/// A meta-variable expression, for expansions based on properties of meta-variables.
1425#[ derive( Debug , PartialEq , Encodable , Decodable ) ]
1526pub ( crate ) enum MetaVarExpr {
@@ -40,11 +51,33 @@ impl MetaVarExpr {
4051 ) -> PResult < ' psess , MetaVarExpr > {
4152 let mut iter = input. iter ( ) ;
4253 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) ) ;
54+ let next = iter. next ( ) ;
55+ let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , args) ) = next else {
56+ // No `()`; wrong or no delimiters
57+ let ( unexpected_span, insert_span) = match next {
58+ Some ( TokenTree :: Delimited ( ..) ) => ( None , None ) ,
59+ Some ( tt) => ( Some ( tt. span ( ) ) , None ) ,
60+ None => ( None , Some ( ident. span . shrink_to_hi ( ) ) ) ,
61+ } ;
62+ let err =
63+ errors:: MveMissingParen { ident_span : ident. span , unexpected_span, insert_span } ;
64+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
4665 } ;
47- check_trailing_token ( & mut iter, psess) ?;
66+
67+ // Ensure there are no other tokens in the
68+ if iter. peek ( ) . is_some ( ) {
69+ let span = iter_span ( & iter) . expect ( "checked is_some above" ) ;
70+ let err = errors:: MveExtraTokens {
71+ span,
72+ ident_span : ident. span ,
73+ extra_count : iter. count ( ) ,
74+ args_note : None ,
75+ max_args : 0 ,
76+ name : "" ,
77+ } ;
78+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
79+ }
80+
4881 let mut iter = args. iter ( ) ;
4982 let rslt = match ident. as_str ( ) {
5083 "concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -67,7 +100,7 @@ impl MetaVarExpr {
67100 return Err ( err) ;
68101 }
69102 } ;
70- check_trailing_token ( & mut iter, psess) ?;
103+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
71104 Ok ( rslt)
72105 }
73106
@@ -87,20 +120,44 @@ impl MetaVarExpr {
87120 }
88121}
89122
90- // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
91- fn check_trailing_token < ' psess > (
123+ /// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
124+ fn check_trailing_tokens < ' psess > (
92125 iter : & mut TokenStreamIter < ' _ > ,
93126 psess : & ' psess ParseSess ,
127+ ident : Ident ,
94128) -> 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 ( ( ) )
129+ if iter. peek ( ) . is_none ( ) {
130+ // All tokens used, no problem
131+ return Ok ( ( ) ) ;
103132 }
133+
134+ let ( name, max) = EXPR_NAME_ARG_MAP
135+ . iter ( )
136+ . find ( |( name, _) | * name == ident. as_str ( ) )
137+ . expect ( "called with an invalid name" ) ;
138+
139+ // For expressions like `concat`, all tokens should be consumed already
140+ let max =
141+ max. unwrap_or_else ( || panic ! ( "{name} takes unlimited tokens but didn't eat them all" ) ) ;
142+
143+ let err = errors:: MveExtraTokens {
144+ span : iter_span ( iter) . expect ( "checked is_none above" ) ,
145+ ident_span : ident. span ,
146+ extra_count : iter. count ( ) ,
147+ args_note : Some ( ( ) ) ,
148+ max_args : max,
149+ name,
150+ } ;
151+ Err ( psess. dcx ( ) . create_err ( err) )
152+ }
153+
154+ /// Returns a span encompassing all tokens in the iterator if there is at least one item.
155+ fn iter_span ( iter : & TokenStreamIter < ' _ > ) -> Option < Span > {
156+ let mut iter = iter. clone ( ) ; // cloning is cheap
157+ let first_sp = iter. next ( ) ?. span ( ) ;
158+ let last_sp = iter. last ( ) . map ( TokenTree :: span) . unwrap_or ( first_sp) ;
159+ let span = first_sp. with_hi ( last_sp. hi ( ) ) ;
160+ Some ( span)
104161}
105162
106163/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments