Skip to content

Add diagnostics for bad semicolons errors, closes #352 #362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
48 changes: 42 additions & 6 deletions source/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -3068,15 +3068,15 @@ class parser
-> void
{
auto m = std::string{msg};
auto i = done() ? -1 : 0;
assert (peek(i));
if (include_curr_token) {
m += std::string(" (at '") + curr().to_string(true) + "')";
m += std::string(" (at '") + peek(i)->to_string(true) + "')";
}
if (
err_pos == source_position{}
&& peek(0)
)
{
err_pos = peek(0)->position();
) {
err_pos = peek(i)->position();
}
errors.emplace_back( err_pos, m, false, fallback );
}
Expand Down Expand Up @@ -3225,6 +3225,12 @@ class parser
next();
return {};
}
if ( // check if a single-expression function is followed by an extra second semicolon
decl->initializer && decl->initializer->is_expression()
&& !done() && curr().type() == lexeme::Semicolon
) {
error("a single-expression function should end with a single semicolon");
}
if (!(*func)->contracts.empty()) {
error("an unnamed function at expression scope currently cannot have contracts");
next();
Expand Down Expand Up @@ -4423,6 +4429,10 @@ class parser
if (!handle_logical_expression ()) { return {}; }
if (!handle_optional_next_clause()) { return {}; }
if (!handle_compound_statement ()) { return {}; }
if (!done() && curr().type() == lexeme::Semicolon) {
error("a loop body may not be followed by a semicolon (empty statements are not allowed)");
return {};
}
return n;
}

Expand Down Expand Up @@ -4485,6 +4495,11 @@ class parser
n->for_with_in = true;
}

if (!done() && curr().type() == lexeme::Semicolon) {
error("a loop body may not be followed by a semicolon (empty statements are not allowed)");
return {};
}

return n;
}

Expand Down Expand Up @@ -4707,6 +4722,11 @@ class parser
)
-> std::unique_ptr<statement_node>
{
if (!done() && curr().type() == lexeme::Semicolon) {
error("empty statement is not allowed - remove extra semicolon");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this works, but just a note: Emitting an error here (not just returning null) could interfere with productions where statement is one alternative but where if it doesn't parse as a statement we would want to try other fallback production(s) instead. I don't think that actually happens today in the grammar though, so this should be okay.

Copy link
Owner

@hsutter hsutter Apr 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, the scenario would be "is there any production where a statement could appear, where if it's not a statement something else that starts with a ; could appear instead" -- I think there isn't today so it should be safe to reject a ; if we're in a position where a statement could appear

return {};
}

auto n = std::make_unique<statement_node>();

// Now handle the rest of the statement
Expand Down Expand Up @@ -5073,7 +5093,8 @@ class parser

// If there's no [ [ then this isn't a contract
if (
curr().type() != lexeme::LeftBracket
done()
|| curr().type() != lexeme::LeftBracket
|| !peek(1)
|| peek(1)->type() != lexeme::LeftBracket
)
Expand Down Expand Up @@ -5690,6 +5711,21 @@ class parser
return {};
}

if (
n->is_function()
&& n->initializer
&& !done() && curr().type() == lexeme::Semicolon
)
{
if (n->initializer->is_compound() && n->has_name()) {
error("a braced function body may not be followed by a semicolon (empty statements are not allowed)");
return {};
} else if (n->initializer->is_expression()) {
error("a single-expression function should end with a single semicolon");
return {};
}
}

// If this is an implicit constructor, it must have two parameters
if (n->is_constructor())
{
Expand Down