@@ -7,6 +7,8 @@ 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
@@ -40,11 +42,32 @@ impl MetaVarExpr {
40
42
) -> PResult < ' psess , MetaVarExpr > {
41
43
let mut iter = input. iter ( ) ;
42
44
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) ) ;
45
+ let next = iter. next ( ) ;
46
+ let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , args) ) = next else {
47
+ // No `()`; wrong or no delimiters. Point at a problematic span or a place to
48
+ // add parens if it makes sense.
49
+ let ( unexpected_span, insert_span) = match next {
50
+ Some ( TokenTree :: Delimited ( ..) ) => ( None , None ) ,
51
+ Some ( tt) => ( Some ( tt. span ( ) ) , None ) ,
52
+ None => ( None , Some ( ident. span . shrink_to_hi ( ) ) ) ,
53
+ } ;
54
+ let err =
55
+ errors:: MveMissingParen { ident_span : ident. span , unexpected_span, insert_span } ;
56
+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
46
57
} ;
47
- check_trailing_token ( & mut iter, psess) ?;
58
+
59
+ // Ensure there are no trailing tokens in the braces, e.g. `${foo() extra}`
60
+ if iter. peek ( ) . is_some ( ) {
61
+ let span = iter_span ( & iter) . expect ( "checked is_some above" ) ;
62
+ let err = errors:: MveExtraTokens {
63
+ span,
64
+ ident_span : ident. span ,
65
+ extra_count : iter. count ( ) ,
66
+ ..Default :: default ( )
67
+ } ;
68
+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
69
+ }
70
+
48
71
let mut iter = args. iter ( ) ;
49
72
let rslt = match ident. as_str ( ) {
50
73
"concat" => parse_concat ( & mut iter, psess, outer_span, ident. span ) ?,
@@ -67,7 +90,7 @@ impl MetaVarExpr {
67
90
return Err ( err) ;
68
91
}
69
92
} ;
70
- check_trailing_token ( & mut iter, psess) ?;
93
+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
71
94
Ok ( rslt)
72
95
}
73
96
@@ -87,20 +110,51 @@ impl MetaVarExpr {
87
110
}
88
111
}
89
112
90
- // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
91
- fn check_trailing_token < ' psess > (
113
+ /// Checks if there are any remaining tokens (for example, `${ignore($valid, extra)}`) and create
114
+ /// a diag with the correct arg count if so.
115
+ fn check_trailing_tokens < ' psess > (
92
116
iter : & mut TokenStreamIter < ' _ > ,
93
117
psess : & ' psess ParseSess ,
118
+ ident : Ident ,
94
119
) -> 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 ( ( ) )
120
+ if iter. peek ( ) . is_none ( ) {
121
+ // All tokens consumed, as expected
122
+ return Ok ( ( ) ) ;
103
123
}
124
+
125
+ // `None` for max indicates the arg count must be exact, `Some` indicates a range is accepted.
126
+ let ( min_or_exact_args, max_args) = match ident. as_str ( ) {
127
+ "concat" => panic ! ( "concat takes unlimited tokens but didn't eat them all" ) ,
128
+ "ignore" => ( 1 , None ) ,
129
+ // 1 or 2 args
130
+ "count" => ( 1 , Some ( 2 ) ) ,
131
+ // 0 or 1 arg
132
+ "index" => ( 0 , Some ( 1 ) ) ,
133
+ "len" => ( 0 , Some ( 1 ) ) ,
134
+ other => unreachable ! ( "unknown MVEs should be rejected earlier (got `{other}`)" ) ,
135
+ } ;
136
+
137
+ let err = errors:: MveExtraTokens {
138
+ span : iter_span ( iter) . expect ( "checked is_none above" ) ,
139
+ ident_span : ident. span ,
140
+ extra_count : iter. count ( ) ,
141
+
142
+ exact_args_note : if max_args. is_some ( ) { None } else { Some ( ( ) ) } ,
143
+ range_args_note : if max_args. is_some ( ) { Some ( ( ) ) } else { None } ,
144
+ min_or_exact_args,
145
+ max_args : max_args. unwrap_or_default ( ) ,
146
+ name : ident. to_string ( ) ,
147
+ } ;
148
+ Err ( psess. dcx ( ) . create_err ( err) )
149
+ }
150
+
151
+ /// Returns a span encompassing all tokens in the iterator if there is at least one item.
152
+ fn iter_span ( iter : & TokenStreamIter < ' _ > ) -> Option < Span > {
153
+ let mut iter = iter. clone ( ) ; // cloning is cheap
154
+ let first_sp = iter. next ( ) ?. span ( ) ;
155
+ let last_sp = iter. last ( ) . map ( TokenTree :: span) . unwrap_or ( first_sp) ;
156
+ let span = first_sp. with_hi ( last_sp. hi ( ) ) ;
157
+ Some ( span)
104
158
}
105
159
106
160
/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments