aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorArthur Cohen <arthur.cohen@embecosm.com>2022-03-14 14:29:58 +0100
committerArthur Cohen <arthur.cohen@embecosm.com>2022-03-14 16:34:50 +0100
commit313e9890d85b688f538fed52d158b4b4f4aea9dc (patch)
tree1e6b8e44c8c9970894be9ff8b1dde0f727443a3d /gcc
parent41f402f0b19c7e4f19f8d4d65d15223d2752f302 (diff)
downloadgcc-313e9890d85b688f538fed52d158b4b4f4aea9dc.zip
gcc-313e9890d85b688f538fed52d158b4b4f4aea9dc.tar.gz
gcc-313e9890d85b688f538fed52d158b4b4f4aea9dc.tar.bz2
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/expand/rust-macro-expand.cc7
-rw-r--r--gcc/rust/parse/rust-parse-impl.h44
-rw-r--r--gcc/rust/parse/rust-parse.h51
-rw-r--r--gcc/testsuite/rust/compile/macro18.rs14
-rw-r--r--gcc/testsuite/rust/compile/macro19.rs19
-rw-r--r--gcc/testsuite/rust/compile/macro9.rs1
6 files changed, 117 insertions, 19 deletions
diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
index 22ba1d8a..f131372 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -479,7 +479,7 @@ MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
break;
case AST::MacroFragSpec::STMT:
- parser.parse_stmt ();
+ parser.parse_stmt (/* allow_no_semi */ true);
break;
case AST::MacroFragSpec::LIFETIME:
@@ -506,6 +506,9 @@ MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
return false;
}
+ for (const auto &error : parser.get_errors ())
+ error.emit_error ();
+
// it matches if the parser did not produce errors trying to parse that type
// of item
return !parser.has_errors ();
@@ -825,7 +828,7 @@ transcribe_many_stmts (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
// transcriber is an expression, but since the macro call is followed by
// a semicolon, it's a valid ExprStmt
return parse_many (parser, delimiter, [&parser] () {
- auto stmt = parser.parse_stmt ();
+ auto stmt = parser.parse_stmt (/* allow_no_semi */ true);
return AST::SingleASTNode (std::move (stmt));
});
}
diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h
index db73828..0bbd8fb 100644
--- a/gcc/rust/parse/rust-parse-impl.h
+++ b/gcc/rust/parse/rust-parse-impl.h
@@ -6034,9 +6034,10 @@ Parser<ManagedTokenSource>::parse_named_function_param (
// Parses a statement (will further disambiguate any statement).
template <typename ManagedTokenSource>
std::unique_ptr<AST::Stmt>
-Parser<ManagedTokenSource>::parse_stmt ()
+Parser<ManagedTokenSource>::parse_stmt (bool allow_no_semi)
{
// quick exit for empty statement
+ // FIXME: Can we have empty statements without semicolons? Just nothing?
const_TokenPtr t = lexer.peek_token ();
if (t->get_id () == SEMICOLON)
{
@@ -6058,7 +6059,7 @@ Parser<ManagedTokenSource>::parse_stmt ()
{
case LET:
// let statement
- return parse_let_stmt (std::move (outer_attrs));
+ return parse_let_stmt (std::move (outer_attrs), allow_no_semi);
case PUB:
case MOD:
case EXTERN_TOK:
@@ -6113,7 +6114,7 @@ Parser<ManagedTokenSource>::parse_stmt ()
// TODO: find out how to disable gcc "implicit fallthrough" warning
default:
// fallback: expression statement
- return parse_expr_stmt (std::move (outer_attrs));
+ return parse_expr_stmt (std::move (outer_attrs), allow_no_semi);
break;
}
}
@@ -6121,7 +6122,8 @@ Parser<ManagedTokenSource>::parse_stmt ()
// Parses a let statement.
template <typename ManagedTokenSource>
std::unique_ptr<AST::LetStmt>
-Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs)
+Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
+ bool allow_no_semi)
{
Location locus = lexer.peek_token ()->get_locus ();
skip_token (LET);
@@ -6176,12 +6178,12 @@ Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs)
}
}
- if (!skip_token (SEMICOLON))
+ if (!maybe_skip_token (SEMICOLON) && !allow_no_semi)
{
// skip after somewhere
return nullptr;
- /* TODO: how wise is it to ditch a mostly-valid let statement just because
- * a semicolon is missing? */
+ /* TODO: how wise is it to ditch a mostly-valid let statement just
+ * because a semicolon is missing? */
}
return std::unique_ptr<AST::LetStmt> (
@@ -7016,7 +7018,8 @@ Parser<ManagedTokenSource>::parse_method ()
* block statement). */
template <typename ManagedTokenSource>
std::unique_ptr<AST::ExprStmt>
-Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
+Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
+ bool allow_no_semi)
{
/* potential thoughts - define new virtual method "has_block()" on expr. parse
* 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)
}
else
{
- return parse_expr_stmt_without_block (std::move (outer_attrs));
+ return parse_expr_stmt_without_block (std::move (outer_attrs),
+ allow_no_semi);
}
}
case UNSAFE: {
@@ -7068,7 +7072,8 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
}
else
{
- return parse_expr_stmt_without_block (std::move (outer_attrs));
+ return parse_expr_stmt_without_block (std::move (outer_attrs),
+ allow_no_semi);
}
}
default:
@@ -7076,7 +7081,8 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
/* TODO: if possible, be more selective about possible expr without block
* initial tokens in order to prevent more syntactical errors at parse
* time. */
- return parse_expr_stmt_without_block (std::move (outer_attrs));
+ return parse_expr_stmt_without_block (std::move (outer_attrs),
+ allow_no_semi);
}
}
@@ -7192,7 +7198,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt_with_block (
template <typename ManagedTokenSource>
std::unique_ptr<AST::ExprStmtWithoutBlock>
Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
- AST::AttrVec outer_attrs)
+ AST::AttrVec outer_attrs, bool allow_no_semi)
{
/* TODO: maybe move more logic for expr without block in here for better error
* handling */
@@ -7217,7 +7223,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
}
// skip semicolon at end that is required
- if (!skip_token (SEMICOLON))
+ if (!maybe_skip_token (SEMICOLON) && !allow_no_semi)
{
// skip somewhere?
return nullptr;
@@ -12219,6 +12225,18 @@ Parser<ManagedTokenSource>::skip_token (TokenId token_id)
return expect_token (token_id) != const_TokenPtr ();
}
+/* Checks if current token has inputted id - skips it and returns true if so,
+ * returns false otherwise without diagnosing an error */
+template <typename ManagedTokenSource>
+bool
+Parser<ManagedTokenSource>::maybe_skip_token (TokenId token_id)
+{
+ if (lexer.peek_token ()->get_id () != token_id)
+ return false;
+ else
+ return skip_token (token_id);
+}
+
/* Checks the current token - if id is same as expected, skips and returns it,
* otherwise diagnoses error and returns null. */
template <typename ManagedTokenSource>
diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h
index b283197..5fcb305 100644
--- a/gcc/rust/parse/rust-parse.h
+++ b/gcc/rust/parse/rust-parse.h
@@ -88,8 +88,25 @@ struct ParseRestrictions
template <typename ManagedTokenSource> class Parser
{
public:
+ /**
+ * Consume a token, reporting an error if it isn't the next token
+ *
+ * @param t ID of the token to consume
+ *
+ * @return true if the token was next, false if it wasn't found
+ */
bool skip_token (TokenId t);
+ /**
+ * Same as `skip_token` but allows for failure without necessarily reporting
+ * an error
+ *
+ * @param t ID of the token to consume
+ *
+ * @return true if the token was next, false if it wasn't found
+ */
+ bool maybe_skip_token (TokenId t);
+
std::unique_ptr<AST::Expr>
parse_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
ParseRestrictions restrictions = ParseRestrictions ());
@@ -103,7 +120,20 @@ public:
std::unique_ptr<AST::Item> parse_item (bool called_from_statement);
std::unique_ptr<AST::Pattern> parse_pattern ();
- std::unique_ptr<AST::Stmt> parse_stmt ();
+
+ /**
+ * Parse a statement
+ *
+ * Statement : ';'
+ * | Item
+ * | LetStatement
+ * | ExpressionStatement
+ * | MacroInvocationSemi
+ *
+ * @param allow_no_semi Allow the parser to not parse a semicolon after
+ * the statement without erroring out
+ */
+ std::unique_ptr<AST::Stmt> parse_stmt (bool allow_no_semi = false);
std::unique_ptr<AST::Type> parse_type ();
AST::PathInExpression parse_path_in_expression ();
std::vector<std::unique_ptr<AST::LifetimeParam> > parse_lifetime_params ();
@@ -575,12 +605,25 @@ private:
AST::MaybeNamedParam parse_maybe_named_param (AST::AttrVec outer_attrs);
// Statement-related
- std::unique_ptr<AST::LetStmt> parse_let_stmt (AST::AttrVec outer_attrs);
- std::unique_ptr<AST::ExprStmt> parse_expr_stmt (AST::AttrVec outer_attrs);
+
+ /**
+ *Parse a let-statement
+ * LetStatement :
+ * OuterAttribute*
+ * 'let' PatternNoTopAlt ( ':' Type )? ('=' Expression )? ';'
+ *
+ * @param allow_no_semi Allow parsing a let-statement without expecting a
+ * semicolon to follow it
+ */
+ std::unique_ptr<AST::LetStmt> parse_let_stmt (AST::AttrVec outer_attrs,
+ bool allow_no_semi = false);
+ std::unique_ptr<AST::ExprStmt> parse_expr_stmt (AST::AttrVec outer_attrs,
+ bool allow_no_semi = false);
std::unique_ptr<AST::ExprStmtWithBlock>
parse_expr_stmt_with_block (AST::AttrVec outer_attrs);
std::unique_ptr<AST::ExprStmtWithoutBlock>
- parse_expr_stmt_without_block (AST::AttrVec outer_attrs);
+ parse_expr_stmt_without_block (AST::AttrVec outer_attrs,
+ bool allow_no_semi = false);
ExprOrStmt parse_stmt_or_expr_without_block ();
ExprOrStmt parse_stmt_or_expr_with_block (AST::AttrVec outer_attrs);
ExprOrStmt parse_macro_invocation_maybe_semi (AST::AttrVec outer_attrs);
diff --git a/gcc/testsuite/rust/compile/macro18.rs b/gcc/testsuite/rust/compile/macro18.rs
new file mode 100644
index 0000000..c297107
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro18.rs
@@ -0,0 +1,14 @@
+// { dg-additional-options "-w" }
+
+macro_rules! take_stmt {
+ ($s:stmt) => {
+ $s;
+ };
+}
+
+fn main() -> i32 {
+ take_stmt!(let complete = 15;);
+ take_stmt!(let lacking = 14);
+
+ 0
+}
diff --git a/gcc/testsuite/rust/compile/macro19.rs b/gcc/testsuite/rust/compile/macro19.rs
new file mode 100644
index 0000000..1bf9a2b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro19.rs
@@ -0,0 +1,19 @@
+// { dg-additional-options "-w" }
+
+macro_rules! call_without_semi {
+ () => {
+ f()
+ };
+ (block) => {{
+ f()
+ }};
+}
+
+fn f() {}
+
+fn main() -> i32 {
+ call_without_semi!();
+ call_without_semi!(block);
+
+ 0
+}
diff --git a/gcc/testsuite/rust/compile/macro9.rs b/gcc/testsuite/rust/compile/macro9.rs
index 9a59089..a06a093 100644
--- a/gcc/testsuite/rust/compile/macro9.rs
+++ b/gcc/testsuite/rust/compile/macro9.rs
@@ -12,6 +12,7 @@ fn main() -> i32 {
let b = add!(15);
let b = add!(15 14); // { dg-error "Failed to match any rule within macro" }
let b = add!(15, 14,); // { dg-error "Failed to match any rule within macro" }
+ // { dg-error "found unexpected token" "" { target *-*-* } .-1 }
0
}