From d4fe9553f65df51a18999e956fd507e26271e74e Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Thu, 7 May 2020 03:57:31 +0200 Subject: [PATCH 1/5] Implement partial error recovery for `let` with `BinOpEq` When parsing `let x: i8 += 1` the compiler interprets `i8` as a trait which makes it more complicated to do error recovery. More advanced error recovery is not implemented in this commit. --- src/librustc_parse/parser/stmt.rs | 29 ++++++++++++++++++++++-- src/test/ui/parser/let-binop-plus.rs | 7 ++++++ src/test/ui/parser/let-binop-plus.stderr | 9 ++++++++ src/test/ui/parser/let-binop.rs | 8 +++++++ src/test/ui/parser/let-binop.stderr | 21 +++++++++++++++++ 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/parser/let-binop-plus.rs create mode 100644 src/test/ui/parser/let-binop-plus.stderr create mode 100644 src/test/ui/parser/let-binop.rs create mode 100644 src/test/ui/parser/let-binop.stderr diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index 849193151c335..049aa7447f4db 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -12,7 +12,7 @@ use rustc_ast::ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKin use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; use rustc_ast::util::classify; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{struct_span_err, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym}; @@ -217,7 +217,32 @@ impl<'a> Parser<'a> { /// Parses the RHS of a local variable declaration (e.g., '= 14;'). fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option
>> {
- if self.eat(&token::Eq) || skip_eq { Ok(Some(self.parse_expr()?)) } else { Ok(None) }
+ let parse = if !self.eat(&token::Eq) && !skip_eq {
+ // Error recovery for `let x += 1`
+ if matches!(self.token.kind, TokenKind::BinOpEq(_)) {
+ struct_span_err!(
+ self.sess.span_diagnostic,
+ self.token.span,
+ E0067,
+ "can't reassign to a uninitialized variable"
+ )
+ .span_suggestion_short(
+ self.token.span,
+ "replace with `=` to initialize the variable",
+ "=".to_string(),
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ self.bump();
+ true
+ } else {
+ false
+ }
+ } else {
+ true
+ };
+
+ if parse { Ok(Some(self.parse_expr()?)) } else { Ok(None) }
}
/// Parses a block. No inner attributes are allowed.
diff --git a/src/test/ui/parser/let-binop-plus.rs b/src/test/ui/parser/let-binop-plus.rs
new file mode 100644
index 0000000000000..8d883d6e24894
--- /dev/null
+++ b/src/test/ui/parser/let-binop-plus.rs
@@ -0,0 +1,7 @@
+#![allow(bare_trait_objects)]
+
+fn main() {
+ let a: i8 += 1;
+ //~^ ERROR expected trait, found builtin type `i8`
+ let _ = a;
+}
diff --git a/src/test/ui/parser/let-binop-plus.stderr b/src/test/ui/parser/let-binop-plus.stderr
new file mode 100644
index 0000000000000..baa935aff713c
--- /dev/null
+++ b/src/test/ui/parser/let-binop-plus.stderr
@@ -0,0 +1,9 @@
+error[E0404]: expected trait, found builtin type `i8`
+ --> $DIR/let-binop-plus.rs:4:12
+ |
+LL | let a: i8 += 1;
+ | ^^ not a trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0404`.
diff --git a/src/test/ui/parser/let-binop.rs b/src/test/ui/parser/let-binop.rs
new file mode 100644
index 0000000000000..d445ab6bb8a1f
--- /dev/null
+++ b/src/test/ui/parser/let-binop.rs
@@ -0,0 +1,8 @@
+fn main() {
+ let a: i8 *= 1; //~ ERROR can't reassign to a uninitialized variable
+ let _ = a;
+ let b += 1; //~ ERROR can't reassign to a uninitialized variable
+ let _ = b;
+ let c *= 1; //~ ERROR can't reassign to a uninitialized variable
+ let _ = c;
+}
diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr
new file mode 100644
index 0000000000000..3e9d4a80a70ef
--- /dev/null
+++ b/src/test/ui/parser/let-binop.stderr
@@ -0,0 +1,21 @@
+error[E0067]: can't reassign to a uninitialized variable
+ --> $DIR/let-binop.rs:2:15
+ |
+LL | let a: i8 *= 1;
+ | ^^ help: replace with `=` to initialize the variable
+
+error[E0067]: can't reassign to a uninitialized variable
+ --> $DIR/let-binop.rs:4:11
+ |
+LL | let b += 1;
+ | ^^ help: replace with `=` to initialize the variable
+
+error[E0067]: can't reassign to a uninitialized variable
+ --> $DIR/let-binop.rs:6:11
+ |
+LL | let c *= 1;
+ | ^^ help: replace with `=` to initialize the variable
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0067`.
From 48ff12acb184672393692e087927a66ff7907d71 Mon Sep 17 00:00:00 2001
From: mibac138 <5672750+mibac138@users.noreply.github.com>
Date: Thu, 7 May 2020 04:09:57 +0200
Subject: [PATCH 2/5] Expand partial error recovery for `let` with `BinOpEq`
---
src/librustc_parse/parser/stmt.rs | 40 +++++++++++++++++++++--------
src/test/ui/parser/let-binop.stderr | 22 ++++++++++++++--
2 files changed, 50 insertions(+), 12 deletions(-)
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index 049aa7447f4db..aceee81432896 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -145,12 +145,12 @@ impl<'a> Parser<'a> {
}
fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> {
- let local = self.parse_local(attrs)?;
+ let local = self.parse_local(lo, attrs)?;
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local)))
}
/// Parses a local variable declaration.
- fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P >> {
+ fn parse_initializer(
+ &mut self,
+ let_span: Span,
+ has_ty: bool,
+ skip_eq: bool,
+ ) -> PResult<'a, Option >> {
let parse = if !self.eat(&token::Eq) && !skip_eq {
// Error recovery for `let x += 1`
if matches!(self.token.kind, TokenKind::BinOpEq(_)) {
- struct_span_err!(
+ let mut err = struct_span_err!(
self.sess.span_diagnostic,
self.token.span,
E0067,
"can't reassign to a uninitialized variable"
- )
- .span_suggestion_short(
+ );
+ err.span_suggestion_short(
self.token.span,
"replace with `=` to initialize the variable",
"=".to_string(),
- Applicability::MaybeIncorrect,
- )
- .emit();
+ if has_ty {
+ // for `let x: i8 += 1` it's highly likely that the `+` is a typo
+ Applicability::MachineApplicable
+ } else {
+ // for `let x += 1` it's a bit less likely that the `+` is a typo
+ Applicability::MaybeIncorrect
+ },
+ );
+ // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1`
+ if !has_ty {
+ err.span_suggestion_short(
+ let_span,
+ "remove to reassign to a previously initialized variable",
+ "".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
self.bump();
true
} else {
diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr
index 3e9d4a80a70ef..c37612430cef1 100644
--- a/src/test/ui/parser/let-binop.stderr
+++ b/src/test/ui/parser/let-binop.stderr
@@ -8,13 +8,31 @@ error[E0067]: can't reassign to a uninitialized variable
--> $DIR/let-binop.rs:4:11
|
LL | let b += 1;
- | ^^ help: replace with `=` to initialize the variable
+ | ^^
+ |
+help: replace with `=` to initialize the variable
+ |
+LL | let b = 1;
+ | ^
+help: remove to reassign to a previously initialized variable
+ |
+LL | b += 1;
+ | --
error[E0067]: can't reassign to a uninitialized variable
--> $DIR/let-binop.rs:6:11
|
LL | let c *= 1;
- | ^^ help: replace with `=` to initialize the variable
+ | ^^
+ |
+help: replace with `=` to initialize the variable
+ |
+LL | let c = 1;
+ | ^
+help: remove to reassign to a previously initialized variable
+ |
+LL | c *= 1;
+ | --
error: aborting due to 3 previous errors
From 05d653199871955eba90abdd3b176603f030ab60 Mon Sep 17 00:00:00 2001
From: mibac138 <5672750+mibac138@users.noreply.github.com>
Date: Thu, 7 May 2020 05:00:59 +0200
Subject: [PATCH 3/5] Error recovery for `let` with `+=`
---
src/librustc_parse/parser/stmt.rs | 65 ++++++++++++------------
src/test/ui/parser/let-binop-plus.rs | 1 +
src/test/ui/parser/let-binop-plus.stderr | 11 +++-
3 files changed, 42 insertions(+), 35 deletions(-)
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index aceee81432896..224f4ebf53828 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -222,44 +222,43 @@ impl<'a> Parser<'a> {
has_ty: bool,
skip_eq: bool,
) -> PResult<'a, Option >> {
- let parse = if !self.eat(&token::Eq) && !skip_eq {
+ // In case of code like `let x: i8 += 1`, `i8` is interpreted as a trait consuming the `+`
+ // from `+=`.
+ let ate_plus = self.prev_token.is_like_plus() && has_ty;
+ let parse = if !skip_eq && (ate_plus || matches!(self.token.kind, TokenKind::BinOpEq(_))) {
// Error recovery for `let x += 1`
- if matches!(self.token.kind, TokenKind::BinOpEq(_)) {
- let mut err = struct_span_err!(
- self.sess.span_diagnostic,
- self.token.span,
- E0067,
- "can't reassign to a uninitialized variable"
- );
+ let mut err = struct_span_err!(
+ self.sess.span_diagnostic,
+ self.token.span,
+ E0067,
+ "can't reassign to a uninitialized variable"
+ );
+ err.span_suggestion_short(
+ self.token.span,
+ "replace with `=` to initialize the variable",
+ "=".to_string(),
+ if has_ty {
+ // for `let x: i8 += 1` it's highly likely that the `+` is a typo
+ Applicability::MachineApplicable
+ } else {
+ // for `let x += 1` it's a bit less likely that the `+` is a typo
+ Applicability::MaybeIncorrect
+ },
+ );
+ // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1`
+ if !has_ty {
err.span_suggestion_short(
- self.token.span,
- "replace with `=` to initialize the variable",
- "=".to_string(),
- if has_ty {
- // for `let x: i8 += 1` it's highly likely that the `+` is a typo
- Applicability::MachineApplicable
- } else {
- // for `let x += 1` it's a bit less likely that the `+` is a typo
- Applicability::MaybeIncorrect
- },
+ let_span,
+ "remove to reassign to a previously initialized variable",
+ "".to_string(),
+ Applicability::MaybeIncorrect,
);
- // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1`
- if !has_ty {
- err.span_suggestion_short(
- let_span,
- "remove to reassign to a previously initialized variable",
- "".to_string(),
- Applicability::MaybeIncorrect,
- );
- }
- err.emit();
- self.bump();
- true
- } else {
- false
}
- } else {
+ err.emit();
+ self.bump();
true
+ } else {
+ self.eat(&token::Eq) || skip_eq
};
if parse { Ok(Some(self.parse_expr()?)) } else { Ok(None) }
diff --git a/src/test/ui/parser/let-binop-plus.rs b/src/test/ui/parser/let-binop-plus.rs
index 8d883d6e24894..98473e9f096d8 100644
--- a/src/test/ui/parser/let-binop-plus.rs
+++ b/src/test/ui/parser/let-binop-plus.rs
@@ -3,5 +3,6 @@
fn main() {
let a: i8 += 1;
//~^ ERROR expected trait, found builtin type `i8`
+ //~| ERROR can't reassign to a uninitialized variable
let _ = a;
}
diff --git a/src/test/ui/parser/let-binop-plus.stderr b/src/test/ui/parser/let-binop-plus.stderr
index baa935aff713c..d7d84ff16a0a1 100644
--- a/src/test/ui/parser/let-binop-plus.stderr
+++ b/src/test/ui/parser/let-binop-plus.stderr
@@ -1,9 +1,16 @@
+error[E0067]: can't reassign to a uninitialized variable
+ --> $DIR/let-binop-plus.rs:4:16
+ |
+LL | let a: i8 += 1;
+ | ^ help: replace with `=` to initialize the variable
+
error[E0404]: expected trait, found builtin type `i8`
--> $DIR/let-binop-plus.rs:4:12
|
LL | let a: i8 += 1;
| ^^ not a trait
-error: aborting due to previous error
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0404`.
+Some errors have detailed explanations: E0067, E0404.
+For more information about an error, try `rustc --explain E0067`.
From 6ad24baf06c687517f188e8c6e6ce848924d001c Mon Sep 17 00:00:00 2001
From: mibac138 <5672750+mibac138@users.noreply.github.com>
Date: Thu, 7 May 2020 23:45:51 +0200
Subject: [PATCH 4/5] Adjust according to estebank's review comments
---
src/librustc_parse/parser/stmt.rs | 19 ++++++++-----------
src/test/ui/parser/let-binop-plus.rs | 2 +-
src/test/ui/parser/let-binop-plus.stderr | 4 ++--
src/test/ui/parser/let-binop.rs | 6 +++---
src/test/ui/parser/let-binop.stderr | 20 ++++++++++----------
5 files changed, 24 insertions(+), 27 deletions(-)
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index 224f4ebf53828..bec810fde081d 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -174,7 +174,10 @@ impl<'a> Parser<'a> {
} else {
(None, None)
};
- let init = match (self.parse_initializer(let_span, ty.is_some(), err.is_some()), err) {
+ let init = match (
+ self.parse_initializer(let_span.until(pat.span), ty.is_some(), err.is_some()),
+ err,
+ ) {
(Ok(init), None) => {
// init parsed, ty parsed
init
@@ -231,25 +234,19 @@ impl<'a> Parser<'a> {
self.sess.span_diagnostic,
self.token.span,
E0067,
- "can't reassign to a uninitialized variable"
+ "can't reassign to an uninitialized variable"
);
err.span_suggestion_short(
self.token.span,
- "replace with `=` to initialize the variable",
+ "initialize the variable",
"=".to_string(),
- if has_ty {
- // for `let x: i8 += 1` it's highly likely that the `+` is a typo
- Applicability::MachineApplicable
- } else {
- // for `let x += 1` it's a bit less likely that the `+` is a typo
- Applicability::MaybeIncorrect
- },
+ Applicability::MaybeIncorrect,
);
// In case of code like `let x += 1` it's possible the user may have meant to write `x += 1`
if !has_ty {
err.span_suggestion_short(
let_span,
- "remove to reassign to a previously initialized variable",
+ "otherwise, reassign to a previously initialized variable",
"".to_string(),
Applicability::MaybeIncorrect,
);
diff --git a/src/test/ui/parser/let-binop-plus.rs b/src/test/ui/parser/let-binop-plus.rs
index 98473e9f096d8..4d6d9b5c8d37f 100644
--- a/src/test/ui/parser/let-binop-plus.rs
+++ b/src/test/ui/parser/let-binop-plus.rs
@@ -3,6 +3,6 @@
fn main() {
let a: i8 += 1;
//~^ ERROR expected trait, found builtin type `i8`
- //~| ERROR can't reassign to a uninitialized variable
+ //~| ERROR can't reassign to an uninitialized variable
let _ = a;
}
diff --git a/src/test/ui/parser/let-binop-plus.stderr b/src/test/ui/parser/let-binop-plus.stderr
index d7d84ff16a0a1..91a59fe24fedc 100644
--- a/src/test/ui/parser/let-binop-plus.stderr
+++ b/src/test/ui/parser/let-binop-plus.stderr
@@ -1,8 +1,8 @@
-error[E0067]: can't reassign to a uninitialized variable
+error[E0067]: can't reassign to an uninitialized variable
--> $DIR/let-binop-plus.rs:4:16
|
LL | let a: i8 += 1;
- | ^ help: replace with `=` to initialize the variable
+ | ^ help: initialize the variable
error[E0404]: expected trait, found builtin type `i8`
--> $DIR/let-binop-plus.rs:4:12
diff --git a/src/test/ui/parser/let-binop.rs b/src/test/ui/parser/let-binop.rs
index d445ab6bb8a1f..7f58f5df2d412 100644
--- a/src/test/ui/parser/let-binop.rs
+++ b/src/test/ui/parser/let-binop.rs
@@ -1,8 +1,8 @@
fn main() {
- let a: i8 *= 1; //~ ERROR can't reassign to a uninitialized variable
+ let a: i8 *= 1; //~ ERROR can't reassign to an uninitialized variable
let _ = a;
- let b += 1; //~ ERROR can't reassign to a uninitialized variable
+ let b += 1; //~ ERROR can't reassign to an uninitialized variable
let _ = b;
- let c *= 1; //~ ERROR can't reassign to a uninitialized variable
+ let c *= 1; //~ ERROR can't reassign to an uninitialized variable
let _ = c;
}
diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr
index c37612430cef1..8a90b7cf74a4a 100644
--- a/src/test/ui/parser/let-binop.stderr
+++ b/src/test/ui/parser/let-binop.stderr
@@ -1,37 +1,37 @@
-error[E0067]: can't reassign to a uninitialized variable
+error[E0067]: can't reassign to an uninitialized variable
--> $DIR/let-binop.rs:2:15
|
LL | let a: i8 *= 1;
- | ^^ help: replace with `=` to initialize the variable
+ | ^^ help: initialize the variable
-error[E0067]: can't reassign to a uninitialized variable
+error[E0067]: can't reassign to an uninitialized variable
--> $DIR/let-binop.rs:4:11
|
LL | let b += 1;
| ^^
|
-help: replace with `=` to initialize the variable
+help: initialize the variable
|
LL | let b = 1;
| ^
-help: remove to reassign to a previously initialized variable
+help: otherwise, reassign to a previously initialized variable
|
-LL | b += 1;
+LL | b += 1;
| --
-error[E0067]: can't reassign to a uninitialized variable
+error[E0067]: can't reassign to an uninitialized variable
--> $DIR/let-binop.rs:6:11
|
LL | let c *= 1;
| ^^
|
-help: replace with `=` to initialize the variable
+help: initialize the variable
|
LL | let c = 1;
| ^
-help: remove to reassign to a previously initialized variable
+help: otherwise, reassign to a previously initialized variable
|
-LL | c *= 1;
+LL | c *= 1;
| --
error: aborting due to 3 previous errors
From 98532a30901d7544c49fe82f499db53699645de0 Mon Sep 17 00:00:00 2001
From: mibac138 <5672750+mibac138@users.noreply.github.com>
Date: Wed, 20 May 2020 22:09:03 +0200
Subject: [PATCH 5/5] Adjust according to petrochenkov's review comments
---
src/librustc_parse/parser/stmt.rs | 65 ++++++++----------------
src/test/ui/parser/let-binop-plus.rs | 8 ---
src/test/ui/parser/let-binop-plus.stderr | 16 ------
src/test/ui/parser/let-binop.stderr | 29 ++---------
4 files changed, 27 insertions(+), 91 deletions(-)
delete mode 100644 src/test/ui/parser/let-binop-plus.rs
delete mode 100644 src/test/ui/parser/let-binop-plus.stderr
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index bec810fde081d..53f32b7c800bd 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -12,7 +12,7 @@ use rustc_ast::ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKin
use rustc_ast::ptr::P;
use rustc_ast::token::{self, TokenKind};
use rustc_ast::util::classify;
-use rustc_errors::{struct_span_err, Applicability, PResult};
+use rustc_errors::{Applicability, PResult};
use rustc_span::source_map::{BytePos, Span};
use rustc_span::symbol::{kw, sym};
@@ -145,12 +145,12 @@ impl<'a> Parser<'a> {
}
fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> {
- let local = self.parse_local(lo, attrs)?;
+ let local = self.parse_local(attrs)?;
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local)))
}
/// Parses a local variable declaration.
- fn parse_local(&mut self, let_span: Span, attrs: AttrVec) -> PResult<'a, P >> {
- // In case of code like `let x: i8 += 1`, `i8` is interpreted as a trait consuming the `+`
- // from `+=`.
- let ate_plus = self.prev_token.is_like_plus() && has_ty;
- let parse = if !skip_eq && (ate_plus || matches!(self.token.kind, TokenKind::BinOpEq(_))) {
- // Error recovery for `let x += 1`
- let mut err = struct_span_err!(
- self.sess.span_diagnostic,
- self.token.span,
- E0067,
- "can't reassign to an uninitialized variable"
- );
- err.span_suggestion_short(
- self.token.span,
- "initialize the variable",
- "=".to_string(),
- Applicability::MaybeIncorrect,
- );
- // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1`
- if !has_ty {
- err.span_suggestion_short(
- let_span,
- "otherwise, reassign to a previously initialized variable",
- "".to_string(),
+ fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option >> {
+ let eq_consumed = match self.token.kind {
+ token::BinOpEq(..) => {
+ // Recover `let x