parse_test_helpers.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef PARSER_PARSE_TEST_HELPERS_H_
  5. #define PARSER_PARSE_TEST_HELPERS_H_
  6. #include <ostream>
  7. #include <string>
  8. #include <vector>
  9. #include "gmock/gmock.h"
  10. #include "lexer/tokenized_buffer.h"
  11. #include "llvm/ADT/STLExtras.h"
  12. #include "llvm/ADT/SmallVector.h"
  13. #include "llvm/ADT/StringRef.h"
  14. #include "parser/parse_node_kind.h"
  15. #include "parser/parse_tree.h"
  16. namespace Carbon {
  17. // Enable printing a parse tree from Google Mock.
  18. inline void PrintTo(const ParseTree& tree, std::ostream* output) {
  19. std::string text;
  20. llvm::raw_string_ostream text_stream(text);
  21. tree.Print(text_stream);
  22. *output << "\n" << text_stream.str() << "\n";
  23. }
  24. namespace Testing {
  25. // An aggregate used to describe an expected parse tree.
  26. //
  27. // This type is designed to be used via aggregate initialization with designated
  28. // initializers. The latter make it easy to default everything and then override
  29. // the desired aspects when writing an expectation in a test.
  30. struct ExpectedNode {
  31. ParseNodeKind kind = ParseNodeKind::EmptyDeclaration();
  32. std::string text;
  33. bool has_error = false;
  34. bool skip_subtree = false;
  35. std::vector<ExpectedNode> children;
  36. };
  37. // Implementation of a matcher for a parse tree based on a tree of expected
  38. // nodes.
  39. //
  40. // Don't create this directly, instead use `MatchParseTreeNodes` to construct a
  41. // matcher based on this.
  42. class ExpectedNodesMatcher
  43. : public ::testing::MatcherInterface<const ParseTree&> {
  44. public:
  45. ExpectedNodesMatcher(llvm::SmallVector<ExpectedNode, 0> expected_nodess)
  46. : expected_nodes(std::move(expected_nodess)) {}
  47. auto MatchAndExplain(const ParseTree& tree,
  48. ::testing::MatchResultListener* output_ptr) const
  49. -> bool override;
  50. auto DescribeTo(std::ostream* output_ptr) const -> void override;
  51. private:
  52. auto MatchExpectedNode(const ParseTree& tree, ParseTree::Node n,
  53. int postorder_index, ExpectedNode expected_node,
  54. ::testing::MatchResultListener& output) const -> bool;
  55. llvm::SmallVector<ExpectedNode, 0> expected_nodes;
  56. };
  57. // Implementation of the Google Mock interface for matching (and explaining any
  58. // failure).
  59. inline auto ExpectedNodesMatcher::MatchAndExplain(
  60. const ParseTree& tree, ::testing::MatchResultListener* output_ptr) const
  61. -> bool {
  62. auto& output = *output_ptr;
  63. bool matches = true;
  64. const auto rpo = llvm::reverse(tree.Postorder());
  65. const auto nodes_begin = rpo.begin();
  66. const auto nodes_end = rpo.end();
  67. auto nodes_it = nodes_begin;
  68. llvm::SmallVector<const ExpectedNode*, 16> expected_node_stack;
  69. for (const ExpectedNode& en : expected_nodes)
  70. expected_node_stack.push_back(&en);
  71. while (!expected_node_stack.empty()) {
  72. if (nodes_it == nodes_end)
  73. // We'll check the size outside the loop.
  74. break;
  75. ParseTree::Node n = *nodes_it++;
  76. int postorder_index = n.GetIndex();
  77. const ExpectedNode& expected_node = *expected_node_stack.pop_back_val();
  78. if (!MatchExpectedNode(tree, n, postorder_index, expected_node, output))
  79. matches = false;
  80. if (expected_node.skip_subtree) {
  81. assert(expected_node.children.empty() &&
  82. "Must not skip an expected subtree while specifying expected "
  83. "children!");
  84. nodes_it = llvm::reverse(tree.Postorder(n)).end();
  85. continue;
  86. }
  87. // We want to make sure we don't end up with unsynchronized walks, so skip
  88. // ahead in the tree to ensure that the number of children of this node and
  89. // the expected number of children match.
  90. int num_children =
  91. std::distance(tree.Children(n).begin(), tree.Children(n).end());
  92. if (num_children != static_cast<int>(expected_node.children.size())) {
  93. output
  94. << "\nParse node (postorder index #" << postorder_index << ") has "
  95. << num_children << " children, expected "
  96. << expected_node.children.size()
  97. << ". Skipping this subtree to avoid any unsynchronized tree walk.";
  98. matches = false;
  99. nodes_it = llvm::reverse(tree.Postorder(n)).end();
  100. continue;
  101. }
  102. // Push the children onto the stack to continue matching. The expectation
  103. // is in preorder, but we visit the parse tree in reverse postorder. This
  104. // causes the siblings to be visited in reverse order from the expected
  105. // list. However, we use a stack which inherently does this reverse for us
  106. // so we simply append to the stack here.
  107. for (const ExpectedNode& child_expected_node : expected_node.children)
  108. expected_node_stack.push_back(&child_expected_node);
  109. }
  110. // We don't directly check the size because we allow expectations to skip
  111. // subtrees. Instead, we need to check that we successfully processed all of
  112. // the actual tree and consumed all of the expected tree.
  113. if (nodes_it != nodes_end) {
  114. assert(expected_node_stack.empty() &&
  115. "If we have unmatched nodes in the input tree, should only finish "
  116. "having fully processed expected tree.");
  117. output << "\nFinished processing expected nodes and there are still "
  118. << (nodes_end - nodes_it) << " unexpected nodes.";
  119. matches = false;
  120. } else if (!expected_node_stack.empty()) {
  121. output << "\nProcessed all " << (nodes_end - nodes_begin)
  122. << " nodes and still have " << expected_node_stack.size()
  123. << " expected nodes that were unmatched.";
  124. matches = false;
  125. }
  126. return matches;
  127. }
  128. // Implementation of the Google Mock interface for describing the expected node
  129. // tree.
  130. //
  131. // This is designed to describe the expected tree node structure in as similar
  132. // of a format to the parse tree's print format as is reasonable. There is both
  133. // more and less information, so it won't be exact, but should be close enough
  134. // to make it easy to visually compare the two.
  135. inline auto ExpectedNodesMatcher::DescribeTo(std::ostream* output_ptr) const
  136. -> void {
  137. auto& output = *output_ptr;
  138. output << "Matches expected node pattern:\n[\n";
  139. // We want to walk these in RPO instead of in preorder to match the printing
  140. // of the actual parse tree.
  141. llvm::SmallVector<std::pair<const ExpectedNode*, int>, 16>
  142. expected_node_stack;
  143. for (const ExpectedNode& expected_node : llvm::reverse(expected_nodes))
  144. expected_node_stack.push_back({&expected_node, 0});
  145. while (!expected_node_stack.empty()) {
  146. const ExpectedNode& expected_node = *expected_node_stack.back().first;
  147. int depth = expected_node_stack.back().second;
  148. expected_node_stack.pop_back();
  149. for (int indent_count = 0; indent_count < depth; ++indent_count)
  150. output << " ";
  151. output << "{kind: '" << expected_node.kind.GetName().str() << "'";
  152. if (!expected_node.text.empty())
  153. output << ", text: '" << expected_node.text << "'";
  154. if (expected_node.has_error)
  155. output << ", has_error: yes";
  156. if (expected_node.skip_subtree)
  157. output << ", skip_subtree: yes";
  158. if (!expected_node.children.empty()) {
  159. assert(!expected_node.skip_subtree &&
  160. "Must not have children and skip a subtree!");
  161. output << ", children: [\n";
  162. for (const ExpectedNode& child_expected_node :
  163. llvm::reverse(expected_node.children))
  164. expected_node_stack.push_back({&child_expected_node, depth + 1});
  165. // If we have children, we know we're not popping off.
  166. continue;
  167. }
  168. // If this is some form of leaf we'll at least need to close it. It may also
  169. // be the last sibling of its parent, and we'll need to close any parents as
  170. // we pop up.
  171. output << "}";
  172. if (!expected_node_stack.empty()) {
  173. assert(depth >= expected_node_stack.back().second &&
  174. "Cannot have an increase in depth on a leaf node!");
  175. // The distance we need to pop is the difference in depth.
  176. int pop_depth = depth - expected_node_stack.back().second;
  177. for (int pop_count = 0; pop_count < pop_depth; ++pop_count)
  178. // Close both the children array and the node mapping.
  179. output << "]}";
  180. }
  181. output << "\n";
  182. }
  183. output << "]\n";
  184. }
  185. inline auto ExpectedNodesMatcher::MatchExpectedNode(
  186. const ParseTree& tree, ParseTree::Node n, int postorder_index,
  187. ExpectedNode expected_node, ::testing::MatchResultListener& output) const
  188. -> bool {
  189. bool matches = true;
  190. ParseNodeKind kind = tree.GetNodeKind(n);
  191. if (kind != expected_node.kind) {
  192. output << "\nParse node (postorder index #" << postorder_index << ") is a "
  193. << kind.GetName().str() << ", expected a "
  194. << expected_node.kind.GetName().str() << ".";
  195. matches = false;
  196. }
  197. if (tree.HasErrorInNode(n) != expected_node.has_error) {
  198. output << "\nParse node (postorder index #" << postorder_index << ") "
  199. << (tree.HasErrorInNode(n) ? "has an error"
  200. : "does not have an error")
  201. << ", expected that it "
  202. << (expected_node.has_error ? "has an error"
  203. : "does not have an error")
  204. << ".";
  205. matches = false;
  206. }
  207. llvm::StringRef node_text = tree.GetNodeText(n);
  208. if (!expected_node.text.empty() && node_text != expected_node.text) {
  209. output << "\nParse node (postorder index #" << postorder_index
  210. << ") is spelled '" << node_text.str() << "', expected '"
  211. << expected_node.text << "'.";
  212. matches = false;
  213. }
  214. return matches;
  215. }
  216. // Creates a matcher for a parse tree using a tree of expected nodes.
  217. //
  218. // This is intended to be used with an braced initializer list style aggregate
  219. // initializer for an argument, allowing it to describe a tree structure via
  220. // nested `ExpectedNode` objects.
  221. inline auto MatchParseTreeNodes(
  222. llvm::SmallVector<ExpectedNode, 0> expected_nodes)
  223. -> ::testing::Matcher<const ParseTree&> {
  224. return ::testing::MakeMatcher(
  225. new ExpectedNodesMatcher(std::move(expected_nodes)));
  226. }
  227. } // namespace Testing
  228. } // namespace Carbon
  229. #endif // PARSER_PARSE_TEST_HELPERS_H_