@@ -18,7 +18,8 @@ use smallvec::{SmallVec, smallvec};
1818
1919use crate :: errors:: {
2020 CountRepetitionMisplaced , MacroVarStillRepeating , MetaVarsDifSeqMatchers , MustRepeatOnce ,
21- MveUnrecognizedVar , NoSyntaxVarsExprRepeat ,
21+ MveUnrecognizedVar , NoRepeatableVar , NoSyntaxVarsExprRepeat , VarNoTypo ,
22+ VarTypoSuggestionRepeatable , VarTypoSuggestionUnrepeatable , VarTypoSuggestionUnrepeatableLabel ,
2223} ;
2324use crate :: mbe:: macro_parser:: NamedMatch ;
2425use crate :: mbe:: macro_parser:: NamedMatch :: * ;
@@ -246,7 +247,7 @@ pub(super) fn transcribe<'a>(
246247 match tree {
247248 // Replace the sequence with its expansion.
248249 seq @ mbe:: TokenTree :: Sequence ( _, seq_rep) => {
249- transcribe_sequence ( & mut tscx, seq, seq_rep) ?;
250+ transcribe_sequence ( & mut tscx, seq, seq_rep, interp ) ?;
250251 }
251252
252253 // Replace the meta-var with the matched token tree from the invocation.
@@ -293,6 +294,8 @@ fn transcribe_sequence<'tx, 'itp>(
293294 tscx : & mut TranscrCtx < ' tx , ' itp > ,
294295 seq : & mbe:: TokenTree ,
295296 seq_rep : & ' itp mbe:: SequenceRepetition ,
297+ // Used only for better diagnostics in the face of typos.
298+ interp : & FxHashMap < MacroRulesNormalizedIdent , NamedMatch > ,
296299) -> PResult < ' tx , ( ) > {
297300 let dcx = tscx. psess . dcx ( ) ;
298301
@@ -301,7 +304,70 @@ fn transcribe_sequence<'tx, 'itp>(
301304 // macro writer has made a mistake.
302305 match lockstep_iter_size ( seq, tscx. interp , & tscx. repeats ) {
303306 LockstepIterSize :: Unconstrained => {
304- return Err ( dcx. create_err ( NoSyntaxVarsExprRepeat { span : seq. span ( ) } ) ) ;
307+ let mut repeatables = Vec :: new ( ) ;
308+ let mut non_repeatables = Vec :: new ( ) ;
309+
310+ #[ allow( rustc:: potential_query_instability) ]
311+ for ( name, matcher) in interp. iter ( ) {
312+ if matcher. is_repeatable ( ) {
313+ repeatables. push ( name) ;
314+ } else {
315+ non_repeatables. push ( name) ;
316+ }
317+ }
318+
319+ let repeatable_names: Vec < Symbol > =
320+ repeatables. iter ( ) . map ( |& name| name. symbol ( ) ) . collect ( ) ;
321+ let non_repeatable_names: Vec < Symbol > =
322+ non_repeatables. iter ( ) . map ( |& name| name. symbol ( ) ) . collect ( ) ;
323+ let mut meta_vars = vec ! [ ] ;
324+ seq. meta_vars ( & mut meta_vars) ;
325+ let mut typo_repeatable = None ;
326+ let mut typo_unrepeatable = None ;
327+ let mut typo_unrepeatable_label = None ;
328+ let mut var_no_typo = None ;
329+ let mut no_repeatable_var = None ;
330+
331+ for ident in meta_vars {
332+ if let Some ( name) = rustc_span:: edit_distance:: find_best_match_for_name (
333+ & repeatable_names[ ..] ,
334+ ident. name ,
335+ None ,
336+ ) {
337+ typo_repeatable = Some ( VarTypoSuggestionRepeatable { span : ident. span , name } ) ;
338+ } else if let Some ( name) = rustc_span:: edit_distance:: find_best_match_for_name (
339+ & non_repeatable_names[ ..] ,
340+ ident. name ,
341+ None ,
342+ ) {
343+ typo_unrepeatable =
344+ Some ( VarTypoSuggestionUnrepeatable { span : ident. span , name } ) ;
345+ if let Some ( & orig_ident) = non_repeatables. iter ( ) . find ( |n| n. symbol ( ) == name) {
346+ typo_unrepeatable_label = Some ( VarTypoSuggestionUnrepeatableLabel {
347+ span : orig_ident. ident ( ) . span ,
348+ } ) ;
349+ }
350+ } else {
351+ if !repeatable_names. is_empty ( ) {
352+ let msg = repeatable_names
353+ . iter ( )
354+ . map ( |sym| format ! ( "${}" , sym) )
355+ . collect :: < Vec < _ > > ( )
356+ . join ( ", " ) ;
357+ var_no_typo = Some ( VarNoTypo { span : ident. span , msg } ) ;
358+ } else {
359+ no_repeatable_var = Some ( NoRepeatableVar { span : ident. span } ) ;
360+ }
361+ }
362+ }
363+ return Err ( dcx. create_err ( NoSyntaxVarsExprRepeat {
364+ span : seq. span ( ) ,
365+ typo_unrepeatable,
366+ typo_repeatable,
367+ typo_unrepeatable_label,
368+ var_no_typo,
369+ no_repeatable_var,
370+ } ) ) ;
305371 }
306372
307373 LockstepIterSize :: Contradiction ( msg) => {
0 commit comments