//===- TreeTest.cpp -------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Tooling/Syntax/Tree.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/TokenKinds.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Testing/CommandLineArgs.h" #include "clang/Testing/TestClangConfig.h" #include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/Syntax/BuildTree.h" #include "clang/Tooling/Syntax/Mutations.h" #include "clang/Tooling/Syntax/Nodes.h" #include "clang/Tooling/Syntax/Tokens.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" #include "llvm/Testing/Support/Annotations.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include using namespace clang; namespace { static ArrayRef tokens(syntax::Node *N) { assert(N->isOriginal() && "tokens of modified nodes are not well-defined"); if (auto *L = dyn_cast(N)) return llvm::makeArrayRef(L->token(), 1); auto *T = cast(N); return llvm::makeArrayRef(T->firstLeaf()->token(), T->lastLeaf()->token() + 1); } class SyntaxTreeTest : public ::testing::Test, public ::testing::WithParamInterface { protected: // Build a syntax tree for the code. syntax::TranslationUnit *buildTree(StringRef Code, const TestClangConfig &ClangConfig) { // FIXME: this code is almost the identical to the one in TokensTest. Share // it. class BuildSyntaxTree : public ASTConsumer { public: BuildSyntaxTree(syntax::TranslationUnit *&Root, std::unique_ptr &TB, std::unique_ptr &Arena, std::unique_ptr Tokens) : Root(Root), TB(TB), Arena(Arena), Tokens(std::move(Tokens)) { assert(this->Tokens); } void HandleTranslationUnit(ASTContext &Ctx) override { TB = std::make_unique(std::move(*Tokens).consume()); Tokens = nullptr; // make sure we fail if this gets called twice. Arena = std::make_unique(Ctx.getSourceManager(), Ctx.getLangOpts(), *TB); Root = syntax::buildSyntaxTree(*Arena, *Ctx.getTranslationUnitDecl()); } private: syntax::TranslationUnit *&Root; std::unique_ptr &TB; std::unique_ptr &Arena; std::unique_ptr Tokens; }; class BuildSyntaxTreeAction : public ASTFrontendAction { public: BuildSyntaxTreeAction(syntax::TranslationUnit *&Root, std::unique_ptr &TB, std::unique_ptr &Arena) : Root(Root), TB(TB), Arena(Arena) {} std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { // We start recording the tokens, ast consumer will take on the result. auto Tokens = std::make_unique(CI.getPreprocessor()); return std::make_unique(Root, TB, Arena, std::move(Tokens)); } private: syntax::TranslationUnit *&Root; std::unique_ptr &TB; std::unique_ptr &Arena; }; constexpr const char *FileName = "./input.cpp"; FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy("")); if (!Diags->getClient()) Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), DiagOpts.get())); Diags->setSeverityForGroup(diag::Flavor::WarningOrError, "unused-value", diag::Severity::Ignored, SourceLocation()); // Prepare to run a compiler. std::vector Args = { "syntax-test", "-fsyntax-only", }; llvm::copy(ClangConfig.getCommandLineArgs(), std::back_inserter(Args)); Args.push_back(FileName); std::vector ArgsCStr; for (const std::string &arg : Args) { ArgsCStr.push_back(arg.c_str()); } Invocation = createInvocationFromCommandLine(ArgsCStr, Diags, FS); assert(Invocation); Invocation->getFrontendOpts().DisableFree = false; Invocation->getPreprocessorOpts().addRemappedFile( FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release()); CompilerInstance Compiler; Compiler.setInvocation(Invocation); Compiler.setDiagnostics(Diags.get()); Compiler.setFileManager(FileMgr.get()); Compiler.setSourceManager(SourceMgr.get()); syntax::TranslationUnit *Root = nullptr; BuildSyntaxTreeAction Recorder(Root, this->TB, this->Arena); // Action could not be executed but the frontend didn't identify any errors // in the code ==> problem in setting up the action. if (!Compiler.ExecuteAction(Recorder) && Diags->getClient()->getNumErrors() == 0) { ADD_FAILURE() << "failed to run the frontend"; std::abort(); } return Root; } ::testing::AssertionResult treeDumpEqual(StringRef Code, StringRef Tree) { SCOPED_TRACE(llvm::join(GetParam().getCommandLineArgs(), " ")); auto *Root = buildTree(Code, GetParam()); if (Diags->getClient()->getNumErrors() != 0) { return ::testing::AssertionFailure() << "Source file has syntax errors, they were printed to the test " "log"; } std::string Actual = std::string(StringRef(Root->dump(*Arena)).trim()); // EXPECT_EQ shows the diff between the two strings if they are different. EXPECT_EQ(Tree.trim().str(), Actual); if (Actual != Tree.trim().str()) { return ::testing::AssertionFailure(); } return ::testing::AssertionSuccess(); } // Adds a file to the test VFS. void addFile(StringRef Path, StringRef Contents) { if (!FS->addFile(Path, time_t(), llvm::MemoryBuffer::getMemBufferCopy(Contents))) { ADD_FAILURE() << "could not add a file to VFS: " << Path; } } /// Finds the deepest node in the tree that covers exactly \p R. /// FIXME: implement this efficiently and move to public syntax tree API. syntax::Node *nodeByRange(llvm::Annotations::Range R, syntax::Node *Root) { ArrayRef Toks = tokens(Root); if (Toks.front().location().isFileID() && Toks.back().location().isFileID() && syntax::Token::range(*SourceMgr, Toks.front(), Toks.back()) == syntax::FileRange(SourceMgr->getMainFileID(), R.Begin, R.End)) return Root; auto *T = dyn_cast(Root); if (!T) return nullptr; for (auto *C = T->firstChild(); C != nullptr; C = C->nextSibling()) { if (auto *Result = nodeByRange(R, C)) return Result; } return nullptr; } // Data fields. IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); IntrusiveRefCntPtr Diags = new DiagnosticsEngine(new DiagnosticIDs, DiagOpts.get()); IntrusiveRefCntPtr FS = new llvm::vfs::InMemoryFileSystem; IntrusiveRefCntPtr FileMgr = new FileManager(FileSystemOptions(), FS); IntrusiveRefCntPtr SourceMgr = new SourceManager(*Diags, *FileMgr); std::shared_ptr Invocation; // Set after calling buildTree(). std::unique_ptr TB; std::unique_ptr Arena; }; TEST_P(SyntaxTreeTest, Simple) { EXPECT_TRUE(treeDumpEqual( R"cpp( int main() {} void foo() {} )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-main | | `-ParametersAndQualifiers | | |-( | | `-) | `-CompoundStatement | |-{ | `-} `-SimpleDeclaration |-void |-SimpleDeclarator | |-foo | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ `-} )txt")); } TEST_P(SyntaxTreeTest, SimpleVariable) { EXPECT_TRUE(treeDumpEqual( R"cpp( int a; int b = 42; )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | `-a | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-b | |-= | `-IntegerLiteralExpression | `-42 `-; )txt")); } TEST_P(SyntaxTreeTest, SimpleFunction) { EXPECT_TRUE(treeDumpEqual( R"cpp( void foo(int a, int b) {} )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-foo | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-a | |-, | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-b | `-) `-CompoundStatement |-{ `-} )txt")); } TEST_P(SyntaxTreeTest, If) { EXPECT_TRUE(treeDumpEqual( R"cpp( int main() { if (1) {} if (1) {} else if (0) {} } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-int |-SimpleDeclarator | |-main | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-IfStatement | |-if | |-( | |-IntegerLiteralExpression | | `-1 | |-) | `-CompoundStatement | |-{ | `-} |-IfStatement | |-if | |-( | |-IntegerLiteralExpression | | `-1 | |-) | |-CompoundStatement | | |-{ | | `-} | |-else | `-IfStatement | |-if | |-( | |-IntegerLiteralExpression | | `-0 | |-) | `-CompoundStatement | |-{ | `-} `-} )txt")); } TEST_P(SyntaxTreeTest, For) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { for (;;) {} } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ForStatement | |-for | |-( | |-; | |-; | |-) | `-CompoundStatement | |-{ | `-} `-} )txt")); } TEST_P(SyntaxTreeTest, RangeBasedFor) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { int a[3]; for (int x : a) ; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-DeclarationStatement | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | |-a | | `-ArraySubscript | | |-[ | | |-IntegerLiteralExpression | | | `-3 | | `-] | `-; |-RangeBasedForStatement | |-for | |-( | |-SimpleDeclaration | | |-int | | |-SimpleDeclarator | | | `-x | | `-: | |-IdExpression | | `-UnqualifiedId | | `-a | |-) | `-EmptyStatement | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, DeclarationStatement) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { int a = 10; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-DeclarationStatement | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | |-a | | |-= | | `-IntegerLiteralExpression | | `-10 | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, Switch) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { switch (1) { case 0: default:; } } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-SwitchStatement | |-switch | |-( | |-IntegerLiteralExpression | | `-1 | |-) | `-CompoundStatement | |-{ | |-CaseStatement | | |-case | | |-IntegerLiteralExpression | | | `-0 | | |-: | | `-DefaultStatement | | |-default | | |-: | | `-EmptyStatement | | `-; | `-} `-} )txt")); } TEST_P(SyntaxTreeTest, While) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { while (1) { continue; break; } } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-WhileStatement | |-while | |-( | |-IntegerLiteralExpression | | `-1 | |-) | `-CompoundStatement | |-{ | |-ContinueStatement | | |-continue | | `-; | |-BreakStatement | | |-break | | `-; | `-} `-} )txt")); } TEST_P(SyntaxTreeTest, UnhandledStatement) { // Unhandled statements should end up as 'unknown statement'. // This example uses a 'label statement', which does not yet have a syntax // counterpart. EXPECT_TRUE(treeDumpEqual( R"cpp( int main() { foo: return 100; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-int |-SimpleDeclarator | |-main | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-UnknownStatement | |-foo | |-: | `-ReturnStatement | |-return | |-IntegerLiteralExpression | | `-100 | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, Expressions) { // expressions should be wrapped in 'ExpressionStatement' when they appear // in a statement position. EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { test(); if (1) test(); else test(); } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-test | | |-( | | `-) | `-; |-IfStatement | |-if | |-( | |-IntegerLiteralExpression | | `-1 | |-) | |-ExpressionStatement | | |-UnknownExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-test | | | |-( | | | `-) | | `-; | |-else | `-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-test | | |-( | | `-) | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, UnqualifiedId) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct X { // TODO: Expose `id-expression` from `Declarator` friend X operator+(const X&, const X&); operator int(); }; template void f(T&); void test(X x) { x; // identifier operator+(x, x); // operator-function-id f(x); // template-id // TODO: Expose `id-expression` from `MemberExpr` x.operator int(); // conversion-funtion-id x.~X(); // ~type-name } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | |-{ | |-UnknownDeclaration | | `-SimpleDeclaration | | |-friend | | |-X | | |-SimpleDeclarator | | | |-operator | | | |-+ | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | |-const | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | |-, | | | |-SimpleDeclaration | | | | |-const | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | `-) | | `-; | |-SimpleDeclaration | | |-SimpleDeclarator | | | |-operator | | | |-int | | | `-ParametersAndQualifiers | | | |-( | | | `-) | | `-; | |-} | `-; |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-typename | | `-T | |-> | `-SimpleDeclaration | |-void | |-SimpleDeclarator | | |-f | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-T | | | `-SimpleDeclarator | | | `-& | | `-) | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-X | | `-SimpleDeclarator | | `-x | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-IdExpression | | `-UnqualifiedId | | `-x | `-; |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | `-UnqualifiedId | | | |-operator | | | `-+ | | |-( | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | |-, | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | `-UnqualifiedId | | | |-f | | | |-< | | | |-X | | | `-> | | |-( | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-UnknownExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-x | | | |-. | | | |-operator | | | `-int | | |-( | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-UnknownExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-x | | | |-. | | | |-~ | | | `-X | | |-( | | `-) | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, UnqualifiedIdCxx11OrLater) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct X { }; unsigned operator "" _w(long long unsigned); void test(X x) { operator "" _w(1llu); // literal-operator-id // TODO: Expose `id-expression` from `MemberExpr` x.~decltype(x)(); // ~decltype-specifier } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | |-{ | |-} | `-; |-SimpleDeclaration | |-unsigned | |-SimpleDeclarator | | |-operator | | |-"" | | |-_w | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-long | | | |-long | | | `-unsigned | | `-) | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-X | | `-SimpleDeclarator | | `-x | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | `-UnqualifiedId | | | |-operator | | | |-"" | | | `-_w | | |-( | | |-IntegerLiteralExpression | | | `-1llu | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-UnknownExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-x | | | |-. | | | `-~ | | |-decltype | | |-( | | |-x | | |-) | | |-( | | `-) | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, QualifiedId) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( namespace n { struct S { template struct ST { static void f(); }; }; } template struct ST { struct S { template static U f(); }; }; void test() { :: // global-namespace-specifier n:: // namespace-specifier S:: // type-name-specifier template ST:: // type-template-instantiation-specifier f(); n:: // namespace-specifier S:: // type-name-specifier ST:: // type-template-instantiation-specifier f(); ST:: // type-name-specifier S:: // type-name-specifier f(); ST:: // type-name-specifier S:: // type-name-specifier template f(); } )cpp", R"txt( *: TranslationUnit |-NamespaceDefinition | |-namespace | |-n | |-{ | |-SimpleDeclaration | | |-struct | | |-S | | |-{ | | |-TemplateDeclaration | | | |-template | | | |-< | | | |-UnknownDeclaration | | | | |-typename | | | | `-T | | | |-> | | | `-SimpleDeclaration | | | |-struct | | | |-ST | | | |-{ | | | |-SimpleDeclaration | | | | |-static | | | | |-void | | | | |-SimpleDeclarator | | | | | |-f | | | | | `-ParametersAndQualifiers | | | | | |-( | | | | | `-) | | | | `-; | | | |-} | | | `-; | | |-} | | `-; | `-} |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-typename | | `-T | |-> | `-SimpleDeclaration | |-struct | |-ST | |-{ | |-SimpleDeclaration | | |-struct | | |-S | | |-{ | | |-TemplateDeclaration | | | |-template | | | |-< | | | |-UnknownDeclaration | | | | |-typename | | | | `-U | | | |-> | | | `-SimpleDeclaration | | | |-static | | | |-U | | | |-SimpleDeclarator | | | | |-f | | | | `-ParametersAndQualifiers | | | | |-( | | | | `-) | | | `-; | | |-} | | `-; | |-} | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-:: | | | | |-IdentifierNameSpecifier | | | | | `-n | | | | |-:: | | | | |-IdentifierNameSpecifier | | | | | `-S | | | | |-:: | | | | |-SimpleTemplateNameSpecifier | | | | | |-template | | | | | |-ST | | | | | |-< | | | | | |-int | | | | | `-> | | | | `-:: | | | `-UnqualifiedId | | | `-f | | |-( | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-IdentifierNameSpecifier | | | | | `-n | | | | |-:: | | | | |-IdentifierNameSpecifier | | | | | `-S | | | | |-:: | | | | |-SimpleTemplateNameSpecifier | | | | | |-ST | | | | | |-< | | | | | |-int | | | | | `-> | | | | `-:: | | | `-UnqualifiedId | | | `-f | | |-( | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-SimpleTemplateNameSpecifier | | | | | |-ST | | | | | |-< | | | | | |-int | | | | | `-> | | | | |-:: | | | | |-IdentifierNameSpecifier | | | | | `-S | | | | `-:: | | | `-UnqualifiedId | | | |-f | | | |-< | | | |-int | | | `-> | | |-( | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-SimpleTemplateNameSpecifier | | | | | |-ST | | | | | |-< | | | | | |-int | | | | | `-> | | | | |-:: | | | | |-IdentifierNameSpecifier | | | | | `-S | | | | `-:: | | | |-template | | | `-UnqualifiedId | | | |-f | | | |-< | | | |-int | | | `-> | | |-( | | `-) | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, QualifiedIdWithDependentType) { if (!GetParam().isCXX()) { return; } if (GetParam().hasDelayedTemplateParsing()) { // FIXME: Make this test work on Windows by generating the expected syntax // tree when `-fdelayed-template-parsing` is active. return; } EXPECT_TRUE(treeDumpEqual( R"cpp( template void test() { T::template U::f(); T::U::f(); T::template f<0>(); } )cpp", R"txt( *: TranslationUnit `-TemplateDeclaration |-template |-< |-UnknownDeclaration | |-typename | `-T |-> `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-IdentifierNameSpecifier | | | | | `-T | | | | |-:: | | | | |-SimpleTemplateNameSpecifier | | | | | |-template | | | | | |-U | | | | | |-< | | | | | |-int | | | | | `-> | | | | `-:: | | | `-UnqualifiedId | | | `-f | | |-( | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-IdentifierNameSpecifier | | | | | `-T | | | | |-:: | | | | |-IdentifierNameSpecifier | | | | | `-U | | | | `-:: | | | `-UnqualifiedId | | | `-f | | |-( | | `-) | `-; |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-IdentifierNameSpecifier | | | | | `-T | | | | `-:: | | | |-template | | | `-UnqualifiedId | | | |-f | | | |-< | | | |-IntegerLiteralExpression | | | | `-0 | | | `-> | | |-( | | `-) | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, QualifiedIdDecltype) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct S { static void f(){} }; void test(S s) { decltype(s):: // decltype-specifier f(); } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-S | |-{ | |-SimpleDeclaration | | |-static | | |-void | | |-SimpleDeclarator | | | |-f | | | `-ParametersAndQualifiers | | | |-( | | | `-) | | `-CompoundStatement | | |-{ | | `-} | |-} | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-S | | `-SimpleDeclarator | | `-s | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-UnknownExpression | | |-IdExpression | | | |-NestedNameSpecifier | | | | |-DecltypeNameSpecifier | | | | | |-decltype | | | | | |-( | | | | | |-IdExpression | | | | | | `-UnqualifiedId | | | | | | `-s | | | | | `-) | | | | `-:: | | | `-UnqualifiedId | | | `-f | | |-( | | `-) | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, ParenExpr) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { (1); ((1)); (1 + (2)); } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-ParenExpression | | |-( | | |-IntegerLiteralExpression | | | `-1 | | `-) | `-; |-ExpressionStatement | |-ParenExpression | | |-( | | |-ParenExpression | | | |-( | | | |-IntegerLiteralExpression | | | | `-1 | | | `-) | | `-) | `-; |-ExpressionStatement | |-ParenExpression | | |-( | | |-BinaryOperatorExpression | | | |-IntegerLiteralExpression | | | | `-1 | | | |-+ | | | `-ParenExpression | | | |-( | | | |-IntegerLiteralExpression | | | | `-2 | | | `-) | | `-) | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, UserDefinedLiteral) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( typedef decltype(sizeof(void *)) size_t; unsigned operator "" _i(unsigned long long); unsigned operator "" _f(long double); unsigned operator "" _c(char); unsigned operator "" _s(const char*, size_t); unsigned operator "" _r(const char*); template unsigned operator "" _t(); void test() { 12_i; // call: operator "" _i(12uLL) | kind: integer 1.2_f; // call: operator "" _f(1.2L) | kind: float '2'_c; // call: operator "" _c('2') | kind: char "12"_s; // call: operator "" _s("12") | kind: string 12_r; // call: operator "" _r("12") | kind: integer 1.2_r; // call: operator "" _i("1.2") | kind: float 12_t; // call: operator<'1', '2'> "" _x() | kind: integer 1.2_t; // call: operator<'1', '2'> "" _x() | kind: float } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-typedef | |-decltype | |-( | |-UnknownExpression | | |-sizeof | | |-( | | |-void | | |-* | | `-) | |-) | |-SimpleDeclarator | | `-size_t | `-; |-SimpleDeclaration | |-unsigned | |-SimpleDeclarator | | |-operator | | |-"" | | |-_i | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-unsigned | | | |-long | | | `-long | | `-) | `-; |-SimpleDeclaration | |-unsigned | |-SimpleDeclarator | | |-operator | | |-"" | | |-_f | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-long | | | `-double | | `-) | `-; |-SimpleDeclaration | |-unsigned | |-SimpleDeclarator | | |-operator | | |-"" | | |-_c | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | `-char | | `-) | `-; |-SimpleDeclaration | |-unsigned | |-SimpleDeclarator | | |-operator | | |-"" | | |-_s | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-const | | | |-char | | | `-SimpleDeclarator | | | `-* | | |-, | | |-SimpleDeclaration | | | `-size_t | | `-) | `-; |-SimpleDeclaration | |-unsigned | |-SimpleDeclarator | | |-operator | | |-"" | | |-_r | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-const | | | |-char | | | `-SimpleDeclarator | | | `-* | | `-) | `-; |-TemplateDeclaration | |-template | |-< | |-SimpleDeclaration | | `-char | |-... | |-> | `-SimpleDeclaration | |-unsigned | |-SimpleDeclarator | | |-operator | | |-"" | | |-_t | | `-ParametersAndQualifiers | | |-( | | `-) | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-IntegerUserDefinedLiteralExpression | | `-12_i | `-; |-ExpressionStatement | |-FloatUserDefinedLiteralExpression | | `-1.2_f | `-; |-ExpressionStatement | |-CharUserDefinedLiteralExpression | | `-'2'_c | `-; |-ExpressionStatement | |-StringUserDefinedLiteralExpression | | `-"12"_s | `-; |-ExpressionStatement | |-IntegerUserDefinedLiteralExpression | | `-12_r | `-; |-ExpressionStatement | |-FloatUserDefinedLiteralExpression | | `-1.2_r | `-; |-ExpressionStatement | |-IntegerUserDefinedLiteralExpression | | `-12_t | `-; |-ExpressionStatement | |-FloatUserDefinedLiteralExpression | | `-1.2_t | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, IntegerLiteralLongLong) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { 12ll; 12ull; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-IntegerLiteralExpression | | `-12ll | `-; |-ExpressionStatement | |-IntegerLiteralExpression | | `-12ull | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, IntegerLiteralBinary) { if (!GetParam().isCXX14OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { 0b1100; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-IntegerLiteralExpression | | `-0b1100 | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, IntegerLiteralWithDigitSeparators) { if (!GetParam().isCXX14OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { 1'2'0ull; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-IntegerLiteralExpression | | `-1'2'0ull | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, CharacterLiteral) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { 'a'; '\n'; '\x20'; '\0'; L'a'; L'α'; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-CharacterLiteralExpression | | `-'a' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-'\n' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-'\x20' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-'\0' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-L'a' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-L'α' | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, CharacterLiteralUtf) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { u'a'; u'構'; U'a'; U'🌲'; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-CharacterLiteralExpression | | `-u'a' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-u'構' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-U'a' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-U'🌲' | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, CharacterLiteralUtf8) { if (!GetParam().isCXX17OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { u8'a'; u8'\x7f'; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-CharacterLiteralExpression | | `-u8'a' | `-; |-ExpressionStatement | |-CharacterLiteralExpression | | `-u8'\x7f' | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, FloatingLiteral) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { 1e-2; 2.; .2; 2.f; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-FloatingLiteralExpression | | `-1e-2 | `-; |-ExpressionStatement | |-FloatingLiteralExpression | | `-2. | `-; |-ExpressionStatement | |-FloatingLiteralExpression | | `-.2 | `-; |-ExpressionStatement | |-FloatingLiteralExpression | | `-2.f | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, FloatingLiteralHexadecimal) { if (!GetParam().isCXX17OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { 0xfp1; 0xf.p1; 0x.fp1; 0xf.fp1f; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-FloatingLiteralExpression | | `-0xfp1 | `-; |-ExpressionStatement | |-FloatingLiteralExpression | | `-0xf.p1 | `-; |-ExpressionStatement | |-FloatingLiteralExpression | | `-0x.fp1 | `-; |-ExpressionStatement | |-FloatingLiteralExpression | | `-0xf.fp1f | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, StringLiteral) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { "a\n\0\x20"; L"αβ"; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-StringLiteralExpression | | `-"a\n\0\x20" | `-; |-ExpressionStatement | |-StringLiteralExpression | | `-L"αβ" | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, StringLiteralUtf) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { u8"a\x1f\x05"; u"C++抽象構文木"; U"📖🌲\n"; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-StringLiteralExpression | | `-u8"a\x1f\x05" | `-; |-ExpressionStatement | |-StringLiteralExpression | | `-u"C++抽象構文木" | `-; |-ExpressionStatement | |-StringLiteralExpression | | `-U"📖🌲\n" | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, StringLiteralRaw) { if (!GetParam().isCXX11OrLater()) { return; } // This test uses regular string literals instead of raw string literals to // hold source code and expected output because of a bug in MSVC up to MSVC // 2019 16.2: // https://developercommunity.visualstudio.com/content/problem/67300/stringifying-raw-string-literal.html EXPECT_TRUE(treeDumpEqual( // "void test() {\n" " R\"SyntaxTree(\n" " Hello \"Syntax\" \\\"\n" " )SyntaxTree\";\n" "}\n", "*: TranslationUnit\n" "`-SimpleDeclaration\n" " |-void\n" " |-SimpleDeclarator\n" " | |-test\n" " | `-ParametersAndQualifiers\n" " | |-(\n" " | `-)\n" " `-CompoundStatement\n" " |-{\n" " |-ExpressionStatement\n" " | |-StringLiteralExpression\n" " | | `-R\"SyntaxTree(\n" " Hello \"Syntax\" \\\"\n" " )SyntaxTree\"\n" " | `-;\n" " `-}\n")); } TEST_P(SyntaxTreeTest, BoolLiteral) { if (GetParam().isC()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { true; false; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-BoolLiteralExpression | | `-true | `-; |-ExpressionStatement | |-BoolLiteralExpression | | `-false | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, CxxNullPtrLiteral) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test() { nullptr; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-CxxNullPtrExpression | | `-nullptr | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, PostfixUnaryOperator) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test(int a) { a++; a--; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-a | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-PostfixUnaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-a | | `-++ | `-; |-ExpressionStatement | |-PostfixUnaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-a | | `--- | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, PrefixUnaryOperator) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test(int a, int *ap) { --a; ++a; ~a; -a; +a; &a; *ap; !a; __real a; __imag a; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-a | |-, | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | |-* | | `-ap | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |--- | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-++ | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-~ | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-- | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-+ | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-& | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-* | | `-IdExpression | | `-UnqualifiedId | | `-ap | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-! | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-__real | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-__imag | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, PrefixUnaryOperatorCxx) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test(int a, bool b) { compl a; not b; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-a | |-, | |-SimpleDeclaration | | |-bool | | `-SimpleDeclarator | | `-b | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-compl | | `-IdExpression | | `-UnqualifiedId | | `-a | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-not | | `-IdExpression | | `-UnqualifiedId | | `-b | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, BinaryOperator) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test(int a) { 1 - 2; 1 == 2; a = 1; a <<= 1; 1 || 0; 1 & 2; a ^= 3; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-a | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-BinaryOperatorExpression | | |-IntegerLiteralExpression | | | `-1 | | |-- | | `-IntegerLiteralExpression | | `-2 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IntegerLiteralExpression | | | `-1 | | |-== | | `-IntegerLiteralExpression | | `-2 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-a | | |-= | | `-IntegerLiteralExpression | | `-1 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-a | | |-<<= | | `-IntegerLiteralExpression | | `-1 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IntegerLiteralExpression | | | `-1 | | |-|| | | `-IntegerLiteralExpression | | `-0 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IntegerLiteralExpression | | | `-1 | | |-& | | `-IntegerLiteralExpression | | `-2 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-a | | |-^= | | `-IntegerLiteralExpression | | `-3 | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, BinaryOperatorCxx) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void test(int a) { true || false; true or false; 1 bitand 2; a xor_eq 3; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-a | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-BinaryOperatorExpression | | |-BoolLiteralExpression | | | `-true | | |-|| | | `-BoolLiteralExpression | | `-false | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-BoolLiteralExpression | | | `-true | | |-or | | `-BoolLiteralExpression | | `-false | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IntegerLiteralExpression | | | `-1 | | |-bitand | | `-IntegerLiteralExpression | | `-2 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-a | | |-xor_eq | | `-IntegerLiteralExpression | | `-3 | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, NestedBinaryOperator) { EXPECT_TRUE(treeDumpEqual( R"cpp( void test(int a, int b) { (1 + 2) * (4 / 2); a + b + 42; a = b = 42; a + b * 4 + 2; a % 2 + b * 42; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-a | |-, | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | `-b | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-BinaryOperatorExpression | | |-ParenExpression | | | |-( | | | |-BinaryOperatorExpression | | | | |-IntegerLiteralExpression | | | | | `-1 | | | | |-+ | | | | `-IntegerLiteralExpression | | | | `-2 | | | `-) | | |-* | | `-ParenExpression | | |-( | | |-BinaryOperatorExpression | | | |-IntegerLiteralExpression | | | | `-4 | | | |-/ | | | `-IntegerLiteralExpression | | | `-2 | | `-) | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-BinaryOperatorExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-a | | | |-+ | | | `-IdExpression | | | `-UnqualifiedId | | | `-b | | |-+ | | `-IntegerLiteralExpression | | `-42 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-a | | |-= | | `-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-b | | |-= | | `-IntegerLiteralExpression | | `-42 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-BinaryOperatorExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-a | | | |-+ | | | `-BinaryOperatorExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-b | | | |-* | | | `-IntegerLiteralExpression | | | `-4 | | |-+ | | `-IntegerLiteralExpression | | `-2 | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-BinaryOperatorExpression | | | |-IdExpression | | | | `-UnqualifiedId | | | | `-a | | | |-% | | | `-IntegerLiteralExpression | | | `-2 | | |-+ | | `-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-b | | |-* | | `-IntegerLiteralExpression | | `-42 | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, UserDefinedBinaryOperator) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct X { X& operator=(const X&); friend X operator+(X, const X&); friend bool operator<(const X&, const X&); friend X operator<<(X&, const X&); X operator,(X&); X operator->*(int); // TODO: Unbox operators in syntax tree. // Represent operators by `+` instead of `IdExpression-UnqualifiedId-+` }; void test(X x, X y, X* xp, int X::* pmi) { x = y; x + y; x < y; x << y; x, y; xp->*pmi; } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | |-{ | |-SimpleDeclaration | | |-X | | |-SimpleDeclarator | | | |-& | | | |-operator | | | |-= | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | |-const | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | `-) | | `-; | |-UnknownDeclaration | | `-SimpleDeclaration | | |-friend | | |-X | | |-SimpleDeclarator | | | |-operator | | | |-+ | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | `-X | | | |-, | | | |-SimpleDeclaration | | | | |-const | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | `-) | | `-; | |-UnknownDeclaration | | `-SimpleDeclaration | | |-friend | | |-bool | | |-SimpleDeclarator | | | |-operator | | | |-< | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | |-const | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | |-, | | | |-SimpleDeclaration | | | | |-const | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | `-) | | `-; | |-UnknownDeclaration | | `-SimpleDeclaration | | |-friend | | |-X | | |-SimpleDeclarator | | | |-operator | | | |-<< | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | |-, | | | |-SimpleDeclaration | | | | |-const | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | `-) | | `-; | |-SimpleDeclaration | | |-X | | |-SimpleDeclarator | | | |-operator | | | |-, | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | |-X | | | | `-SimpleDeclarator | | | | `-& | | | `-) | | `-; | |-SimpleDeclaration | | |-X | | |-SimpleDeclarator | | | |-operator | | | |-->* | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | `-int | | | `-) | | `-; | |-} | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-X | | `-SimpleDeclarator | | `-x | |-, | |-SimpleDeclaration | | |-X | | `-SimpleDeclarator | | `-y | |-, | |-SimpleDeclaration | | |-X | | `-SimpleDeclarator | | |-* | | `-xp | |-, | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | |-MemberPointer | | | |-X | | | |-:: | | | `-* | | `-pmi | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | |-= | | `-IdExpression | | `-UnqualifiedId | | `-y | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-UnknownExpression | | | `-IdExpression | | | `-UnqualifiedId | | | `-x | | |-+ | | `-IdExpression | | `-UnqualifiedId | | `-y | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | |-< | | `-IdExpression | | `-UnqualifiedId | | `-y | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | |-<< | | `-IdExpression | | `-UnqualifiedId | | `-y | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | |-, | | `-IdExpression | | `-UnqualifiedId | | `-y | `-; |-ExpressionStatement | |-BinaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-xp | | |-->* | | `-IdExpression | | `-UnqualifiedId | | `-pmi | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, UserDefinedUnaryPrefixOperator) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct X { X operator++(); bool operator!(); X* operator&(); }; void test(X x) { ++x; !x; &x; } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | |-{ | |-SimpleDeclaration | | |-X | | |-SimpleDeclarator | | | |-operator | | | |-++ | | | `-ParametersAndQualifiers | | | |-( | | | `-) | | `-; | |-SimpleDeclaration | | |-bool | | |-SimpleDeclarator | | | |-operator | | | |-! | | | `-ParametersAndQualifiers | | | |-( | | | `-) | | `-; | |-SimpleDeclaration | | |-X | | |-SimpleDeclarator | | | |-* | | | |-operator | | | |-& | | | `-ParametersAndQualifiers | | | |-( | | | `-) | | `-; | |-} | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-X | | `-SimpleDeclarator | | `-x | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-++ | | `-IdExpression | | `-UnqualifiedId | | `-x | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-! | | `-IdExpression | | `-UnqualifiedId | | `-x | `-; |-ExpressionStatement | |-PrefixUnaryOperatorExpression | | |-& | | `-IdExpression | | `-UnqualifiedId | | `-x | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, UserDefinedUnaryPostfixOperator) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct X { X operator++(int); }; void test(X x) { x++; } )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | |-{ | |-SimpleDeclaration | | |-X | | |-SimpleDeclarator | | | |-operator | | | |-++ | | | `-ParametersAndQualifiers | | | |-( | | | |-SimpleDeclaration | | | | `-int | | | `-) | | `-; | |-} | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-X | | `-SimpleDeclarator | | `-x | `-) `-CompoundStatement |-{ |-ExpressionStatement | |-PostfixUnaryOperatorExpression | | |-IdExpression | | | `-UnqualifiedId | | | `-x | | `-++ | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, MultipleDeclaratorsGrouping) { EXPECT_TRUE(treeDumpEqual( R"cpp( int *a, b; int *c, d; )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-* | | `-a | |-, | |-SimpleDeclarator | | `-b | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-* | `-c |-, |-SimpleDeclarator | `-d `-; )txt")); } TEST_P(SyntaxTreeTest, MultipleDeclaratorsGroupingTypedef) { EXPECT_TRUE(treeDumpEqual( R"cpp( typedef int *a, b; )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-typedef |-int |-SimpleDeclarator | |-* | `-a |-, |-SimpleDeclarator | `-b `-; )txt")); } TEST_P(SyntaxTreeTest, MultipleDeclaratorsInsideStatement) { EXPECT_TRUE(treeDumpEqual( R"cpp( void foo() { int *a, b; typedef int *ta, tb; } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-foo | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-DeclarationStatement | |-SimpleDeclaration | | |-int | | |-SimpleDeclarator | | | |-* | | | `-a | | |-, | | `-SimpleDeclarator | | `-b | `-; |-DeclarationStatement | |-SimpleDeclaration | | |-typedef | | |-int | | |-SimpleDeclarator | | | |-* | | | `-ta | | |-, | | `-SimpleDeclarator | | `-tb | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, Namespaces) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( namespace a { namespace b {} } namespace a::b {} namespace {} namespace foo = a; )cpp", R"txt( *: TranslationUnit |-NamespaceDefinition | |-namespace | |-a | |-{ | |-NamespaceDefinition | | |-namespace | | |-b | | |-{ | | `-} | `-} |-NamespaceDefinition | |-namespace | |-a | |-:: | |-b | |-{ | `-} |-NamespaceDefinition | |-namespace | |-{ | `-} `-NamespaceAliasDefinition |-namespace |-foo |-= |-a `-; )txt")); } TEST_P(SyntaxTreeTest, UsingDirective) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( namespace ns {} using namespace ::ns; )cpp", R"txt( *: TranslationUnit |-NamespaceDefinition | |-namespace | |-ns | |-{ | `-} `-UsingNamespaceDirective |-using |-namespace |-NestedNameSpecifier | `-:: |-ns `-; )txt")); } TEST_P(SyntaxTreeTest, UsingDeclaration) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( namespace ns { int a; } using ns::a; )cpp", R"txt( *: TranslationUnit |-NamespaceDefinition | |-namespace | |-ns | |-{ | |-SimpleDeclaration | | |-int | | |-SimpleDeclarator | | | `-a | | `-; | `-} `-UsingDeclaration |-using |-NestedNameSpecifier | |-IdentifierNameSpecifier | | `-ns | `-:: |-a `-; )txt")); } TEST_P(SyntaxTreeTest, FreeStandingClasses) { // Free-standing classes, must live inside a SimpleDeclaration. EXPECT_TRUE(treeDumpEqual( R"cpp( struct X; struct X {}; struct Y *y1; struct Y {} *y2; struct {} *a1; )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | `-; |-SimpleDeclaration | |-struct | |-X | |-{ | |-} | `-; |-SimpleDeclaration | |-struct | |-Y | |-SimpleDeclarator | | |-* | | `-y1 | `-; |-SimpleDeclaration | |-struct | |-Y | |-{ | |-} | |-SimpleDeclarator | | |-* | | `-y2 | `-; `-SimpleDeclaration |-struct |-{ |-} |-SimpleDeclarator | |-* | `-a1 `-; )txt")); } TEST_P(SyntaxTreeTest, Templates) { if (!GetParam().isCXX()) { return; } if (GetParam().hasDelayedTemplateParsing()) { // FIXME: Make this test work on Windows by generating the expected syntax // tree when `-fdelayed-template-parsing` is active. return; } EXPECT_TRUE(treeDumpEqual( R"cpp( template struct cls {}; template int var = 10; template int fun() {} )cpp", R"txt( *: TranslationUnit |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-class | | `-T | |-> | `-SimpleDeclaration | |-struct | |-cls | |-{ | |-} | `-; |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-class | | `-T | |-> | `-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-var | | |-= | | `-IntegerLiteralExpression | | `-10 | `-; `-TemplateDeclaration |-template |-< |-UnknownDeclaration | |-class | `-T |-> `-SimpleDeclaration |-int |-SimpleDeclarator | |-fun | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ `-} )txt")); } TEST_P(SyntaxTreeTest, NestedTemplates) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( template struct X { template U foo(); }; )cpp", R"txt( *: TranslationUnit `-TemplateDeclaration |-template |-< |-UnknownDeclaration | |-class | `-T |-> `-SimpleDeclaration |-struct |-X |-{ |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-class | | `-U | |-> | `-SimpleDeclaration | |-U | |-SimpleDeclarator | | |-foo | | `-ParametersAndQualifiers | | |-( | | `-) | `-; |-} `-; )txt")); } TEST_P(SyntaxTreeTest, Templates2) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( template struct X { struct Y; }; template struct X::Y {}; )cpp", R"txt( *: TranslationUnit |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-class | | `-T | |-> | `-SimpleDeclaration | |-struct | |-X | |-{ | |-SimpleDeclaration | | |-struct | | |-Y | | `-; | |-} | `-; `-TemplateDeclaration |-template |-< |-UnknownDeclaration | |-class | `-T |-> `-SimpleDeclaration |-struct |-NestedNameSpecifier | |-SimpleTemplateNameSpecifier | | |-X | | |-< | | |-T | | `-> | `-:: |-Y |-{ |-} `-; )txt")); } TEST_P(SyntaxTreeTest, TemplatesUsingUsing) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( template struct X { using T::foo; using typename T::bar; }; )cpp", R"txt( *: TranslationUnit `-TemplateDeclaration |-template |-< |-UnknownDeclaration | |-class | `-T |-> `-SimpleDeclaration |-struct |-X |-{ |-UsingDeclaration | |-using | |-NestedNameSpecifier | | |-IdentifierNameSpecifier | | | `-T | | `-:: | |-foo | `-; |-UsingDeclaration | |-using | |-typename | |-NestedNameSpecifier | | |-IdentifierNameSpecifier | | | `-T | | `-:: | |-bar | `-; |-} `-; )txt")); } TEST_P(SyntaxTreeTest, ExplicitTemplateInstantations) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( template struct X {}; template struct X {}; template <> struct X {}; template struct X; extern template struct X; )cpp", R"txt( *: TranslationUnit |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-class | | `-T | |-> | `-SimpleDeclaration | |-struct | |-X | |-{ | |-} | `-; |-TemplateDeclaration | |-template | |-< | |-UnknownDeclaration | | |-class | | `-T | |-> | `-SimpleDeclaration | |-struct | |-X | |-< | |-T | |-* | |-> | |-{ | |-} | `-; |-TemplateDeclaration | |-template | |-< | |-> | `-SimpleDeclaration | |-struct | |-X | |-< | |-int | |-> | |-{ | |-} | `-; |-ExplicitTemplateInstantiation | |-template | `-SimpleDeclaration | |-struct | |-X | |-< | |-double | |-> | `-; `-ExplicitTemplateInstantiation |-extern |-template `-SimpleDeclaration |-struct |-X |-< |-float |-> `-; )txt")); } TEST_P(SyntaxTreeTest, UsingType) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( using type = int; )cpp", R"txt( *: TranslationUnit `-TypeAliasDeclaration |-using |-type |-= |-int `-; )txt")); } TEST_P(SyntaxTreeTest, EmptyDeclaration) { EXPECT_TRUE(treeDumpEqual( R"cpp( ; )cpp", R"txt( *: TranslationUnit `-EmptyDeclaration `-; )txt")); } TEST_P(SyntaxTreeTest, StaticAssert) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( static_assert(true, "message"); static_assert(true); )cpp", R"txt( *: TranslationUnit |-StaticAssertDeclaration | |-static_assert | |-( | |-BoolLiteralExpression | | `-true | |-, | |-StringLiteralExpression | | `-"message" | |-) | `-; `-StaticAssertDeclaration |-static_assert |-( |-BoolLiteralExpression | `-true |-) `-; )txt")); } TEST_P(SyntaxTreeTest, ExternC) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( extern "C" int a; extern "C" { int b; int c; } )cpp", R"txt( *: TranslationUnit |-LinkageSpecificationDeclaration | |-extern | |-"C" | `-SimpleDeclaration | |-int | |-SimpleDeclarator | | `-a | `-; `-LinkageSpecificationDeclaration |-extern |-"C" |-{ |-SimpleDeclaration | |-int | |-SimpleDeclarator | | `-b | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | `-c | `-; `-} )txt")); } TEST_P(SyntaxTreeTest, NonModifiableNodes) { // Some nodes are non-modifiable, they are marked with 'I:'. EXPECT_TRUE(treeDumpEqual( R"cpp( #define HALF_IF if (1+ #define HALF_IF_2 1) {} void test() { HALF_IF HALF_IF_2 else {} })cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-IfStatement | |-I: if | |-I: ( | |-I: BinaryOperatorExpression | | |-I: IntegerLiteralExpression | | | `-I: 1 | | |-I: + | | `-I: IntegerLiteralExpression | | `-I: 1 | |-I: ) | |-I: CompoundStatement | | |-I: { | | `-I: } | |-else | `-CompoundStatement | |-{ | `-} `-} )txt")); } TEST_P(SyntaxTreeTest, ModifiableNodes) { // All nodes can be mutated. EXPECT_TRUE(treeDumpEqual( R"cpp( #define OPEN { #define CLOSE } void test() { OPEN 1; CLOSE OPEN 2; } } )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-test | `-ParametersAndQualifiers | |-( | `-) `-CompoundStatement |-{ |-CompoundStatement | |-{ | |-ExpressionStatement | | |-IntegerLiteralExpression | | | `-1 | | `-; | `-} |-CompoundStatement | |-{ | |-ExpressionStatement | | |-IntegerLiteralExpression | | | `-2 | | `-; | `-} `-} )txt")); } TEST_P(SyntaxTreeTest, ArraySubscriptsInDeclarators) { EXPECT_TRUE(treeDumpEqual( R"cpp( int a[10]; int b[1][2][3]; int c[] = {1,2,3}; )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-a | | `-ArraySubscript | | |-[ | | |-IntegerLiteralExpression | | | `-10 | | `-] | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-b | | |-ArraySubscript | | | |-[ | | | |-IntegerLiteralExpression | | | | `-1 | | | `-] | | |-ArraySubscript | | | |-[ | | | |-IntegerLiteralExpression | | | | `-2 | | | `-] | | `-ArraySubscript | | |-[ | | |-IntegerLiteralExpression | | | `-3 | | `-] | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-c | |-ArraySubscript | | |-[ | | `-] | |-= | `-UnknownExpression | `-UnknownExpression | |-{ | |-IntegerLiteralExpression | | `-1 | |-, | |-IntegerLiteralExpression | | `-2 | |-, | |-IntegerLiteralExpression | | `-3 | `-} `-; )txt")); } TEST_P(SyntaxTreeTest, StaticArraySubscriptsInDeclarators) { if (!GetParam().isC99OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( void f(int xs[static 10]); )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-f | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | |-xs | | `-ArraySubscript | | |-[ | | |-static | | |-IntegerLiteralExpression | | | `-10 | | `-] | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, ParametersAndQualifiersInFreeFunctions) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( int func1(); int func2a(int a); int func2b(int); int func3a(int *ap); int func3b(int *); int func4a(int a, float b); int func4b(int, float); )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-func1 | | `-ParametersAndQualifiers | | |-( | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-func2a | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-int | | | `-SimpleDeclarator | | | `-a | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-func2b | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | `-int | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-func3a | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-int | | | `-SimpleDeclarator | | | |-* | | | `-ap | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-func3b | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-int | | | `-SimpleDeclarator | | | `-* | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-func4a | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-int | | | `-SimpleDeclarator | | | `-a | | |-, | | |-SimpleDeclaration | | | |-float | | | `-SimpleDeclarator | | | `-b | | `-) | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-func4b | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | `-int | |-, | |-SimpleDeclaration | | `-float | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, ParametersAndQualifiersInFreeFunctionsCxx) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( int func1(const int a, volatile int b, const volatile int c); int func2(int& a); )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-func1 | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-const | | | |-int | | | `-SimpleDeclarator | | | `-a | | |-, | | |-SimpleDeclaration | | | |-volatile | | | |-int | | | `-SimpleDeclarator | | | `-b | | |-, | | |-SimpleDeclaration | | | |-const | | | |-volatile | | | |-int | | | `-SimpleDeclarator | | | `-c | | `-) | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-func2 | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | |-& | | `-a | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, ParametersAndQualifiersInFreeFunctionsCxx11) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( int func1(int&& a); )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-int |-SimpleDeclarator | |-func1 | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-int | | `-SimpleDeclarator | | |-&& | | `-a | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, ParametersAndQualifiersInMemberFunctions) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct Test { int a(); int b() const; int c() volatile; int d() const volatile; int e() &; int f() &&; }; )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-struct |-Test |-{ |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-a | | `-ParametersAndQualifiers | | |-( | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-b | | `-ParametersAndQualifiers | | |-( | | |-) | | `-const | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-c | | `-ParametersAndQualifiers | | |-( | | |-) | | `-volatile | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-d | | `-ParametersAndQualifiers | | |-( | | |-) | | |-const | | `-volatile | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-e | | `-ParametersAndQualifiers | | |-( | | |-) | | `-& | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-f | | `-ParametersAndQualifiers | | |-( | | |-) | | `-&& | `-; |-} `-; )txt")); } TEST_P(SyntaxTreeTest, TrailingReturn) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( auto foo() -> int; )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-auto |-SimpleDeclarator | |-foo | `-ParametersAndQualifiers | |-( | |-) | `-TrailingReturnType | |--> | `-int `-; )txt")); } TEST_P(SyntaxTreeTest, DynamicExceptionSpecification) { if (!GetParam().supportsCXXDynamicExceptionSpecification()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct MyException1 {}; struct MyException2 {}; int a() throw(); int b() throw(...); int c() throw(MyException1); int d() throw(MyException1, MyException2); )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-MyException1 | |-{ | |-} | `-; |-SimpleDeclaration | |-struct | |-MyException2 | |-{ | |-} | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-a | | `-ParametersAndQualifiers | | |-( | | |-) | | |-throw | | |-( | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-b | | `-ParametersAndQualifiers | | |-( | | |-) | | |-throw | | |-( | | |-... | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-c | | `-ParametersAndQualifiers | | |-( | | |-) | | |-throw | | |-( | | |-MyException1 | | `-) | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-d | `-ParametersAndQualifiers | |-( | |-) | |-throw | |-( | |-MyException1 | |-, | |-MyException2 | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, NoexceptExceptionSpecification) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( int a() noexcept; int b() noexcept(true); )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-a | | `-ParametersAndQualifiers | | |-( | | |-) | | `-noexcept | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-b | `-ParametersAndQualifiers | |-( | |-) | |-noexcept | |-( | |-BoolLiteralExpression | | `-true | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, DeclaratorsInParentheses) { EXPECT_TRUE(treeDumpEqual( R"cpp( int (a); int *(b); int (*c)(int); int *(d)(int); )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-int | |-SimpleDeclarator | | `-ParenDeclarator | | |-( | | |-a | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-* | | `-ParenDeclarator | | |-( | | |-b | | `-) | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-ParenDeclarator | | | |-( | | | |-* | | | |-c | | | `-) | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | `-int | | `-) | `-; `-SimpleDeclaration |-int |-SimpleDeclarator | |-* | |-ParenDeclarator | | |-( | | |-d | | `-) | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | `-int | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, ConstVolatileQualifiers) { EXPECT_TRUE(treeDumpEqual( R"cpp( const int west = -1; int const east = 1; const int const universal = 0; const int const *const *volatile b; )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-const | |-int | |-SimpleDeclarator | | |-west | | |-= | | `-PrefixUnaryOperatorExpression | | |-- | | `-IntegerLiteralExpression | | `-1 | `-; |-SimpleDeclaration | |-int | |-const | |-SimpleDeclarator | | |-east | | |-= | | `-IntegerLiteralExpression | | `-1 | `-; |-SimpleDeclaration | |-const | |-int | |-const | |-SimpleDeclarator | | |-universal | | |-= | | `-IntegerLiteralExpression | | `-0 | `-; `-SimpleDeclaration |-const |-int |-const |-SimpleDeclarator | |-* | |-const | |-* | |-volatile | `-b `-; )txt")); } TEST_P(SyntaxTreeTest, RangesOfDeclaratorsWithTrailingReturnTypes) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( auto foo() -> auto(*)(int) -> double*; )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-auto |-SimpleDeclarator | |-foo | `-ParametersAndQualifiers | |-( | |-) | `-TrailingReturnType | |--> | |-auto | `-SimpleDeclarator | |-ParenDeclarator | | |-( | | |-* | | `-) | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | `-int | |-) | `-TrailingReturnType | |--> | |-double | `-SimpleDeclarator | `-* `-; )txt")); } TEST_P(SyntaxTreeTest, MemberPointers) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct X {}; int X::* a; const int X::* b; )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | |-{ | |-} | `-; |-SimpleDeclaration | |-int | |-SimpleDeclarator | | |-MemberPointer | | | |-X | | | |-:: | | | `-* | | `-a | `-; `-SimpleDeclaration |-const |-int |-SimpleDeclarator | |-MemberPointer | | |-X | | |-:: | | `-* | `-b `-; )txt")); } TEST_P(SyntaxTreeTest, MemberFunctionPointer) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(treeDumpEqual( R"cpp( struct X { struct Y {}; }; void (X::*xp)(); void (X::**xpp)(const int*); // FIXME: Generate the right syntax tree for this type, // i.e. create a syntax node for the outer member pointer void (X::Y::*xyp)(const int*, char); )cpp", R"txt( *: TranslationUnit |-SimpleDeclaration | |-struct | |-X | |-{ | |-SimpleDeclaration | | |-struct | | |-Y | | |-{ | | |-} | | `-; | |-} | `-; |-SimpleDeclaration | |-void | |-SimpleDeclarator | | |-ParenDeclarator | | | |-( | | | |-MemberPointer | | | | |-X | | | | |-:: | | | | `-* | | | |-xp | | | `-) | | `-ParametersAndQualifiers | | |-( | | `-) | `-; |-SimpleDeclaration | |-void | |-SimpleDeclarator | | |-ParenDeclarator | | | |-( | | | |-MemberPointer | | | | |-X | | | | |-:: | | | | `-* | | | |-* | | | |-xpp | | | `-) | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-const | | | |-int | | | `-SimpleDeclarator | | | `-* | | `-) | `-; `-SimpleDeclaration |-void |-SimpleDeclarator | |-ParenDeclarator | | |-( | | |-X | | |-:: | | |-MemberPointer | | | |-Y | | | |-:: | | | `-* | | |-xyp | | `-) | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-const | | |-int | | `-SimpleDeclarator | | `-* | |-, | |-SimpleDeclaration | | `-char | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, ComplexDeclarator) { EXPECT_TRUE(treeDumpEqual( R"cpp( void x(char a, short (*b)(int)); )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-x | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-char | | `-SimpleDeclarator | | `-a | |-, | |-SimpleDeclaration | | |-short | | `-SimpleDeclarator | | |-ParenDeclarator | | | |-( | | | |-* | | | |-b | | | `-) | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | `-int | | `-) | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, ComplexDeclarator2) { EXPECT_TRUE(treeDumpEqual( R"cpp( void x(char a, short (*b)(int), long (**c)(long long)); )cpp", R"txt( *: TranslationUnit `-SimpleDeclaration |-void |-SimpleDeclarator | |-x | `-ParametersAndQualifiers | |-( | |-SimpleDeclaration | | |-char | | `-SimpleDeclarator | | `-a | |-, | |-SimpleDeclaration | | |-short | | `-SimpleDeclarator | | |-ParenDeclarator | | | |-( | | | |-* | | | |-b | | | `-) | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | `-int | | `-) | |-, | |-SimpleDeclaration | | |-long | | `-SimpleDeclarator | | |-ParenDeclarator | | | |-( | | | |-* | | | |-* | | | |-c | | | `-) | | `-ParametersAndQualifiers | | |-( | | |-SimpleDeclaration | | | |-long | | | `-long | | `-) | `-) `-; )txt")); } TEST_P(SyntaxTreeTest, Mutations) { if (!GetParam().isCXX11OrLater()) { return; } using Transformation = std::function; auto CheckTransformation = [this](std::string Input, std::string Expected, Transformation Transform) -> void { llvm::Annotations Source(Input); auto *Root = buildTree(Source.code(), GetParam()); Transform(Source, Root); auto Replacements = syntax::computeReplacements(*Arena, *Root); auto Output = tooling::applyAllReplacements(Source.code(), Replacements); if (!Output) { ADD_FAILURE() << "could not apply replacements: " << llvm::toString(Output.takeError()); return; } EXPECT_EQ(Expected, *Output) << "input is:\n" << Input; }; // Removes the selected statement. Input should have exactly one selected // range and it should correspond to a single statement. auto RemoveStatement = [this](const llvm::Annotations &Input, syntax::TranslationUnit *TU) { auto *S = cast(nodeByRange(Input.range(), TU)); ASSERT_TRUE(S->canModify()) << "cannot remove a statement"; syntax::removeStatement(*Arena, S); EXPECT_TRUE(S->isDetached()); EXPECT_FALSE(S->isOriginal()) << "node removed from tree cannot be marked as original"; }; std::vector> Cases = { {"void test() { [[100+100;]] test(); }", "void test() { test(); }"}, {"void test() { if (true) [[{}]] else {} }", "void test() { if (true) ; else {} }"}, {"void test() { [[;]] }", "void test() { }"}}; for (const auto &C : Cases) CheckTransformation(C.first, C.second, RemoveStatement); } TEST_P(SyntaxTreeTest, SynthesizedNodes) { buildTree("", GetParam()); auto *C = syntax::createPunctuation(*Arena, tok::comma); ASSERT_NE(C, nullptr); EXPECT_EQ(C->token()->kind(), tok::comma); EXPECT_TRUE(C->canModify()); EXPECT_FALSE(C->isOriginal()); EXPECT_TRUE(C->isDetached()); auto *S = syntax::createEmptyStatement(*Arena); ASSERT_NE(S, nullptr); EXPECT_TRUE(S->canModify()); EXPECT_FALSE(S->isOriginal()); EXPECT_TRUE(S->isDetached()); } static std::vector allTestClangConfigs() { std::vector all_configs; for (TestLanguage lang : {Lang_C89, Lang_C99, Lang_CXX03, Lang_CXX11, Lang_CXX14, Lang_CXX17, Lang_CXX20}) { TestClangConfig config; config.Language = lang; config.Target = "x86_64-pc-linux-gnu"; all_configs.push_back(config); // Windows target is interesting to test because it enables // `-fdelayed-template-parsing`. config.Target = "x86_64-pc-win32-msvc"; all_configs.push_back(config); } return all_configs; } INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, SyntaxTreeTest, testing::ValuesIn(allTestClangConfigs()), ); } // namespace