Skip to content

Commit 313e989

Browse files
committed
parser: Allow parsing stmts without closing semicolon
In certain cases such as macro matching or macro expansion, it is important to allow the parser to return a valid statement even if no closing semicolon is given. This commit adds an optional parameter to the concerned functions to allow a lack of semicolon those special cases
1 parent 41f402f commit 313e989

File tree

6 files changed

+117
-19
lines changed

6 files changed

+117
-19
lines changed

gcc/rust/expand/rust-macro-expand.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
479479
break;
480480

481481
case AST::MacroFragSpec::STMT:
482-
parser.parse_stmt ();
482+
parser.parse_stmt (/* allow_no_semi */ true);
483483
break;
484484

485485
case AST::MacroFragSpec::LIFETIME:
@@ -506,6 +506,9 @@ MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
506506
return false;
507507
}
508508

509+
for (const auto &error : parser.get_errors ())
510+
error.emit_error ();
511+
509512
// it matches if the parser did not produce errors trying to parse that type
510513
// of item
511514
return !parser.has_errors ();
@@ -825,7 +828,7 @@ transcribe_many_stmts (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
825828
// transcriber is an expression, but since the macro call is followed by
826829
// a semicolon, it's a valid ExprStmt
827830
return parse_many (parser, delimiter, [&parser] () {
828-
auto stmt = parser.parse_stmt ();
831+
auto stmt = parser.parse_stmt (/* allow_no_semi */ true);
829832
return AST::SingleASTNode (std::move (stmt));
830833
});
831834
}

gcc/rust/parse/rust-parse-impl.h

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6034,9 +6034,10 @@ Parser<ManagedTokenSource>::parse_named_function_param (
60346034
// Parses a statement (will further disambiguate any statement).
60356035
template <typename ManagedTokenSource>
60366036
std::unique_ptr<AST::Stmt>
6037-
Parser<ManagedTokenSource>::parse_stmt ()
6037+
Parser<ManagedTokenSource>::parse_stmt (bool allow_no_semi)
60386038
{
60396039
// quick exit for empty statement
6040+
// FIXME: Can we have empty statements without semicolons? Just nothing?
60406041
const_TokenPtr t = lexer.peek_token ();
60416042
if (t->get_id () == SEMICOLON)
60426043
{
@@ -6058,7 +6059,7 @@ Parser<ManagedTokenSource>::parse_stmt ()
60586059
{
60596060
case LET:
60606061
// let statement
6061-
return parse_let_stmt (std::move (outer_attrs));
6062+
return parse_let_stmt (std::move (outer_attrs), allow_no_semi);
60626063
case PUB:
60636064
case MOD:
60646065
case EXTERN_TOK:
@@ -6113,15 +6114,16 @@ Parser<ManagedTokenSource>::parse_stmt ()
61136114
// TODO: find out how to disable gcc "implicit fallthrough" warning
61146115
default:
61156116
// fallback: expression statement
6116-
return parse_expr_stmt (std::move (outer_attrs));
6117+
return parse_expr_stmt (std::move (outer_attrs), allow_no_semi);
61176118
break;
61186119
}
61196120
}
61206121

61216122
// Parses a let statement.
61226123
template <typename ManagedTokenSource>
61236124
std::unique_ptr<AST::LetStmt>
6124-
Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs)
6125+
Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
6126+
bool allow_no_semi)
61256127
{
61266128
Location locus = lexer.peek_token ()->get_locus ();
61276129
skip_token (LET);
@@ -6176,12 +6178,12 @@ Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs)
61766178
}
61776179
}
61786180

6179-
if (!skip_token (SEMICOLON))
6181+
if (!maybe_skip_token (SEMICOLON) && !allow_no_semi)
61806182
{
61816183
// skip after somewhere
61826184
return nullptr;
6183-
/* TODO: how wise is it to ditch a mostly-valid let statement just because
6184-
* a semicolon is missing? */
6185+
/* TODO: how wise is it to ditch a mostly-valid let statement just
6186+
* because a semicolon is missing? */
61856187
}
61866188

