@@ -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,25 @@ 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 ( 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) ) ;
46
64
} ;
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
+
48
73
let mut iter = args. iter ( ) ;
49
74
let rslt = match ident. as_str ( ) {
50
75
"concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -67,7 +92,7 @@ impl MetaVarExpr {
67
92
return Err ( err) ;
68
93
}
69
94
} ;
70
- check_trailing_token ( & mut iter, psess) ?;
95
+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
71
96
Ok ( rslt)
72
97
}
73
98
@@ -87,20 +112,44 @@ impl MetaVarExpr {
87
112
}
88
113
}
89
114
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 > (
92
117
iter : & mut TokenStreamIter < ' _ > ,
93
118
psess : & ' psess ParseSess ,
119
+ ident : Ident ,
94
120
) -> 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 ( ( ) ) ;
103
124
}
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)
104
153
}
105
154
106
155
/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments