@@ -12,8 +12,18 @@ use crate::errors::{self, MveExpectedIdentContext};
12
12
pub ( crate ) const RAW_IDENT_ERR : & str = "`${concat(..)}` currently does not support raw identifiers" ;
13
13
pub ( crate ) const UNSUPPORTED_CONCAT_ELEM_ERR : & str = "expected identifier or string literal" ;
14
14
15
+ /// List of the below list for diagnostics.
15
16
const VALID_METAVAR_EXPR_NAMES : & str = "`count`, `ignore`, `index`, `len`, and `concat`" ;
16
17
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
+
17
27
/// A meta-variable expression, for expansions based on properties of meta-variables.
18
28
#[ derive( Debug , PartialEq , Encodable , Decodable ) ]
19
29
pub ( crate ) enum MetaVarExpr {
@@ -49,11 +59,26 @@ impl MetaVarExpr {
49
59
outer_span,
50
60
MveExpectedIdentContext :: ExprName { valid_expr_list : VALID_METAVAR_EXPR_NAMES } ,
51
61
) ?;
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) ) ;
55
73
} ;
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
+
57
82
let mut iter = args. iter ( ) ;
58
83
let rslt = match ident. as_str ( ) {
59
84
"concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -78,7 +103,7 @@ impl MetaVarExpr {
78
103
return Err ( err) ;
79
104
}
80
105
} ;
81
- check_trailing_token ( & mut iter, psess) ?;
106
+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
82
107
Ok ( rslt)
83
108
}
84
109
@@ -98,20 +123,44 @@ impl MetaVarExpr {
98
123
}
99
124
}
100
125
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 > (
103
128
iter : & mut TokenStreamIter < ' _ > ,
104
129
psess : & ' psess ParseSess ,
130
+ ident : Ident ,
105
131
) -> 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 ( ( ) ) ;
114
135
}
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)
115
164
}
116
165
117
166
/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments