parse_tree_test.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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. #include "toolchain/parser/parse_tree.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include <forward_list>
  8. #include "llvm/Support/FormatVariadic.h"
  9. #include "toolchain/common/yaml_test_helpers.h"
  10. #include "toolchain/diagnostics/diagnostic_emitter.h"
  11. #include "toolchain/diagnostics/mocks.h"
  12. #include "toolchain/lexer/tokenized_buffer.h"
  13. namespace Carbon::Testing {
  14. namespace {
  15. using ::testing::AtLeast;
  16. using ::testing::ElementsAre;
  17. using ::testing::Eq;
  18. class ParseTreeTest : public ::testing::Test {
  19. protected:
  20. auto GetSourceBuffer(llvm::Twine t) -> SourceBuffer& {
  21. source_storage.push_front(
  22. std::move(*SourceBuffer::CreateFromText(t.str())));
  23. return source_storage.front();
  24. }
  25. auto GetTokenizedBuffer(llvm::Twine t) -> TokenizedBuffer& {
  26. token_storage.push_front(
  27. TokenizedBuffer::Lex(GetSourceBuffer(t), consumer));
  28. return token_storage.front();
  29. }
  30. std::forward_list<SourceBuffer> source_storage;
  31. std::forward_list<TokenizedBuffer> token_storage;
  32. DiagnosticConsumer& consumer = ConsoleDiagnosticConsumer();
  33. };
  34. TEST_F(ParseTreeTest, DefaultInvalid) {
  35. ParseTree::Node node;
  36. EXPECT_FALSE(node.is_valid());
  37. }
  38. TEST_F(ParseTreeTest, IsValid) {
  39. TokenizedBuffer tokens = GetTokenizedBuffer("");
  40. ParseTree tree = ParseTree::Parse(tokens, consumer, /*vlog_stream=*/nullptr);
  41. EXPECT_TRUE((*tree.postorder().begin()).is_valid());
  42. }
  43. TEST_F(ParseTreeTest, OperatorWhitespaceErrors) {
  44. // Test dispositions: Recovered means we issued an error but recovered a
  45. // proper parse tree; Failed means we didn't fully recover from the error.
  46. enum Kind { Valid, Recovered, Failed };
  47. struct Testcase {
  48. const char* input;
  49. Kind kind;
  50. } testcases[] = {
  51. {"var v: Type = i8*;", Valid},
  52. {"var v: Type = i8 *;", Recovered},
  53. {"var v: Type = i8* ;", Valid},
  54. {"var v: Type = i8 * ;", Recovered},
  55. {"var n: i8 = n * n;", Valid},
  56. {"var n: i8 = n*n;", Valid},
  57. {"var n: i8 = (n)*3;", Valid},
  58. {"var n: i8 = 3*(n);", Valid},
  59. {"var n: i8 = n *n;", Recovered},
  60. // TODO: We could figure out that this first Failed example is infix
  61. // with one-token lookahead.
  62. {"var n: i8 = n* n;", Failed},
  63. {"var n: i8 = n* -n;", Failed},
  64. {"var n: i8 = n* *p;", Failed},
  65. // TODO: We try to form (n*)*p and reject due to missing parentheses
  66. // before we notice the missing whitespace around the second `*`.
  67. // It'd be better to (somehow) form n*(*p) and reject due to the missing
  68. // whitespace around the first `*`.
  69. {"var n: i8 = n**p;", Failed},
  70. {"var n: i8 = -n;", Valid},
  71. {"var n: i8 = - n;", Recovered},
  72. {"var n: i8 =-n;", Valid},
  73. {"var n: i8 =- n;", Recovered},
  74. {"var n: i8 = F(i8 *);", Recovered},
  75. {"var n: i8 = F(i8 *, 0);", Recovered},
  76. };
  77. for (auto [input, kind] : testcases) {
  78. TokenizedBuffer tokens = GetTokenizedBuffer(input);
  79. ErrorTrackingDiagnosticConsumer error_tracker(consumer);
  80. ParseTree tree =
  81. ParseTree::Parse(tokens, error_tracker, /*vlog_stream=*/nullptr);
  82. EXPECT_THAT(tree.has_errors(), Eq(kind == Failed)) << input;
  83. EXPECT_THAT(error_tracker.seen_error(), Eq(kind != Valid)) << input;
  84. }
  85. }
  86. TEST_F(ParseTreeTest, StructErrors) {
  87. struct Testcase {
  88. llvm::StringLiteral input;
  89. ::testing::Matcher<const Diagnostic&> diag_matcher;
  90. };
  91. Testcase testcases[] = {
  92. {"var x: {i32} = {};",
  93. IsDiagnosticMessage("Expected `.field: type` or `.field = value`.")},
  94. {"var x: {a} = {};",
  95. IsDiagnosticMessage("Expected `.field: type` or `.field = value`.")},
  96. {"var x: {a:} = {};",
  97. IsDiagnosticMessage("Expected `.field: type` or `.field = value`.")},
  98. {"var x: {a=} = {};",
  99. IsDiagnosticMessage("Expected `.field: type` or `.field = value`.")},
  100. {"var x: {.} = {};",
  101. IsDiagnosticMessage("Expected identifier after `.`.")},
  102. {"var x: {.\"hello\" = 0, .y = 4} = {};",
  103. IsDiagnosticMessage("Expected identifier after `.`.")},
  104. {"var x: {.\"hello\": i32, .y: i32} = {};",
  105. IsDiagnosticMessage("Expected identifier after `.`.")},
  106. {"var x: {.a} = {};",
  107. IsDiagnosticMessage("Expected `.field: type` or `.field = value`.")},
  108. {"var x: {.a:} = {};", IsDiagnosticMessage("Expected expression.")},
  109. {"var x: {.a=} = {};", IsDiagnosticMessage("Expected expression.")},
  110. {"var x: {.a: i32, .b = 0} = {};",
  111. IsDiagnosticMessage("Expected `.field: type`.")},
  112. {"var x: {.a = 0, b: i32} = {};",
  113. IsDiagnosticMessage("Expected `.field = value`.")},
  114. {"var x: {,} = {};",
  115. IsDiagnosticMessage("Expected `.field: type` or `.field = value`.")},
  116. {"var x: {.a: i32,,} = {};",
  117. IsDiagnosticMessage("Expected `.field: type`.")},
  118. {"var x: {.a = 0,,} = {};",
  119. IsDiagnosticMessage("Expected `.field = value`.")},
  120. {"var x: {.a: i32 banana} = {.a = 0};",
  121. IsDiagnosticMessage("Expected `,` or `}`.")},
  122. {"var x: {.a: i32} = {.a = 0 banana};",
  123. IsDiagnosticMessage("Expected `,` or `}`.")},
  124. };
  125. for (const Testcase& testcase : testcases) {
  126. TokenizedBuffer tokens = GetTokenizedBuffer(testcase.input);
  127. Testing::MockDiagnosticConsumer consumer;
  128. EXPECT_CALL(consumer, HandleDiagnostic(testcase.diag_matcher));
  129. ParseTree tree =
  130. ParseTree::Parse(tokens, consumer, /*vlog_stream=*/nullptr);
  131. EXPECT_TRUE(tree.has_errors());
  132. }
  133. }
  134. TEST_F(ParseTreeTest, PrintPostorderAsYAML) {
  135. TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
  136. ParseTree tree = ParseTree::Parse(tokens, consumer, /*vlog_stream=*/nullptr);
  137. EXPECT_FALSE(tree.has_errors());
  138. std::string print_output;
  139. llvm::raw_string_ostream print_stream(print_output);
  140. tree.Print(print_stream);
  141. print_stream.flush();
  142. auto file = Yaml::SequenceValue{
  143. Yaml::MappingValue{{"kind", "FunctionIntroducer"}, {"text", "fn"}},
  144. Yaml::MappingValue{{"kind", "DeclaredName"}, {"text", "F"}},
  145. Yaml::MappingValue{{"kind", "ParameterListStart"}, {"text", "("}},
  146. Yaml::MappingValue{
  147. {"kind", "ParameterList"}, {"text", ")"}, {"subtree_size", "2"}},
  148. Yaml::MappingValue{{"kind", "FunctionDeclaration"},
  149. {"text", ";"},
  150. {"subtree_size", "5"}},
  151. Yaml::MappingValue{{"kind", "FileEnd"}, {"text", ""}},
  152. };
  153. EXPECT_THAT(Yaml::Value::FromText(print_output), ElementsAre(file));
  154. }
  155. TEST_F(ParseTreeTest, PrintPreorderAsYAML) {
  156. TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
  157. ParseTree tree = ParseTree::Parse(tokens, consumer, /*vlog_stream=*/nullptr);
  158. EXPECT_FALSE(tree.has_errors());
  159. std::string print_output;
  160. llvm::raw_string_ostream print_stream(print_output);
  161. tree.Print(print_stream, /*preorder=*/true);
  162. print_stream.flush();
  163. auto parameter_list = Yaml::SequenceValue{
  164. Yaml::MappingValue{
  165. {"node_index", "2"}, {"kind", "ParameterListStart"}, {"text", "("}},
  166. };
  167. auto function_decl = Yaml::SequenceValue{
  168. Yaml::MappingValue{
  169. {"node_index", "0"}, {"kind", "FunctionIntroducer"}, {"text", "fn"}},
  170. Yaml::MappingValue{
  171. {"node_index", "1"}, {"kind", "DeclaredName"}, {"text", "F"}},
  172. Yaml::MappingValue{{"node_index", "3"},
  173. {"kind", "ParameterList"},
  174. {"text", ")"},
  175. {"subtree_size", "2"},
  176. {"children", parameter_list}},
  177. };
  178. auto file = Yaml::SequenceValue{
  179. Yaml::MappingValue{{"node_index", "4"},
  180. {"kind", "FunctionDeclaration"},
  181. {"text", ";"},
  182. {"subtree_size", "5"},
  183. {"children", function_decl}},
  184. Yaml::MappingValue{
  185. {"node_index", "5"}, {"kind", "FileEnd"}, {"text", ""}},
  186. };
  187. EXPECT_THAT(Yaml::Value::FromText(print_output), ElementsAre(file));
  188. }
  189. TEST_F(ParseTreeTest, HighRecursion) {
  190. std::string code = "fn Foo() { return ";
  191. code.append(10000, '(');
  192. code.append(10000, ')');
  193. code += "; }";
  194. TokenizedBuffer tokens = GetTokenizedBuffer(code);
  195. ASSERT_FALSE(tokens.has_errors());
  196. Testing::MockDiagnosticConsumer consumer;
  197. ParseTree tree = ParseTree::Parse(tokens, consumer, /*vlog_stream=*/nullptr);
  198. EXPECT_FALSE(tree.has_errors());
  199. }
  200. TEST_F(ParseTreeTest, PackageErrors) {
  201. struct TestCase {
  202. llvm::StringLiteral input;
  203. ::testing::Matcher<const Diagnostic&> diag_matcher;
  204. };
  205. TestCase testcases[] = {
  206. {"package;", IsDiagnosticMessage("Expected identifier after `package`.")},
  207. {"package fn;",
  208. IsDiagnosticMessage("Expected identifier after `package`.")},
  209. {"package library \"Shapes\" api;",
  210. IsDiagnosticMessage("Expected identifier after `package`.")},
  211. {"package Geometry library Shapes api;",
  212. IsDiagnosticMessage(
  213. "Expected a string literal to specify the library name.")},
  214. {"package Geometry \"Shapes\" api;",
  215. IsDiagnosticMessage("Missing `library` keyword.")},
  216. {"package Geometry api",
  217. IsDiagnosticMessage("Expected `;` to end package directive.")},
  218. {"package Geometry;", IsDiagnosticMessage("Expected a `api` or `impl`.")},
  219. {R"(package Foo library "bar" "baz";)",
  220. IsDiagnosticMessage("Expected a `api` or `impl`.")}};
  221. for (const TestCase& testcase : testcases) {
  222. TokenizedBuffer tokens = GetTokenizedBuffer(testcase.input);
  223. Testing::MockDiagnosticConsumer consumer;
  224. EXPECT_CALL(consumer, HandleDiagnostic(testcase.diag_matcher));
  225. ParseTree tree =
  226. ParseTree::Parse(tokens, consumer, /*vlog_stream=*/nullptr);
  227. EXPECT_TRUE(tree.has_errors());
  228. }
  229. }
  230. } // namespace
  231. } // namespace Carbon::Testing