@@ -24,6 +24,13 @@ pub enum RecoverComma {
2424 No ,
2525}
2626
27+ /// Whether or not to recover a `:` when parsing patterns that were meant to be paths.
28+ #[ derive( PartialEq , Copy , Clone ) ]
29+ pub enum RecoverColon {
30+ Yes ,
31+ No ,
32+ }
33+
2734/// The result of `eat_or_separator`. We want to distinguish which case we are in to avoid
2835/// emitting duplicate diagnostics.
2936#[ derive( Debug , Clone , Copy ) ]
@@ -58,8 +65,9 @@ impl<'a> Parser<'a> {
5865 & mut self ,
5966 expected : Expected ,
6067 rc : RecoverComma ,
68+ ra : RecoverColon ,
6169 ) -> PResult < ' a , P < Pat > > {
62- self . parse_pat_allow_top_alt_inner ( expected, rc) . map ( |( pat, _) | pat)
70+ self . parse_pat_allow_top_alt_inner ( expected, rc, ra ) . map ( |( pat, _) | pat)
6371 }
6472
6573 /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
@@ -68,6 +76,7 @@ impl<'a> Parser<'a> {
6876 & mut self ,
6977 expected : Expected ,
7078 rc : RecoverComma ,
79+ ra : RecoverColon ,
7180 ) -> PResult < ' a , ( P < Pat > , bool ) > {
7281 // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
7382 // suggestions (which bothers rustfix).
@@ -89,6 +98,56 @@ impl<'a> Parser<'a> {
8998 // If we parsed a leading `|` which should be gated,
9099 // then we should really gate the leading `|`.
91100 // This complicated procedure is done purely for diagnostics UX.
101+ let mut first_pat = first_pat;
102+
103+ if let ( RecoverColon :: Yes , token:: Colon ) = ( ra, & self . token . kind ) {
104+ if matches ! (
105+ first_pat. kind,
106+ PatKind :: Ident ( BindingMode :: ByValue ( Mutability :: Not ) , _, None )
107+ | PatKind :: Path ( ..)
108+ ) && self . look_ahead ( 1 , |token| token. is_ident ( ) && !token. is_reserved_ident ( ) )
109+ {
110+ // The pattern looks like it might be a path with a `::` -> `:` typo:
111+ // `match foo { bar:baz => {} }`
112+ let span = self . token . span ;
113+ // We only emit "unexpected `:`" error here if we can successfully parse the
114+ // whole pattern correctly in that case.
115+ let snapshot = self . clone ( ) ;
116+
117+ // Create error for "unexpected `:`".
118+ match self . expected_one_of_not_found ( & [ ] , & [ ] ) {
119+ Err ( mut err) => {
120+ self . bump ( ) ; // Skip the `:`.
121+ match self . parse_pat_no_top_alt ( expected) {
122+ Err ( mut inner_err) => {
123+ // Carry on as if we had not done anything, callers will emit a
124+ // reasonable error.
125+ inner_err. cancel ( ) ;
126+ err. cancel ( ) ;
127+ * self = snapshot;
128+ }
129+ Ok ( pat) => {
130+ // We've parsed the rest of the pattern.
131+ err. span_suggestion (
132+ span,
133+ "maybe write a path separator here" ,
134+ "::" . to_string ( ) ,
135+ Applicability :: MachineApplicable ,
136+ ) ;
137+ err. emit ( ) ;
138+ first_pat =
139+ self . mk_pat ( first_pat. span . to ( pat. span ) , PatKind :: Wild ) ;
140+ }
141+ }
142+ }
143+ _ => {
144+ // Carry on as if we had not done anything. This should be unreachable.
145+ * self = snapshot;
146+ }
147+ } ;
148+ }
149+ }
150+
92151 if let Some ( leading_vert_span) = leading_vert_span {
93152 // If there was a leading vert, treat this as an or-pattern. This improves
94153 // diagnostics.
@@ -140,7 +199,8 @@ impl<'a> Parser<'a> {
140199 // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level
141200 // or-patterns so that we can detect when a user tries to use it. This allows us to print a
142201 // better error message.
143- let ( pat, trailing_vert) = self . parse_pat_allow_top_alt_inner ( expected, rc) ?;
202+ let ( pat, trailing_vert) =
203+ self . parse_pat_allow_top_alt_inner ( expected, rc, RecoverColon :: No ) ?;
144204 let colon = self . eat ( & token:: Colon ) ;
145205
146206 if let PatKind :: Or ( pats) = & pat. kind {
@@ -350,7 +410,7 @@ impl<'a> Parser<'a> {
350410 } else if self . check ( & token:: OpenDelim ( token:: Bracket ) ) {
351411 // Parse `[pat, pat,...]` as a slice pattern.
352412 let ( pats, _) = self . parse_delim_comma_seq ( token:: Bracket , |p| {
353- p. parse_pat_allow_top_alt ( None , RecoverComma :: No )
413+ p. parse_pat_allow_top_alt ( None , RecoverComma :: No , RecoverColon :: No )
354414 } ) ?;
355415 PatKind :: Slice ( pats)
356416 } else if self . check ( & token:: DotDot ) && !self . is_pat_range_end_start ( 1 ) {
@@ -563,8 +623,9 @@ impl<'a> Parser<'a> {
563623
564624 /// Parse a tuple or parenthesis pattern.
565625 fn parse_pat_tuple_or_parens ( & mut self ) -> PResult < ' a , PatKind > {
566- let ( fields, trailing_comma) =
567- self . parse_paren_comma_seq ( |p| p. parse_pat_allow_top_alt ( None , RecoverComma :: No ) ) ?;
626+ let ( fields, trailing_comma) = self . parse_paren_comma_seq ( |p| {
627+ p. parse_pat_allow_top_alt ( None , RecoverComma :: No , RecoverColon :: No )
628+ } ) ?;
568629
569630 // Here, `(pat,)` is a tuple pattern.
570631 // For backward compatibility, `(..)` is a tuple pattern as well.
@@ -873,8 +934,9 @@ impl<'a> Parser<'a> {
873934
874935 /// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`).
875936 fn parse_pat_tuple_struct ( & mut self , qself : Option < QSelf > , path : Path ) -> PResult < ' a , PatKind > {
876- let ( fields, _) =
877- self . parse_paren_comma_seq ( |p| p. parse_pat_allow_top_alt ( None , RecoverComma :: No ) ) ?;
937+ let ( fields, _) = self . parse_paren_comma_seq ( |p| {
938+ p. parse_pat_allow_top_alt ( None , RecoverComma :: No , RecoverColon :: No )
939+ } ) ?;
878940 if qself. is_some ( ) {
879941 self . sess . gated_spans . gate ( sym:: more_qualified_paths, path. span ) ;
880942 }
@@ -1033,7 +1095,7 @@ impl<'a> Parser<'a> {
10331095 // Parsing a pattern of the form `fieldname: pat`.
10341096 let fieldname = self . parse_field_name ( ) ?;
10351097 self . bump ( ) ;
1036- let pat = self . parse_pat_allow_top_alt ( None , RecoverComma :: No ) ?;
1098+ let pat = self . parse_pat_allow_top_alt ( None , RecoverComma :: No , RecoverColon :: No ) ?;
10371099 hi = pat. span ;
10381100 ( pat, fieldname, false )
10391101 } else {
0 commit comments