mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 11:41:06 +01:00
[toolchain] Implement #623: require braces.
For error recovery, allow the braces around the controlled statement of an `if`, `while`, or similar to be omitted. Remove support for nested code blocks as this conflicts with struct literal syntax.
This commit is contained in:
@@ -354,28 +354,6 @@ TEST_F(ParseTreeTest, BasicFunctionDefinition) {
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, FunctionDefinitionWithNestedBlocks) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" {\n"
|
||||
" {{}}\n"
|
||||
" }\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
EXPECT_THAT(
|
||||
tree, MatchParseTreeNodes(
|
||||
{MatchFunctionDeclaration(
|
||||
MatchDeclaredName("F"), MatchParameters(),
|
||||
MatchCodeBlock(
|
||||
MatchCodeBlock(
|
||||
MatchCodeBlock(MatchCodeBlock(MatchCodeBlockEnd()),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInStatements) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
@@ -393,26 +371,6 @@ TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInStatements) {
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInNestedBlock) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" {bar}\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
// Note: this might become valid depending on the expression syntax. This test
|
||||
// shouldn't be taken as a sign it should remain invalid.
|
||||
EXPECT_TRUE(tree.HasErrors());
|
||||
EXPECT_THAT(tree,
|
||||
MatchParseTreeNodes(
|
||||
{MatchFunctionDeclaration(
|
||||
MatchDeclaredName("F"), MatchParameters(),
|
||||
MatchCodeBlock(
|
||||
MatchCodeBlock(HasError, MatchNameReference("bar"),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, FunctionDefinitionWithFunctionCall) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
@@ -673,6 +631,43 @@ TEST_F(ParseTreeTest, VariableDeclarations) {
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, IfNoElse) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if (a) {\n"
|
||||
" if (b) {\n"
|
||||
" if (c) {\n"
|
||||
" d;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}");
|
||||
ErrorTrackingDiagnosticConsumer error_tracker(consumer);
|
||||
ParseTree tree = ParseTree::Parse(tokens, error_tracker);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
EXPECT_FALSE(error_tracker.SeenError());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
MatchParseTreeNodes(
|
||||
{MatchFunctionWithBody(MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("a"), MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("b"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("c"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(MatchExpressionStatement(
|
||||
MatchNameReference("d")),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchCodeBlockEnd()))),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, IfNoElseUnbraced) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if (a)\n"
|
||||
@@ -680,8 +675,11 @@ TEST_F(ParseTreeTest, IfNoElse) {
|
||||
" if (c)\n"
|
||||
" d;\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
ErrorTrackingDiagnosticConsumer error_tracker(consumer);
|
||||
ParseTree tree = ParseTree::Parse(tokens, error_tracker);
|
||||
// The missing braces are invalid, but we should be able to recover.
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
EXPECT_TRUE(error_tracker.SeenError());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
@@ -698,6 +696,74 @@ TEST_F(ParseTreeTest, IfNoElse) {
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, IfElse) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if (a) {\n"
|
||||
" if (b) {\n"
|
||||
" c;\n"
|
||||
" } else {\n"
|
||||
" d;\n"
|
||||
" }\n"
|
||||
" } else {\n"
|
||||
" e;\n"
|
||||
" }\n"
|
||||
" if (x) { G(1); }\n"
|
||||
" else if (x) { G(2); }\n"
|
||||
" else { G(3); }\n"
|
||||
"}");
|
||||
ErrorTrackingDiagnosticConsumer error_tracker(consumer);
|
||||
ParseTree tree = ParseTree::Parse(tokens, error_tracker);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
EXPECT_FALSE(error_tracker.SeenError());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
MatchParseTreeNodes(
|
||||
{MatchFunctionWithBody(
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("a"), MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("b"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(MatchExpressionStatement(
|
||||
MatchNameReference("c")),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchIfStatementElse(),
|
||||
MatchCodeBlock(MatchExpressionStatement(
|
||||
MatchNameReference("d")),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchIfStatementElse(),
|
||||
MatchCodeBlock(
|
||||
MatchExpressionStatement(MatchNameReference("e")),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("x"), MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchExpressionStatement(MatchCallExpression(
|
||||
MatchNameReference("G"), MatchLiteral("1"),
|
||||
MatchCallExpressionEnd())),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchIfStatementElse(),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("x"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchExpressionStatement(MatchCallExpression(
|
||||
MatchNameReference("G"), MatchLiteral("2"),
|
||||
MatchCallExpressionEnd())),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchIfStatementElse(),
|
||||
MatchCodeBlock(
|
||||
MatchExpressionStatement(MatchCallExpression(
|
||||
MatchNameReference("G"), MatchLiteral("3"),
|
||||
MatchCallExpressionEnd())),
|
||||
MatchCodeBlockEnd())))),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, IfElseUnbraced) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if (a)\n"
|
||||
@@ -711,8 +777,11 @@ TEST_F(ParseTreeTest, IfElse) {
|
||||
" else if (x) { G(2); }\n"
|
||||
" else { G(3); }\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
ErrorTrackingDiagnosticConsumer error_tracker(consumer);
|
||||
ParseTree tree = ParseTree::Parse(tokens, error_tracker);
|
||||
// The missing braces are invalid, but we should be able to recover.
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
EXPECT_TRUE(error_tracker.SeenError());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
@@ -786,13 +855,17 @@ TEST_F(ParseTreeTest, WhileBreakContinue) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" while (a) {\n"
|
||||
" if (b)\n"
|
||||
" if (b) {\n"
|
||||
" break;\n"
|
||||
" if (c)\n"
|
||||
" }\n"
|
||||
" if (c) {\n"
|
||||
" continue;\n"
|
||||
" }\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
ErrorTrackingDiagnosticConsumer error_tracker(consumer);
|
||||
ParseTree tree = ParseTree::Parse(tokens, error_tracker);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
EXPECT_FALSE(error_tracker.SeenError());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
@@ -800,22 +873,46 @@ TEST_F(ParseTreeTest, WhileBreakContinue) {
|
||||
{MatchFunctionWithBody(MatchWhileStatement(
|
||||
MatchCondition(MatchNameReference("a"), MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchIfStatement(MatchCondition(MatchNameReference("b"),
|
||||
MatchConditionEnd()),
|
||||
MatchBreakStatement(MatchStatementEnd())),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("c"),
|
||||
MatchCondition(MatchNameReference("b"),
|
||||
MatchConditionEnd()),
|
||||
MatchContinueStatement(MatchStatementEnd())),
|
||||
MatchCodeBlock(MatchBreakStatement(MatchStatementEnd()),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchIfStatement(MatchCondition(MatchNameReference("c"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(MatchContinueStatement(
|
||||
MatchStatementEnd()),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchCodeBlockEnd()))),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, WhileUnbraced) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" while (a) \n"
|
||||
" break;\n"
|
||||
"}");
|
||||
ErrorTrackingDiagnosticConsumer error_tracker(consumer);
|
||||
ParseTree tree = ParseTree::Parse(tokens, error_tracker);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
EXPECT_TRUE(error_tracker.SeenError());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
MatchParseTreeNodes(
|
||||
{MatchFunctionWithBody(MatchWhileStatement(
|
||||
MatchCondition(MatchNameReference("a"), MatchConditionEnd()),
|
||||
MatchBreakStatement(MatchStatementEnd()))),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, Return) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if (c)\n"
|
||||
" if (c) {\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"fn G(x: Int) -> Int {\n"
|
||||
" return x;\n"
|
||||
@@ -828,7 +925,8 @@ TEST_F(ParseTreeTest, Return) {
|
||||
MatchParseTreeNodes(
|
||||
{MatchFunctionWithBody(MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("c"), MatchConditionEnd()),
|
||||
MatchReturnStatement(MatchStatementEnd()))),
|
||||
MatchCodeBlock(MatchReturnStatement(MatchStatementEnd()),
|
||||
MatchCodeBlockEnd()))),
|
||||
MatchFunctionDeclaration(
|
||||
MatchDeclaredName(),
|
||||
MatchParameters(MatchPatternBinding(MatchDeclaredName("x"), ":",
|
||||
|
||||
Reference in New Issue
Block a user