@@ -7,6 +7,8 @@ use rustc_macros::{Decodable, Encodable};
77use  rustc_session:: parse:: ParseSess ; 
88use  rustc_span:: { Ident ,  Span ,  Symbol } ; 
99
10+ use  crate :: errors; 
11+ 
1012pub ( crate )  const  RAW_IDENT_ERR :  & str  = "`${concat(..)}` currently does not support raw identifiers" ; 
1113pub ( crate )  const  UNSUPPORTED_CONCAT_ELEM_ERR :  & str  = "expected identifier or string literal" ; 
1214
@@ -40,11 +42,32 @@ impl MetaVarExpr {
4042    )  -> PResult < ' psess ,  MetaVarExpr >  { 
4143        let  mut  iter = input. iter ( ) ; 
4244        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) ) ; 
4657        } ; 
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+ 
4871        let  mut  iter = args. iter ( ) ; 
4972        let  rslt = match  ident. as_str ( )  { 
5073            "concat"  => parse_concat ( & mut  iter,  psess,  outer_span,  ident. span ) ?, 
@@ -56,18 +79,14 @@ impl MetaVarExpr {
5679            "index"  => MetaVarExpr :: Index ( parse_depth ( & mut  iter,  psess,  ident. span ) ?) , 
5780            "len"  => MetaVarExpr :: Len ( parse_depth ( & mut  iter,  psess,  ident. span ) ?) , 
5881            _ => { 
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) ) ; 
6887            } 
6988        } ; 
70-         check_trailing_token ( & mut  iter,  psess) ?; 
89+         check_trailing_tokens ( & mut  iter,  psess,  ident ) ?; 
7190        Ok ( rslt) 
7291    } 
7392
@@ -87,20 +106,51 @@ impl MetaVarExpr {
87106    } 
88107} 
89108
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 > ( 
92112    iter :  & mut  TokenStreamIter < ' _ > , 
93113    psess :  & ' psess  ParseSess , 
114+     ident :  Ident , 
94115)  -> 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 ( ( ) ) ; 
103119    } 
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) 
104154} 
105155
106156/// Indicates what is placed in a `concat` parameter. For example, literals 
0 commit comments