61876189
return std::unique_ptr<AST::LetStmt> (
@@ -7016,7 +7018,8 @@ Parser<ManagedTokenSource>::parse_method ()
70167018
* block statement). */
70177019
template <typename ManagedTokenSource>
70187020
std::unique_ptr<AST::ExprStmt>
7019-
Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
7021+
Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
7022+
bool allow_no_semi)
70207023
{
70217024
/* potential thoughts - define new virtual method "has_block()" on expr. parse
70227025
* expr and then determine whether semicolon is needed as a result of this
@@ -7055,7 +7058,8 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
70557058
}
70567059
else
70577060
{
7058-
return parse_expr_stmt_without_block (std::move (outer_attrs));
7061+
return parse_expr_stmt_without_block (std::move (outer_attrs),
7062+
allow_no_semi);
70597063
}
70607064
}
70617065
case UNSAFE: {
@@ -7068,15 +7072,17 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
70687072
}
70697073
else
70707074
{
7071-
return parse_expr_stmt_without_block (std::move (outer_attrs));
7075+
return parse_expr_stmt_without_block (std::move (outer_attrs),
7076+
allow_no_semi);
70727077
}
70737078
}
70747079
default:
70757080
// not a parse expr with block, so must be expr without block
70767081
/* TODO: if possible, be more selective about possible expr without block
70777082
* initial tokens in order to prevent more syntactical errors at parse
70787083
* time. */
7079-
return parse_expr_stmt_without_block (std::move (outer_attrs));
7084+
return parse_expr_stmt_without_block (std::move (outer_attrs),
7085+
allow_no_semi);
70807086
}
70817087
}
70827088

@@ -7192,7 +7198,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt_with_block (
71927198
template <typename ManagedTokenSource>
71937199
std::unique_ptr<AST::ExprStmtWithoutBlock>
71947200
Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
7195-
AST::AttrVec outer_attrs)
7201+
AST::AttrVec outer_attrs, bool allow_no_semi)
71967202
{
71977203
/* TODO: maybe move more logic for expr without block in here for better error
71987204
* handling */
@@ -7217,7 +7223,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
72177223
}
72187224

72197225
// skip semicolon at end that is required
7220-
if (!skip_token (SEMICOLON))
7226+
if (!maybe_skip_token (SEMICOLON) && !allow_no_semi)
72217227
{
72227228
// skip somewhere?
72237229
return nullptr;
@@ -12219,6 +12225,18 @@ Parser<ManagedTokenSource>::skip_token (TokenId token_id)
1221912225
return expect_token (token_id) != const_TokenPtr ();
1222012226
}
1222112227

12228+
/* Checks if current token has inputted id - skips it and returns true if so,
12229+
* returns false otherwise without diagnosing an error */
12230+
template <typename ManagedTokenSource>
12231+
bool
12232+
Parser<ManagedTokenSource>::maybe_skip_token (TokenId token_id)
12233+
{
12234+
if (lexer.peek_token ()->get_id () != token_id)
12235+
return false;
12236+
else
12237+
return skip_token (token_id);
12238+
}
12239+
1222212240
/* Checks the current token - if id is same as expected, skips and returns it,
1222312241
* otherwise diagnoses error and returns null. */
1222412242
template <typename ManagedTokenSource>

gcc/rust/parse/rust-parse.h

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,25 @@ struct ParseRestrictions
8888
template <typename ManagedTokenSource> class Parser
8989
{
9090
public:
91+
/**
92+
* Consume a token, reporting an error if it isn't the next token
93+
*
94+
* @param t ID of the token to consume
95+
*
96+
* @return true if the token was next, false if it wasn't found
97+
*/
9198
bool skip_token (TokenId t);
9299

100+
/**
101+
* Same as `skip_token` but allows for failure without necessarily reporting
102+
* an error
103+
*
104+
* @param t ID of the token to consume
105+
*
106+
* @return true if the token was next, false if it wasn't found
107+
*/
108+
bool maybe_skip_token (TokenId t);
109+
93110
std::unique_ptr<AST::Expr>
94111
parse_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
95112
ParseRestrictions restrictions = ParseRestrictions ());
@@ -103,7 +120,20 @@ template <typename ManagedTokenSource> class Parser
103120

