@@ -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 ) ?,
@@ -56,18 +79,14 @@ impl MetaVarExpr {
56
79
"index" => MetaVarExpr :: Index ( parse_depth ( & mut iter, psess, ident. span ) ?) ,
57
80
"len" => MetaVarExpr :: Len ( parse_depth ( & mut iter, psess, ident. span ) ?) ,
58
81
_ => {
59
- let err_msg = "unrecognized meta-variable expression" ;
60
- let mut err = psess. dcx ( ) . struct_span_err ( ident. span , err_msg) ;
61
- err. span_suggestion (
62
- ident. span ,
63
- "supported expressions are count, ignore, index and len" ,
64
- "" ,
65
- Applicability :: MachineApplicable ,
66
- ) ;
67
- return Err ( err) ;
82
+ let err = errors:: MveUnrecognizedExpr {
83
+ span : ident. span ,
84
+ valid_expr_list : "`count`, `ignore`, `index`, `len`, and `concat`" ,
85
+ } ;
86
+ return Err ( psess. dcx ( ) . create_err ( err) ) ;
68
87
}
69
88
} ;
70
- check_trailing_token ( & mut iter, psess) ?;
89
+ check_trailing_tokens ( & mut iter, psess, ident ) ?;
71
90
Ok ( rslt)
72
91
}
73
92
@@ -87,20 +106,51 @@ impl MetaVarExpr {
87
106
}
88
107
}
89
108
90
- // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
91
- fn check_trailing_token < ' psess > (
109
+ /// Checks if there are any remaining tokens (for example, `${ignore($valid, extra)}`) and create
110
+ /// a diag with the correct arg count if so.
111
+ fn check_trailing_tokens < ' psess > (
92
112
iter : & mut TokenStreamIter < ' _ > ,
93
113
psess : & ' psess ParseSess ,
114
+ ident : Ident ,
94
115
) -> 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 ( ( ) )
116
+ if iter. peek ( ) . is_none ( ) {
117
+ // All tokens consumed, as expected
118
+ return Ok ( ( ) ) ;
103
119
}
120
+
121
+ // `None` for max indicates the arg count must be exact, `Some` indicates a range is accepted.
122
+ let ( min_or_exact_args, max_args) = match ident. as_str ( ) {
123
+ "concat" => panic ! ( "concat takes unlimited tokens but didn't eat them all" ) ,
124
+ "ignore" => ( 1 , None ) ,
125
+ // 1 or 2 args
126
+ "count" => ( 1 , Some ( 2 ) ) ,
127
+ // 0 or 1 arg
128
+ "index" => ( 0 , Some ( 1 ) ) ,
129
+ "len" => ( 0 , Some ( 1 ) ) ,
130
+ other => unreachable ! ( "unknown MVEs should be rejected earlier (got `{other}`)" ) ,
131
+ } ;
132
+
133
+ let err = errors:: MveExtraTokens {
134
+ span : iter_span ( iter) . expect ( "checked is_none above" ) ,
135
+ ident_span : ident. span ,
136
+ extra_count : iter. count ( ) ,
137
+
138
+ exact_args_note : if max_args. is_some ( ) { None } else { Some ( ( ) ) } ,
139
+ range_args_note : if max_args. is_some ( ) { Some ( ( ) ) } else { None } ,
140
+ min_or_exact_args,
141
+ max_args : max_args. unwrap_or_default ( ) ,
142
+ name : ident. to_string ( ) ,
143
+ } ;
144
+ Err ( psess. dcx ( ) . create_err ( err) )
145
+ }
146
+
147
+ /// Returns a span encompassing all tokens in the iterator if there is at least one item.
148
+ fn iter_span ( iter : & TokenStreamIter < ' _ > ) -> Option < Span > {
149
+ let mut iter = iter. clone ( ) ; // cloning is cheap
150
+ let first_sp = iter. next ( ) ?. span ( ) ;
151
+ let last_sp = iter. last ( ) . map ( TokenTree :: span) . unwrap_or ( first_sp) ;
152
+ let span = first_sp. with_hi ( last_sp. hi ( ) ) ;
153
+ Some ( span)
104
154
}
105
155
106
156
/// Indicates what is placed in a `concat` parameter. For example, literals
0 commit comments