parse_test_helpers.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. }
  72. while (!expected_node_stack.empty()) {
  73. if (nodes_it == nodes_end) {
  74. // We'll check the size outside the loop.
  75. break;
  76. }
  77. ParseTree::Node n = *nodes_it++;
  78. int postorder_index = n.GetIndex();
  79. const ExpectedNode& expected_node = *expected_node_stack.pop_back_val();
  80. if (!MatchExpectedNode(tree, n, postorder_index, expected_node, output)) {
  81. matches = false;
  82. }
  83. if (expected_node.skip_subtree) {
  84. assert(expected_node.children.empty() &&
  85. "Must not skip an expected subtree while specifying expected "
  86. "children!");
  87. nodes_it = llvm::reverse(tree.Postorder(n)).end();
  88. continue;
  89. }
  90. // We want to make sure we don't end up with unsynchronized walks, so skip
  91. // ahead in the tree to ensure that the number of children of this node and
  92. // the expected number of children match.
  93. int num_children =
  94. std::distance(tree.Children(n).begin(), tree.Children(n).end());
  95. if (num_children != static_cast<int>(expected_node.children.size())) {
  96. output
  97. << "\nParse node (postorder index #" << postorder_index << ") has "
  98. << num_children << " children, expected "
  99. << expected_node.children.size()
  100. << ". Skipping this subtree to avoid any unsynchronized tree walk.";
  101. matches = false;
  102. nodes_it = llvm::reverse(tree.Postorder(n)).end();
  103. continue;
  104. }
  105. // Push the children onto the stack to continue matching. The expectation
  106. // is in preorder, but we visit the parse tree in reverse postorder. This
  107. // causes the siblings to be visited in reverse order from the expected
  108. // list. However, we use a stack which inherently does this reverse for us
  109. // so we simply append to the stack here.
  110. for (const ExpectedNode& child_expected_node : expected_node.children) {
  111. expected_node_stack.push_back(&child_expected_node);
  112. }
  113. }
  114. // We don't directly check the size because we allow expectations to skip
  115. // subtrees. Instead, we need to check that we successfully processed all of
  116. // the actual tree and consumed all of the expected tree.
  117. if (nodes_it != nodes_end) {
  118. assert(expected_node_stack.empty() &&
  119. "If we have unmatched nodes in the input tree, should only finish "
  120. "having fully processed expected tree.");
  121. output << "\nFinished processing expected nodes and there are still "
  122. << (nodes_end - nodes_it) << " unexpected nodes.";
  123. matches = false;
  124. } else if (!expected_node_stack.empty()) {
  125. output << "\nProcessed all " << (nodes_end - nodes_begin)
  126. << " nodes and still have " << expected_node_stack.size()
  127. << " expected nodes that were unmatched.";
  128. matches = false;
  129. }
  130. return matches;
  131. }
  132. // Implementation of the Google Mock interface for describing the expected node
  133. // tree.
  134. //
  135. // This is designed to describe the expected tree node structure in as similar
  136. // of a format to the parse tree's print format as is reasonable. There is both
  137. // more and less information, so it won't be exact, but should be close enough
  138. // to make it easy to visually compare the two.
  139. inline auto ExpectedNodesMatcher::DescribeTo(std::ostream* output_ptr) const
  140. -> void {
  141. auto& output = *output_ptr;
  142. output << "Matches expected node pattern:\n[\n";
  143. // We want to walk these in RPO instead of in preorder to match the printing
  144. // of the actual parse tree.
  145. llvm::SmallVector<std::pair<const ExpectedNode*, int>, 16>
  146. expected_node_stack;
  147. for (const ExpectedNode& expected_node : llvm::reverse(expected_nodes)) {
  148. expected_node_stack.push_back({&expected_node, 0});
  149. }
  150. while (!expected_node_stack.empty()) {
  151. const ExpectedNode& expected_node = *expected_node_stack.back().first;
  152. int depth = expected_node_stack.back().second;
  153. expected_node_stack.pop_back();
  154. for (int indent_count = 0; indent_count < depth; ++indent_count) {
  155. output << " ";
  156. }
  157. output << "{kind: '" << expected_node.kind.GetName().str() << "'";
  158. if (!expected_node.text.empty()) {
  159. output << ", text: '" << expected_node.text << "'";
  160. }
  161. if (expected_node.has_error) {
  162. output << ", has_error: yes";
  163. }
  164. if (expected_node.skip_subtree) {
  165. output << ", skip_subtree: yes";
  166. }
  167. if (!expected_node.children.empty()) {
  168. assert(!expected_node.skip_subtree &&
  169. "Must not have children and skip a subtree!");
  170. output << ", children: [\n";
  171. for (const ExpectedNode& child_expected_node :
  172. llvm::reverse(expected_node.children)) {
  173. expected_node_stack.push_back({&child_expected_node, depth + 1});
  174. }
  175. // If we have children, we know we're not popping off.
  176. continue;
  177. }
  178. // If this is some form of leaf we'll at least need to close it. It may also
  179. // be the last sibling of its parent, and we'll need to close any parents as
  180. // we pop up.
  181. output << "}";
  182. if (!expected_node_stack.empty()) {
  183. assert(depth >= expected_node_stack.back().second &&
  184. "Cannot have an increase in depth on a leaf node!");
  185. // The distance we need to pop is the difference in depth.
  186. int pop_depth = depth - expected_node_stack.back().second;
  187. for (int pop_count = 0; pop_count < pop_depth; ++pop_count) {
  188. // Close both the children array and the node mapping.
  189. output << "]}";
  190. }
  191. }
  192. output << "\n";
  193. }
  194. output << "]\n";
  195. }
  196. inline auto ExpectedNodesMatcher::MatchExpectedNode(
  197. const ParseTree& tree, ParseTree::Node n, int postorder_index,
  198. ExpectedNode expected_node, ::testing::MatchResultListener& output) const
  199. -> bool {
  200. bool matches = true;
  201. ParseNodeKind kind = tree.GetNodeKind(n);
  202. if (kind != expected_node.kind) {
  203. output << "\nParse node (postorder index #" << postorder_index << ") is a "
  204. << kind.GetName().str() << ", expected a "
  205. << expected_node.kind.GetName().str() << ".";
  206. matches = false;
  207. }
  208. if (tree.HasErrorInNode(n) != expected_node.has_error) {
  209. output << "\nParse node (postorder index #" << postorder_index << ") "
  210. << (tree.HasErrorInNode(n) ? "has an error"
  211. : "does not have an error")
  212. << ", expected that it "
  213. << (expected_node.has_error ? "has an error"
  214. : "does not have an error")
  215. << ".";
  216. matches = false;
  217. }
  218. llvm::StringRef node_text = tree.GetNodeText(n);
  219. if (!expected_node.text.empty() && node_text != expected_node.text) {
  220. output << "\nParse node (postorder index #" << postorder_index
  221. << ") is spelled '" << node_text.str() << "', expected '"
  222. << expected_node.text << "'.";
  223. matches = false;
  224. }
  225. return matches;
  226. }
  227. // Creates a matcher for a parse tree using a tree of expected nodes.
  228. //
  229. // This is intended to be used with an braced initializer list style aggregate
  230. // initializer for an argument, allowing it to describe a tree structure via
  231. // nested `ExpectedNode` objects.
  232. inline auto MatchParseTreeNodes(
  233. llvm::SmallVector<ExpectedNode, 0> expected_nodes)
  234. -> ::testing::Matcher<const ParseTree&> {
  235. return ::testing::MakeMatcher(
  236. new ExpectedNodesMatcher(std::move(expected_nodes)));
  237. }
  238. } // namespace Testing
  239. } // namespace Carbon
  240. #endif // PARSER_PARSE_TEST_HELPERS_H_