| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #include "toolchain/parser/parse_tree.h"
- #include <gmock/gmock.h>
- #include <gtest/gtest.h>
- #include <forward_list>
- #include "common/ostream.h"
- #include "llvm/ADT/Sequence.h"
- #include "llvm/Support/FormatVariadic.h"
- #include "llvm/Support/SourceMgr.h"
- #include "toolchain/common/yaml_test_helpers.h"
- #include "toolchain/diagnostics/diagnostic_emitter.h"
- #include "toolchain/diagnostics/mocks.h"
- #include "toolchain/lexer/tokenized_buffer.h"
- #include "toolchain/parser/parse_node_kind.h"
- #include "toolchain/parser/parse_test_helpers.h"
- namespace Carbon::Testing {
- namespace {
- using ::testing::AtLeast;
- using ::testing::ElementsAre;
- using ::testing::Eq;
- using ::testing::Ne;
- using ::testing::StrEq;
- class ParseTreeTest : public ::testing::Test {
- protected:
- auto GetSourceBuffer(llvm::Twine t) -> SourceBuffer& {
- source_storage.push_front(SourceBuffer::CreateFromText(t.str()));
- return source_storage.front();
- }
- auto GetTokenizedBuffer(llvm::Twine t) -> TokenizedBuffer& {
- token_storage.push_front(
- TokenizedBuffer::Lex(GetSourceBuffer(t), consumer));
- return token_storage.front();
- }
- std::forward_list<SourceBuffer> source_storage;
- std::forward_list<TokenizedBuffer> token_storage;
- DiagnosticConsumer& consumer = ConsoleDiagnosticConsumer();
- };
- TEST_F(ParseTreeTest, Empty) {
- TokenizedBuffer tokens = GetTokenizedBuffer("");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(tree, MatchParseTreeNodes({MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, EmptyDeclaration) {
- TokenizedBuffer tokens = GetTokenizedBuffer(";");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- auto it = tree.Postorder().begin();
- auto end = tree.Postorder().end();
- ASSERT_THAT(it, Ne(end));
- ParseTree::Node n = *it++;
- ASSERT_THAT(it, Ne(end));
- ParseTree::Node eof = *it++;
- EXPECT_THAT(it, Eq(end));
- // Directly test the main API so that we get easier to understand errors in
- // simple cases than what the custom matcher will produce.
- EXPECT_FALSE(tree.HasErrorInNode(n));
- EXPECT_FALSE(tree.HasErrorInNode(eof));
- EXPECT_THAT(tree.GetNodeKind(n), Eq(ParseNodeKind::EmptyDeclaration()));
- EXPECT_THAT(tree.GetNodeKind(eof), Eq(ParseNodeKind::FileEnd()));
- auto t = tree.GetNodeToken(n);
- ASSERT_THAT(tokens.Tokens().begin(), Ne(tokens.Tokens().end()));
- EXPECT_THAT(t, Eq(*tokens.Tokens().begin()));
- EXPECT_THAT(tokens.GetTokenText(t), Eq(";"));
- EXPECT_THAT(tree.Children(n).begin(), Eq(tree.Children(n).end()));
- EXPECT_THAT(tree.Children(eof).begin(), Eq(tree.Children(eof).end()));
- EXPECT_THAT(tree.Postorder().begin(), Eq(tree.Postorder(n).begin()));
- EXPECT_THAT(tree.Postorder(n).end(), Eq(tree.Postorder(eof).begin()));
- EXPECT_THAT(tree.Postorder(eof).end(), Eq(tree.Postorder().end()));
- }
- TEST_F(ParseTreeTest, BasicFunctionDeclaration) {
- TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(tree, MatchParseTreeNodes(
- {MatchFunctionDeclaration("fn", MatchDeclaredName("F"),
- MatchParameters(),
- 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({MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, NoDeclarationIntroducerWithSemi) {
- TokenizedBuffer tokens = GetTokenizedBuffer("foo;");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- 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({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({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(
- {MatchFunctionDeclaration(HasError, MatchDeclaredName("foo")),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest,
- FunctionDeclarationWithIdentifierInsteadOfSignatureAndSemi) {
- TokenizedBuffer tokens = GetTokenizedBuffer("fn foo bar;");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(tree, MatchParseTreeNodes({MatchFunctionDeclaration(
- HasError, MatchDeclaredName("foo"),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationWithParameterList) {
- TokenizedBuffer tokens = GetTokenizedBuffer("fn foo(bar: i32, baz: i32);");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionDeclaration(
- MatchDeclaredName("foo"),
- MatchParameterList(MatchPatternBinding(MatchDeclaredName("bar"),
- ":", MatchLiteral("i32")),
- MatchParameterListComma(),
- MatchPatternBinding(MatchDeclaredName("baz"),
- ":", MatchLiteral("i32")),
- MatchParameterListEnd()),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDefinitionWithParameterList) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn foo(bar: i64, baz: i64) {\n"
- " foo(baz, bar + baz);\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionDeclaration(
- MatchDeclaredName("foo"),
- MatchParameterList(MatchPatternBinding(MatchDeclaredName("bar"),
- ":", MatchLiteral("i64")),
- MatchParameterListComma(),
- MatchPatternBinding(MatchDeclaredName("baz"),
- ":", MatchLiteral("i64")),
- MatchParameterListEnd()),
- MatchCodeBlock(
- MatchExpressionStatement(MatchCallExpression(
- MatchNameReference("foo"), MatchNameReference("baz"),
- MatchCallExpressionComma(),
- MatchInfixOperator(MatchNameReference("bar"), "+",
- MatchNameReference("baz")),
- MatchCallExpressionEnd())),
- MatchCodeBlockEnd())),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationWithReturnType) {
- TokenizedBuffer tokens = GetTokenizedBuffer("fn foo() -> u32;");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionDeclaration(MatchDeclaredName("foo"), MatchParameters(),
- MatchReturnType(MatchLiteral("u32")),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDefinitionWithReturnType) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn foo() -> f64 {\n"
- " return 42;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(tree,
- MatchParseTreeNodes(
- {MatchFunctionDeclaration(
- MatchDeclaredName("foo"), MatchParameters(),
- MatchReturnType(MatchLiteral("f64")),
- MatchCodeBlock(MatchReturnStatement(MatchLiteral("42"),
- MatchStatementEnd()),
- MatchCodeBlockEnd())),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationWithSingleIdentifierParameterList) {
- TokenizedBuffer tokens = GetTokenizedBuffer("fn foo(bar);");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- // 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(
- {MatchFunctionDeclaration(
- MatchDeclaredName("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({MatchFunctionDeclaration(
- HasError, MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest,
- FunctionDeclarationWithoutNameAndManyTokensToSkipInGroupedSymbols) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "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({MatchFunctionDeclaration(
- HasError, MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationSkipToNewlineWithoutSemi) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn ()\n"
- "fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree, MatchParseTreeNodes({MatchFunctionDeclaration(HasError),
- MatchFunctionDeclaration(
- MatchDeclaredName("F"), MatchParameters(),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithSemi) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn (x,\n"
- " y,\n"
- " z);\n"
- "fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionDeclaration(HasError, MatchDeclarationEnd()),
- MatchFunctionDeclaration(MatchDeclaredName("F"), MatchParameters(),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithoutSemi) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn (x,\n"
- " y,\n"
- " z)\n"
- "fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree, MatchParseTreeNodes({MatchFunctionDeclaration(HasError),
- MatchFunctionDeclaration(
- MatchDeclaredName("F"), MatchParameters(),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineUntilOutdent) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- " fn (x,\n"
- " y,\n"
- " z)\n"
- "fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree, MatchParseTreeNodes({MatchFunctionDeclaration(HasError),
- MatchFunctionDeclaration(
- MatchDeclaredName("F"), MatchParameters(),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDeclarationSkipWithoutSemiToCurly) {
- // FIXME: We don't have a grammar construct that uses curlies yet so this just
- // won't parse at all. Once it does, we should ensure that the close brace
- // gets properly parsed for the struct (or whatever other curly-braced syntax
- // we have grouping function declarations) despite the invalid function
- // declaration missing a semicolon.
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "struct X { fn () }\n"
- "fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- }
- TEST_F(ParseTreeTest, BasicFunctionDefinition) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(tree, MatchParseTreeNodes(
- {MatchFunctionDeclaration(
- MatchDeclaredName("F"), MatchParameters(),
- MatchCodeBlock("{", MatchCodeBlockEnd("}"))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInStatements) {
- 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(HasError, MatchNameReference("bar"),
- MatchCodeBlockEnd())),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, FunctionDefinitionWithFunctionCall) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " a.b.f(c.d, (e)).g();\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- ExpectedNode call_to_f = MatchCallExpression(
- MatchDesignator(MatchDesignator(MatchNameReference("a"), "b"), "f"),
- MatchDesignator(MatchNameReference("c"), "d"), MatchCallExpressionComma(),
- MatchParenExpression(MatchNameReference("e"), MatchParenExpressionEnd()),
- MatchCallExpressionEnd());
- ExpectedNode statement = MatchExpressionStatement(MatchCallExpression(
- MatchDesignator(call_to_f, "g"), MatchCallExpressionEnd()));
- EXPECT_THAT(tree, MatchParseTreeNodes(
- {MatchFunctionWithBody(statement), MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, InvalidDesignators) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " a.;\n"
- " a.fn;\n"
- " a.42;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(tree, MatchParseTreeNodes(
- {MatchFunctionWithBody(
- MatchExpressionStatement(
- MatchDesignatorExpression(
- MatchNameReference("a"), ".", HasError),
- HasError, ";"),
- MatchExpressionStatement(
- MatchDesignatorExpression(
- MatchNameReference("a"), ".",
- MatchDesignatedName("fn", HasError)),
- ";"),
- MatchExpressionStatement(
- MatchDesignatorExpression(
- MatchNameReference("a"), ".", HasError),
- HasError, ";")),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, Operators) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " n = a * b + c * d = d * d << e & f - not g;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchExpressionStatement(MatchInfixOperator(
- MatchNameReference("n"), "=",
- MatchInfixOperator(
- MatchInfixOperator(
- MatchInfixOperator(MatchNameReference("a"), "*",
- MatchNameReference("b")),
- "+",
- MatchInfixOperator(MatchNameReference("c"), "*",
- MatchNameReference("d"))),
- "=",
- MatchInfixOperator(
- HasError,
- MatchInfixOperator(
- HasError,
- MatchInfixOperator(
- HasError,
- MatchInfixOperator(MatchNameReference("d"), "*",
- MatchNameReference("d")),
- "<<", MatchNameReference("e")),
- "&", MatchNameReference("f")),
- "-",
- MatchPrefixOperator("not", MatchNameReference("g"))))))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorsPrefixUnary) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " ++++n;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchExpressionStatement(MatchPrefixOperator(
- "++", MatchPrefixOperator("++", MatchNameReference("n"))))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorsPostfixUnary) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " n++++;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchExpressionStatement(MatchPostfixOperator(
- MatchPostfixOperator(MatchNameReference("n"), "++"), "++"))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorsAssociative) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " a and b and c;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchExpressionStatement(MatchInfixOperator(
- MatchInfixOperator(MatchNameReference("a"), "and",
- MatchNameReference("b")),
- "and", MatchNameReference("c")))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorsMissingPrecedence1) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " a and b or c;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchExpressionStatement(MatchInfixOperator(
- HasError,
- MatchInfixOperator(MatchNameReference("a"), "and",
- MatchNameReference("b")),
- "or", MatchNameReference("c")))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorsMissingPrecedence2) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " a or b and c;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchExpressionStatement(MatchInfixOperator(
- HasError,
- MatchInfixOperator(MatchNameReference("a"), "or",
- MatchNameReference("b")),
- "and", MatchNameReference("c")))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorsMissingPrecedenceForNot) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " not a and not b and not c;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchExpressionStatement(MatchInfixOperator(
- MatchInfixOperator(
- MatchPrefixOperator("not", MatchNameReference("a")), "and",
- MatchPrefixOperator("not", MatchNameReference("b"))),
- "and", MatchPrefixOperator("not", MatchNameReference("c"))))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorFixity) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F(p: i32*, n: i32) {\n"
- " var q: i32* = p;\n"
- " var t: Type = i32*;\n"
- " t = t**;\n"
- " n = n * n;\n"
- " n = n * *p;\n"
- " n = n*n;\n"
- " G(i32*, n * n);\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionDeclaration(
- MatchDeclaredName("F"),
- MatchParameters(
- MatchPatternBinding(
- MatchDeclaredName("p"),
- MatchPostfixOperator(MatchLiteral("i32"), "*")),
- MatchParameterListComma(),
- MatchPatternBinding(MatchDeclaredName("n"),
- MatchLiteral("i32"))),
- MatchCodeBlock(
- MatchVariableDeclaration(
- MatchPatternBinding(
- MatchDeclaredName("q"),
- MatchPostfixOperator(MatchLiteral("i32"), "*")),
- MatchVariableInitializer(MatchNameReference("p")),
- MatchDeclarationEnd()),
- MatchVariableDeclaration(
- MatchPatternBinding(MatchDeclaredName("t"),
- MatchNameReference("Type")),
- MatchVariableInitializer(
- MatchPostfixOperator(MatchLiteral("i32"), "*")),
- MatchDeclarationEnd()),
- MatchExpressionStatement(MatchInfixOperator(
- MatchNameReference("t"), "=",
- MatchPostfixOperator(
- MatchPostfixOperator(MatchNameReference("t"), "*"),
- "*"))),
- MatchExpressionStatement(MatchInfixOperator(
- MatchNameReference("n"), "=",
- MatchInfixOperator(MatchNameReference("n"), "*",
- MatchNameReference("n")))),
- MatchExpressionStatement(MatchInfixOperator(
- MatchNameReference("n"), "=",
- MatchInfixOperator(
- MatchNameReference("n"), "*",
- MatchPrefixOperator("*", MatchNameReference("p"))))),
- MatchExpressionStatement(MatchInfixOperator(
- MatchNameReference("n"), "=",
- MatchInfixOperator(MatchNameReference("n"), "*",
- MatchNameReference("n")))),
- MatchExpressionStatement(MatchCallExpression(
- MatchNameReference("G"),
- MatchPostfixOperator(MatchLiteral("i32"), "*"),
- MatchCallExpressionComma(),
- MatchInfixOperator(MatchNameReference("n"), "*",
- MatchNameReference("n")),
- MatchCallExpressionEnd())),
- MatchCodeBlockEnd())),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, OperatorWhitespaceErrors) {
- // Test dispositions: Recovered means we issued an error but recovered a
- // proper parse tree; Failed means we didn't fully recover from the error.
- enum Kind { Valid, Recovered, Failed };
- struct Testcase {
- const char* input;
- Kind kind;
- } testcases[] = {
- {"var v: Type = i8*;", Valid},
- {"var v: Type = i8 *;", Recovered},
- {"var v: Type = i8* ;", Valid},
- {"var v: Type = i8 * ;", Recovered},
- {"var n: i8 = n * n;", Valid},
- {"var n: i8 = n*n;", Valid},
- {"var n: i8 = (n)*3;", Valid},
- {"var n: i8 = 3*(n);", Valid},
- {"var n: i8 = n *n;", Recovered},
- // FIXME: We could figure out that this first Failed example is infix
- // with one-token lookahead.
- {"var n: i8 = n* n;", Failed},
- {"var n: i8 = n* -n;", Failed},
- {"var n: i8 = n* *p;", Failed},
- // FIXME: We try to form (n*)*p and reject due to missing parentheses
- // before we notice the missing whitespace around the second `*`.
- // It'd be better to (somehow) form n*(*p) and reject due to the missing
- // whitespace around the first `*`.
- {"var n: i8 = n**p;", Failed},
- {"var n: i8 = -n;", Valid},
- {"var n: i8 = - n;", Recovered},
- {"var n: i8 =-n;", Valid},
- {"var n: i8 =- n;", Recovered},
- {"var n: i8 = F(i8 *);", Recovered},
- {"var n: i8 = F(i8 *, 0);", Recovered},
- };
- for (auto [input, kind] : testcases) {
- TokenizedBuffer tokens = GetTokenizedBuffer(input);
- ErrorTrackingDiagnosticConsumer error_tracker(consumer);
- ParseTree tree = ParseTree::Parse(tokens, error_tracker);
- EXPECT_THAT(tree.HasErrors(), Eq(kind == Failed)) << input;
- EXPECT_THAT(error_tracker.SeenError(), Eq(kind != Valid)) << input;
- }
- }
- TEST_F(ParseTreeTest, VariableDeclarations) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "var v: i32 = 0;\n"
- "var w: i32;\n"
- "fn F() {\n"
- " var s: String = \"hello\";\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(tree,
- MatchParseTreeNodes(
- {MatchVariableDeclaration(
- MatchPatternBinding(MatchDeclaredName("v"), ":",
- MatchLiteral("i32")),
- MatchVariableInitializer(MatchLiteral("0")),
- MatchDeclarationEnd()),
- MatchVariableDeclaration(
- MatchPatternBinding(MatchDeclaredName("w"), ":",
- MatchLiteral("i32")),
- MatchDeclarationEnd()),
- MatchFunctionWithBody(MatchVariableDeclaration(
- MatchPatternBinding(MatchDeclaredName("s"), ":",
- MatchNameReference("String")),
- MatchVariableInitializer(MatchLiteral("\"hello\"")),
- MatchDeclarationEnd())),
- MatchFileEnd()}));
- }
- 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"
- " if (b)\n"
- " if (c)\n"
- " d;\n"
- "}");
- 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,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchIfStatement(
- MatchCondition(MatchNameReference("a"), MatchConditionEnd()),
- MatchIfStatement(
- MatchCondition(MatchNameReference("b"), MatchConditionEnd()),
- MatchIfStatement(
- MatchCondition(MatchNameReference("c"),
- MatchConditionEnd()),
- MatchExpressionStatement(MatchNameReference("d")))))),
- MatchFileEnd()}));
- }
- 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"
- " if (b)\n"
- " c;\n"
- " else\n"
- " d;\n"
- " else\n"
- " e;\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);
- // The missing braces are invalid, but we should be able to recover.
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_TRUE(error_tracker.SeenError());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(
- MatchIfStatement(
- MatchCondition(MatchNameReference("a"), MatchConditionEnd()),
- MatchIfStatement(
- MatchCondition(MatchNameReference("b"),
- MatchConditionEnd()),
- MatchExpressionStatement(MatchNameReference("c")),
- MatchIfStatementElse(),
- MatchExpressionStatement(MatchNameReference("d"))),
- MatchIfStatementElse(),
- MatchExpressionStatement(MatchNameReference("e"))),
- 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, IfError) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " if a {}\n"
- " if () {}\n"
- " if (b c) {}\n"
- " if (d)\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(
- MatchIfStatement(HasError, MatchNameReference("a"),
- MatchCodeBlock(MatchCodeBlockEnd())),
- MatchIfStatement(MatchCondition(HasError, MatchConditionEnd()),
- MatchCodeBlock(MatchCodeBlockEnd())),
- MatchIfStatement(
- MatchCondition(HasError, MatchNameReference("b"),
- MatchConditionEnd()),
- MatchCodeBlock(MatchCodeBlockEnd())),
- MatchIfStatement(HasError,
- MatchCondition(MatchNameReference("d"),
- MatchConditionEnd()))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, WhileBreakContinue) {
- TokenizedBuffer tokens = GetTokenizedBuffer(
- "fn F() {\n"
- " while (a) {\n"
- " if (b) {\n"
- " break;\n"
- " }\n"
- " if (c) {\n"
- " continue;\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(MatchWhileStatement(
- MatchCondition(MatchNameReference("a"), MatchConditionEnd()),
- MatchCodeBlock(
- MatchIfStatement(
- MatchCondition(MatchNameReference("b"),
- MatchConditionEnd()),
- 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"
- " return;\n"
- " }\n"
- "}\n"
- "fn G(x: Foo) -> Foo {\n"
- " return x;\n"
- "}");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchFunctionWithBody(MatchIfStatement(
- MatchCondition(MatchNameReference("c"), MatchConditionEnd()),
- MatchCodeBlock(MatchReturnStatement(MatchStatementEnd()),
- MatchCodeBlockEnd()))),
- MatchFunctionDeclaration(
- MatchDeclaredName(),
- MatchParameters(MatchPatternBinding(MatchDeclaredName("x"), ":",
- MatchNameReference("Foo"))),
- MatchReturnType(MatchNameReference("Foo")),
- MatchCodeBlock(MatchReturnStatement(MatchNameReference("x"),
- MatchStatementEnd()),
- MatchCodeBlockEnd())),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, Tuples) {
- TokenizedBuffer tokens = GetTokenizedBuffer(R"(
- var x: (i32, i32) = (1, 2);
- var y: ((), (), ());
- )");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- auto empty_tuple = MatchTupleLiteral(MatchTupleLiteralEnd());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchVariableDeclaration(
- MatchPatternBinding(MatchDeclaredName("x"), ":",
- MatchTupleLiteral(MatchLiteral("i32"),
- MatchTupleLiteralComma(),
- MatchLiteral("i32"),
- MatchTupleLiteralEnd())),
- MatchVariableInitializer(MatchTupleLiteral(
- MatchLiteral("1"), MatchTupleLiteralComma(),
- MatchLiteral("2"), MatchTupleLiteralEnd())),
- MatchDeclarationEnd()),
- MatchVariableDeclaration(
- MatchPatternBinding(
- MatchDeclaredName("y"), ":",
- MatchTupleLiteral(empty_tuple, MatchTupleLiteralComma(),
- empty_tuple, MatchTupleLiteralComma(),
- empty_tuple, MatchTupleLiteralEnd())),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, Structs) {
- TokenizedBuffer tokens = GetTokenizedBuffer(R"(
- var x: {.a: i32, .b: i32} = {.a = 1, .b = 2};
- var y: {} = {};
- var z: {.n: i32,} = {.n = 4,};
- )");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- EXPECT_THAT(
- tree,
- MatchParseTreeNodes(
- {MatchVariableDeclaration(
- MatchPatternBinding(
- MatchDeclaredName("x"), ":",
- MatchStructTypeLiteral(
- MatchStructFieldType(MatchStructFieldDesignator(
- ".", MatchDesignatedName("a")),
- ":", MatchLiteral("i32")),
- MatchStructComma(),
- MatchStructFieldType(MatchStructFieldDesignator(
- ".", MatchDesignatedName("b")),
- ":", MatchLiteral("i32")),
- MatchStructEnd())),
- MatchVariableInitializer(MatchStructLiteral(
- MatchStructFieldValue(MatchStructFieldDesignator(
- ".", MatchDesignatedName("a")),
- "=", MatchLiteral("1")),
- MatchStructComma(),
- MatchStructFieldValue(MatchStructFieldDesignator(
- ".", MatchDesignatedName("b")),
- "=", MatchLiteral("2")),
- MatchStructEnd())),
- MatchDeclarationEnd()),
- MatchVariableDeclaration(
- MatchPatternBinding(MatchDeclaredName("y"), ":",
- MatchStructLiteral(MatchStructEnd())),
- MatchVariableInitializer(MatchStructLiteral(MatchStructEnd())),
- MatchDeclarationEnd()),
- MatchVariableDeclaration(
- MatchPatternBinding(
- MatchDeclaredName("z"), ":",
- MatchStructTypeLiteral(
- MatchStructFieldType(MatchStructFieldDesignator(
- ".", MatchDesignatedName("n")),
- ":", MatchLiteral("i32")),
- MatchStructComma(), MatchStructEnd())),
- MatchVariableInitializer(MatchStructLiteral(
- MatchStructFieldValue(MatchStructFieldDesignator(
- ".", MatchDesignatedName("n")),
- "=", MatchLiteral("4")),
- MatchStructComma(), MatchStructEnd())),
- MatchDeclarationEnd()),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, StructErrors) {
- struct Testcase {
- llvm::StringLiteral input;
- ::testing::Matcher<const Diagnostic&> diag_matcher;
- };
- Testcase testcases[] = {
- {"var x: {i32} = {};",
- DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
- {"var x: {a} = {};",
- DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
- {"var x: {a:} = {};",
- DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
- {"var x: {a=} = {};",
- DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
- {"var x: {.} = {};", DiagnosticMessage("Expected identifier after `.`.")},
- {"var x: {.\"hello\" = 0, .y = 4} = {};",
- DiagnosticMessage("Expected identifier after `.`.")},
- {"var x: {.\"hello\": i32, .y: i32} = {};",
- DiagnosticMessage("Expected identifier after `.`.")},
- {"var x: {.a} = {};",
- DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
- {"var x: {.a:} = {};", DiagnosticMessage("Expected expression.")},
- {"var x: {.a=} = {};", DiagnosticMessage("Expected expression.")},
- {"var x: {.a: i32, .b = 0} = {};",
- DiagnosticMessage("Expected `.field: type`.")},
- {"var x: {.a = 0, b: i32} = {};",
- DiagnosticMessage("Expected `.field = value`.")},
- {"var x: {,} = {};",
- DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
- {"var x: {.a: i32,,} = {};",
- DiagnosticMessage("Expected `.field: type`.")},
- {"var x: {.a = 0,,} = {};",
- DiagnosticMessage("Expected `.field = value`.")},
- {"var x: {.a: i32 banana} = {.a = 0};",
- DiagnosticMessage("Expected `,` or `}`.")},
- {"var x: {.a: i32} = {.a = 0 banana};",
- DiagnosticMessage("Expected `,` or `}`.")},
- };
- for (const Testcase& testcase : testcases) {
- TokenizedBuffer tokens = GetTokenizedBuffer(testcase.input);
- Testing::MockDiagnosticConsumer consumer;
- EXPECT_CALL(consumer, HandleDiagnostic(testcase.diag_matcher));
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- }
- }
- auto GetAndDropLine(llvm::StringRef& s) -> std::string {
- auto newline_offset = s.find_first_of('\n');
- llvm::StringRef line = s.slice(0, newline_offset);
- if (newline_offset != llvm::StringRef::npos) {
- s = s.substr(newline_offset + 1);
- } else {
- s = "";
- }
- return line.str();
- }
- TEST_F(ParseTreeTest, Printing) {
- TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- std::string print_storage;
- llvm::raw_string_ostream print_stream(print_storage);
- tree.Print(print_stream);
- llvm::StringRef print = print_stream.str();
- EXPECT_THAT(GetAndDropLine(print), StrEq("["));
- EXPECT_THAT(GetAndDropLine(print),
- StrEq("{node_index: 4, kind: 'FunctionDeclaration', text: 'fn', "
- "subtree_size: 5, children: ["));
- EXPECT_THAT(GetAndDropLine(print),
- StrEq(" {node_index: 0, kind: 'DeclaredName', text: 'F'},"));
- EXPECT_THAT(GetAndDropLine(print),
- StrEq(" {node_index: 2, kind: 'ParameterList', text: '(', "
- "subtree_size: 2, children: ["));
- EXPECT_THAT(GetAndDropLine(print),
- StrEq(" {node_index: 1, kind: 'ParameterListEnd', "
- "text: ')'}]},"));
- EXPECT_THAT(GetAndDropLine(print),
- StrEq(" {node_index: 3, kind: 'DeclarationEnd', text: ';'}]},"));
- EXPECT_THAT(GetAndDropLine(print),
- StrEq("{node_index: 5, kind: 'FileEnd', text: ''},"));
- EXPECT_THAT(GetAndDropLine(print), StrEq("]"));
- EXPECT_TRUE(print.empty()) << print;
- }
- TEST_F(ParseTreeTest, PrintingAsYAML) {
- TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_FALSE(tree.HasErrors());
- std::string print_output;
- llvm::raw_string_ostream print_stream(print_output);
- tree.Print(print_stream);
- print_stream.flush();
- EXPECT_THAT(
- Yaml::Value::FromText(print_output),
- ElementsAre(Yaml::SequenceValue{
- Yaml::MappingValue{
- {"node_index", "4"},
- {"kind", "FunctionDeclaration"},
- {"text", "fn"},
- {"subtree_size", "5"},
- {"children",
- Yaml::SequenceValue{
- Yaml::MappingValue{{"node_index", "0"},
- {"kind", "DeclaredName"},
- {"text", "F"}},
- Yaml::MappingValue{{"node_index", "2"},
- {"kind", "ParameterList"},
- {"text", "("},
- {"subtree_size", "2"},
- {"children", //
- Yaml::SequenceValue{Yaml::MappingValue{
- {"node_index", "1"},
- {"kind", "ParameterListEnd"},
- {"text", ")"}}}}},
- Yaml::MappingValue{{"node_index", "3"},
- {"kind", "DeclarationEnd"},
- {"text", ";"}}}}},
- Yaml::MappingValue{{"node_index", "5"}, //
- {"kind", "FileEnd"},
- {"text", ""}}}));
- }
- TEST_F(ParseTreeTest, ParenMatchRegression) {
- // A regression test that the search for the closing `)` doesn't end early on
- // the closing `}` when it skips over the nested scope.
- TokenizedBuffer tokens = GetTokenizedBuffer("var = (foo {})");
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- EXPECT_THAT(
- tree, MatchParseTreeNodes(
- {MatchVariableDeclaration(
- HasError, MatchVariableInitializer(
- "=", MatchParenExpression(
- HasError, MatchNameReference("foo"),
- MatchParenExpressionEnd()))),
- MatchFileEnd()}));
- }
- TEST_F(ParseTreeTest, RecursionLimit) {
- std::string code = "fn Foo() { return ";
- code.append(10000, '(');
- code.append(10000, ')');
- code += "; }";
- TokenizedBuffer tokens = GetTokenizedBuffer(code);
- ASSERT_FALSE(tokens.HasErrors());
- Testing::MockDiagnosticConsumer consumer;
- // Recursion might be exceeded multiple times due to quirks in parse tree
- // handling; we only need to be sure it's hit at least once for test
- // correctness.
- EXPECT_CALL(
- consumer,
- HandleDiagnostic(DiagnosticMessage(llvm::formatv(
- "Exceeded recursion limit ({0})", ParseTree::StackDepthLimit))))
- .Times(AtLeast(1));
- ParseTree tree = ParseTree::Parse(tokens, consumer);
- EXPECT_TRUE(tree.HasErrors());
- }
- } // namespace
- } // namespace Carbon::Testing
|