Просмотр исходного кода

Add terser matching support for parse trees. (#454)

Richard Smith 5 лет назад
Родитель
Сommit
e96a42bd09
2 измененных файлов с 137 добавлено и 142 удалено
  1. 50 0
      parser/parse_test_helpers.h
  2. 87 142
      parser/parse_tree_test.cpp

+ 50 - 0
parser/parse_test_helpers.h

@@ -268,6 +268,56 @@ inline auto MatchParseTreeNodes(
       new ExpectedNodesMatcher(std::move(expected_nodes)));
 }
 
+// Node matchers. Intended to be brought in by 'using namespace'.
+namespace NodeMatchers {
+
+// Matcher argument for a node with errors.
+struct HasErrorTag {};
+inline constexpr HasErrorTag HasError;
+
+// Matcher argument to skip checking the children of a node.
+struct AnyChildrenTag {};
+inline constexpr AnyChildrenTag AnyChildren;
+
+// A function to generate ExpectedNodes a little more tersely and readably. The
+// meaning of each argument is inferred from its type.
+template <typename... Args>
+auto MatchNode(Args... args) -> ExpectedNode {
+  struct ArgHandler {
+    ExpectedNode expected;
+    void UpdateExpectationsForArg(ParseNodeKind kind) { expected.kind = kind; }
+    void UpdateExpectationsForArg(std::string text) {
+      expected.text = std::move(text);
+    }
+    void UpdateExpectationsForArg(HasErrorTag) { expected.has_error = true; }
+    void UpdateExpectationsForArg(AnyChildrenTag) {
+      expected.skip_subtree = true;
+    }
+    void UpdateExpectationsForArg(ExpectedNode node) {
+      expected.children.push_back(std::move(node));
+    }
+  };
+  ArgHandler handler;
+  (handler.UpdateExpectationsForArg(args), ...);
+  return handler.expected;
+}
+
+// A MatchFoo function for each parse node Foo. Used to construct ExpectedNodes
+// for use in MatchParseTreeNodes. Example:
+//
+// MatchParseTreeNodes(
+//     {MatchFunctionDeclaration("fn", MatchIdentifier("F"),
+//                               MatchParameterList(MatchParameterListEnd()),
+//                               MatchDeclarationEnd(";")),
+//      MatchFileEnd()});
+#define CARBON_PARSE_NODE_KIND(kind)                  \
+  template <typename... Args>                         \
+  auto Match##kind(Args... args)->ExpectedNode {      \
+    return MatchNode(ParseNodeKind::kind(), args...); \
+  }
+#include "parse_node_kind.def"
+}  // namespace NodeMatchers
+
 }  // namespace Testing
 }  // namespace Carbon
 

+ 87 - 142
parser/parse_tree_test.cpp

