@@ -7,9 +7,31 @@ use rustc_macros::{Decodable, Encodable};
7
7
use rustc_session:: parse:: ParseSess ;
8
8
use rustc_span:: { Ident , Span , Symbol } ;
9
9
10
+ use crate :: errors;
11
+
10
12
pub ( crate ) const RAW_IDENT_ERR : & str = "`${concat(..)}` currently does not support raw identifiers" ;
11
13
pub ( crate ) const UNSUPPORTED_CONCAT_ELEM_ERR : & str = "expected identifier or string literal" ;
12
14
15
+ /// Argument specification for a metavariable expression
16
+ #[ derive( Clone , Copy ) ]
17
+ enum ArgSpec {
18
+ /// Any number of args
19
+ Any ,
20
+ /// Between n and m args (inclusive)
21
+ Between ( usize , usize ) ,
22
+ /// Exactly n args
23
+ Exact ( usize ) ,
24
+ }
25
+
26
+ /// Map of `(name, max_arg_count, variable_count)`.
27
+ const EXPR_NAME_ARG_MAP : & [ ( & str , ArgSpec ) ] = & [
28
+ ( "concat" , ArgSpec :: Any ) ,
29
+ ( "count" , ArgSpec :: Between ( 1 , 2 ) ) ,
30
+ ( "ignore" , ArgSpec :: Exact ( 1 ) ) ,
31
+ ( "index" , ArgSpec :: Between ( 0 , 1 ) ) ,
32
+ ( "len" , ArgSpec :: Between ( 0 , 1 ) ) ,
33
+ ] ;
34
+
13
35
/// A meta-variable expression, for expansions based on properties of meta-variables.
14
36
#[ derive( Debug , PartialEq , Encodable , Decodable ) ]
15
37
pub ( crate ) enum MetaVarExpr {
@@ -40,11 +62,32 @@ impl MetaVarExpr {
40
62
) -> PResult < ' psess , MetaVarExpr > {
41
63
let mut iter = input. iter ( ) ;
42
64
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) ) ;
65
+ let next = iter. next ( ) ;
66
+ let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , args) ) = next else {
67
+ // No `()`; wrong or no delimiters. Point at a problematic span or a place to
68
+ // add parens if it makes sense.
69
+ let ( unexpected_span, insert_span) = match next {
70
+ Some ( TokenTree :: Delimited ( ..) ) => ( None , None ) ,
71
+ Some ( tt) => ( Some ( tt. span ( ) ) , None ) ,
72
+ None => ( None , Some ( ident. span . shrink_to_hi ( ) ) ) ,
73
+ } ;
74
+ let err =
75
+ errors:: MveMissingParen { ident_span : ident. span , unexpected_span, insert_span } ;
76
+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
46
77
} ;
47
- check_trailing_token ( & mut iter, psess) ?;
78
+
79
+ // Ensure there are no trailing tokens in the braces, e.g. `${foo() extra}`
80
+ if iter. peek ( ) . is_some ( ) {
81
+ let span = iter_span ( & iter) . expect ( "checked is_some above" ) ;
82
+ let err = errors:: MveExtraTokens {
83
+ span,
84
+ ident_span : ident. span ,
85
+ extra_count : iter. count ( ) ,
86
+ ..Default :: default ( )
87
+ } ;
88
+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
89
+ }
90
+
48
91
let mut iter = args. iter ( ) ;
49
92
let rslt = match ident. as_str ( ) {
50
93
"concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -67,7 +110,7 @@ impl MetaVarExpr {
67
110
return Err ( err) ;
68
111
}
69
112
} ;
70
- check_trailing_token ( & mut iter, psess) ?;
113
+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
71
114
Ok ( rslt)
72
115
}
73
116
@@ -87,20 +130,51 @@ impl MetaVarExpr {
87
130
}
88
131
}
89
132
90
- // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
91
- fn check_trailing_token < ' psess > (
133
+ /// Checks if there are any remaining tokens (for example, `${ignore($valid, extra)}`) and create
134
+ /// a diag with the correct arg count if so.
135
+ fn check_trailing_tokens < ' psess > (
92
136
iter : & mut TokenStreamIter < ' _ > ,
93
137
psess : & ' psess ParseSess ,
138
+ ident : Ident ,
94
139
) -> 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 ( ( ) )
140
+ if iter. peek ( ) . is_none ( ) {
141
+ // All tokens consumed, as expected
142
+ return Ok ( ( ) ) ;
103
143
}
144
+
145
+ let ( name, spec) = EXPR_NAME_ARG_MAP
146
+ . iter ( )
147
+ . find ( |( name, _) | * name == ident. as_str ( ) )
148
+ . expect ( "called with an invalid name" ) ;
149
+
150
+ let ( min_or_exact_args, max_args) = match * spec {
151
+ // For expressions like `concat`, all tokens should be consumed already
152
+ ArgSpec :: Any => panic ! ( "{name} takes unlimited tokens but didn't eat them all" ) ,
153
+ ArgSpec :: Between ( min, max) => ( min, Some ( max) ) ,
154
+ ArgSpec :: Exact ( n) => ( n, None ) ,
155
+ } ;
156
+
157
+ let err = errors:: MveExtraTokens {
158
+ span : iter_span ( iter) . expect ( "checked is_none above" ) ,
159
+ ident_span : ident. span ,
160
+ extra_count : iter. count ( ) ,
161
+
162
+ exact_args_note : if max_args. is_some ( ) { None } else { Some ( ( ) ) } ,
163
+ range_args_note : if max_args. is_some ( ) { Some ( ( ) ) } else { None } ,
164
+ min_or_exact_args,
165
+ max_args : max_args. unwrap_or_default ( ) ,
166
+ name,
167
+ } ;
168
+ Err ( psess. dcx ( ) . create_err ( err) )
169
+ }
170
+
171
+ /// Returns a span encompassing all tokens in the iterator if there is at least one item.
172
+ fn iter_span ( iter : & TokenStreamIter < ' _ > ) -> Option < Span > {
173
+ let mut iter = iter. clone ( ) ; // cloning is cheap
174
+ let first_sp = iter. next ( ) ?. span ( ) ;
175
+ let last_sp = iter. last ( ) . map ( TokenTree :: span) . unwrap_or ( first_sp) ;
176
+ let span = first_sp. with_hi ( last_sp. hi ( ) ) ;
177
+ Some ( span)
104
178
}
105
179
106
180
/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments