diff options
author | Arthur Cohen <arthur.cohen@embecosm.com> | 2022-03-14 14:29:58 +0100 |
---|---|---|
committer | Arthur Cohen <arthur.cohen@embecosm.com> | 2022-03-14 16:34:50 +0100 |
commit | 313e9890d85b688f538fed52d158b4b4f4aea9dc (patch) | |
tree | 1e6b8e44c8c9970894be9ff8b1dde0f727443a3d /gcc | |
parent | 41f402f0b19c7e4f19f8d4d65d15223d2752f302 (diff) | |
download | gcc-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.cc | 7 | ||||
-rw-r--r-- | gcc/rust/parse/rust-parse-impl.h | 44 | ||||
-rw-r--r-- | gcc/rust/parse/rust-parse.h | 51 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/macro18.rs | 14 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/macro19.rs | 19 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/macro9.rs | 1 |
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 } |