Skip to content

Commit 3ab6b60

Browse files
committed
Auto merge of #87071 - inquisitivecrystal:inclusive-range, r=estebank
Add diagnostics for mistyped inclusive range Inclusive ranges are correctly typed as `..=`. However, it's quite easy to think of it as being like `==`, and type `..==` instead. This PR adds helpful diagnostics for this case. Resolves #86395 (there are some other cases there, but I think those should probably have separate issues). r? `@estebank`
2 parents 77d1559 + b56079e commit 3ab6b60

File tree

4 files changed

+62
-7
lines changed

4 files changed

+62
-7
lines changed

compiler/rustc_parse/src/parser/expr.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ impl<'a> Parser<'a> {
431431
let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span);
432432
let limits =
433433
if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
434-
Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits), AttrVec::new()))
434+
let range = self.mk_range(Some(lhs), rhs, limits);
435+
Ok(self.mk_expr(span, range, AttrVec::new()))
435436
}
436437

437438
fn is_at_start_of_range_notation_rhs(&self) -> bool {
@@ -479,7 +480,8 @@ impl<'a> Parser<'a> {
479480
} else {
480481
(lo, None)
481482
};
482-
Ok(this.mk_expr(span, this.mk_range(None, opt_end, limits), attrs.into()))
483+
let range = this.mk_range(None, opt_end, limits);
484+
Ok(this.mk_expr(span, range, attrs.into()))
483485
})
484486
}
485487

@@ -2517,13 +2519,13 @@ impl<'a> Parser<'a> {
25172519
}
25182520

25192521
fn mk_range(
2520-
&self,
2522+
&mut self,
25212523
start: Option<P<Expr>>,
25222524
end: Option<P<Expr>>,
25232525
limits: RangeLimits,
25242526
) -> ExprKind {
25252527
if end.is_none() && limits == RangeLimits::Closed {
2526-
self.error_inclusive_range_with_no_end(self.prev_token.span);
2528+
self.inclusive_range_with_incorrect_end(self.prev_token.span);
25272529
ExprKind::Err
25282530
} else {
25292531
ExprKind::Range(start, end, limits)

compiler/rustc_parse/src/parser/pat.rs

+36-3
Original file line numberDiff line numberDiff line change
@@ -797,15 +797,48 @@ impl<'a> Parser<'a> {
797797
// Parsing e.g. `X..`.
798798
if let RangeEnd::Included(_) = re.node {
799799
// FIXME(Centril): Consider semantic errors instead in `ast_validation`.
800-
// Possibly also do this for `X..=` in *expression* contexts.
801-
self.error_inclusive_range_with_no_end(re.span);
800+
self.inclusive_range_with_incorrect_end(re.span);
802801
}
803802
None
804803
};
805804
Ok(PatKind::Range(Some(begin), end, re))
806805
}
807806

808-
pub(super) fn error_inclusive_range_with_no_end(&self, span: Span) {
807+
pub(super) fn inclusive_range_with_incorrect_end(&mut self, span: Span) {
808+
let tok = &self.token;
809+
810+
// If the user typed "..==" instead of "..=", we want to give them
811+
// a specific error message telling them to use "..=".
812+
// Otherwise, we assume that they meant to type a half open exclusive
813+
// range and give them an error telling them to do that instead.
814+
if matches!(tok.kind, token::Eq) && tok.span.lo() == span.hi() {
815+
let span_with_eq = span.to(tok.span);
816+
817+
// Ensure the user doesn't receive unhelpful unexpected token errors
818+
self.bump();
819+
if self.is_pat_range_end_start(0) {
820+
let _ = self.parse_pat_range_end();
821+
}
822+
823+
self.error_inclusive_range_with_extra_equals(span_with_eq);
824+
} else {
825+
self.error_inclusive_range_with_no_end(span);
826+
}
827+
}
828+
829+
fn error_inclusive_range_with_extra_equals(&self, span: Span) {
830+
self.struct_span_err(span, "unexpected `=` after inclusive range")
831+
.span_suggestion_short(
832+
span,
833+
"use `..=` instead",
834+
"..=".to_string(),
835+
Applicability::MaybeIncorrect,
836+
)
837+
.note("inclusive ranges end with a single equals sign (`..=`)")
838+
.emit();
839+
}
840+
841+
fn error_inclusive_range_with_no_end(&self, span: Span) {
809842
struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end")
810843
.span_suggestion_short(
811844
span,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Makes sure that a helpful message is shown when someone mistypes
2+
// an inclusive range as `..==` rather than `..=`. This is an
3+
// easy mistake, because of the resemblance to`==`.
4+
// See #86395 for a bit of background.
5+
6+
pub fn main() {
7+
if let 1..==3 = 1 {} //~ERROR unexpected `=` after inclusive range
8+
//~|HELP use `..=` instead
9+
//~|NOTE inclusive ranges end with a single equals sign
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: unexpected `=` after inclusive range
2+
--> $DIR/range-inclusive-extra-equals.rs:7:13
3+
|
4+
LL | if let 1..==3 = 1 {}
5+
| ^^^^ help: use `..=` instead
6+
|
7+
= note: inclusive ranges end with a single equals sign (`..=`)
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)