From fd406a8865d601572ac0252ea57965e8b784de28 Mon Sep 17 00:00:00 2001 From: Aris Merchant <22333129+inquisitivecrystal@users.noreply.github.com> Date: Fri, 25 Jun 2021 19:46:41 -0700 Subject: [PATCH 1/2] Give a helpful error for the mistake `..==` --- compiler/rustc_parse/src/parser/expr.rs | 10 ++++--- compiler/rustc_parse/src/parser/pat.rs | 39 +++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9dff40ff1ed..5d765a474ae 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -431,7 +431,8 @@ impl<'a> Parser<'a> { let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; - Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits), AttrVec::new())) + let range = self.mk_range(Some(lhs), rhs, limits); + Ok(self.mk_expr(span, range, AttrVec::new())) } fn is_at_start_of_range_notation_rhs(&self) -> bool { @@ -479,7 +480,8 @@ impl<'a> Parser<'a> { } else { (lo, None) }; - Ok(this.mk_expr(span, this.mk_range(None, opt_end, limits), attrs.into())) + let range = this.mk_range(None, opt_end, limits); + Ok(this.mk_expr(span, range, attrs.into())) }) } @@ -2517,13 +2519,13 @@ impl<'a> Parser<'a> { } fn mk_range( - &self, + &mut self, start: Option
>, end: Option
>, limits: RangeLimits, ) -> ExprKind { if end.is_none() && limits == RangeLimits::Closed { - self.error_inclusive_range_with_no_end(self.prev_token.span); + self.inclusive_range_with_incorrect_end(self.prev_token.span); 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 566677d032a..7d07c3b1211 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -736,15 +736,48 @@ impl<'a> Parser<'a> { // Parsing e.g. `X..`. if let RangeEnd::Included(_) = re.node { // FIXME(Centril): Consider semantic errors instead in `ast_validation`. - // Possibly also do this for `X..=` in *expression* contexts. - self.error_inclusive_range_with_no_end(re.span); + self.inclusive_range_with_incorrect_end(re.span); } None }; Ok(PatKind::Range(Some(begin), end, re)) } - pub(super) fn error_inclusive_range_with_no_end(&self, span: Span) { + pub(super) fn inclusive_range_with_incorrect_end(&mut self, span: Span) { + let tok = &self.token; + + // If the user typed "..==" instead of "..=", we want to give them + // a specific error message telling them to 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); + + // 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(); + } + + self.error_inclusive_range_with_extra_equals(span_with_eq); + } else { + self.error_inclusive_range_with_no_end(span); + } + } + + 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", + "..=".to_string(), + Applicability::MaybeIncorrect, + ) + .note("inclusive ranges end with a single equals sign (`..=`)") + .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, From b56079ec5466af5bff7d7d174ed6ae248aaa92c4 Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Sun, 11 Jul 2021 16:51:56 -0700 Subject: [PATCH 2/2] Add diagnostics test for mistyped inclusive ranges --- src/test/ui/parser/range-inclusive-extra-equals.rs | 10 ++++++++++ src/test/ui/parser/range-inclusive-extra-equals.stderr | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/test/ui/parser/range-inclusive-extra-equals.rs create mode 100644 src/test/ui/parser/range-inclusive-extra-equals.stderr diff --git a/src/test/ui/parser/range-inclusive-extra-equals.rs b/src/test/ui/parser/range-inclusive-extra-equals.rs new file mode 100644 index 00000000000..d41c0699cf7 --- /dev/null +++ b/src/test/ui/parser/range-inclusive-extra-equals.rs @@ -0,0 +1,10 @@ +// Makes sure that a helpful message is shown when someone mistypes +// an inclusive range as `..==` rather than `..=`. This is an +// easy mistake, because of the resemblance to`==`. +// See #86395 for a bit of background. + +pub fn main() { + if let 1..==3 = 1 {} //~ERROR unexpected `=` after inclusive range + //~|HELP use `..=` instead + //~|NOTE inclusive ranges end with a single equals sign +} diff --git a/src/test/ui/parser/range-inclusive-extra-equals.stderr b/src/test/ui/parser/range-inclusive-extra-equals.stderr new file mode 100644 index 00000000000..d37b6be4fa1 --- /dev/null +++ b/src/test/ui/parser/range-inclusive-extra-equals.stderr @@ -0,0 +1,10 @@ +error: unexpected `=` after inclusive range + --> $DIR/range-inclusive-extra-equals.rs:7:13 + | +LL | if let 1..==3 = 1 {} + | ^^^^ help: use `..=` instead + | + = note: inclusive ranges end with a single equals sign (`..=`) + +error: aborting due to previous error +