@@ -22,6 +22,7 @@ namespace {
 
 using Carbon::Testing::IsKeyValueScalars;
 using Carbon::Testing::MatchParseTreeNodes;
+using namespace Carbon::Testing::NodeMatchers;
 using ::testing::Eq;
 using ::testing::Ne;
 using ::testing::NotNull;
@@ -48,7 +49,7 @@ TEST_F(ParseTreeTest, Empty) {
   TokenizedBuffer tokens = GetTokenizedBuffer("");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_FALSE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes({{.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, EmptyDeclaration) {
@@ -87,68 +88,55 @@ TEST_F(ParseTreeTest, BasicFunctionDeclaration) {
   TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_FALSE(tree.HasErrors());
-  EXPECT_THAT(
-      tree, MatchParseTreeNodes(
-                {{.kind = ParseNodeKind::FunctionDeclaration(),
-                  .text = "fn",
-                  .children = {{ParseNodeKind::Identifier(), "F"},
-                               {.kind = ParseNodeKind::ParameterList(),
-                                .text = "(",
-                                .children = {{ParseNodeKind::ParameterListEnd(),
-                                              ")"}}},
-                               {ParseNodeKind::DeclarationEnd(), ";"}}},
-                 {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree,
+              MatchParseTreeNodes(
+                  {MatchFunctionDeclaration(
+                       "fn", MatchIdentifier("F"),
+                       MatchParameterList("(", MatchParameterListEnd(")")),
+                       MatchDeclarationEnd(";")),
+                   MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, NoDeclarationIntroducerOrSemi) {
   TokenizedBuffer tokens = GetTokenizedBuffer("foo bar baz");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes({{.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, NoDeclarationIntroducerWithSemi) {
   TokenizedBuffer tokens = GetTokenizedBuffer("foo;");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree,
-              MatchParseTreeNodes({{.kind = ParseNodeKind::EmptyDeclaration(),
-                                    .text = ";",
-                                    .has_error = true},
-                                   {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchEmptyDeclaration(";", HasError),
+                                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, JustFunctionIntroducerAndSemi) {
   TokenizedBuffer tokens = GetTokenizedBuffer("fn;");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes(
-                        {{.kind = ParseNodeKind::FunctionDeclaration(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::DeclarationEnd()}}},
-                         {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFunctionDeclaration(
+                                             HasError, MatchDeclarationEnd()),
+                                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, RepeatedFunctionIntroducerAndSemi) {
   TokenizedBuffer tokens = GetTokenizedBuffer("fn fn;");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes(
-                        {{.kind = ParseNodeKind::FunctionDeclaration(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::DeclarationEnd()}}},
-                         {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFunctionDeclaration(
+                                             HasError, MatchDeclarationEnd()),
+                                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationWithNoSignatureOrSemi) {
   TokenizedBuffer tokens = GetTokenizedBuffer("fn foo");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes(
-                        {{.kind = ParseNodeKind::FunctionDeclaration(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::Identifier(), "foo"}}},
-                         {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFunctionDeclaration(
+                                             HasError, MatchIdentifier("foo")),
+                                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest,
@@ -156,12 +144,10 @@ TEST_F(ParseTreeTest,
   TokenizedBuffer tokens = GetTokenizedBuffer("fn foo bar;");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes(
-                        {{.kind = ParseNodeKind::FunctionDeclaration(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::Identifier(), "foo"},
-                                       {ParseNodeKind::DeclarationEnd()}}},
-                         {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFunctionDeclaration(
+                                             HasError, MatchIdentifier("foo"),
+                                             MatchDeclarationEnd()),
+                                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationWithSingleIdentifierParameterList) {
@@ -170,28 +156,22 @@ TEST_F(ParseTreeTest, FunctionDeclarationWithSingleIdentifierParameterList) {
   // Note: this might become valid depending on the parameter syntax, this test
   // shouldn't be taken as a sign it should remain invalid.
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(
-      tree,
-      MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(),
-            .has_error = true,
-            .children = {{ParseNodeKind::Identifier(), "foo"},
-                         {.kind = ParseNodeKind::ParameterList(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::ParameterListEnd()}}},
-                         {ParseNodeKind::DeclarationEnd()}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree,
+              MatchParseTreeNodes(
+                  {MatchFunctionDeclaration(
+                       HasError, MatchIdentifier("foo"),
+                       MatchParameterList(HasError, MatchParameterListEnd()),
+                       MatchDeclarationEnd()),
+                   MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationWithoutName) {
   TokenizedBuffer tokens = GetTokenizedBuffer("fn ();");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes(
-                        {{.kind = ParseNodeKind::FunctionDeclaration(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::DeclarationEnd()}}},
-                         {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFunctionDeclaration(
+                                             HasError, MatchDeclarationEnd()),
+                                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest,
@@ -200,11 +180,9 @@ TEST_F(ParseTreeTest,
       "fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z);");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_TRUE(tree.HasErrors());
-  EXPECT_THAT(tree, MatchParseTreeNodes(
-                        {{.kind = ParseNodeKind::FunctionDeclaration(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::DeclarationEnd()}}},
-                         {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes({MatchFunctionDeclaration(
+                                             HasError, MatchDeclarationEnd()),
+                                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationSkipToNewlineWithoutSemi) {
@@ -216,13 +194,11 @@ TEST_F(ParseTreeTest, FunctionDeclarationSkipToNewlineWithoutSemi) {
   EXPECT_THAT(
       tree,
       MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(), .has_error = true},
-           {.kind = ParseNodeKind::FunctionDeclaration(),
-            .children = {{ParseNodeKind::Identifier(), "F"},
-                         {.kind = ParseNodeKind::ParameterList(),
-                          .children = {{ParseNodeKind::ParameterListEnd()}}},
-                         {ParseNodeKind::DeclarationEnd()}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+          {MatchFunctionDeclaration(HasError),
+           MatchFunctionDeclaration(MatchIdentifier("F"),
+                                    MatchParameterList(MatchParameterListEnd()),
+                                    MatchDeclarationEnd()),
+           MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithSemi) {
@@ -236,15 +212,11 @@ TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithSemi) {
   EXPECT_THAT(
       tree,
       MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(),
-            .has_error = true,
-            .children = {{ParseNodeKind::DeclarationEnd()}}},
-           {.kind = ParseNodeKind::FunctionDeclaration(),
-            .children = {{ParseNodeKind::Identifier(), "F"},
-                         {.kind = ParseNodeKind::ParameterList(),
-                          .children = {{ParseNodeKind::ParameterListEnd()}}},
-                         {ParseNodeKind::DeclarationEnd()}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+          {MatchFunctionDeclaration(HasError, MatchDeclarationEnd()),
+           MatchFunctionDeclaration(MatchIdentifier("F"),
+                                    MatchParameterList(MatchParameterListEnd()),
+                                    MatchDeclarationEnd()),
+           MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithoutSemi) {
@@ -258,13 +230,11 @@ TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithoutSemi) {
   EXPECT_THAT(
       tree,
       MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(), .has_error = true},
-           {.kind = ParseNodeKind::FunctionDeclaration(),
-            .children = {{ParseNodeKind::Identifier(), "F"},
-                         {.kind = ParseNodeKind::ParameterList(),
-                          .children = {{ParseNodeKind::ParameterListEnd()}}},
-                         {ParseNodeKind::DeclarationEnd()}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+          {MatchFunctionDeclaration(HasError),
+           MatchFunctionDeclaration(MatchIdentifier("F"),
+                                    MatchParameterList(MatchParameterListEnd()),
+                                    MatchDeclarationEnd()),
+           MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineUntilOutdent) {
@@ -278,13 +248,11 @@ TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineUntilOutdent) {
   EXPECT_THAT(
       tree,
       MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(), .has_error = true},
-           {.kind = ParseNodeKind::FunctionDeclaration(),
-            .children = {{ParseNodeKind::Identifier(), "F"},
-                         {.kind = ParseNodeKind::ParameterList(),
-                          .children = {{ParseNodeKind::ParameterListEnd()}}},
-                         {ParseNodeKind::DeclarationEnd()}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+          {MatchFunctionDeclaration(HasError),
+           MatchFunctionDeclaration(MatchIdentifier("F"),
+                                    MatchParameterList(MatchParameterListEnd()),
+                                    MatchDeclarationEnd()),
+           MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationSkipWithoutSemiToCurly) {
@@ -306,17 +274,12 @@ TEST_F(ParseTreeTest, BasicFunctionDefinition) {
       "}");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_FALSE(tree.HasErrors());
-  EXPECT_THAT(
-      tree,
-      MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(),
-            .children = {{ParseNodeKind::Identifier(), "F"},
-                         {.kind = ParseNodeKind::ParameterList(),
-                          .children = {{ParseNodeKind::ParameterListEnd()}}},
-                         {.kind = ParseNodeKind::CodeBlock(),
-                          .text = "{",
-                          .children = {{ParseNodeKind::CodeBlockEnd(), "}"}}}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes(
+                        {MatchFunctionDeclaration(
+                             MatchIdentifier("F"),
+                             MatchParameterList(MatchParameterListEnd()),
+                             MatchCodeBlock("{", MatchCodeBlockEnd("}"))),
+                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDefinitionWithNestedBlocks) {
@@ -329,25 +292,17 @@ TEST_F(ParseTreeTest, FunctionDefinitionWithNestedBlocks) {
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_FALSE(tree.HasErrors());
   EXPECT_THAT(
-      tree,
-      MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(),
-            .children =
-                {{ParseNodeKind::Identifier(), "F"},
-                 {.kind = ParseNodeKind::ParameterList(),
-                  .children = {{ParseNodeKind::ParameterListEnd()}}},
-                 {.kind = ParseNodeKind::CodeBlock(),
-                  .children =
-                      {{.kind = ParseNodeKind::CodeBlock(),
-                        .children =
-                            {{.kind = ParseNodeKind::CodeBlock(),
-                              .children = {{.kind = ParseNodeKind::CodeBlock(),
-                                            .children = {{ParseNodeKind::
-                                                              CodeBlockEnd()}}},
-                                           {ParseNodeKind::CodeBlockEnd()}}},
-                             {ParseNodeKind::CodeBlockEnd()}}},
-                       {ParseNodeKind::CodeBlockEnd()}}}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+      tree, MatchParseTreeNodes(
+                {MatchFunctionDeclaration(
+                     MatchIdentifier("F"),
+                     MatchParameterList(MatchParameterListEnd()),
+                     MatchCodeBlock(
+                         MatchCodeBlock(
+                             MatchCodeBlock(MatchCodeBlock(MatchCodeBlockEnd()),
+                                            MatchCodeBlockEnd()),
+                             MatchCodeBlockEnd()),
+                         MatchCodeBlockEnd())),
+                 MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInStatements) {
@@ -359,17 +314,12 @@ TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInStatements) {
   // 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(
-          {{.kind = ParseNodeKind::FunctionDeclaration(),
-            .children = {{ParseNodeKind::Identifier(), "F"},
-                         {.kind = ParseNodeKind::ParameterList(),
-                          .children = {{ParseNodeKind::ParameterListEnd()}}},
-                         {.kind = ParseNodeKind::CodeBlock(),
-                          .has_error = true,
-                          .children = {{ParseNodeKind::CodeBlockEnd()}}}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+  EXPECT_THAT(tree, MatchParseTreeNodes(
+                        {MatchFunctionDeclaration(
+                             MatchIdentifier("F"),
+                             MatchParameterList(MatchParameterListEnd()),
+                             MatchCodeBlock(HasError, MatchCodeBlockEnd())),
+                         MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInNestedBlock) {
@@ -384,17 +334,12 @@ TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInNestedBlock) {
   EXPECT_THAT(
       tree,
       MatchParseTreeNodes(
-          {{.kind = ParseNodeKind::FunctionDeclaration(),
-            .children =
-                {{ParseNodeKind::Identifier(), "F"},
-                 {.kind = ParseNodeKind::ParameterList(),
-                  .children = {{ParseNodeKind::ParameterListEnd()}}},
-                 {.kind = ParseNodeKind::CodeBlock(),
-                  .children = {{.kind = ParseNodeKind::CodeBlock(),
-                                .has_error = true,
-                                .children = {{ParseNodeKind::CodeBlockEnd()}}},
-                               {ParseNodeKind::CodeBlockEnd()}}}}},
-           {.kind = ParseNodeKind::FileEnd()}}));
+          {MatchFunctionDeclaration(
+               MatchIdentifier("F"),
+               MatchParameterList(MatchParameterListEnd()),
+               MatchCodeBlock(MatchCodeBlock(HasError, MatchCodeBlockEnd()),
+                              MatchCodeBlockEnd())),
+           MatchFileEnd()}));
 }
 
 auto GetAndDropLine(llvm::StringRef& s) -> std::string {