Skip to content

Commit

Permalink
Emit suggestion when trying to write exclusive ranges as ..<
Browse files Browse the repository at this point in the history
  • Loading branch information
sjwang05 committed Jan 3, 2024
1 parent 1a47f5b commit 2547a32
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 8 deletions.
32 changes: 26 additions & 6 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::parser;
use crate::parser::attr::InnerAttrPolicy;
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind};
use rustc_ast::tokenstream::AttrTokenTree;
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
Expand All @@ -35,7 +35,7 @@ use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, FatalError,
PResult,
PErr, PResult,
};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::Spanned;
Expand Down Expand Up @@ -448,12 +448,16 @@ impl<'a> Parser<'a> {
})
}

let mut expected = edible
self.expected_tokens.extend(
edible
.iter()
.map(|x| TokenType::Token(x.clone()))
.chain(inedible.iter().map(|x| TokenType::Token(x.clone()))),
);
let mut expected = self
.expected_tokens
.iter()
.chain(inedible)
.cloned()
.map(TokenType::Token)
.chain(self.expected_tokens.iter().cloned())
.filter(|token| {
// Filter out suggestions that suggest the same token which was found and deemed incorrect.
fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
Expand Down Expand Up @@ -2929,6 +2933,22 @@ impl<'a> Parser<'a> {
Ok(())
}

/// Check for exclusive ranges written as `..<`
pub(crate) fn maybe_err_dotdotlt_syntax(&self, maybe_lt: Token, mut err: PErr<'a>) -> PErr<'a> {
if maybe_lt == token::Lt
&& (self.expected_tokens.contains(&TokenType::Token(token::Gt))
|| matches!(self.token.kind, token::Literal(..)))
{
err.span_suggestion(
maybe_lt.span,
"remove the `<` to write an exclusive range",
"",
Applicability::MachineApplicable,
);
}
err
}

pub fn is_diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> bool {
(0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind))
&& self.look_ahead(3, |tok| tok == short_kind)
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,11 @@ impl<'a> Parser<'a> {
cur_op_span: Span,
) -> PResult<'a, P<Expr>> {
let rhs = if self.is_at_start_of_range_notation_rhs() {
Some(self.parse_expr_assoc_with(prec + 1, LhsExpr::NotYetParsed)?)
let maybe_lt = self.token.clone();
Some(
self.parse_expr_assoc_with(prec + 1, LhsExpr::NotYetParsed)
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?,
)
} else {
None
};
Expand Down Expand Up @@ -532,11 +536,13 @@ impl<'a> Parser<'a> {
let attrs = self.parse_or_use_outer_attributes(attrs)?;
self.collect_tokens_for_expr(attrs, |this, attrs| {
let lo = this.token.span;
let maybe_lt = this.look_ahead(1, |t| t.clone());
this.bump();
let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
// RHS must be parsed with more associativity than the dots.
this.parse_expr_assoc_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed)
.map(|x| (lo.to(x.span), Some(x)))?
.map(|x| (lo.to(x.span), Some(x)))
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
} else {
(lo, None)
};
Expand Down
43 changes: 43 additions & 0 deletions tests/ui/parser/range-exclusive-dotdotlt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
fn foo() {
let _ = 0..<10;
//~^ ERROR: expected type, found `10`
//~| HELP: remove the `<` to write an exclusive range
}

fn bar() {
let _ = 0..<foo;
//~^ ERROR: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `;`
//~| HELP: remove the `<` to write an exclusive range
}

fn baz() {
let _ = <foo>;
//~^ ERROR: expected `::`, found `;`
}

fn qux() {
let _ = [1, 2, 3][..<1];
//~^ ERROR: expected type, found `1`
//~| HELP: remove the `<` to write an exclusive range
}

fn quux() {
let _ = [1, 2, 3][..<foo];
//~^ ERROR: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `]`
//~| HELP: remove the `<` to write an exclusive range
}

fn foobar() {
let _ = [1, 2, 3][..<foo>];
//~^ ERROR: expected `::`, found `]`
}

fn ok1() {
let _ = [1, 2, 3][..<usize>::default()];
}

fn ok2() {
let _ = 0..<i32>::default();
}

fn main() {}
46 changes: 46 additions & 0 deletions tests/ui/parser/range-exclusive-dotdotlt.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
error: expected type, found `10`
--> $DIR/range-exclusive-dotdotlt.rs:2:17
|
LL | let _ = 0..<10;
| -^^ expected type
| |
| help: remove the `<` to write an exclusive range

error: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `;`
--> $DIR/range-exclusive-dotdotlt.rs:8:20
|
LL | let _ = 0..<foo;
| - ^ expected one of 7 possible tokens
| |
| help: remove the `<` to write an exclusive range

error: expected `::`, found `;`
--> $DIR/range-exclusive-dotdotlt.rs:14:18
|
LL | let _ = <foo>;
| ^ expected `::`

error: expected type, found `1`
--> $DIR/range-exclusive-dotdotlt.rs:19:26
|
LL | let _ = [1, 2, 3][..<1];
| -^ expected type
| |
| help: remove the `<` to write an exclusive range

error: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `]`
--> $DIR/range-exclusive-dotdotlt.rs:25:29
|
LL | let _ = [1, 2, 3][..<foo];
| - ^ expected one of 7 possible tokens
| |
| help: remove the `<` to write an exclusive range

error: expected `::`, found `]`
--> $DIR/range-exclusive-dotdotlt.rs:31:30
|
LL | let _ = [1, 2, 3][..<foo>];
| ^ expected `::`

error: aborting due to 6 previous errors

0 comments on commit 2547a32

Please sign in to comment.