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`
This commit is contained in:
commit
3ab6b60337
4 changed files with 62 additions and 7 deletions
|
@ -431,7 +431,8 @@ impl<'a> Parser<'a> {
|
||||||
let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span);
|
let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span);
|
||||||
let limits =
|
let limits =
|
||||||
if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
|
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 {
|
fn is_at_start_of_range_notation_rhs(&self) -> bool {
|
||||||
|
@ -479,7 +480,8 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
(lo, None)
|
(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(
|
fn mk_range(
|
||||||
&self,
|
&mut self,
|
||||||
start: Option<P<Expr>>,
|
start: Option<P<Expr>>,
|
||||||
end: Option<P<Expr>>,
|
end: Option<P<Expr>>,
|
||||||
limits: RangeLimits,
|
limits: RangeLimits,
|
||||||
) -> ExprKind {
|
) -> ExprKind {
|
||||||
if end.is_none() && limits == RangeLimits::Closed {
|
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
|
ExprKind::Err
|
||||||
} else {
|
} else {
|
||||||
ExprKind::Range(start, end, limits)
|
ExprKind::Range(start, end, limits)
|
||||||
|
|
|
@ -797,15 +797,48 @@ impl<'a> Parser<'a> {
|
||||||
// Parsing e.g. `X..`.
|
// Parsing e.g. `X..`.
|
||||||
if let RangeEnd::Included(_) = re.node {
|
if let RangeEnd::Included(_) = re.node {
|
||||||
// FIXME(Centril): Consider semantic errors instead in `ast_validation`.
|
// FIXME(Centril): Consider semantic errors instead in `ast_validation`.
|
||||||
// Possibly also do this for `X..=` in *expression* contexts.
|
self.inclusive_range_with_incorrect_end(re.span);
|
||||||
self.error_inclusive_range_with_no_end(re.span);
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Ok(PatKind::Range(Some(begin), end, re))
|
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")
|
struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end")
|
||||||
.span_suggestion_short(
|
.span_suggestion_short(
|
||||||
span,
|
span,
|
||||||
|
|
10
src/test/ui/parser/range-inclusive-extra-equals.rs
Normal file
10
src/test/ui/parser/range-inclusive-extra-equals.rs
Normal file
|
@ -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
|
||||||
|
}
|
10
src/test/ui/parser/range-inclusive-extra-equals.stderr
Normal file
10
src/test/ui/parser/range-inclusive-extra-equals.stderr
Normal file
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue