@@ -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,25 @@ 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 ( span, insert_span) = match next {
58+ Some ( TokenTree :: Delimited ( delim, ..) ) => ( delim. open , None ) ,
59+ Some ( tt) => ( tt. span ( ) , Some ( ident. span . shrink_to_hi ( ) ) ) ,
60+ None => ( ident. span . shrink_to_hi ( ) , Some ( ident. span . shrink_to_hi ( ) ) ) ,
61+ } ;
62+ let err = errors:: MveMissingParen { span, insert_span } ;
63+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
4664 } ;
47- check_trailing_token ( & mut iter, psess) ?;
65+
66+ // Ensure there are no other tokens in the
67+ if iter. peek ( ) . is_some ( ) {
68+ let span = iter_span ( & iter) . expect ( "checked is_some above" ) ;
69+ let err = errors:: MveExtraTokensInBraces { span } ;
70+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
71+ }
72+
4873 let mut iter = args. iter ( ) ;
4974 let rslt = match ident. as_str ( ) {
5075 "concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -67,7 +92,7 @@ impl MetaVarExpr {
6792 return Err ( err) ;
6893 }
6994 } ;
70- check_trailing_token ( & mut iter, psess) ?;
95+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
7196 Ok ( rslt)
7297 }
7398
@@ -87,20 +112,44 @@ impl MetaVarExpr {
87112 }
88113}
89114
90- // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
91- fn check_trailing_token < ' psess > (
115+ /// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
116+ fn check_trailing_tokens < ' psess > (
92117 iter : & mut TokenStreamIter < ' _ > ,
93118 psess : & ' psess ParseSess ,
119+ ident : Ident ,
94120) -> 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 ( ( ) )
121+ if iter. peek ( ) . is_none ( ) {
122+ // All tokens used, no problem
123+ return Ok ( ( ) ) ;
103124 }
125+
126+ let ( name, max) = EXPR_NAME_ARG_MAP
127+ . iter ( )
128+ . find ( |( name, _) | * name == ident. as_str ( ) )
129+ . expect ( "called with an invalid name" ) ;
130+
131+ let Some ( max) = * max else {
132+ // For expressions like `concat`, all tokens should be consumed already
133+ panic ! ( "{name} takes unlimited tokens but didn't eat them all" ) ;
134+ } ;
135+
136+ let err = errors:: MveExtraTokensInExpr {
137+ span : iter_span ( iter) . expect ( "checked is_none above" ) ,
138+ ident_span : ident. span ,
139+ count : iter. count ( ) ,
140+ max,
141+ name,
142+ } ;
143+ Err ( psess. dcx ( ) . create_err ( err) )
144+ }
145+
146+ /// Returns a span encompassing all tokens in the iterator if there is at least one item.
147+ fn iter_span ( iter : & TokenStreamIter < ' _ > ) -> Option < Span > {
148+ let mut iter = iter. clone ( ) ; // cloning is cheap
149+ let first_sp = iter. next ( ) ?. span ( ) ;
150+ let last_sp = iter. last ( ) . map ( TokenTree :: span) . unwrap_or ( first_sp) ;
151+ let span = first_sp. with_hi ( last_sp. hi ( ) ) ;
152+ Some ( span)
104153}
105154
106155/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments