Skip to content

Commit

Permalink
Ensure an error is emitted when parsing a stmt after a last stmt in t…
Browse files Browse the repository at this point in the history
…he top level block (#302)

There is code that "merges" two top level blocks when performing initial
parsing. This code does not account for the fact that a stmt parsed
after a last stmt is invalid syntax. We extend it to ensure an error is
reported in this case.

Note that we do not resolve the problem where the stmts after the last
stmt "teleport" in front of the last stmt in the AST. That is more
difficult to sort without restructuring the AST. However this was
originally parsed as invalid syntax so we now at least emit an error and
do not guarantee the state of an invalid syntax tree.

Partially resolves #298
  • Loading branch information
JohnnyMorganz authored Jul 6, 2024
1 parent 0d5187f commit 74c20d5
Show file tree
Hide file tree
Showing 19 changed files with 1,333 additions and 2 deletions.
17 changes: 15 additions & 2 deletions full-moon/src/ast/parser_structs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::borrow::Cow;

use crate::tokenizer::{Lexer, LexerResult, Symbol, TokenKind, TokenReference};
use crate::node::Node;
use crate::tokenizer::{Lexer, LexerResult, Symbol, Token, TokenKind, TokenReference};

use super::{parsers::parse_block, Ast, Block, LuaVersion};

Expand Down Expand Up @@ -192,7 +193,10 @@ impl ParserState {
.push(crate::Error::AstError(crate::ast::AstError {
token: token_reference.token,
additional: error.into(),
range: Some((start_token.start_position(), end_token.end_position())),
range: Some((
Token::start_position(start_token),
Token::end_position(end_token),
)),
}));
}
}
Expand Down Expand Up @@ -277,6 +281,8 @@ impl AstResult {
_ => Block::new(),
};

let block_has_last_stmt = block.last_stmt().is_some();

loop {
match parser_state.lexer.current() {
Some(LexerResult::Ok(token)) if token.token_kind() == TokenKind::Eof => {
Expand Down Expand Up @@ -315,6 +321,13 @@ impl AstResult {
continue;
}

if block_has_last_stmt {
parser_state.token_error(
new_block.tokens().next().unwrap().clone(),
"unexpected statement after last statement",
)
}

block.merge_blocks(new_block);
}
}
Expand Down
235 changes: 235 additions & 0 deletions full-moon/tests/cases/fail/parser/stmt-after-break-1/ast.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
---
source: full-moon/tests/fail_cases.rs
expression: result.ast()
---
nodes:
stmts:
- - FunctionCall:
prefix:
Name:
leading_trivia: []
token:
start_position:
bytes: 0
line: 1
character: 1
end_position:
bytes: 5
line: 1
character: 6
token_type:
type: Identifier
identifier: print
trailing_trivia: []
suffixes:
- Call:
AnonymousCall:
Parentheses:
parentheses:
tokens:
- leading_trivia: []
token:
start_position:
bytes: 5
line: 1
character: 6
end_position:
bytes: 6
line: 1
character: 7
token_type:
type: Symbol
symbol: (
trailing_trivia: []
- leading_trivia: []
token:
start_position:
bytes: 9
line: 1
character: 10
end_position:
bytes: 10
line: 1
character: 11
token_type:
type: Symbol
symbol: )
trailing_trivia:
- start_position:
bytes: 10
line: 1
character: 11
end_position:
bytes: 11
line: 1
character: 11
token_type:
type: Whitespace
characters: "\n"
arguments:
pairs:
- End:
String:
leading_trivia: []
token:
start_position:
bytes: 6
line: 1
character: 7
end_position:
bytes: 9
line: 1
character: 10
token_type:
type: StringLiteral
literal: "1"
quote_type: Double
trailing_trivia: []
- ~
- - FunctionCall:
prefix:
Name:
leading_trivia: []
token:
start_position:
bytes: 59
line: 3
character: 1
end_position:
bytes: 64
line: 3
character: 6
token_type:
type: Identifier
identifier: print
trailing_trivia: []
suffixes:
- Call:
AnonymousCall:
Parentheses:
parentheses:
tokens:
- leading_trivia: []
token:
start_position:
bytes: 64
line: 3
character: 6
end_position:
bytes: 65
line: 3
character: 7
token_type:
type: Symbol
symbol: (
trailing_trivia: []
- leading_trivia: []
token:
start_position:
bytes: 68
line: 3
character: 10
end_position:
bytes: 69
line: 3
character: 11
token_type:
type: Symbol
symbol: )
trailing_trivia:
- start_position:
bytes: 69
line: 3
character: 11
end_position:
bytes: 70
line: 3
character: 11
token_type:
type: Whitespace
characters: "\n"
arguments:
pairs:
- End:
String:
leading_trivia: []
token:
start_position:
bytes: 65
line: 3
character: 7
end_position:
bytes: 68
line: 3
character: 10
token_type:
type: StringLiteral
literal: "2"
quote_type: Double
trailing_trivia: []
- ~
last_stmt:
- Break:
leading_trivia: []
token:
start_position:
bytes: 11
line: 2
character: 1
end_position:
bytes: 16
line: 2
character: 6
token_type:
type: Symbol
symbol: break
trailing_trivia:
- start_position:
bytes: 16
line: 2
character: 6
end_position:
bytes: 17
line: 2
character: 7
token_type:
type: Whitespace
characters: " "
- start_position:
bytes: 17
line: 2
character: 7
end_position:
bytes: 58
line: 2
character: 48
token_type:
type: SingleLineComment
comment: " this can be replaced with break/return"
- start_position:
bytes: 58
line: 2
character: 48
end_position:
bytes: 59
line: 2
character: 48
token_type:
type: Whitespace
characters: "\n"
- ~
eof:
leading_trivia: []
token:
start_position:
bytes: 70
line: 4
character: 1
end_position:
bytes: 70
line: 4
character: 1
token_type:
type: Eof
trailing_trivia: []

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: full-moon/tests/fail_cases.rs
expression: "full_moon::print(&ast)"
---
"print(\"1\")\nprint(\"2\")\nbreak -- this can be replaced with break/return\n"

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: full-moon/tests/fail_cases.rs
expression: "String::from_utf8(output.into_inner()).unwrap()"
---
error[ast]: unexpected statement after last statement
┌─ source.lua:3:1
3print("2")
^^^^^


19 changes: 19 additions & 0 deletions full-moon/tests/cases/fail/parser/stmt-after-break-1/errors.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
source: full-moon/tests/fail_cases.rs
expression: result.errors()
---
- AstError:
token:
start_position:
bytes: 59
line: 3
character: 1
end_position:
bytes: 64
line: 3
character: 6
token_type:
type: Identifier
identifier: print
additional: unexpected statement after last statement

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
print("1")
break -- this can be replaced with break/return
print("2")
Loading

0 comments on commit 74c20d5

Please sign in to comment.