Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/rustc_parse/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4690,3 +4690,15 @@ pub(crate) struct SuggestIntroduceTypeParameter {
pub span: Span,
pub parameters: String,
}

#[derive(Subdiagnostic)]
#[suggestion(
"you might have meant to write a diverging block on a refutable `let` statement by using `let-else`
for more information, visit <https://doc.rust-lang.org/beta/rust-by-example/flow_control/let_else.html>",
code = " else ",
applicability = "maybe-incorrect"
)]
pub(crate) struct MissingElseInLet {
#[primary_span]
pub span: Span,
}
15 changes: 14 additions & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3865,7 +3865,7 @@ impl<'a> Parser<'a> {
let mut base = ast::StructRest::None;
let mut recovered_async = None;
let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD);

let in_let = self.restrictions.contains(Restrictions::IN_LET);
let async_block_err = |e: &mut Diag<'_>, span: Span| {
diagnostics::AsyncBlockIn2015 { span }.add_to_diag(e);
diagnostics::HelpUseLatestEdition::new().add_to_diag(e);
Expand Down Expand Up @@ -3954,6 +3954,19 @@ impl<'a> Parser<'a> {
return Err(e);
}

if in_let {
let mut snapshot = self.create_snapshot_for_diagnostic();
let might_be_stmt = snapshot.token.is_keyword(kw::Return);
snapshot.recover_stmt();
snapshot.bump();
if might_be_stmt && snapshot.eat(exp!(Semi)) {
e.subdiagnostic(diagnostics::MissingElseInLet {
span: pth.span.shrink_to_hi(),
});
self.restore_snapshot(snapshot);
return Err(e);
}
}
let guar = e.emit();
if pth == kw::Async {
recovered_async = Some(guar);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ bitflags::bitflags! {
/// expression, but halts parsing the expression when reaching certain
/// tokens like `=`.
const IS_PAT = 1 << 5;
/// Used to detect a missing `else` in a let statement.
/// e.g. let Some(foo) = bar{return;};
const IN_LET = 1 << 6;
}
}

Expand Down
13 changes: 9 additions & 4 deletions compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ use std::mem;
use std::ops::Bound;

use ast::Label;
use rustc_ast as ast;
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
use rustc_ast::util::classify::{self, TrailingBrace};
use rustc_ast::visit::{Visitor, walk_expr};
use rustc_ast::{
AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, HasAttrs, Local,
LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
self as ast, AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind,
HasAttrs, Local, LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
};
use rustc_errors::{Applicability, Diag, PResult};
use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
Expand Down Expand Up @@ -494,7 +493,13 @@ impl<'a> Parser<'a> {
_ => self.eat(exp!(Eq)),
};

Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None })
Ok(if eq_consumed || eq_optional {
self.current_closure.take();
let attrs = self.parse_outer_attributes()?;
Some(self.parse_expr_res(Restrictions::IN_LET, attrs).map(|res| res.0)?)
} else {
None
})
}

/// Parses a block. No inner attributes are allowed.
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/parser/detect-missing-else-in-let-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// detect missing else in let statement. (issue #135857)
fn main() {
let foo = Some(1);
let Some(a) = foo{return;};
//~^ HELP you might have meant to write a diverging block
//~| HELP escape `return` to use it as an identifier
//~| ERROR expected identifier, found keyword `return`
}
20 changes: 20 additions & 0 deletions tests/ui/parser/detect-missing-else-in-let-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error: expected identifier, found keyword `return`
--> $DIR/detect-missing-else-in-let-1.rs:4:23
|
LL | let Some(a) = foo{return;};
| --- ^^^^^^ expected identifier, found keyword
| |
| while parsing this struct
|
help: escape `return` to use it as an identifier
|
LL | let Some(a) = foo{r#return;};
| ++
help: you might have meant to write a diverging block on a refutable `let` statement by using `let-else`
for more information, visit <https://doc.rust-lang.org/beta/rust-by-example/flow_control/let_else.html>
|
LL | let Some(a) = foo else {return;};
| ++++

error: aborting due to 1 previous error

10 changes: 10 additions & 0 deletions tests/ui/parser/detect-missing-else-in-let-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// the compiler currently will suggest the user to add `else` in this case
// it shouldn't because `bar` is an irrefutable pattern, the else block would never be valid/useful
// it is currently unmitigated
fn main(){
let foo = 12;
let bar = foo{return;};
//~^ HELP you might have meant to write a diverging block
//~| HELP escape `return` to use it as an identifier
//~| ERROR expected identifier, found keyword `return`
}
20 changes: 20 additions & 0 deletions tests/ui/parser/detect-missing-else-in-let-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error: expected identifier, found keyword `return`
--> $DIR/detect-missing-else-in-let-2.rs:6:19
|
LL | let bar = foo{return;};
| --- ^^^^^^ expected identifier, found keyword
| |
| while parsing this struct
|
help: escape `return` to use it as an identifier
|
LL | let bar = foo{r#return;};
| ++
help: you might have meant to write a diverging block on a refutable `let` statement by using `let-else`
for more information, visit <https://doc.rust-lang.org/beta/rust-by-example/flow_control/let_else.html>
|
LL | let bar = foo else {return;};
| ++++

error: aborting due to 1 previous error

Loading