diff --git a/parser/parse_node_kind.def b/parser/parse_node_kind.def index 030de307b902..ab2cdadb6411 100644 --- a/parser/parse_node_kind.def +++ b/parser/parse_node_kind.def @@ -38,6 +38,7 @@ CARBON_PARSE_NODE_KIND(Condition) CARBON_PARSE_NODE_KIND(ConditionEnd) CARBON_PARSE_NODE_KIND(ContinueStatement) CARBON_PARSE_NODE_KIND(BreakStatement) +CARBON_PARSE_NODE_KIND(ReturnStatement) CARBON_PARSE_NODE_KIND(StatementEnd) // Expressions. diff --git a/parser/parse_tree_test.cpp b/parser/parse_tree_test.cpp index 580131f1e6d6..ab63aef30ab0 100644 --- a/parser/parse_tree_test.cpp +++ b/parser/parse_tree_test.cpp @@ -212,19 +212,19 @@ TEST_F(ParseTreeTest, FunctionDeclarationWithReturnType) { TEST_F(ParseTreeTest, FunctionDefinitionWithReturnType) { TokenizedBuffer tokens = GetTokenizedBuffer( "fn foo() -> Int {\n" - " // return 42;\n" + " return 42;\n" "}"); ParseTree tree = ParseTree::Parse(tokens, consumer); EXPECT_FALSE(tree.HasErrors()); - EXPECT_THAT( - tree, - MatchParseTreeNodes( - {MatchFunctionDeclaration(MatchDeclaredName("foo"), MatchParameters(), - MatchReturnType(MatchNameReference("Int")), - MatchCodeBlock( - // TODO: Match a return statement. - MatchCodeBlockEnd())), - MatchFileEnd()})); + EXPECT_THAT(tree, + MatchParseTreeNodes( + {MatchFunctionDeclaration( + MatchDeclaredName("foo"), MatchParameters(), + MatchReturnType(MatchNameReference("Int")), + MatchCodeBlock(MatchReturnStatement(MatchLiteral("42"), + MatchStatementEnd()), + MatchCodeBlockEnd())), + MatchFileEnd()})); } TEST_F(ParseTreeTest, FunctionDeclarationWithSingleIdentifierParameterList) { @@ -693,6 +693,35 @@ TEST_F(ParseTreeTest, WhileBreakContinue) { MatchFileEnd()})); } +TEST_F(ParseTreeTest, Return) { + TokenizedBuffer tokens = GetTokenizedBuffer( + "fn F() {\n" + " if (c)\n" + " return;\n" + "}\n" + "fn G(Int x) -> Int {\n" + " return x;\n" + "}"); + ParseTree tree = ParseTree::Parse(tokens, consumer); + EXPECT_FALSE(tree.HasErrors()); + + EXPECT_THAT( + tree, + MatchParseTreeNodes( + {MatchFunctionWithBody(MatchIfStatement( + MatchCondition(MatchNameReference("c"), MatchConditionEnd()), + MatchReturnStatement(MatchStatementEnd()))), + MatchFunctionDeclaration( + MatchDeclaredName(), + MatchParameters( + MatchParameterDeclaration(MatchNameReference("Int"), "x")), + MatchReturnType(MatchNameReference("Int")), + MatchCodeBlock(MatchReturnStatement(MatchNameReference("x"), + MatchStatementEnd()), + MatchCodeBlockEnd())), + MatchFileEnd()})); +} + auto GetAndDropLine(llvm::StringRef& s) -> std::string { auto newline_offset = s.find_first_of('\n'); llvm::StringRef line = s.slice(0, newline_offset); diff --git a/parser/parser_impl.cpp b/parser/parser_impl.cpp index a6e78cfae9f3..27efbbffc7c3 100644 --- a/parser/parser_impl.cpp +++ b/parser/parser_impl.cpp @@ -827,20 +827,30 @@ auto ParseTree::Parser::ParseWhileStatement() -> llvm::Optional { /*has_errors=*/!cond || !body); } -auto ParseTree::Parser::ParseKeywordStatement(ParseNodeKind kind) +auto ParseTree::Parser::ParseKeywordStatement(ParseNodeKind kind, + KeywordStatementArgument argument) -> llvm::Optional { auto keyword_kind = tokens.GetKind(*position); assert(keyword_kind.IsKeyword()); auto start = StartSubtree(); auto keyword = Consume(keyword_kind); + + bool arg_error = false; + if ((argument == KeywordStatementArgument::Optional && + tokens.GetKind(*position) != TokenKind::Semi()) || + argument == KeywordStatementArgument::Mandatory) { + arg_error = !ParseExpression(); + } + auto semi = ConsumeAndAddLeafNodeIf(TokenKind::Semi(), ParseNodeKind::StatementEnd()); if (!semi) { emitter.EmitError(*position, {.preceding = keyword_kind}); + // FIXME: Try to skip to a semicolon to recover. } - return AddNode(kind, keyword, start, /*has_errors=*/!semi); + return AddNode(kind, keyword, start, /*has_errors=*/!semi || arg_error); } auto ParseTree::Parser::ParseStatement() -> llvm::Optional { @@ -855,10 +865,16 @@ auto ParseTree::Parser::ParseStatement() -> llvm::Optional { return ParseWhileStatement(); case TokenKind::ContinueKeyword(): - return ParseKeywordStatement(ParseNodeKind::ContinueStatement()); + return ParseKeywordStatement(ParseNodeKind::ContinueStatement(), + KeywordStatementArgument::None); case TokenKind::BreakKeyword(): - return ParseKeywordStatement(ParseNodeKind::BreakStatement()); + return ParseKeywordStatement(ParseNodeKind::BreakStatement(), + KeywordStatementArgument::None); + + case TokenKind::ReturnKeyword(): + return ParseKeywordStatement(ParseNodeKind::ReturnStatement(), + KeywordStatementArgument::Optional); case TokenKind::OpenCurlyBrace(): return ParseCodeBlock(); diff --git a/parser/parser_impl.h b/parser/parser_impl.h index eb19737c7c1a..b5508aafda09 100644 --- a/parser/parser_impl.h +++ b/parser/parser_impl.h @@ -199,8 +199,16 @@ class ParseTree::Parser { // Parses a while-statement. auto ParseWhileStatement() -> llvm::Optional; + enum class KeywordStatementArgument { + None, + Optional, + Mandatory, + }; + // Parses a statement of the form `keyword;` such as `break;` or `continue;`. - auto ParseKeywordStatement(ParseNodeKind kind) -> llvm::Optional; + auto ParseKeywordStatement(ParseNodeKind kind, + KeywordStatementArgument argument) + -> llvm::Optional; // Parses a statement. auto ParseStatement() -> llvm::Optional;