From 2d824206655bfb26cb5eed43490ee396542b153e Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sun, 15 Jan 2023 22:36:46 +0000 Subject: Teach parser to understand fake anonymous enum syntax Parse `-> Ty | OtherTy`. Parse type ascription in top level patterns. --- compiler/rustc_parse/src/parser/diagnostics.rs | 50 ++++++++++++++----- compiler/rustc_parse/src/parser/pat.rs | 3 +- compiler/rustc_parse/src/parser/ty.rs | 67 ++++++++++++++++++++++++-- 3 files changed, 102 insertions(+), 18 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4c918c6702e..07bd76dc39b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2372,7 +2372,7 @@ impl<'a> Parser<'a> { /// 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, expected: Expected, @@ -2383,26 +2383,41 @@ impl<'a> Parser<'a> { 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. @@ -2466,8 +2481,8 @@ impl<'a> Parser<'a> { _ => {} } 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, @@ -2475,13 +2490,22 @@ impl<'a> Parser<'a> { } 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); + } + } + err.emit(); } _ => { // Carry on as if we had not done anything. This should be unreachable. - self.restore_snapshot(snapshot); } }; first_pat diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e73a17ced7d..e5411538eea 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -116,7 +116,8 @@ impl<'a> Parser<'a> { // Check if the user wrote `foo:bar` instead of `foo::bar`. if ra == RecoverColon::Yes { - first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected); + first_pat = + self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected); } if let Some(leading_vert_span) = leading_vert_span { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a6f702e5428..72994df6f9f 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -11,6 +11,7 @@ use rustc_ast::{ self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, }; +use rustc_ast_pretty::pprust; use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident}; @@ -43,17 +44,24 @@ pub(super) enum AllowPlus { No, } -#[derive(PartialEq)] +#[derive(PartialEq, Clone, Copy)] pub(super) enum RecoverQPath { Yes, No, } +#[derive(PartialEq, Clone, Copy)] pub(super) enum RecoverQuestionMark { Yes, No, } +#[derive(PartialEq, Clone, Copy)] +pub(super) enum RecoverAnonEnum { + Yes, + No, +} + /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -86,7 +94,7 @@ impl RecoverReturnSign { } // Is `...` (`CVarArgs`) legal at this level of type parsing? -#[derive(PartialEq)] +#[derive(PartialEq, Clone, Copy)] enum AllowCVariadic { Yes, No, @@ -111,6 +119,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -125,6 +134,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -139,6 +149,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, ) } @@ -156,6 +167,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -169,6 +181,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, + RecoverAnonEnum::No, ) } @@ -180,6 +193,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, + RecoverAnonEnum::No, ) } @@ -192,6 +206,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -211,6 +226,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -232,6 +248,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, )?; FnRetTy::Ty(ty) } else { @@ -247,6 +264,7 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, + recover_anon_enum: RecoverAnonEnum, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -325,14 +343,55 @@ impl<'a> Parser<'a> { let mut ty = self.mk_ty(span, kind); // Try to recover from use of `+` with incorrect priority. - if matches!(allow_plus, AllowPlus::Yes) { + if allow_plus == AllowPlus::Yes { self.maybe_recover_from_bad_type_plus(&ty)?; } else { self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty); } - if let RecoverQuestionMark::Yes = recover_question_mark { + if RecoverQuestionMark::Yes == recover_question_mark { ty = self.maybe_recover_from_question_mark(ty); } + if recover_anon_enum == RecoverAnonEnum::Yes + && self.check_noexpect(&token::BinOp(token::Or)) + && self.look_ahead(1, |t| t.can_begin_type()) + { + let mut pipes = vec![self.token.span]; + let mut types = vec![ty]; + loop { + if !self.eat(&token::BinOp(token::Or)) { + break; + } + pipes.push(self.prev_token.span); + types.push(self.parse_ty_common( + allow_plus, + allow_c_variadic, + recover_qpath, + recover_return_sign, + ty_generics, + recover_question_mark, + RecoverAnonEnum::No, + )?); + } + let mut err = self.struct_span_err(pipes, "anonymous enums are not supported"); + for ty in &types { + err.span_label(ty.span, ""); + } + err.help(&format!( + "create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}", + types + .iter() + .enumerate() + .map(|(i, t)| format!( + " Variant{}({}),", + i + 1, // Lets not confuse people with zero-indexing :) + pprust::to_string(|s| s.print_type(&t)), + )) + .collect::>() + .join("\n"), + )); + err.emit(); + return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::AnonEnum(types))); + } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } -- cgit 1.4.1-3-g733a5 From c847a01a3b1f620c4fdb98c75805033e768975d1 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Tue, 17 Jan 2023 01:50:45 +0000 Subject: Emit fewer errors on patterns with possible type ascription --- compiler/rustc_parse/src/parser/diagnostics.rs | 2 ++ tests/ui/parser/anon-enums.rs | 2 -- tests/ui/parser/anon-enums.stderr | 21 ++++----------------- .../ui/parser/issues/issue-87086-colon-path-sep.rs | 1 - .../parser/issues/issue-87086-colon-path-sep.stderr | 11 ++--------- 5 files changed, 8 insertions(+), 29 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 07bd76dc39b..eda7046c748 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2500,6 +2500,8 @@ impl<'a> Parser<'a> { 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(); diff --git a/tests/ui/parser/anon-enums.rs b/tests/ui/parser/anon-enums.rs index a8b5e8359d9..56b8a3d43be 100644 --- a/tests/ui/parser/anon-enums.rs +++ b/tests/ui/parser/anon-enums.rs @@ -3,7 +3,6 @@ fn foo(x: bool | i32) -> i32 | f64 { //~| ERROR anonymous enums are not supported match x { x: i32 => x, //~ ERROR expected - //~^ ERROR failed to resolve true => 42., false => 0.333, } @@ -14,6 +13,5 @@ fn main() { 42: i32 => (), //~ ERROR expected _: f64 => (), //~ ERROR expected x: i32 => (), //~ ERROR expected - //~^ ERROR failed to resolve } } diff --git a/tests/ui/parser/anon-enums.stderr b/tests/ui/parser/anon-enums.stderr index 4febf2df0dc..84158225660 100644 --- a/tests/ui/parser/anon-enums.stderr +++ b/tests/ui/parser/anon-enums.stderr @@ -36,7 +36,7 @@ LL | x::i32 => x, | ~~ error: expected one of `...`, `..=`, `..`, or `|`, found `:` - --> $DIR/anon-enums.rs:14:11 + --> $DIR/anon-enums.rs:13:11 | LL | 42: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -44,7 +44,7 @@ LL | 42: i32 => (), | expected one of `...`, `..=`, `..`, or `|` error: expected `|`, found `:` - --> $DIR/anon-enums.rs:15:10 + --> $DIR/anon-enums.rs:14:10 | LL | _: f64 => (), | ^ --- specifying the type of a pattern isn't supported @@ -52,7 +52,7 @@ LL | _: f64 => (), | expected `|` error: expected one of `@` or `|`, found `:` - --> $DIR/anon-enums.rs:16:10 + --> $DIR/anon-enums.rs:15:10 | LL | x: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -64,18 +64,5 @@ help: maybe write a path separator here LL | x::i32 => (), | ~~ -error[E0433]: failed to resolve: use of undeclared crate or module `x` - --> $DIR/anon-enums.rs:5:9 - | -LL | x: i32 => x, - | ^ use of undeclared crate or module `x` - -error[E0433]: failed to resolve: use of undeclared crate or module `x` - --> $DIR/anon-enums.rs:16:9 - | -LL | x: i32 => (), - | ^ use of undeclared crate or module `x` - -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs index 0b7b67496d6..e1ea38f2795 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs @@ -68,7 +68,6 @@ fn main() { Foo:Bar::Baz => {} //~^ ERROR: expected one of //~| HELP: maybe write a path separator here - //~| ERROR: failed to resolve: `Bar` is a variant, not a module } match myfoo { Foo::Bar => {} diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr index ed05bfe8b0a..63b072ac4cd 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr @@ -103,7 +103,7 @@ LL | Foo::Bar::Baz => {} | ~~ error: expected one of `@` or `|`, found `:` - --> $DIR/issue-87086-colon-path-sep.rs:75:12 + --> $DIR/issue-87086-colon-path-sep.rs:74:12 | LL | Foo:Bar => {} | ^--- specifying the type of a pattern isn't supported @@ -115,12 +115,5 @@ help: maybe write a path separator here LL | Foo::Bar => {} | ~~ -error[E0433]: failed to resolve: `Bar` is a variant, not a module - --> $DIR/issue-87086-colon-path-sep.rs:68:13 - | -LL | Foo:Bar::Baz => {} - | ^^^ `Bar` is a variant, not a module - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0433`. -- cgit 1.4.1-3-g733a5 From 020cca8d36cb678e3ddc2ead41364be314d19e93 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 23 Jan 2023 14:29:53 +0000 Subject: review comment: Remove AST AnonTy --- compiler/rustc_ast/src/ast.rs | 3 --- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 1 - compiler/rustc_ast_pretty/src/pprust/state.rs | 3 --- compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_passes/src/hir_stats.rs | 1 - src/tools/rustfmt/src/types.rs | 4 +--- 8 files changed, 4 insertions(+), 14 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3aad51325dc..7de594719dd 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2096,9 +2096,6 @@ pub enum TyKind { Err, /// Placeholder for a `va_list`. CVarArgs, - /// Placeholder for "anonymous enums", which don't exist, but keeping their - /// information around lets us produce better diagnostics. - AnonEnum(Vec>), } impl TyKind { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 894885cf0fe..77f342d1eb3 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -470,7 +470,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_fn_decl(decl); vis.visit_span(decl_span); } - TyKind::AnonEnum(tys) | TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), + TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Path(qself, path) => { vis.visit_qself(qself); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1ab70f0309c..feb5187536f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -400,7 +400,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref); visitor.visit_ty(&mutable_type.ty) } - TyKind::AnonEnum(tys) | TyKind::Tup(tys) => { + TyKind::Tup(tys) => { walk_list!(visitor, visit_ty, tys); } TyKind::BareFn(function_declaration) => { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a60d02002da..41d4a5679f1 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1235,7 +1235,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match &t.kind { TyKind::Infer => hir::TyKind::Infer, TyKind::Err => hir::TyKind::Err, - TyKind::AnonEnum(_) => hir::TyKind::Err, TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 3f9b96a6158..6a8064b0e87 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1041,9 +1041,6 @@ impl<'a> State<'a> { } self.pclose(); } - ast::TyKind::AnonEnum(elts) => { - self.strsep("|", false, Inconsistent, elts, |s, ty| s.print_type(ty)); - } ast::TyKind::Paren(typ) => { self.popen(); self.print_type(typ); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 72994df6f9f..43e6eac438b 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -390,7 +390,7 @@ impl<'a> Parser<'a> { .join("\n"), )); err.emit(); - return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::AnonEnum(types))); + return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err)); } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 312bd8839e0..b86d2316820 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -579,7 +579,6 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { [ Slice, Array, - AnonEnum, Ptr, Ref, BareFn, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index f065ec680d7..c1991e8d2c8 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -839,9 +839,7 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Some("...".to_owned()), - ast::TyKind::AnonEnum(_) | ast::TyKind::Err => { - Some(context.snippet(self.span).to_owned()) - } + ast::TyKind::Err => Some(context.snippet(self.span).to_owned()), ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof", -- cgit 1.4.1-3-g733a5 From a8b77cfe5464e29b25389593ced9d080bf0dd6c8 Mon Sep 17 00:00:00 2001 From: Edward Shen Date: Mon, 23 Jan 2023 20:31:45 -0800 Subject: Add suggestion to remove if in let...else block Adds an additional hint to failures where we encounter an else keyword while we're parsing an if-let block. This is likely that the user has accidentally mixed if-let and let...else together. --- .../rustc_error_messages/locales/en-US/parse.ftl | 1 + compiler/rustc_parse/src/errors.rs | 11 +++++++- compiler/rustc_parse/src/parser/expr.rs | 33 +++++++++++++--------- tests/ui/let-else/accidental-if.rs | 6 ++++ tests/ui/let-else/accidental-if.stderr | 19 +++++++++++++ ...t-else-does-not-interact-with-let-chains.stderr | 5 ++++ 6 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 tests/ui/let-else/accidental-if.rs create mode 100644 tests/ui/let-else/accidental-if.stderr (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl index 8f063f5082c..a3e2002da78 100644 --- a/compiler/rustc_error_messages/locales/en-US/parse.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl @@ -238,6 +238,7 @@ parse_const_let_mutually_exclusive = `const` and `let` are mutually exclusive parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` parse_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed +parse_extra_if_in_let_else = remove the `if` if you meant to write a `let...else` statement parse_compound_assignment_expression_in_let = can't reassign to an uninitialized variable .suggestion = initialize the variable diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 06b970ad979..40763da0bb5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -337,7 +337,9 @@ pub(crate) struct IfExpressionMissingThenBlock { #[primary_span] pub if_span: Span, #[subdiagnostic] - pub sub: IfExpressionMissingThenBlockSub, + pub missing_then_block_sub: IfExpressionMissingThenBlockSub, + #[subdiagnostic] + pub let_else_sub: Option, } #[derive(Subdiagnostic)] @@ -348,6 +350,13 @@ pub(crate) enum IfExpressionMissingThenBlockSub { AddThenBlock(#[primary_span] Span), } +#[derive(Subdiagnostic)] +#[help(parse_extra_if_in_let_else)] +pub(crate) struct IfExpressionLetSomeSub { + #[primary_span] + pub if_span: Span, +} + #[derive(Diagnostic)] #[diag(parse_if_expression_missing_condition)] pub(crate) struct IfExpressionMissingCondition { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index bf93a89f065..3225a309a31 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -11,15 +11,15 @@ use crate::errors::{ ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, - IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, - InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, - InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator, - InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, - LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, - MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, - MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, - NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub, - OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, + IfExpressionLetSomeSub, IfExpressionMissingCondition, IfExpressionMissingThenBlock, + IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, + InvalidComparisonOperatorSub, InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, + InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, + LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, + MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, + MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, + MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator, + NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf, UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, @@ -2251,9 +2251,10 @@ impl<'a> Parser<'a> { if let ExprKind::Block(_, None) = right.kind => { self.sess.emit_err(IfExpressionMissingThenBlock { if_span: lo, - sub: IfExpressionMissingThenBlockSub::UnfinishedCondition( - cond_span.shrink_to_lo().to(*binop_span) - ), + missing_then_block_sub: + IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)), + let_else_sub: None, + }); std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi())) }, @@ -2279,9 +2280,15 @@ impl<'a> Parser<'a> { if let Some(block) = recover_block_from_condition(self) { block } else { + let let_else_sub = matches!(cond.kind, ExprKind::Let(..)) + .then(|| IfExpressionLetSomeSub { if_span: lo }); + self.sess.emit_err(IfExpressionMissingThenBlock { if_span: lo, - sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()), + missing_then_block_sub: IfExpressionMissingThenBlockSub::AddThenBlock( + cond_span.shrink_to_hi(), + ), + let_else_sub, }); self.mk_block_err(cond_span.shrink_to_hi()) } diff --git a/tests/ui/let-else/accidental-if.rs b/tests/ui/let-else/accidental-if.rs new file mode 100644 index 00000000000..3fba630435c --- /dev/null +++ b/tests/ui/let-else/accidental-if.rs @@ -0,0 +1,6 @@ +fn main() { + let x = Some(123); + if let Some(y) = x else { //~ ERROR this `if` expression is missing a block + return; + }; +} diff --git a/tests/ui/let-else/accidental-if.stderr b/tests/ui/let-else/accidental-if.stderr new file mode 100644 index 00000000000..5474a67aac4 --- /dev/null +++ b/tests/ui/let-else/accidental-if.stderr @@ -0,0 +1,19 @@ +error: this `if` expression is missing a block after the condition + --> $DIR/accidental-if.rs:3:5 + | +LL | if let Some(y) = x else { + | ^^ + | +help: add a block here + --> $DIR/accidental-if.rs:3:23 + | +LL | if let Some(y) = x else { + | ^ +help: remove the `if` if you meant to write a `let...else` statement + --> $DIR/accidental-if.rs:3:5 + | +LL | if let Some(y) = x else { + | ^^ + +error: aborting due to previous error + diff --git a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr index 498a112fa9b..f34ccecdd45 100644 --- a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr +++ b/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr @@ -37,6 +37,11 @@ help: add a block here | LL | if let Some(n) = opt else { | ^ +help: remove the `if` if you meant to write a `let...else` statement + --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:5 + | +LL | if let Some(n) = opt else { + | ^^ error: this `if` expression is missing a block after the condition --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:5 -- cgit 1.4.1-3-g733a5 From cd233231aa215b7a5642e6a80869959b375e0725 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 26 Jan 2023 11:02:19 +0800 Subject: Improve unexpected close and mismatch delimiter hint in TokenTreesReader --- compiler/rustc_parse/src/lexer/diagnostics.rs | 119 +++++++++++++++++++ compiler/rustc_parse/src/lexer/mod.rs | 1 + compiler/rustc_parse/src/lexer/tokentrees.rs | 129 +++++++-------------- tests/ui/parser/deli-ident-issue-1.rs | 1 - tests/ui/parser/deli-ident-issue-1.stderr | 16 +-- tests/ui/parser/deli-ident-issue-2.stderr | 6 +- tests/ui/parser/issue-68987-unmatch-issue-1.stderr | 11 +- tests/ui/parser/issue-68987-unmatch-issue-2.stderr | 8 +- tests/ui/parser/issue-68987-unmatch-issue-3.stderr | 6 +- tests/ui/parser/issue-68987-unmatch-issue.stderr | 11 +- tests/ui/parser/issue-81827.stderr | 10 +- tests/ui/parser/issues/issue-62973.stderr | 12 +- tests/ui/parser/issues/issue-63116.stderr | 5 +- tests/ui/parser/issues/issue-69259.stderr | 5 - .../issues/issue-70583-block-is-empty-1.stderr | 4 +- .../issues/issue-70583-block-is-empty-2.stderr | 6 +- .../macro-mismatched-delim-paren-brace.stderr | 4 +- tests/ui/typeck/issue-91334.stderr | 10 +- 18 files changed, 225 insertions(+), 139 deletions(-) create mode 100644 compiler/rustc_parse/src/lexer/diagnostics.rs (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs new file mode 100644 index 00000000000..386bf026bb4 --- /dev/null +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -0,0 +1,119 @@ +use super::UnmatchedBrace; +use rustc_ast::token::Delimiter; +use rustc_errors::Diagnostic; +use rustc_span::source_map::SourceMap; +use rustc_span::Span; + +#[derive(Default)] +pub struct TokenTreeDiagInfo { + /// Stack of open delimiters and their spans. Used for error message. + pub open_braces: Vec<(Delimiter, Span)>, + pub unmatched_braces: Vec, + + /// Used only for error recovery when arriving to EOF with mismatched braces. + pub last_unclosed_found_span: Option, + + /// Collect empty block spans that might have been auto-inserted by editors. + pub empty_block_spans: Vec, + + /// Collect the spans of braces (Open, Close). Used only + /// for detecting if blocks are empty and only braces. + pub matching_block_spans: Vec<(Span, Span)>, +} + +pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool { + match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) { + (Some(open_padding), Some(close_padding)) => open_padding == close_padding, + _ => false, + } +} + +// When we get a `)` or `]` for `{`, we should emit help message here +// it's more friendly compared to report `unmatched error` in later phase +pub fn report_missing_open_delim( + err: &mut Diagnostic, + unmatched_braces: &[UnmatchedBrace], +) -> bool { + let mut reported_missing_open = false; + for unmatch_brace in unmatched_braces.iter() { + if let Some(delim) = unmatch_brace.found_delim + && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket) + { + let missed_open = match delim { + Delimiter::Parenthesis => "(", + Delimiter::Bracket => "[", + _ => unreachable!(), + }; + err.span_label( + unmatch_brace.found_span.shrink_to_lo(), + format!("missing open `{}` for this delimiter", missed_open), + ); + reported_missing_open = true; + } + } + reported_missing_open +} + +pub fn report_suspicious_mismatch_block( + err: &mut Diagnostic, + diag_info: &TokenTreeDiagInfo, + sm: &SourceMap, + delim: Delimiter, +) { + if report_missing_open_delim(err, &diag_info.unmatched_braces) { + return; + } + + let mut matched_spans: Vec<(Span, bool)> = diag_info + .matching_block_spans + .iter() + .map(|&(open, close)| (open.with_hi(close.lo()), same_identation_level(sm, open, close))) + .collect(); + + // sort by `lo`, so the large block spans in the front + matched_spans.sort_by(|a, b| a.0.lo().cmp(&b.0.lo())); + + // We use larger block whose identation is well to cover those inner mismatched blocks + // O(N^2) here, but we are on error reporting path, so it is fine + for i in 0..matched_spans.len() { + let (block_span, same_ident) = matched_spans[i]; + if same_ident { + for j in i + 1..matched_spans.len() { + let (inner_block, inner_same_ident) = matched_spans[j]; + if block_span.contains(inner_block) && !inner_same_ident { + matched_spans[j] = (inner_block, true); + } + } + } + } + + // Find the inner-most span candidate for final report + let candidate_span = + matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span); + + if let Some(block_span) = candidate_span { + err.span_label(block_span.shrink_to_lo(), "this delimiter might not be properly closed..."); + err.span_label( + block_span.shrink_to_hi(), + "...as it matches this but it has different indentation", + ); + + // If there is a empty block in the mismatched span, note it + if delim == Delimiter::Brace { + for span in diag_info.empty_block_spans.iter() { + if block_span.contains(*span) { + err.span_label(*span, "block is empty, you might have not meant to close it"); + break; + } + } + } + } else { + // If there is no suspicious span, give the last properly closed block may help + if let Some(parent) = diag_info.matching_block_spans.last() + && diag_info.open_braces.last().is_none() + && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) { + err.span_label(parent.0, "this opening brace..."); + err.span_label(parent.1, "...matches this closing brace"); + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 9fe8d9836ba..e957224a033 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -17,6 +17,7 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{edition::Edition, BytePos, Pos, Span}; +mod diagnostics; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b2701817d48..0de8f79112c 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,29 +1,18 @@ +use super::diagnostics::report_suspicious_mismatch_block; +use super::diagnostics::same_identation_level; +use super::diagnostics::TokenTreeDiagInfo; use super::{StringReader, UnmatchedBrace}; use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::{PErr, PResult}; -use rustc_span::Span; pub(super) struct TokenTreesReader<'a> { string_reader: StringReader<'a>, /// The "next" token, which has been obtained from the `StringReader` but /// not yet handled by the `TokenTreesReader`. token: Token, - /// Stack of open delimiters and their spans. Used for error message. - open_braces: Vec<(Delimiter, Span)>, - unmatched_braces: Vec, - /// The type and spans for all braces - /// - /// Used only for error recovery when arriving to EOF with mismatched braces. - matching_delim_spans: Vec<(Delimiter, Span, Span)>, - last_unclosed_found_span: Option, - /// Collect empty block spans that might have been auto-inserted by editors. - last_delim_empty_block_spans: FxHashMap, - /// Collect the spans of braces (Open, Close). Used only - /// for detecting if blocks are empty and only braces. - matching_block_spans: Vec<(Span, Span)>, + diag_info: TokenTreeDiagInfo, } impl<'a> TokenTreesReader<'a> { @@ -33,15 +22,10 @@ impl<'a> TokenTreesReader<'a> { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), - open_braces: Vec::new(), - unmatched_braces: Vec::new(), - matching_delim_spans: Vec::new(), - last_unclosed_found_span: None, - last_delim_empty_block_spans: FxHashMap::default(), - matching_block_spans: Vec::new(), + diag_info: TokenTreeDiagInfo::default(), }; let res = tt_reader.parse_token_trees(/* is_delimited */ false); - (res, tt_reader.unmatched_braces) + (res, tt_reader.diag_info.unmatched_braces) } // Parse a stream of tokens into a list of `TokenTree`s. @@ -92,9 +76,9 @@ impl<'a> TokenTreesReader<'a> { fn eof_err(&mut self) -> PErr<'a> { let msg = "this file contains an unclosed delimiter"; let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg); - for &(_, sp) in &self.open_braces { + for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); - self.unmatched_braces.push(UnmatchedBrace { + self.diag_info.unmatched_braces.push(UnmatchedBrace { expected_delim: Delimiter::Brace, found_delim: None, found_span: self.token.span, @@ -103,23 +87,13 @@ impl<'a> TokenTreesReader<'a> { }); } - if let Some((delim, _)) = self.open_braces.last() { - if let Some((_, open_sp, close_sp)) = - self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| { - let sm = self.string_reader.sess.source_map(); - if let Some(close_padding) = sm.span_to_margin(*close_sp) { - if let Some(open_padding) = sm.span_to_margin(*open_sp) { - return delim == d && close_padding != open_padding; - } - } - false - }) - // these are in reverse order as they get inserted on close, but - { - // we want the last open/first close - err.span_label(*open_sp, "this delimiter might not be properly closed..."); - err.span_label(*close_sp, "...as it matches this but it has different indentation"); - } + if let Some((delim, _)) = self.diag_info.open_braces.last() { + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + *delim, + ) } err } @@ -128,7 +102,7 @@ impl<'a> TokenTreesReader<'a> { // The span for beginning of the delimited section let pre_span = self.token.span; - self.open_braces.push((open_delim, self.token.span)); + self.diag_info.open_braces.push((open_delim, self.token.span)); // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user @@ -137,35 +111,29 @@ impl<'a> TokenTreesReader<'a> { // Expand to cover the entire delimited token tree let delim_span = DelimSpan::from_pair(pre_span, self.token.span); + let sm = self.string_reader.sess.source_map(); match self.token.kind { // Correct delimiter. token::CloseDelim(close_delim) if close_delim == open_delim => { - let (open_brace, open_brace_span) = self.open_braces.pop().unwrap(); + let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap(); let close_brace_span = self.token.span; - if tts.is_empty() { + if tts.is_empty() && close_delim == Delimiter::Brace { let empty_block_span = open_brace_span.to(close_brace_span); - let sm = self.string_reader.sess.source_map(); if !sm.is_multiline(empty_block_span) { // Only track if the block is in the form of `{}`, otherwise it is // likely that it was written on purpose. - self.last_delim_empty_block_spans.insert(open_delim, empty_block_span); + self.diag_info.empty_block_spans.push(empty_block_span); } } - //only add braces + // only add braces if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) { - self.matching_block_spans.push((open_brace_span, close_brace_span)); + // Add all the matching spans, we will sort by span later + self.diag_info.matching_block_spans.push((open_brace_span, close_brace_span)); } - if self.open_braces.is_empty() { - // Clear up these spans to avoid suggesting them as we've found - // properly matched delimiters so far for an entire block. - self.matching_delim_spans.clear(); - } else { - self.matching_delim_spans.push((open_brace, open_brace_span, close_brace_span)); - } // Move past the closing delimiter. self.token = self.string_reader.next_token().0; } @@ -174,28 +142,25 @@ impl<'a> TokenTreesReader<'a> { let mut unclosed_delimiter = None; let mut candidate = None; - if self.last_unclosed_found_span != Some(self.token.span) { + if self.diag_info.last_unclosed_found_span != Some(self.token.span) { // do not complain about the same unclosed delimiter multiple times - self.last_unclosed_found_span = Some(self.token.span); + self.diag_info.last_unclosed_found_span = Some(self.token.span); // This is a conservative error: only report the last unclosed // delimiter. The previous unclosed delimiters could actually be // closed! The parser just hasn't gotten to them yet. - if let Some(&(_, sp)) = self.open_braces.last() { + if let Some(&(_, sp)) = self.diag_info.open_braces.last() { unclosed_delimiter = Some(sp); }; - let sm = self.string_reader.sess.source_map(); - if let Some(current_padding) = sm.span_to_margin(self.token.span) { - for (brace, brace_span) in &self.open_braces { - if let Some(padding) = sm.span_to_margin(*brace_span) { - // high likelihood of these two corresponding - if current_padding == padding && brace == &close_delim { - candidate = Some(*brace_span); - } - } + for (brace, brace_span) in &self.diag_info.open_braces { + if same_identation_level(&sm, self.token.span, *brace_span) + && brace == &close_delim + { + // high likelihood of these two corresponding + candidate = Some(*brace_span); } } - let (tok, _) = self.open_braces.pop().unwrap(); - self.unmatched_braces.push(UnmatchedBrace { + let (tok, _) = self.diag_info.open_braces.pop().unwrap(); + self.diag_info.unmatched_braces.push(UnmatchedBrace { expected_delim: tok, found_delim: Some(close_delim), found_span: self.token.span, @@ -203,7 +168,7 @@ impl<'a> TokenTreesReader<'a> { candidate_span: candidate, }); } else { - self.open_braces.pop(); + self.diag_info.open_braces.pop(); } // If the incorrect delimiter matches an earlier opening @@ -213,7 +178,7 @@ impl<'a> TokenTreesReader<'a> { // fn foo() { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` - if !self.open_braces.iter().any(|&(b, _)| b == close_delim) { + if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) { self.token = self.string_reader.next_token().0; } } @@ -236,22 +201,12 @@ impl<'a> TokenTreesReader<'a> { let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg); - // Braces are added at the end, so the last element is the biggest block - if let Some(parent) = self.matching_block_spans.last() { - if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { - // Check if the (empty block) is in the last properly closed block - if (parent.0.to(parent.1)).contains(span) { - err.span_label(span, "block is empty, you might have not meant to close it"); - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); - } - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); - } - } - + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + delim, + ); err.span_label(self.token.span, "unexpected closing delimiter"); err } diff --git a/tests/ui/parser/deli-ident-issue-1.rs b/tests/ui/parser/deli-ident-issue-1.rs index a8cbf8640f7..54485262a0c 100644 --- a/tests/ui/parser/deli-ident-issue-1.rs +++ b/tests/ui/parser/deli-ident-issue-1.rs @@ -1,4 +1,3 @@ -// error-pattern: this file contains an unclosed delimiter #![feature(let_chains)] trait Demo {} diff --git a/tests/ui/parser/deli-ident-issue-1.stderr b/tests/ui/parser/deli-ident-issue-1.stderr index 784f598f9d0..1119edb199f 100644 --- a/tests/ui/parser/deli-ident-issue-1.stderr +++ b/tests/ui/parser/deli-ident-issue-1.stderr @@ -1,26 +1,26 @@ error: this file contains an unclosed delimiter - --> $DIR/deli-ident-issue-1.rs:25:66 + --> $DIR/deli-ident-issue-1.rs:24:66 | LL | impl dyn Demo { | - unclosed delimiter ... -LL | c: u32| { - | - this delimiter might not be properly closed... -LL | a + b + c -LL | }; - | - ...as it matches this but it has different indentation +LL | && let Some(c) = num { + | - this delimiter might not be properly closed... +... +LL | } + | - ...as it matches this but it has different indentation ... LL | fn main() { } | ^ error[E0574]: expected struct, variant or union type, found local variable `c` - --> $DIR/deli-ident-issue-1.rs:18:17 + --> $DIR/deli-ident-issue-1.rs:17:17 | LL | && b == c { | ^ not a struct, variant or union type error[E0308]: mismatched types - --> $DIR/deli-ident-issue-1.rs:18:9 + --> $DIR/deli-ident-issue-1.rs:17:9 | LL | fn check(&self, val: Option, num: Option) { | - expected `()` because of default return type diff --git a/tests/ui/parser/deli-ident-issue-2.stderr b/tests/ui/parser/deli-ident-issue-2.stderr index 782dac8f32d..c8f59c9d32b 100644 --- a/tests/ui/parser/deli-ident-issue-2.stderr +++ b/tests/ui/parser/deli-ident-issue-2.stderr @@ -1,11 +1,9 @@ error: unexpected closing delimiter: `}` --> $DIR/deli-ident-issue-2.rs:5:1 | -LL | fn main() { - | - this opening brace... -... +LL | let _a = vec!]; + | - missing open `[` for this delimiter LL | } - | - ...matches this closing brace LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-1.stderr b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr index b5c1887f76f..2d873b46193 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-1.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-1.stderr @@ -1,9 +1,14 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-1.rs:10:1 | -LL | None => {} - | -- block is empty, you might have not meant to close it -LL | } +LL | match o { + | - this delimiter might not be properly closed... +LL | Some(_x) => {} // Extra '}' + | -- block is empty, you might have not meant to close it +LL | let _ = if true {}; +LL | } + | - ...as it matches this but it has different indentation +... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr index 253f12d44b8..2c08d41a15f 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr @@ -1,11 +1,9 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-2.rs:14:1 | -LL | } else { - | - this opening brace... -LL | -LL | } - | - ...matches this closing brace +LL | let obs_connect = || -> Result<(), MyError) { + | - missing open `(` for this delimiter +... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr index 258a692c855..a3fc46a1e88 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr @@ -1,11 +1,9 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-3.rs:8:1 | -LL | fn f(i: u32, j: u32) { - | - this opening brace... -... +LL | write!&mut res, " "); + | - missing open `(` for this delimiter LL | } - | - ...matches this closing brace LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-68987-unmatch-issue.stderr b/tests/ui/parser/issue-68987-unmatch-issue.stderr index 00e9c9a5cce..cabd133242f 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue.stderr @@ -1,9 +1,14 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue.rs:10:1 | -LL | None => {} - | -- block is empty, you might have not meant to close it -LL | } +LL | match o { + | - this delimiter might not be properly closed... +LL | Some(_x) => // Missing '{' +LL | let _ = if true {}; + | -- block is empty, you might have not meant to close it +LL | } + | - ...as it matches this but it has different indentation +... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issue-81827.stderr b/tests/ui/parser/issue-81827.stderr index 069de339194..867244b72e8 100644 --- a/tests/ui/parser/issue-81827.stderr +++ b/tests/ui/parser/issue-81827.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-81827.rs:11:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `[` for this delimiter | | unclosed delimiter | unclosed delimiter @@ -11,8 +12,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-81827.rs:11:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `[` for this delimiter | | unclosed delimiter | unclosed delimiter diff --git a/tests/ui/parser/issues/issue-62973.stderr b/tests/ui/parser/issues/issue-62973.stderr index 4737bc71860..3cb6d75a675 100644 --- a/tests/ui/parser/issues/issue-62973.stderr +++ b/tests/ui/parser/issues/issue-62973.stderr @@ -2,8 +2,10 @@ error: this file contains an unclosed delimiter --> $DIR/issue-62973.rs:8:2 | LL | fn p() { match s { v, E { [) {) } - | - - unclosed delimiter - | | + | - - - - missing open `(` for this delimiter + | | | | + | | | missing open `(` for this delimiter + | | unclosed delimiter | unclosed delimiter LL | LL | @@ -13,8 +15,10 @@ error: this file contains an unclosed delimiter --> $DIR/issue-62973.rs:8:2 | LL | fn p() { match s { v, E { [) {) } - | - - unclosed delimiter - | | + | - - - - missing open `(` for this delimiter + | | | | + | | | missing open `(` for this delimiter + | | unclosed delimiter | unclosed delimiter LL | LL | diff --git a/tests/ui/parser/issues/issue-63116.stderr b/tests/ui/parser/issues/issue-63116.stderr index cfdd99d1434..a1f8a77ffa7 100644 --- a/tests/ui/parser/issues/issue-63116.stderr +++ b/tests/ui/parser/issues/issue-63116.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-63116.rs:3:18 | LL | impl W $DIR/issue-69259.rs:3:5 | -LL | fn main() {} - | -- ...matches this closing brace - | | - | this opening brace... -LL | LL | fn f) {} | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr b/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr index 39bf113ef83..46cbb056d1d 100644 --- a/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr +++ b/tests/ui/parser/issues/issue-70583-block-is-empty-1.stderr @@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-70583-block-is-empty-1.rs:20:1 | LL | fn struct_generic(x: Vec) { - | - this opening brace... + | - this delimiter might not be properly closed... ... LL | } - | - ...matches this closing brace + | - ...as it matches this but it has different indentation LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr b/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr index 5d37b216427..9ae94c70186 100644 --- a/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr +++ b/tests/ui/parser/issues/issue-70583-block-is-empty-2.stderr @@ -1,8 +1,12 @@ error: unexpected closing delimiter: `}` --> $DIR/issue-70583-block-is-empty-2.rs:14:1 | +LL | match self { + | - this delimiter might not be properly closed... LL | ErrorHandled::Reported => {}} - | -- block is empty, you might have not meant to close it + | --- ...as it matches this but it has different indentation + | | + | block is empty, you might have not meant to close it ... LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr b/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr index 967a3e6fdc1..689ce1eb6b7 100644 --- a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr @@ -2,10 +2,10 @@ error: unexpected closing delimiter: `}` --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 | LL | fn main() { - | - this opening brace... + | - this delimiter might not be properly closed... ... LL | } - | - ...matches this closing brace + | - ...as it matches this but it has different indentation LL | } | ^ unexpected closing delimiter diff --git a/tests/ui/typeck/issue-91334.stderr b/tests/ui/typeck/issue-91334.stderr index 8508f7a38e2..78f392c9a8a 100644 --- a/tests/ui/typeck/issue-91334.stderr +++ b/tests/ui/typeck/issue-91334.stderr @@ -2,8 +2,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-91334.rs:10:23 | LL | fn f(){||yield(((){), - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `(` for this delimiter | | unclosed delimiter | unclosed delimiter @@ -11,8 +12,9 @@ error: this file contains an unclosed delimiter --> $DIR/issue-91334.rs:10:23 | LL | fn f(){||yield(((){), - | - - ^ - | | | + | - - - ^ + | | | | + | | | missing open `(` for this delimiter | | unclosed delimiter | unclosed delimiter -- cgit 1.4.1-3-g733a5 From 80a1536c7ab73c867d5a60f4441058e7e2231d8d Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Sun, 22 Jan 2023 12:05:36 +0100 Subject: recover more unbraced const args --- compiler/rustc_parse/src/parser/diagnostics.rs | 22 +++++ compiler/rustc_parse/src/parser/path.rs | 48 +++++++--- tests/ui/const-generics/bad-const-generic-exprs.rs | 34 ++++++- .../const-generics/bad-const-generic-exprs.stderr | 106 ++++++++++++++++++++- 4 files changed, 188 insertions(+), 22 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4c918c6702e..6596a06afab 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2353,6 +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> { + 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, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 5333d3b8587..2e706a00cf7 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -675,22 +675,42 @@ impl<'a> Parser<'a> { GenericArg::Const(self.parse_const_arg()?) } else if self.check_type() { // Parse type argument. - let is_const_fn = - self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)); - let mut snapshot = self.create_snapshot_for_diagnostic(); + + // Proactively create a parser snapshot enabling us to rewind and try to reparse the + // input as a const expression in case we fail to parse a type. If we successfully + // do so, we will report an error that it needs to be wrapped in braces. + let mut snapshot = None; + if self.may_recover() && self.token.can_begin_expr() { + snapshot = Some(self.create_snapshot_for_diagnostic()); + } + match self.parse_ty() { - Ok(ty) => GenericArg::Type(ty), + Ok(ty) => { + // Since the type parser recovers from some malformed slice and array types and + // successfully returns a type, we need to look for `TyKind::Err`s in the + // type to determine if error recovery has occurred and if the input is not a + // syntactically valid type after all. + if let ast::TyKind::Slice(inner_ty) | ast::TyKind::Array(inner_ty, _) = &ty.kind + && let ast::TyKind::Err = inner_ty.kind + && let Some(snapshot) = snapshot + && let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot) + { + return Ok(Some(self.dummy_const_arg_needs_braces( + self.struct_span_err(expr.span, "invalid const generic expression"), + expr.span, + ))); + } + + GenericArg::Type(ty) + } Err(err) => { - if is_const_fn { - match (*snapshot).parse_expr_res(Restrictions::CONST_EXPR, None) { - Ok(expr) => { - self.restore_snapshot(snapshot); - return Ok(Some(self.dummy_const_arg_needs_braces(err, expr.span))); - } - Err(err) => { - err.cancel(); - } - } + if let Some(snapshot) = snapshot + && let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot) + { + return Ok(Some(self.dummy_const_arg_needs_braces( + err, + expr.span, + ))); } // Try to recover from possible `const` arg without braces. return self.recover_const_arg(start, err).map(Some); diff --git a/tests/ui/const-generics/bad-const-generic-exprs.rs b/tests/ui/const-generics/bad-const-generic-exprs.rs index ca91643edf7..423752ca25e 100644 --- a/tests/ui/const-generics/bad-const-generic-exprs.rs +++ b/tests/ui/const-generics/bad-const-generic-exprs.rs @@ -13,10 +13,34 @@ fn main() { let _: Wow; //~^ ERROR expected one of //~| HELP expressions must be enclosed in braces to be used as const generic arguments - - // FIXME(compiler-errors): This one is still unsatisfying, - // and probably a case I could see someone typing by accident.. + let _: Wow<[]>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments let _: Wow<[12]>; - //~^ ERROR expected type, found - //~| ERROR type provided when a constant was expected + //~^ ERROR expected type + //~| ERROR invalid const generic expression + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<[0, 1, 3]>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<[0xff; 8]>; + //~^ ERROR expected type + //~| ERROR invalid const generic expression + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<[1, 2]>; // Regression test for issue #81698. + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<&0>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<("", 0)>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + let _: Wow<(1 + 2) * 3>; + //~^ ERROR expected type + //~| HELP expressions must be enclosed in braces to be used as const generic arguments + // FIXME(fmease): This one is pretty bad. + let _: Wow; + //~^ ERROR expected one of + //~| HELP you might have meant to end the type parameters here } diff --git a/tests/ui/const-generics/bad-const-generic-exprs.stderr b/tests/ui/const-generics/bad-const-generic-exprs.stderr index 24668b08b8a..17a63a96fe4 100644 --- a/tests/ui/const-generics/bad-const-generic-exprs.stderr +++ b/tests/ui/const-generics/bad-const-generic-exprs.stderr @@ -42,18 +42,118 @@ help: expressions must be enclosed in braces to be used as const generic argumen LL | let _: Wow<{ A.0 }>; | + + +error: expected type, found `]` + --> $DIR/bad-const-generic-exprs.rs:16:17 + | +LL | let _: Wow<[]>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [] }>; + | + + + error: expected type, found `12` --> $DIR/bad-const-generic-exprs.rs:19:17 | LL | let _: Wow<[12]>; | ^^ expected type -error[E0747]: type provided when a constant was expected +error: invalid const generic expression --> $DIR/bad-const-generic-exprs.rs:19:16 | LL | let _: Wow<[12]>; | ^^^^ + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [12] }>; + | + + + +error: expected type, found `0` + --> $DIR/bad-const-generic-exprs.rs:23:17 + | +LL | let _: Wow<[0, 1, 3]>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [0, 1, 3] }>; + | + + + +error: expected type, found `0xff` + --> $DIR/bad-const-generic-exprs.rs:26:17 + | +LL | let _: Wow<[0xff; 8]>; + | ^^^^ expected type + +error: invalid const generic expression + --> $DIR/bad-const-generic-exprs.rs:26:16 + | +LL | let _: Wow<[0xff; 8]>; + | ^^^^^^^^^ + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [0xff; 8] }>; + | + + + +error: expected type, found `1` + --> $DIR/bad-const-generic-exprs.rs:30:17 + | +LL | let _: Wow<[1, 2]>; // Regression test for issue #81698. + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ [1, 2] }>; // Regression test for issue #81698. + | + + + +error: expected type, found `0` + --> $DIR/bad-const-generic-exprs.rs:33:17 + | +LL | let _: Wow<&0>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ &0 }>; + | + + + +error: expected type, found `""` + --> $DIR/bad-const-generic-exprs.rs:36:17 + | +LL | let _: Wow<("", 0)>; + | ^^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ ("", 0) }>; + | + + + +error: expected type, found `1` + --> $DIR/bad-const-generic-exprs.rs:39:17 + | +LL | let _: Wow<(1 + 2) * 3>; + | ^ expected type + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | let _: Wow<{ (1 + 2) * 3 }>; + | + + + +error: expected one of `,` or `>`, found `0` + --> $DIR/bad-const-generic-exprs.rs:43:17 + | +LL | let _: Wow; + | - ^ expected one of `,` or `>` + | | + | while parsing the type for `_` + | +help: you might have meant to end the type parameters here + | +LL | let _: Wow0>; + | + -error: aborting due to 6 previous errors +error: aborting due to 15 previous errors -For more information about this error, try `rustc --explain E0747`. -- cgit 1.4.1-3-g733a5 From 4bfab39f9b466fa89f85c57a42c1e71a99c6aa4e Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sat, 28 Jan 2023 20:02:00 +0000 Subject: Check for missing space between fat arrow and range pattern --- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/pat.rs | 44 +++++++++++++++------- .../half-open-range-pats-inclusive-match-arrow.rs | 8 ++++ ...lf-open-range-pats-inclusive-match-arrow.stderr | 19 ++++++++++ 4 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.rs create mode 100644 tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.stderr (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3225a309a31..17d1e200b41 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3168,7 +3168,7 @@ impl<'a> Parser<'a> { limits: RangeLimits, ) -> ExprKind { if end.is_none() && limits == RangeLimits::Closed { - self.inclusive_range_with_incorrect_end(self.prev_token.span); + self.inclusive_range_with_incorrect_end(); ExprKind::Err } else { ExprKind::Range(start, end, limits) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e5411538eea..9cde0026920 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -746,32 +746,38 @@ impl<'a> Parser<'a> { // Parsing e.g. `X..`. if let RangeEnd::Included(_) = re.node { // FIXME(Centril): Consider semantic errors instead in `ast_validation`. - self.inclusive_range_with_incorrect_end(re.span); + self.inclusive_range_with_incorrect_end(); } None }; Ok(PatKind::Range(Some(begin), end, re)) } - pub(super) fn inclusive_range_with_incorrect_end(&mut self, span: Span) { + pub(super) fn inclusive_range_with_incorrect_end(&mut self) { let tok = &self.token; - + let span = self.prev_token.span; // If the user typed "..==" instead of "..=", we want to give them // a specific error message telling them to use "..=". + // If they typed "..=>", suggest they use ".. =>". // Otherwise, we assume that they meant to type a half open exclusive // range and give them an error telling them to do that instead. - if matches!(tok.kind, token::Eq) && tok.span.lo() == span.hi() { - let span_with_eq = span.to(tok.span); + let no_space = tok.span.lo() == span.hi(); + match tok.kind { + token::Eq if no_space => { + let span_with_eq = span.to(tok.span); - // Ensure the user doesn't receive unhelpful unexpected token errors - self.bump(); - if self.is_pat_range_end_start(0) { - let _ = self.parse_pat_range_end().map_err(|e| e.cancel()); - } + // Ensure the user doesn't receive unhelpful unexpected token errors + self.bump(); + if self.is_pat_range_end_start(0) { + let _ = self.parse_pat_range_end().map_err(|e| e.cancel()); + } - self.error_inclusive_range_with_extra_equals(span_with_eq); - } else { - self.error_inclusive_range_with_no_end(span); + self.error_inclusive_range_with_extra_equals(span_with_eq); + } + token::Gt if no_space => { + self.error_inclusive_range_match_arrow(span); + } + _ => self.error_inclusive_range_with_no_end(span), } } @@ -782,6 +788,18 @@ impl<'a> Parser<'a> { .emit(); } + fn error_inclusive_range_match_arrow(&self, span: Span) { + let without_eq = span.with_hi(span.hi() - rustc_span::BytePos(1)); + self.struct_span_err(span, "unexpected `=>` after open range") + .span_suggestion_verbose( + without_eq.shrink_to_hi(), + "add a space between the pattern and `=>`", + " ", + Applicability::MachineApplicable, + ) + .emit(); + } + fn error_inclusive_range_with_no_end(&self, span: Span) { struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end") .span_suggestion_short(span, "use `..` instead", "..", Applicability::MachineApplicable) diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.rs new file mode 100644 index 00000000000..7ba2b6d857c --- /dev/null +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.rs @@ -0,0 +1,8 @@ +fn main() { + let x = 42; + match x { + 0..=73 => {}, + 74..=> {}, //~ ERROR unexpected `=>` after open range + //~^ ERROR expected one of `=>`, `if`, or `|`, found `>` + } +} diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.stderr new file mode 100644 index 00000000000..9ba6d15113c --- /dev/null +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-match-arrow.stderr @@ -0,0 +1,19 @@ +error: unexpected `=>` after open range + --> $DIR/half-open-range-pats-inclusive-match-arrow.rs:5:11 + | +LL | 74..=> {}, + | ^^^ + | +help: add a space between the pattern and `=>` + | +LL | 74.. => {}, + | + + +error: expected one of `=>`, `if`, or `|`, found `>` + --> $DIR/half-open-range-pats-inclusive-match-arrow.rs:5:14 + | +LL | 74..=> {}, + | ^ expected one of `=>`, `if`, or `|` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From c5688794e2c3bb94f93e37a4f77576ac0d86a14e Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sat, 28 Jan 2023 21:16:50 +0000 Subject: Migrate some range parsing diagnostics --- .../rustc_error_messages/locales/en-US/parse.ftl | 11 ++++++ compiler/rustc_parse/src/errors.rs | 42 ++++++++++++++++++++++ compiler/rustc_parse/src/parser/pat.rs | 27 +++++--------- 3 files changed, 61 insertions(+), 19 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl index a3e2002da78..1728ef70cba 100644 --- a/compiler/rustc_error_messages/locales/en-US/parse.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl @@ -199,6 +199,17 @@ parse_match_arm_body_without_braces = `match` arm body without braces } with a body .suggestion_use_comma_not_semicolon = use a comma to end a `match` arm expression +parse_inclusive_range_extra_equals = unexpected `=` after inclusive range + .suggestion_remove_eq = use `..=` instead + .note = inclusive ranges end with a single equals sign (`..=`) + +parse_inclusive_range_match_arrow = unexpected `=>` after open range + .suggestion_add_space = add a space between the pattern and `=>` + +parse_inclusive_range_no_end = inclusive range with no end + .suggestion_open_range = use `..` instead + .note = inclusive ranges must be bounded at the end (`..=b` or `a..=b`) + parse_struct_literal_not_allowed_here = struct literals are not allowed here .suggestion = surround the struct literal with parentheses diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 40763da0bb5..054b41b478d 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -649,6 +649,48 @@ pub(crate) struct MatchArmBodyWithoutBraces { pub sub: MatchArmBodyWithoutBracesSugg, } +#[derive(Diagnostic)] +#[diag(parse_inclusive_range_extra_equals)] +#[note] +pub(crate) struct InclusiveRangeExtraEquals { + #[primary_span] + #[suggestion( + suggestion_remove_eq, + style = "short", + code = "..=", + applicability = "maybe-incorrect" + )] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_inclusive_range_match_arrow)] +pub(crate) struct InclusiveRangeMatchArrow { + #[primary_span] + pub span: Span, + #[suggestion( + suggestion_add_space, + style = "verbose", + code = " ", + applicability = "machine-applicable" + )] + pub after_pat: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_inclusive_range_no_end, code = "E0586")] +#[note] +pub(crate) struct InclusiveRangeNoEnd { + #[primary_span] + #[suggestion( + suggestion_open_range, + code = "..", + applicability = "machine-applicable", + style = "short" + )] + pub span: Span, +} + #[derive(Subdiagnostic)] pub(crate) enum MatchArmBodyWithoutBracesSugg { #[multipart_suggestion(suggestion_add_braces, applicability = "machine-applicable")] diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 9cde0026920..912f7cc14f6 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,5 +1,7 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; -use crate::errors::RemoveLet; +use crate::errors::{ + InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, RemoveLet, +}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; @@ -9,7 +11,7 @@ use rustc_ast::{ PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax, }; use rustc_ast_pretty::pprust; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::{respan, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; @@ -782,29 +784,16 @@ impl<'a> Parser<'a> { } fn error_inclusive_range_with_extra_equals(&self, span: Span) { - self.struct_span_err(span, "unexpected `=` after inclusive range") - .span_suggestion_short(span, "use `..=` instead", "..=", Applicability::MaybeIncorrect) - .note("inclusive ranges end with a single equals sign (`..=`)") - .emit(); + self.sess.emit_err(InclusiveRangeExtraEquals { span }); } fn error_inclusive_range_match_arrow(&self, span: Span) { - let without_eq = span.with_hi(span.hi() - rustc_span::BytePos(1)); - self.struct_span_err(span, "unexpected `=>` after open range") - .span_suggestion_verbose( - without_eq.shrink_to_hi(), - "add a space between the pattern and `=>`", - " ", - Applicability::MachineApplicable, - ) - .emit(); + let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi(); + self.sess.emit_err(InclusiveRangeMatchArrow { span, after_pat }); } fn error_inclusive_range_with_no_end(&self, span: Span) { - struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end") - .span_suggestion_short(span, "use `..` instead", "..", Applicability::MachineApplicable) - .note("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)") - .emit(); + self.sess.emit_err(InclusiveRangeNoEnd { span }); } /// Parse a range-to pattern, `..X` or `..=X` where `X` remains to be parsed. -- cgit 1.4.1-3-g733a5 From 807ebac887a535b047da349297bdd8d14e31f794 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Fri, 6 Jan 2023 04:43:08 +0900 Subject: Insert whitespace to avoid ident concatenation in suggestion --- compiler/rustc_parse/src/parser/ty.rs | 2 +- tests/ui/parser/trait-object-delimiters.rs | 2 ++ tests/ui/parser/trait-object-delimiters.stderr | 28 ++++++++++++++++++-------- 3 files changed, 23 insertions(+), 9 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 25de0a9e750..82d9138c7a3 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1048,7 +1048,7 @@ impl<'a> Parser<'a> { self.parse_remaining_bounds(bounds, true)?; self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; let sp = vec![lo, self.prev_token.span]; - let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect(); + let sugg = vec![(lo, String::from(" ")), (self.prev_token.span, String::new())]; self.struct_span_err(sp, "incorrect braces around trait bounds") .multipart_suggestion( "remove the parentheses", diff --git a/tests/ui/parser/trait-object-delimiters.rs b/tests/ui/parser/trait-object-delimiters.rs index cc04ac05204..c41cda18743 100644 --- a/tests/ui/parser/trait-object-delimiters.rs +++ b/tests/ui/parser/trait-object-delimiters.rs @@ -5,6 +5,8 @@ fn foo1(_: &dyn Drop + AsRef) {} //~ ERROR ambiguous `+` in a type fn foo2(_: &dyn (Drop + AsRef)) {} //~ ERROR incorrect braces around trait bounds +fn foo2_no_space(_: &dyn(Drop + AsRef)) {} //~ ERROR incorrect braces around trait bounds + fn foo3(_: &dyn {Drop + AsRef}) {} //~ ERROR expected parameter name, found `{` //~^ ERROR expected one of `!`, `(`, `)`, `*`, `,`, `?`, `for`, `~`, lifetime, or path, found `{` //~| ERROR at least one trait is required for an object type diff --git a/tests/ui/parser/trait-object-delimiters.stderr b/tests/ui/parser/trait-object-delimiters.stderr index 99c4515459d..ccce3a8053e 100644 --- a/tests/ui/parser/trait-object-delimiters.stderr +++ b/tests/ui/parser/trait-object-delimiters.stderr @@ -13,17 +13,29 @@ LL | fn foo2(_: &dyn (Drop + AsRef)) {} help: remove the parentheses | LL - fn foo2(_: &dyn (Drop + AsRef)) {} -LL + fn foo2(_: &dyn Drop + AsRef) {} +LL + fn foo2(_: &dyn Drop + AsRef) {} + | + +error: incorrect braces around trait bounds + --> $DIR/trait-object-delimiters.rs:8:25 + | +LL | fn foo2_no_space(_: &dyn(Drop + AsRef)) {} + | ^ ^ + | +help: remove the parentheses + | +LL - fn foo2_no_space(_: &dyn(Drop + AsRef)) {} +LL + fn foo2_no_space(_: &dyn Drop + AsRef) {} | error: expected parameter name, found `{` - --> $DIR/trait-object-delimiters.rs:8:17 + --> $DIR/trait-object-delimiters.rs:10:17 | LL | fn foo3(_: &dyn {Drop + AsRef}) {} | ^ expected parameter name error: expected one of `!`, `(`, `)`, `*`, `,`, `?`, `for`, `~`, lifetime, or path, found `{` - --> $DIR/trait-object-delimiters.rs:8:17 + --> $DIR/trait-object-delimiters.rs:10:17 | LL | fn foo3(_: &dyn {Drop + AsRef}) {} | -^ expected one of 10 possible tokens @@ -31,13 +43,13 @@ LL | fn foo3(_: &dyn {Drop + AsRef}) {} | help: missing `,` error: expected identifier, found `<` - --> $DIR/trait-object-delimiters.rs:12:17 + --> $DIR/trait-object-delimiters.rs:14:17 | LL | fn foo4(_: &dyn >) {} | ^ expected identifier error: invalid `dyn` keyword - --> $DIR/trait-object-delimiters.rs:14:25 + --> $DIR/trait-object-delimiters.rs:16:25 | LL | fn foo5(_: &(dyn Drop + dyn AsRef)) {} | ^^^ help: remove this keyword @@ -56,13 +68,13 @@ LL | fn foo1(_: &dyn Drop + AsRef) {} = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0224]: at least one trait is required for an object type - --> $DIR/trait-object-delimiters.rs:8:13 + --> $DIR/trait-object-delimiters.rs:10:13 | LL | fn foo3(_: &dyn {Drop + AsRef}) {} | ^^^ error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/trait-object-delimiters.rs:14:29 + --> $DIR/trait-object-delimiters.rs:16:29 | LL | fn foo5(_: &(dyn Drop + dyn AsRef)) {} | ---- ^^^^^^^^^^ additional non-auto trait @@ -72,7 +84,7 @@ LL | fn foo5(_: &(dyn Drop + dyn AsRef)) {} = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Drop + AsRef {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0224, E0225. For more information about an error, try `rustc --explain E0224`. -- cgit 1.4.1-3-g733a5