@@ -7,9 +7,20 @@ 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
+ /// 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
+
13
24
/// A meta-variable expression, for expansions based on properties of meta-variables.
14
25
#[ derive( Debug , PartialEq , Encodable , Decodable ) ]
15
26
pub ( crate ) enum MetaVarExpr {
@@ -40,11 +51,33 @@ impl MetaVarExpr {
40
51
) -> PResult < ' psess , MetaVarExpr > {
41
52
let mut iter = input. iter ( ) ;
42
53
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) ) ;
46
65
} ;
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
+
48
81
let mut iter = args. iter ( ) ;
49
82
let rslt = match ident. as_str ( ) {
50
83
"concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -67,7 +100,7 @@ impl MetaVarExpr {
67
100
return Err ( err) ;
68
101
}
69
102
} ;
70
- check_trailing_token ( & mut iter, psess) ?;
103
+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
71
104
Ok ( rslt)
72
105
}
73
106
@@ -87,20 +120,44 @@ impl MetaVarExpr {
87
120
}
88
121
}
89
122
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 > (
92
125
iter : & mut TokenStreamIter < ' _ > ,
93
126
psess : & ' psess ParseSess ,
127
+ ident : Ident ,
94
128
) -> 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 ( ( ) ) ;
103
132
}
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)
104
161
}
105
162
106
163
/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments