]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #107190 - fmease:fix-81698, r=compiler-errors
authorMatthias Krüger <matthias.krueger@famsik.de>
Sat, 28 Jan 2023 04:20:17 +0000 (05:20 +0100)
committerGitHub <noreply@github.com>
Sat, 28 Jan 2023 04:20:17 +0000 (05:20 +0100)
Recover from more const arguments that are not wrapped in curly braces

Recover from some array, borrow, tuple & arithmetic expressions in const argument positions that lack curly braces and provide a suggestion to fix the issue continuing where #92884 left off. Examples of such expressions: `[]`, `[0]`, `[1, 2]`, `[0; 0xff]`, `&9`, `("", 0)` and `(1 + 2) * 3` (we previously did not recover from them).

I am not entirely happy with my current solution because the code that recovers from `[0]` (coinciding with a malformed slice type) and `[0; 0]` (coinciding with a malformed array type) is quite fragile as the aforementioned snippets are actually successfully parsed as types by `parse_ty` since it itself already recovers from them (returning `[⟨error⟩]` and `[⟨error⟩; 0]` respectively) meaning I have to manually look for `TyKind::Err`s and construct a separate diagnostic for the suggestion to attach to (thereby emitting two diagnostics in total).

Fixes #81698.
`@rustbot` label A-diagnostics
r? diagnostics

1  2 
compiler/rustc_parse/src/parser/diagnostics.rs

index eda7046c748e5f52121854349a3fa6f903e502d2,6596a06afab659fdad764741f4878389766cf7db..f4c08031bcca08c15542d6eb326d56bd07e54749
@@@ -2353,6 -2353,28 +2353,28 @@@ impl<'a> Parser<'a> 
          Err(err)
      }
  
+     /// Try to recover from an unbraced const argument whose first token [could begin a type][ty].
+     ///
+     /// [ty]: token::Token::can_begin_type
+     pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty(
+         &mut self,
+         mut snapshot: SnapshotParser<'a>,
+     ) -> Option<P<ast::Expr>> {
+         match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) {
+             // Since we don't know the exact reason why we failed to parse the type or the
+             // expression, employ a simple heuristic to weed out some pathological cases.
+             Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => {
+                 self.restore_snapshot(snapshot);
+                 Some(expr)
+             }
+             Ok(_) => None,
+             Err(err) => {
+                 err.cancel();
+                 None
+             }
+         }
+     }
      /// Creates a dummy const argument, and reports that the expression must be enclosed in braces
      pub fn dummy_const_arg_needs_braces(
          &self,
  
      /// Some special error handling for the "top-level" patterns in a match arm,
      /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
 -    pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
 +    pub(crate) fn maybe_recover_colon_colon_in_pat_typo_or_anon_enum(
          &mut self,
          mut first_pat: P<Pat>,
          expected: Expected,
          if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
              || !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
          {
 +            let mut snapshot_type = self.create_snapshot_for_diagnostic();
 +            snapshot_type.bump(); // `:`
 +            match snapshot_type.parse_ty() {
 +                Err(inner_err) => {
 +                    inner_err.cancel();
 +                }
 +                Ok(ty) => {
 +                    let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
 +                        return first_pat;
 +                    };
 +                    err.span_label(ty.span, "specifying the type of a pattern isn't supported");
 +                    self.restore_snapshot(snapshot_type);
 +                    let span = first_pat.span.to(ty.span);
 +                    first_pat = self.mk_pat(span, PatKind::Wild);
 +                    err.emit();
 +                }
 +            }
              return first_pat;
          }
          // The pattern looks like it might be a path with a `::` -> `:` typo:
          // `match foo { bar:baz => {} }`
 -        let span = self.token.span;
 +        let colon_span = self.token.span;
          // We only emit "unexpected `:`" error here if we can successfully parse the
          // whole pattern correctly in that case.
 -        let snapshot = self.create_snapshot_for_diagnostic();
 +        let mut snapshot_pat = self.create_snapshot_for_diagnostic();
 +        let mut snapshot_type = self.create_snapshot_for_diagnostic();
  
          // Create error for "unexpected `:`".
          match self.expected_one_of_not_found(&[], &[]) {
              Err(mut err) => {
 -                self.bump(); // Skip the `:`.
 -                match self.parse_pat_no_top_alt(expected) {
 +                snapshot_pat.bump(); // Skip the `:`.
 +                snapshot_type.bump(); // Skip the `:`.
 +                match snapshot_pat.parse_pat_no_top_alt(expected) {
                      Err(inner_err) => {
 -                        // Carry on as if we had not done anything, callers will emit a
 -                        // reasonable error.
                          inner_err.cancel();
 -                        err.cancel();
 -                        self.restore_snapshot(snapshot);
                      }
                      Ok(mut pat) => {
                          // We've parsed the rest of the pattern.
                              _ => {}
                          }
                          if show_sugg {
 -                            err.span_suggestion(
 -                                span,
 +                            err.span_suggestion_verbose(
 +                                colon_span.until(self.look_ahead(1, |t| t.span)),
                                  "maybe write a path separator here",
                                  "::",
                                  Applicability::MaybeIncorrect,
                          } else {
                              first_pat = self.mk_pat(new_span, PatKind::Wild);
                          }
 -                        err.emit();
 +                        self.restore_snapshot(snapshot_pat);
                      }
                  }
 +                match snapshot_type.parse_ty() {
 +                    Err(inner_err) => {
 +                        inner_err.cancel();
 +                    }
 +                    Ok(ty) => {
 +                        err.span_label(ty.span, "specifying the type of a pattern isn't supported");
 +                        self.restore_snapshot(snapshot_type);
 +                        let new_span = first_pat.span.to(ty.span);
 +                        first_pat = self.mk_pat(new_span, PatKind::Wild);
 +                    }
 +                }
 +                err.emit();
              }
              _ => {
                  // Carry on as if we had not done anything. This should be unreachable.
 -                self.restore_snapshot(snapshot);
              }
          };
          first_pat