104121
std::unique_ptr<AST::Item> parse_item (bool called_from_statement);
105122
std::unique_ptr<AST::Pattern> parse_pattern ();
106-
std::unique_ptr<AST::Stmt> parse_stmt ();
123+
124+
/**
125+
* Parse a statement
126+
*
127+
* Statement : ';'
128+
* | Item
129+
* | LetStatement
130+
* | ExpressionStatement
131+
* | MacroInvocationSemi
132+
*
133+
* @param allow_no_semi Allow the parser to not parse a semicolon after
134+
* the statement without erroring out
135+
*/
136+
std::unique_ptr<AST::Stmt> parse_stmt (bool allow_no_semi = false);
107137
std::unique_ptr<AST::Type> parse_type ();
108138
AST::PathInExpression parse_path_in_expression ();
109139
std::vector<std::unique_ptr<AST::LifetimeParam> > parse_lifetime_params ();
@@ -575,12 +605,25 @@ template <typename ManagedTokenSource> class Parser
575605
AST::MaybeNamedParam parse_maybe_named_param (AST::AttrVec outer_attrs);
576606

577607
// Statement-related
578-
std::unique_ptr<AST::LetStmt> parse_let_stmt (AST::AttrVec outer_attrs);
579-
std::unique_ptr<AST::ExprStmt> parse_expr_stmt (AST::AttrVec outer_attrs);
608+
609+
/**
610+
*Parse a let-statement
611+
* LetStatement :
612+
* OuterAttribute*
613+
* 'let' PatternNoTopAlt ( ':' Type )? ('=' Expression )? ';'
614+
*
615+
* @param allow_no_semi Allow parsing a let-statement without expecting a
616+
* semicolon to follow it
617+
*/
618+
std::unique_ptr<AST::LetStmt> parse_let_stmt (AST::AttrVec outer_attrs,
619+
bool allow_no_semi = false);
620+
std::unique_ptr<AST::ExprStmt> parse_expr_stmt (AST::AttrVec outer_attrs,
621+
bool allow_no_semi = false);
580622
std::unique_ptr<AST::ExprStmtWithBlock>
581623
parse_expr_stmt_with_block (AST::AttrVec outer_attrs);
582624
std::unique_ptr<AST::ExprStmtWithoutBlock>
583-
parse_expr_stmt_without_block (AST::AttrVec outer_attrs);
625+
parse_expr_stmt_without_block (AST::AttrVec outer_attrs,
626+
bool allow_no_semi = false);
584627
ExprOrStmt parse_stmt_or_expr_without_block ();
585628
ExprOrStmt parse_stmt_or_expr_with_block (AST::AttrVec outer_attrs);
586629
ExprOrStmt parse_macro_invocation_maybe_semi (AST::AttrVec outer_attrs);

gcc/testsuite/rust/compile/macro18.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// { dg-additional-options "-w" }
2+
3+
macro_rules! take_stmt {
4+
($s:stmt) => {
5+
$s;
6+
};
7+
}
8+
9+
fn main() -> i32 {
10+
take_stmt!(let complete = 15;);
11+
take_stmt!(let lacking = 14);
12+
13+
0
14+
}

gcc/testsuite/rust/compile/macro19.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// { dg-additional-options "-w" }
2+
3+
macro_rules! call_without_semi {
4+
() => {
5+
f()
6+
};
7+
(block) => {{
8+
f()
9+
}};
10+
}
11+
12+
fn f() {}
13+
14+
fn main() -> i32 {
15+
call_without_semi!();
16+
call_without_semi!(block);
17+
18+
0
19+
}

gcc/testsuite/rust/compile/macro9.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn main() -> i32 {
1212
let b = add!(15);
1313
let b = add!(15 14); // { dg-error "Failed to match any rule within macro" }
1414
let b = add!(15, 14,); // { dg-error "Failed to match any rule within macro" }
15+
// { dg-error "found unexpected token" "" { target *-*-* } .-1 }
1516

1617
0
1718
}

0 commit comments

Comments
 (0)