Merge pull request #18908 from jnyfah/error-braces

Fix: Detect missing errors for } braces before else in let...else statements
This commit is contained in:
Lukas Wirth 2025-01-17 13:29:56 +00:00 committed by GitHub
commit cd199eacd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 637 additions and 12 deletions

View File

@ -12,7 +12,7 @@ use crate::{
/// `Parser` produces a flat list of `Event`s.
/// They are converted to a tree-structure in
/// a separate pass, via `TreeBuilder`.
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub(crate) enum Event {
/// This event signifies the start of the node.
/// It should be either abandoned (in which case the

View File

@ -134,10 +134,12 @@ pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
// test_err let_else_right_curly_brace
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
if let Some(expr) = expr_after_eq {
if BlockLike::is_blocklike(expr.kind()) {
p.error(
"right curly brace `}` before `else` in a `let...else` statement not allowed",
)
if let Some(token) = expr.last_token(p) {
if token == T!['}'] {
p.error(
"right curly brace `}` before `else` in a `let...else` statement not allowed"
)
}
}
}

View File

@ -318,7 +318,8 @@ impl Marker {
_ => unreachable!(),
}
p.push_event(Event::Finish);
CompletedMarker::new(self.pos, kind)
let end_pos = p.events.len() as u32;
CompletedMarker::new(self.pos, end_pos, kind)
}
/// Abandons the syntax tree node. All its children
@ -336,13 +337,14 @@ impl Marker {
}
pub(crate) struct CompletedMarker {
pos: u32,
start_pos: u32,
end_pos: u32,
kind: SyntaxKind,
}
impl CompletedMarker {
fn new(pos: u32, kind: SyntaxKind) -> Self {
CompletedMarker { pos, kind }
fn new(start_pos: u32, end_pos: u32, kind: SyntaxKind) -> Self {
CompletedMarker { start_pos, end_pos, kind }
}
/// This method allows to create a new node which starts
@ -360,10 +362,10 @@ impl CompletedMarker {
/// distance to `NEWSTART` into forward_parent(=2 in this case);
pub(crate) fn precede(self, p: &mut Parser<'_>) -> Marker {
let new_pos = p.start();
let idx = self.pos as usize;
let idx = self.start_pos as usize;
match &mut p.events[idx] {
Event::Start { forward_parent, .. } => {
*forward_parent = Some(new_pos.pos - self.pos);
*forward_parent = Some(new_pos.pos - self.start_pos);
}
_ => unreachable!(),
}
@ -376,7 +378,7 @@ impl CompletedMarker {
let idx = m.pos as usize;
match &mut p.events[idx] {
Event::Start { forward_parent, .. } => {
*forward_parent = Some(self.pos - m.pos);
*forward_parent = Some(self.start_pos - m.pos);
}
_ => unreachable!(),
}
@ -386,4 +388,13 @@ impl CompletedMarker {
pub(crate) fn kind(&self) -> SyntaxKind {
self.kind
}
pub(crate) fn last_token(&self, p: &Parser<'_>) -> Option<SyntaxKind> {
let end_pos = self.end_pos as usize;
debug_assert_eq!(p.events[end_pos - 1], Event::Finish);
p.events[..end_pos].iter().rev().find_map(|event| match event {
Event::Token { kind, .. } => Some(*kind),
_ => None,
})
}
}

View File

@ -0,0 +1,79 @@
SOURCE_FILE
STRUCT
STRUCT_KW "struct"
WHITESPACE " "
NAME
IDENT "X"
WHITESPACE " "
RECORD_FIELD_LIST
L_CURLY "{"
RECORD_FIELD
NAME
IDENT "a"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i32"
R_CURLY "}"
WHITESPACE "\n"
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "f"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LET_STMT
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
RECORD_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "X"
WHITESPACE " "
RECORD_EXPR_FIELD_LIST
L_CURLY "{"
WHITESPACE "\n "
RECORD_EXPR_FIELD
NAME_REF
IDENT "a"
COLON ":"
WHITESPACE " "
LITERAL
INT_NUMBER "1"
WHITESPACE "\n "
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n "
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
error 63: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,8 @@
struct X {a: i32}
fn f() {
let foo = X {
a: 1
} else {
return;
};
}

View File

@ -0,0 +1,42 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
BIN_EXPR
LITERAL
INT_NUMBER "1"
WHITESPACE " "
PLUS "+"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LITERAL
INT_NUMBER "1"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
error 23: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
let foo = 1 + {
1
} else {
return;
};

View File

@ -0,0 +1,90 @@
SOURCE_FILE
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "r"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LET_STMT
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "ok"
WHITESPACE " "
EQ "="
WHITESPACE " "
MACRO_EXPR
MACRO_CALL
PATH
PATH_SEGMENT
NAME_REF
IDENT "format_args"
BANG "!"
TOKEN_TREE
L_PAREN "("
STRING "\"\""
R_PAREN ")"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE " "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE " "
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n\n "
LET_STMT
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "bad"
WHITESPACE " "
EQ "="
WHITESPACE " "
MACRO_EXPR
MACRO_CALL
PATH
PATH_SEGMENT
NAME_REF
IDENT "format_args"
BANG "!"
WHITESPACE " "
TOKEN_TREE
L_CURLY "{"
STRING "\"\""
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE " "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE " "
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
error 89: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
fn r() {
let ok = format_args!("") else { return; };
let bad = format_args! {""} else { return; };
}

View File

@ -0,0 +1,40 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
RANGE_EXPR
LITERAL
INT_NUMBER "1"
DOT2 ".."
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LITERAL
INT_NUMBER "1"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
error 22: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
let foo = 1..{
1
} else {
return;
};

View File

@ -0,0 +1,55 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
CLOSURE_EXPR
PARAM_LIST
PIPE "|"
PARAM
IDENT_PAT
NAME
IDENT "x"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i32"
PIPE "|"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "x"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
error 28: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
let foo = |x: i32| {
x
} else {
return;
};

View File

@ -0,0 +1,38 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
PREFIX_EXPR
MINUS "-"
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LITERAL
INT_NUMBER "1"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
error 20: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
let foo = -{
1
} else {
return;
};

View File

@ -0,0 +1,90 @@
SOURCE_FILE
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "o"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
RET_TYPE
THIN_ARROW "->"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "Result"
GENERIC_ARG_LIST
L_ANGLE "<"
TYPE_ARG
TUPLE_TYPE
L_PAREN "("
R_PAREN ")"
COMMA ","
WHITESPACE " "
TYPE_ARG
TUPLE_TYPE
L_PAREN "("
R_PAREN ")"
R_ANGLE ">"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LET_STMT
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
YEET_EXPR
DO_KW "do"
WHITESPACE " "
YEET_KW "yeet"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
TUPLE_EXPR
L_PAREN "("
R_PAREN ")"
WHITESPACE "\n "
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
WHITESPACE " "
CALL_EXPR
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "Ok"
ARG_LIST
L_PAREN "("
TUPLE_EXPR
L_PAREN "("
R_PAREN ")"
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n "
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
error 67: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,7 @@
fn o() -> Result<(), ()> {
let foo = do yeet {
()
} else {
return Ok(());
};
}

View File

@ -0,0 +1,40 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
BECOME_EXPR
BECOME_KW "become"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
TUPLE_EXPR
L_PAREN "("
R_PAREN ")"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
error 27: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
let foo = become {
()
} else {
return;
};

View File

@ -0,0 +1,38 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
REF_EXPR
AMP "&"
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LITERAL
INT_NUMBER "1"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
error 20: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
let foo = &{
1
} else {
return;
};

View File

@ -0,0 +1,45 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "foo"
WHITESPACE " "
EQ "="
WHITESPACE " "
BIN_EXPR
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "bar"
WHITESPACE " "
EQ "="
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
LITERAL
INT_NUMBER "1"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE " "
LET_ELSE
ELSE_KW "else"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
RETURN_EXPR
RETURN_KW "return"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
error 25: right curly brace `}` before `else` in a `let...else` statement not allowed

View File

@ -0,0 +1,5 @@
let foo = bar = {
1
} else {
return;
};