summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/clang/unittests/AST
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
commite5dd70708596ae51455a0ffa086a00c5b29f8583 (patch)
tree5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/unittests/AST
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz
wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/clang/unittests/AST')
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTContextParentMapTest.cpp120
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTImporterFixtures.cpp237
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTImporterFixtures.h239
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTImporterGenericRedeclTest.cpp577
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTImporterODRStrategiesTest.cpp676
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTImporterTest.cpp5907
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTImporterVisibilityTest.cpp446
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTPrint.h92
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTTraverserTest.cpp617
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTTypeTraitsTest.cpp183
-rw-r--r--gnu/llvm/clang/unittests/AST/ASTVectorTest.cpp90
-rw-r--r--gnu/llvm/clang/unittests/AST/CMakeLists.txt45
-rw-r--r--gnu/llvm/clang/unittests/AST/CommentLexer.cpp1845
-rw-r--r--gnu/llvm/clang/unittests/AST/CommentParser.cpp1445
-rw-r--r--gnu/llvm/clang/unittests/AST/CommentTextTest.cpp127
-rw-r--r--gnu/llvm/clang/unittests/AST/DataCollectionTest.cpp172
-rw-r--r--gnu/llvm/clang/unittests/AST/DeclMatcher.h78
-rw-r--r--gnu/llvm/clang/unittests/AST/DeclPrinterTest.cpp1320
-rw-r--r--gnu/llvm/clang/unittests/AST/DeclTest.cpp109
-rw-r--r--gnu/llvm/clang/unittests/AST/EvaluateAsRValueTest.cpp110
-rw-r--r--gnu/llvm/clang/unittests/AST/ExternalASTSourceTest.cpp82
-rw-r--r--gnu/llvm/clang/unittests/AST/Language.cpp53
-rw-r--r--gnu/llvm/clang/unittests/AST/Language.h42
-rw-r--r--gnu/llvm/clang/unittests/AST/MatchVerifier.h319
-rw-r--r--gnu/llvm/clang/unittests/AST/NamedDeclPrinterTest.cpp271
-rw-r--r--gnu/llvm/clang/unittests/AST/OMPStructuredBlockTest.cpp540
-rw-r--r--gnu/llvm/clang/unittests/AST/RecursiveASTVisitorTest.cpp105
-rw-r--r--gnu/llvm/clang/unittests/AST/SourceLocationTest.cpp846
-rw-r--r--gnu/llvm/clang/unittests/AST/StmtPrinterTest.cpp247
-rw-r--r--gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp1400
30 files changed, 18340 insertions, 0 deletions
diff --git a/gnu/llvm/clang/unittests/AST/ASTContextParentMapTest.cpp b/gnu/llvm/clang/unittests/AST/ASTContextParentMapTest.cpp
new file mode 100644
index 00000000000..14da737500a
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTContextParentMapTest.cpp
@@ -0,0 +1,120 @@
+//===- unittest/AST/ASTContextParentMapTest.cpp - AST parent map test -----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for the getParents(...) methods of ASTContext.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "MatchVerifier.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+using testing::ElementsAre;
+
+namespace clang {
+namespace ast_matchers {
+
+TEST(GetParents, ReturnsParentForDecl) {
+ MatchVerifier<Decl> Verifier;
+ EXPECT_TRUE(
+ Verifier.match("class C { void f(); };",
+ cxxMethodDecl(hasParent(recordDecl(hasName("C"))))));
+}
+
+TEST(GetParents, ReturnsParentForStmt) {
+ MatchVerifier<Stmt> Verifier;
+ EXPECT_TRUE(Verifier.match("class C { void f() { if (true) {} } };",
+ ifStmt(hasParent(compoundStmt()))));
+}
+
+TEST(GetParents, ReturnsParentForTypeLoc) {
+ MatchVerifier<TypeLoc> Verifier;
+ EXPECT_TRUE(
+ Verifier.match("namespace a { class b {}; } void f(a::b) {}",
+ typeLoc(hasParent(typeLoc(hasParent(functionDecl()))))));
+}
+
+TEST(GetParents, ReturnsParentForNestedNameSpecifierLoc) {
+ MatchVerifier<NestedNameSpecifierLoc> Verifier;
+ EXPECT_TRUE(Verifier.match("namespace a { class b {}; } void f(a::b) {}",
+ nestedNameSpecifierLoc(hasParent(typeLoc()))));
+}
+
+TEST(GetParents, ReturnsParentInsideTemplateInstantiations) {
+ MatchVerifier<Decl> DeclVerifier;
+ EXPECT_TRUE(DeclVerifier.match(
+ "template<typename T> struct C { void f() {} };"
+ "void g() { C<int> c; c.f(); }",
+ cxxMethodDecl(hasName("f"),
+ hasParent(cxxRecordDecl(isTemplateInstantiation())))));
+ EXPECT_TRUE(DeclVerifier.match(
+ "template<typename T> struct C { void f() {} };"
+ "void g() { C<int> c; c.f(); }",
+ cxxMethodDecl(hasName("f"),
+ hasParent(cxxRecordDecl(unless(isTemplateInstantiation()))))));
+ EXPECT_FALSE(DeclVerifier.match(
+ "template<typename T> struct C { void f() {} };"
+ "void g() { C<int> c; c.f(); }",
+ cxxMethodDecl(
+ hasName("f"),
+ allOf(hasParent(cxxRecordDecl(unless(isTemplateInstantiation()))),
+ hasParent(cxxRecordDecl(isTemplateInstantiation()))))));
+}
+
+TEST(GetParents, ReturnsMultipleParentsInTemplateInstantiations) {
+ MatchVerifier<Stmt> TemplateVerifier;
+ EXPECT_TRUE(TemplateVerifier.match(
+ "template<typename T> struct C { void f() {} };"
+ "void g() { C<int> c; c.f(); }",
+ compoundStmt(allOf(
+ hasAncestor(cxxRecordDecl(isTemplateInstantiation())),
+ hasAncestor(cxxRecordDecl(unless(isTemplateInstantiation())))))));
+}
+
+TEST(GetParents, RespectsTraversalScope) {
+ auto AST =
+ tooling::buildASTFromCode("struct foo { int bar; };", "foo.cpp",
+ std::make_shared<PCHContainerOperations>());
+ auto &Ctx = AST->getASTContext();
+ auto &TU = *Ctx.getTranslationUnitDecl();
+ auto &Foo = *TU.lookup(&Ctx.Idents.get("foo")).front();
+ auto &Bar = *cast<DeclContext>(Foo).lookup(&Ctx.Idents.get("bar")).front();
+
+ using ast_type_traits::DynTypedNode;
+ // Initially, scope is the whole TU.
+ EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo)));
+ EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU)));
+
+ // Restrict the scope, now some parents are gone.
+ Ctx.setTraversalScope({&Foo});
+ EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo)));
+ EXPECT_THAT(Ctx.getParents(Foo), ElementsAre());
+
+ // Reset the scope, we get back the original results.
+ Ctx.setTraversalScope({&TU});
+ EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo)));
+ EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU)));
+}
+
+TEST(GetParents, ImplicitLambdaNodes) {
+ MatchVerifier<Decl> LambdaVerifier;
+ EXPECT_TRUE(LambdaVerifier.match(
+ "auto x = []{int y;};",
+ varDecl(hasName("y"), hasAncestor(functionDecl(
+ hasOverloadedOperatorName("()"),
+ hasParent(cxxRecordDecl(
+ isImplicit(), hasParent(lambdaExpr())))))),
+ Lang_CXX11));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTImporterFixtures.cpp b/gnu/llvm/clang/unittests/AST/ASTImporterFixtures.cpp
new file mode 100644
index 00000000000..80fcd1f1fc1
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTImporterFixtures.cpp
@@ -0,0 +1,237 @@
+//===- unittest/AST/ASTImporterFixtures.cpp - AST unit test support -------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Implementation of fixture classes for testing the ASTImporter.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTImporterFixtures.h"
+
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterSharedState.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Tooling.h"
+
+namespace clang {
+namespace ast_matchers {
+
+void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {
+ assert(ToAST);
+ ASTContext &ToCtx = ToAST->getASTContext();
+ auto *OFS = static_cast<llvm::vfs::OverlayFileSystem *>(
+ &ToCtx.getSourceManager().getFileManager().getVirtualFileSystem());
+ auto *MFS = static_cast<llvm::vfs::InMemoryFileSystem *>(
+ OFS->overlays_begin()->get());
+ MFS->addFile(FileName, 0, std::move(Buffer));
+}
+
+void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ StringRef Code) {
+ return createVirtualFileIfNeeded(ToAST, FileName,
+ llvm::MemoryBuffer::getMemBuffer(Code));
+}
+
+ASTImporterTestBase::TU::TU(StringRef Code, StringRef FileName, ArgVector Args,
+ ImporterConstructor C,
+ ASTImporter::ODRHandlingType ODRHandling)
+ : Code(Code), FileName(FileName),
+ Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, this->FileName)),
+ TUDecl(Unit->getASTContext().getTranslationUnitDecl()), Creator(C),
+ ODRHandling(ODRHandling) {
+ Unit->enableSourceFileDiagnostics();
+
+ // If the test doesn't need a specific ASTImporter, we just create a
+ // normal ASTImporter with it.
+ if (!Creator)
+ Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
+ ASTContext &FromContext, FileManager &FromFileManager,
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
+ return new ASTImporter(ToContext, ToFileManager, FromContext,
+ FromFileManager, MinimalImport, SharedState);
+ };
+}
+
+ASTImporterTestBase::TU::~TU() {}
+
+void ASTImporterTestBase::TU::lazyInitImporter(
+ const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST) {
+ assert(ToAST);
+ if (!Importer) {
+ Importer.reset(Creator(ToAST->getASTContext(), ToAST->getFileManager(),
+ Unit->getASTContext(), Unit->getFileManager(), false,
+ SharedState));
+ Importer->setODRHandling(ODRHandling);
+ }
+ assert(&ToAST->getASTContext() == &Importer->getToContext());
+ createVirtualFileIfNeeded(ToAST, FileName, Code);
+}
+
+Decl *ASTImporterTestBase::TU::import(
+ const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
+ Decl *FromDecl) {
+ lazyInitImporter(SharedState, ToAST);
+ if (auto ImportedOrErr = Importer->Import(FromDecl))
+ return *ImportedOrErr;
+ else {
+ llvm::consumeError(ImportedOrErr.takeError());
+ return nullptr;
+ }
+}
+
+llvm::Expected<Decl *> ASTImporterTestBase::TU::importOrError(
+ const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
+ Decl *FromDecl) {
+ lazyInitImporter(SharedState, ToAST);
+ return Importer->Import(FromDecl);
+}
+
+QualType ASTImporterTestBase::TU::import(
+ const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
+ QualType FromType) {
+ lazyInitImporter(SharedState, ToAST);
+ if (auto ImportedOrErr = Importer->Import(FromType))
+ return *ImportedOrErr;
+ else {
+ llvm::consumeError(ImportedOrErr.takeError());
+ return QualType{};
+ }
+}
+
+void ASTImporterTestBase::lazyInitSharedState(TranslationUnitDecl *ToTU) {
+ assert(ToTU);
+ if (!SharedStatePtr)
+ SharedStatePtr = std::make_shared<ASTImporterSharedState>(*ToTU);
+}
+
+void ASTImporterTestBase::lazyInitToAST(Language ToLang, StringRef ToSrcCode,
+ StringRef FileName) {
+ if (ToAST)
+ return;
+ ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+ // Source code must be a valid live buffer through the tests lifetime.
+ ToCode = ToSrcCode;
+ // Build the AST from an empty file.
+ ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);
+ ToAST->enableSourceFileDiagnostics();
+ lazyInitSharedState(ToAST->getASTContext().getTranslationUnitDecl());
+}
+
+ASTImporterTestBase::TU *ASTImporterTestBase::findFromTU(Decl *From) {
+ // Create a virtual file in the To Ctx which corresponds to the file from
+ // which we want to import the `From` Decl. Without this source locations
+ // will be invalid in the ToCtx.
+ auto It = llvm::find_if(FromTUs, [From](const TU &E) {
+ return E.TUDecl == From->getTranslationUnitDecl();
+ });
+ assert(It != FromTUs.end());
+ return &*It;
+}
+
+std::tuple<Decl *, Decl *>
+ASTImporterTestBase::getImportedDecl(StringRef FromSrcCode, Language FromLang,
+ StringRef ToSrcCode, Language ToLang,
+ StringRef Identifier) {
+ ArgVector FromArgs = getArgVectorForLanguage(FromLang),
+ ToArgs = getArgVectorForLanguage(ToLang);
+
+ FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Creator,
+ ODRHandling);
+ TU &FromTU = FromTUs.back();
+
+ assert(!ToAST);
+ lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
+
+ ASTContext &FromCtx = FromTU.Unit->getASTContext();
+
+ IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
+ assert(ImportedII && "Declaration with the given identifier "
+ "should be specified in test!");
+ DeclarationName ImportDeclName(ImportedII);
+ SmallVector<NamedDecl *, 1> FoundDecls;
+ FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
+ FoundDecls);
+
+ assert(FoundDecls.size() == 1);
+
+ Decl *Imported =
+ FromTU.import(SharedStatePtr, ToAST.get(), FoundDecls.front());
+
+ assert(Imported);
+ return std::make_tuple(*FoundDecls.begin(), Imported);
+}
+
+TranslationUnitDecl *ASTImporterTestBase::getTuDecl(StringRef SrcCode,
+ Language Lang,
+ StringRef FileName) {
+ assert(llvm::find_if(FromTUs, [FileName](const TU &E) {
+ return E.FileName == FileName;
+ }) == FromTUs.end());
+
+ ArgVector Args = getArgVectorForLanguage(Lang);
+ FromTUs.emplace_back(SrcCode, FileName, Args, Creator, ODRHandling);
+ TU &Tu = FromTUs.back();
+
+ return Tu.TUDecl;
+}
+
+TranslationUnitDecl *ASTImporterTestBase::getToTuDecl(StringRef ToSrcCode,
+ Language ToLang) {
+ ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+ assert(!ToAST);
+ lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
+ return ToAST->getASTContext().getTranslationUnitDecl();
+}
+
+Decl *ASTImporterTestBase::Import(Decl *From, Language ToLang) {
+ lazyInitToAST(ToLang, "", OutputFileName);
+ TU *FromTU = findFromTU(From);
+ assert(SharedStatePtr);
+ Decl *To = FromTU->import(SharedStatePtr, ToAST.get(), From);
+ return To;
+}
+
+llvm::Expected<Decl *> ASTImporterTestBase::importOrError(Decl *From,
+ Language ToLang) {
+ lazyInitToAST(ToLang, "", OutputFileName);
+ TU *FromTU = findFromTU(From);
+ assert(SharedStatePtr);
+ llvm::Expected<Decl *> To =
+ FromTU->importOrError(SharedStatePtr, ToAST.get(), From);
+ return To;
+}
+
+QualType ASTImporterTestBase::ImportType(QualType FromType, Decl *TUDecl,
+ Language ToLang) {
+ lazyInitToAST(ToLang, "", OutputFileName);
+ TU *FromTU = findFromTU(TUDecl);
+ assert(SharedStatePtr);
+ return FromTU->import(SharedStatePtr, ToAST.get(), FromType);
+}
+
+ASTImporterTestBase::~ASTImporterTestBase() {
+ if (!::testing::Test::HasFailure())
+ return;
+
+ for (auto &Tu : FromTUs) {
+ assert(Tu.Unit);
+ llvm::errs() << "FromAST:\n";
+ Tu.Unit->getASTContext().getTranslationUnitDecl()->dump();
+ llvm::errs() << "\n";
+ }
+ if (ToAST) {
+ llvm::errs() << "ToAST:\n";
+ ToAST->getASTContext().getTranslationUnitDecl()->dump();
+ }
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTImporterFixtures.h b/gnu/llvm/clang/unittests/AST/ASTImporterFixtures.h
new file mode 100644
index 00000000000..34cef16712d
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTImporterFixtures.h
@@ -0,0 +1,239 @@
+//===- unittest/AST/ASTImporterFixtures.h - AST unit test support ---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Fixture classes for testing the ASTImporter.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
+#define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
+
+#include "gmock/gmock.h"
+
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterSharedState.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include "DeclMatcher.h"
+#include "Language.h"
+
+#include <sstream>
+
+namespace clang {
+
+class ASTImporter;
+class ASTImporterSharedState;
+class ASTUnit;
+
+namespace ast_matchers {
+
+const StringRef DeclToImportID = "declToImport";
+const StringRef DeclToVerifyID = "declToVerify";
+
+// Creates a virtual file and assigns that to the context of given AST. If the
+// file already exists then the file will not be created again as a duplicate.
+void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ std::unique_ptr<llvm::MemoryBuffer> &&Buffer);
+
+void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ StringRef Code);
+
+// Common base for the different families of ASTImporter tests that are
+// parameterized on the compiler options which may result a different AST. E.g.
+// -fms-compatibility or -fdelayed-template-parsing.
+class CompilerOptionSpecificTest : public ::testing::Test {
+protected:
+ // Return the extra arguments appended to runtime options at compilation.
+ virtual ArgVector getExtraArgs() const { return ArgVector(); }
+
+ // Returns the argument vector used for a specific language option, this set
+ // can be tweaked by the test parameters.
+ ArgVector getArgVectorForLanguage(Language Lang) const {
+ ArgVector Args = getBasicRunOptionsForLanguage(Lang);
+ ArgVector ExtraArgs = getExtraArgs();
+ for (const auto &Arg : ExtraArgs) {
+ Args.push_back(Arg);
+ }
+ return Args;
+ }
+};
+
+const auto DefaultTestValuesForRunOptions = ::testing::Values(
+ ArgVector(), ArgVector{"-fdelayed-template-parsing"},
+ ArgVector{"-fms-compatibility"},
+ ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"});
+
+// This class provides generic methods to write tests which can check internal
+// attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,
+// this fixture makes it possible to import from several "From" contexts.
+class ASTImporterTestBase : public CompilerOptionSpecificTest {
+
+ const char *const InputFileName = "input.cc";
+ const char *const OutputFileName = "output.cc";
+
+public:
+ /// Allocates an ASTImporter (or one of its subclasses).
+ typedef std::function<ASTImporter *(
+ ASTContext &, FileManager &, ASTContext &, FileManager &, bool,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState)>
+ ImporterConstructor;
+
+ // ODR handling type for the AST importer.
+ ASTImporter::ODRHandlingType ODRHandling;
+
+ // The lambda that constructs the ASTImporter we use in this test.
+ ImporterConstructor Creator;
+
+private:
+ // Buffer for the To context, must live in the test scope.
+ std::string ToCode;
+
+ // Represents a "From" translation unit and holds an importer object which we
+ // use to import from this translation unit.
+ struct TU {
+ // Buffer for the context, must live in the test scope.
+ std::string Code;
+ std::string FileName;
+ std::unique_ptr<ASTUnit> Unit;
+ TranslationUnitDecl *TUDecl = nullptr;
+ std::unique_ptr<ASTImporter> Importer;
+ ImporterConstructor Creator;
+ ASTImporter::ODRHandlingType ODRHandling;
+
+ TU(StringRef Code, StringRef FileName, ArgVector Args,
+ ImporterConstructor C = ImporterConstructor(),
+ ASTImporter::ODRHandlingType ODRHandling =
+ ASTImporter::ODRHandlingType::Conservative);
+ ~TU();
+
+ void
+ lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST);
+ Decl *import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST, Decl *FromDecl);
+ llvm::Expected<Decl *>
+ importOrError(const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST, Decl *FromDecl);
+ QualType import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST, QualType FromType);
+ };
+
+ // We may have several From contexts and related translation units. In each
+ // AST, the buffers for the source are handled via references and are set
+ // during the creation of the AST. These references must point to a valid
+ // buffer until the AST is alive. Thus, we must use a list in order to avoid
+ // moving of the stored objects because that would mean breaking the
+ // references in the AST. By using a vector a move could happen when the
+ // vector is expanding, with the list we won't have these issues.
+ std::list<TU> FromTUs;
+
+ // Initialize the shared state if not initialized already.
+ void lazyInitSharedState(TranslationUnitDecl *ToTU);
+
+ void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName);
+
+protected:
+ std::shared_ptr<ASTImporterSharedState> SharedStatePtr;
+
+public:
+ // We may have several From context but only one To context.
+ std::unique_ptr<ASTUnit> ToAST;
+
+ // Returns with the TU associated with the given Decl.
+ TU *findFromTU(Decl *From);
+
+ // Creates an AST both for the From and To source code and imports the Decl
+ // of the identifier into the To context.
+ // Must not be called more than once within the same test.
+ std::tuple<Decl *, Decl *>
+ getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode,
+ Language ToLang, StringRef Identifier = DeclToImportID);
+
+ // Creates a TU decl for the given source code which can be used as a From
+ // context. May be called several times in a given test (with different file
+ // name).
+ TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang,
+ StringRef FileName = "input.cc");
+
+ // Creates the To context with the given source code and returns the TU decl.
+ TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang);
+
+ // Import the given Decl into the ToCtx.
+ // May be called several times in a given test.
+ // The different instances of the param From may have different ASTContext.
+ Decl *Import(Decl *From, Language ToLang);
+
+ template <class DeclT> DeclT *Import(DeclT *From, Language Lang) {
+ return cast_or_null<DeclT>(Import(cast<Decl>(From), Lang));
+ }
+
+ // Import the given Decl into the ToCtx.
+ // Same as Import but returns the result of the import which can be an error.
+ llvm::Expected<Decl *> importOrError(Decl *From, Language ToLang);
+
+ QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang);
+
+ ASTImporterTestBase()
+ : ODRHandling(ASTImporter::ODRHandlingType::Conservative) {}
+ ~ASTImporterTestBase();
+};
+
+class ASTImporterOptionSpecificTestBase
+ : public ASTImporterTestBase,
+ public ::testing::WithParamInterface<ArgVector> {
+protected:
+ ArgVector getExtraArgs() const override { return GetParam(); }
+};
+
+template <class T>
+::testing::AssertionResult isSuccess(llvm::Expected<T> &ValOrErr) {
+ if (ValOrErr)
+ return ::testing::AssertionSuccess() << "Expected<> contains no error.";
+ else
+ return ::testing::AssertionFailure()
+ << "Expected<> contains error: " << toString(ValOrErr.takeError());
+}
+
+template <class T>
+::testing::AssertionResult isImportError(llvm::Expected<T> &ValOrErr,
+ ImportError::ErrorKind Kind) {
+ if (ValOrErr) {
+ return ::testing::AssertionFailure() << "Expected<> is expected to contain "
+ "error but does contain value \""
+ << (*ValOrErr) << "\"";
+ } else {
+ std::ostringstream OS;
+ bool Result = false;
+ auto Err = llvm::handleErrors(
+ ValOrErr.takeError(), [&OS, &Result, Kind](clang::ImportError &IE) {
+ if (IE.Error == Kind) {
+ Result = true;
+ OS << "Expected<> contains an ImportError " << IE.toString();
+ } else {
+ OS << "Expected<> contains an ImportError " << IE.toString()
+ << " instead of kind " << Kind;
+ }
+ });
+ if (Err) {
+ OS << "Expected<> contains unexpected error: "
+ << toString(std::move(Err));
+ }
+ if (Result)
+ return ::testing::AssertionSuccess() << OS.str();
+ else
+ return ::testing::AssertionFailure() << OS.str();
+ }
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif
diff --git a/gnu/llvm/clang/unittests/AST/ASTImporterGenericRedeclTest.cpp b/gnu/llvm/clang/unittests/AST/ASTImporterGenericRedeclTest.cpp
new file mode 100644
index 00000000000..0f994c10734
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTImporterGenericRedeclTest.cpp
@@ -0,0 +1,577 @@
+//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Type-parameterized tests for the correct import of redecl chains.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTImporterFixtures.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using internal::BindableMatcher;
+
+struct Function {
+ using DeclTy = FunctionDecl;
+ static constexpr auto *Prototype = "void X();";
+ static constexpr auto *Definition = "void X() {}";
+ BindableMatcher<Decl> getPattern() {
+ return functionDecl(hasName("X"), unless(isImplicit()));
+ }
+};
+
+struct Class {
+ using DeclTy = CXXRecordDecl;
+ static constexpr auto *Prototype = "class X;";
+ static constexpr auto *Definition = "class X {};";
+ BindableMatcher<Decl> getPattern() {
+ return cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ }
+};
+
+struct Variable {
+ using DeclTy = VarDecl;
+ static constexpr auto *Prototype = "extern int X;";
+ static constexpr auto *Definition = "int X;";
+ BindableMatcher<Decl> getPattern() { return varDecl(hasName("X")); }
+};
+
+struct FunctionTemplate {
+ using DeclTy = FunctionTemplateDecl;
+ static constexpr auto *Prototype = "template <class T> void X();";
+ static constexpr auto *Definition =
+ R"(
+ template <class T> void X() {};
+ // Explicit instantiation is a must because of -fdelayed-template-parsing:
+ template void X<int>();
+ )";
+ BindableMatcher<Decl> getPattern() {
+ return functionTemplateDecl(hasName("X"), unless(isImplicit()));
+ }
+};
+
+struct ClassTemplate {
+ using DeclTy = ClassTemplateDecl;
+ static constexpr auto *Prototype = "template <class T> class X;";
+ static constexpr auto *Definition = "template <class T> class X {};";
+ BindableMatcher<Decl> getPattern() {
+ return classTemplateDecl(hasName("X"), unless(isImplicit()));
+ }
+};
+
+struct FunctionTemplateSpec {
+ using DeclTy = FunctionDecl;
+ static constexpr auto *Prototype =
+ R"(
+ // Proto of the primary template.
+ template <class T>
+ void X();
+ // Proto of the specialization.
+ template <>
+ void X<int>();
+ )";
+ static constexpr auto *Definition =
+ R"(
+ // Proto of the primary template.
+ template <class T>
+ void X();
+ // Specialization and definition.
+ template <>
+ void X<int>() {}
+ )";
+ BindableMatcher<Decl> getPattern() {
+ return functionDecl(hasName("X"), isExplicitTemplateSpecialization());
+ }
+};
+
+struct ClassTemplateSpec {
+ using DeclTy = ClassTemplateSpecializationDecl;
+ static constexpr auto *Prototype =
+ R"(
+ template <class T> class X;
+ template <> class X<int>;
+ )";
+ static constexpr auto *Definition =
+ R"(
+ template <class T> class X;
+ template <> class X<int> {};
+ )";
+ BindableMatcher<Decl> getPattern() {
+ return classTemplateSpecializationDecl(hasName("X"), unless(isImplicit()));
+ }
+};
+
+template <typename TypeParam>
+struct RedeclChain : ASTImporterOptionSpecificTestBase {
+
+ using DeclTy = typename TypeParam::DeclTy;
+ std::string getPrototype() { return TypeParam::Prototype; }
+ std::string getDefinition() { return TypeParam::Definition; }
+ BindableMatcher<Decl> getPattern() const { return TypeParam().getPattern(); }
+
+ void CheckPreviousDecl(Decl *Prev, Decl *Current) {
+ ASSERT_NE(Prev, Current);
+ ASSERT_EQ(&Prev->getASTContext(), &Current->getASTContext());
+ EXPECT_EQ(Prev->getCanonicalDecl(), Current->getCanonicalDecl());
+
+ // Templates.
+ if (auto *PrevT = dyn_cast<TemplateDecl>(Prev)) {
+ EXPECT_EQ(Current->getPreviousDecl(), Prev);
+ auto *CurrentT = cast<TemplateDecl>(Current);
+ ASSERT_TRUE(PrevT->getTemplatedDecl());
+ ASSERT_TRUE(CurrentT->getTemplatedDecl());
+ EXPECT_EQ(CurrentT->getTemplatedDecl()->getPreviousDecl(),
+ PrevT->getTemplatedDecl());
+ return;
+ }
+
+ // Specializations.
+ if (auto *PrevF = dyn_cast<FunctionDecl>(Prev)) {
+ if (PrevF->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization) {
+ // There may be a hidden fwd spec decl before a spec decl.
+ // In that case the previous visible decl can be reached through that
+ // invisible one.
+ EXPECT_THAT(Prev, testing::AnyOf(
+ Current->getPreviousDecl(),
+ Current->getPreviousDecl()->getPreviousDecl()));
+ auto *ToTU = Prev->getTranslationUnitDecl();
+ auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ ToTU, functionTemplateDecl());
+ auto *FirstSpecD = *(TemplateD->spec_begin());
+ EXPECT_EQ(FirstSpecD->getCanonicalDecl(), PrevF->getCanonicalDecl());
+ return;
+ }
+ }
+
+ // The rest: Classes, Functions, etc.
+ EXPECT_EQ(Current->getPreviousDecl(), Prev);
+ }
+
+ void
+ TypedTest_PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition() {
+ Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX);
+ auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ ASSERT_FALSE(FromD->isThisDeclarationADefinition());
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u);
+ auto *ToD = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(ImportedD == ToD);
+ EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+ if (auto *ToT = dyn_cast<TemplateDecl>(ToD)) {
+ EXPECT_TRUE(ToT->getTemplatedDecl());
+ }
+ }
+
+ void TypedTest_DefinitionShouldBeImportedAsADefinition() {
+ Decl *FromTU = getTuDecl(getDefinition(), Lang_CXX);
+ auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ ASSERT_TRUE(FromD->isThisDeclarationADefinition());
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u);
+ auto *ToD = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(ToD->isThisDeclarationADefinition());
+ if (auto *ToT = dyn_cast<TemplateDecl>(ToD)) {
+ EXPECT_TRUE(ToT->getTemplatedDecl());
+ }
+ }
+
+ void TypedTest_ImportPrototypeAfterImportedPrototype() {
+ Decl *FromTU = getTuDecl(getPrototype() + getPrototype(), Lang_CXX);
+ auto *From0 = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ auto *From1 = LastDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ ASSERT_FALSE(From0->isThisDeclarationADefinition());
+ ASSERT_FALSE(From1->isThisDeclarationADefinition());
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+ auto *To0 = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ auto *To1 = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+
+ CheckPreviousDecl(To0, To1);
+ }
+
+ void TypedTest_ImportDefinitionAfterImportedPrototype() {
+ Decl *FromTU = getTuDecl(getPrototype() + getDefinition(), Lang_CXX);
+ auto *FromProto = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ auto *FromDef = LastDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ ASSERT_FALSE(FromProto->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDef->isThisDeclarationADefinition());
+
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ToTU = ImportedProto->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+ auto *ToProto = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ auto *ToDef = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+
+ CheckPreviousDecl(ToProto, ToDef);
+ }
+
+ void TypedTest_ImportPrototypeAfterImportedDefinition() {
+ Decl *FromTU = getTuDecl(getDefinition() + getPrototype(), Lang_CXX);
+ auto *FromDef = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ auto *FromProto = LastDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ ASSERT_TRUE(FromDef->isThisDeclarationADefinition());
+ ASSERT_FALSE(FromProto->isThisDeclarationADefinition());
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+ auto *ToDef = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ auto *ToProto = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+
+ CheckPreviousDecl(ToDef, ToProto);
+ }
+
+ void TypedTest_ImportPrototypes() {
+ Decl *FromTU0 = getTuDecl(getPrototype(), Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl(getPrototype(), Lang_CXX, "input1.cc");
+ auto *From0 = FirstDeclMatcher<DeclTy>().match(FromTU0, getPattern());
+ auto *From1 = FirstDeclMatcher<DeclTy>().match(FromTU1, getPattern());
+ ASSERT_FALSE(From0->isThisDeclarationADefinition());
+ ASSERT_FALSE(From1->isThisDeclarationADefinition());
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+ auto *To0 = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ auto *To1 = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+
+ CheckPreviousDecl(To0, To1);
+ }
+
+ void TypedTest_ImportDefinitions() {
+ Decl *FromTU0 = getTuDecl(getDefinition(), Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl(getDefinition(), Lang_CXX, "input1.cc");
+ auto *From0 = FirstDeclMatcher<DeclTy>().match(FromTU0, getPattern());
+ auto *From1 = FirstDeclMatcher<DeclTy>().match(FromTU1, getPattern());
+ ASSERT_TRUE(From0->isThisDeclarationADefinition());
+ ASSERT_TRUE(From1->isThisDeclarationADefinition());
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(Imported0, Imported1);
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u);
+ auto *To0 = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(To0->isThisDeclarationADefinition());
+ if (auto *ToT0 = dyn_cast<TemplateDecl>(To0)) {
+ EXPECT_TRUE(ToT0->getTemplatedDecl());
+ }
+ }
+
+ void TypedTest_ImportDefinitionThenPrototype() {
+ Decl *FromTUDef = getTuDecl(getDefinition(), Lang_CXX, "input0.cc");
+ Decl *FromTUProto = getTuDecl(getPrototype(), Lang_CXX, "input1.cc");
+ auto *FromDef = FirstDeclMatcher<DeclTy>().match(FromTUDef, getPattern());
+ auto *FromProto =
+ FirstDeclMatcher<DeclTy>().match(FromTUProto, getPattern());
+ ASSERT_TRUE(FromDef->isThisDeclarationADefinition());
+ ASSERT_FALSE(FromProto->isThisDeclarationADefinition());
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+ auto *ToDef = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ auto *ToProto = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+
+ CheckPreviousDecl(ToDef, ToProto);
+ }
+
+ void TypedTest_ImportPrototypeThenDefinition() {
+ Decl *FromTUProto = getTuDecl(getPrototype(), Lang_CXX, "input0.cc");
+ Decl *FromTUDef = getTuDecl(getDefinition(), Lang_CXX, "input1.cc");
+ auto *FromProto =
+ FirstDeclMatcher<DeclTy>().match(FromTUProto, getPattern());
+ auto *FromDef = FirstDeclMatcher<DeclTy>().match(FromTUDef, getPattern());
+ ASSERT_TRUE(FromDef->isThisDeclarationADefinition());
+ ASSERT_FALSE(FromProto->isThisDeclarationADefinition());
+
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+ auto *ToProto = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ auto *ToDef = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+
+ CheckPreviousDecl(ToProto, ToDef);
+ }
+
+ void TypedTest_WholeRedeclChainIsImportedAtOnce() {
+ Decl *FromTU = getTuDecl(getPrototype() + getDefinition(), Lang_CXX);
+ auto *FromD = // Definition
+ LastDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ ASSERT_TRUE(FromD->isThisDeclarationADefinition());
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ // The whole redecl chain is imported at once.
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+ EXPECT_TRUE(cast<DeclTy>(ImportedD)->isThisDeclarationADefinition());
+ }
+
+ void TypedTest_ImportPrototypeThenProtoAndDefinition() {
+ {
+ Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU =
+ getTuDecl(getPrototype() + getDefinition(), Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+ Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ ASSERT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 3u);
+ DeclTy *ProtoD = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_FALSE(ProtoD->isThisDeclarationADefinition());
+
+ DeclTy *DefinitionD = LastDeclMatcher<DeclTy>().match(ToTU, getPattern());
+ EXPECT_TRUE(DefinitionD->isThisDeclarationADefinition());
+
+ EXPECT_TRUE(DefinitionD->getPreviousDecl());
+ EXPECT_FALSE(
+ DefinitionD->getPreviousDecl()->isThisDeclarationADefinition());
+
+ CheckPreviousDecl(ProtoD, DefinitionD->getPreviousDecl());
+ }
+};
+
+#define ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(BaseTemplate, TypeParam, \
+ NamePrefix, TestCase) \
+ using BaseTemplate##TypeParam = BaseTemplate<TypeParam>; \
+ TEST_P(BaseTemplate##TypeParam, NamePrefix##TestCase) { \
+ TypedTest_##TestCase(); \
+ }
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(
+ RedeclChain, Function, ,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(
+ RedeclChain, Class, ,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(
+ RedeclChain, Variable, ,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(
+ RedeclChain, FunctionTemplate, ,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(
+ RedeclChain, ClassTemplate, ,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(
+ RedeclChain, FunctionTemplateSpec, ,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(
+ RedeclChain, ClassTemplateSpec, ,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ DefinitionShouldBeImportedAsADefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, ,
+ DefinitionShouldBeImportedAsADefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ DefinitionShouldBeImportedAsADefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ DefinitionShouldBeImportedAsADefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ DefinitionShouldBeImportedAsADefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ DefinitionShouldBeImportedAsADefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ DefinitionShouldBeImportedAsADefinition)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportPrototypeAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, ,
+ ImportPrototypeAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportPrototypeAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportPrototypeAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ ImportPrototypeAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportPrototypeAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ ImportPrototypeAfterImportedPrototype)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportDefinitionAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, ,
+ ImportDefinitionAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportDefinitionAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportDefinitionAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ ImportDefinitionAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportDefinitionAfterImportedPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ ImportDefinitionAfterImportedPrototype)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportPrototypeAfterImportedDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, ,
+ ImportPrototypeAfterImportedDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportPrototypeAfterImportedDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportPrototypeAfterImportedDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ ImportPrototypeAfterImportedDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportPrototypeAfterImportedDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ ImportPrototypeAfterImportedDefinition)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportPrototypes)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , ImportPrototypes)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportPrototypes)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportPrototypes)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ ImportPrototypes)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ ImportPrototypes)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportPrototypes)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportDefinitions)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , ImportDefinitions)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportDefinitions)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportDefinitions)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ ImportDefinitions)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ ImportDefinitions)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportDefinitions)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportDefinitionThenPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, ,
+ ImportDefinitionThenPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportDefinitionThenPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportDefinitionThenPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ ImportDefinitionThenPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportDefinitionThenPrototype)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ ImportDefinitionThenPrototype)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportPrototypeThenDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, ,
+ ImportPrototypeThenDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportPrototypeThenDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportPrototypeThenDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, ,
+ ImportPrototypeThenDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportPrototypeThenDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, ,
+ ImportPrototypeThenDefinition)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ WholeRedeclChainIsImportedAtOnce)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ WholeRedeclChainIsImportedAtOnce)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ WholeRedeclChainIsImportedAtOnce)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ WholeRedeclChainIsImportedAtOnce)
+
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, ,
+ ImportPrototypeThenProtoAndDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, ,
+ ImportPrototypeThenProtoAndDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, ,
+ ImportPrototypeThenProtoAndDefinition)
+ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, ,
+ ImportPrototypeThenProtoAndDefinition)
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunction,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainClass,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainVariable,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunctionTemplate,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainClassTemplate,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunctionTemplateSpec,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainClassTemplateSpec,
+ DefaultTestValuesForRunOptions, );
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTImporterODRStrategiesTest.cpp b/gnu/llvm/clang/unittests/AST/ASTImporterODRStrategiesTest.cpp
new file mode 100644
index 00000000000..6a2aa2bfc32
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTImporterODRStrategiesTest.cpp
@@ -0,0 +1,676 @@
+//===- unittest/AST/ASTImporterODRStrategiesTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Type-parameterized tests to verify the import behaviour in case of ODR
+// violation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTImporterFixtures.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using internal::BindableMatcher;
+
+// DeclTy: Type of the Decl to check.
+// Prototype: "Prototype" (forward declaration) of the Decl.
+// Definition: A definition for the Prototype.
+// ConflictingPrototype: A prototype with the same name but different
+// declaration.
+// ConflictingDefinition: A different definition for Prototype.
+// ConflictingProtoDef: A definition for ConflictingPrototype.
+// getPattern: Return a matcher that matches any of Prototype, Definition,
+// ConflictingPrototype, ConflictingDefinition, ConflictingProtoDef.
+
+struct Function {
+ using DeclTy = FunctionDecl;
+ static constexpr auto *Prototype = "void X(int);";
+ static constexpr auto *ConflictingPrototype = "void X(double);";
+ static constexpr auto *Definition = "void X(int a) {}";
+ static constexpr auto *ConflictingDefinition = "void X(double a) {}";
+ BindableMatcher<Decl> getPattern() {
+ return functionDecl(hasName("X"), unless(isImplicit()));
+ }
+ Language getLang() { return Lang_C; }
+};
+
+struct Typedef {
+ using DeclTy = TypedefNameDecl;
+ static constexpr auto *Definition = "typedef int X;";
+ static constexpr auto *ConflictingDefinition = "typedef double X;";
+ BindableMatcher<Decl> getPattern() { return typedefNameDecl(hasName("X")); }
+ Language getLang() { return Lang_CXX; }
+};
+
+struct TypedefAlias {
+ using DeclTy = TypedefNameDecl;
+ static constexpr auto *Definition = "using X = int;";
+ static constexpr auto *ConflictingDefinition = "using X = double;";
+ BindableMatcher<Decl> getPattern() { return typedefNameDecl(hasName("X")); }
+ Language getLang() { return Lang_CXX11; }
+};
+
+struct Enum {
+ using DeclTy = EnumDecl;
+ static constexpr auto *Definition = "enum X { a, b };";
+ static constexpr auto *ConflictingDefinition = "enum X { a, b, c };";
+ BindableMatcher<Decl> getPattern() { return enumDecl(hasName("X")); }
+ Language getLang() { return Lang_CXX; }
+};
+
+struct EnumConstant {
+ using DeclTy = EnumConstantDecl;
+ static constexpr auto *Definition = "enum E { X = 0 };";
+ static constexpr auto *ConflictingDefinition = "enum E { X = 1 };";
+ BindableMatcher<Decl> getPattern() { return enumConstantDecl(hasName("X")); }
+ Language getLang() { return Lang_CXX; }
+};
+
+struct Class {
+ using DeclTy = CXXRecordDecl;
+ static constexpr auto *Prototype = "class X;";
+ static constexpr auto *Definition = "class X {};";
+ static constexpr auto *ConflictingDefinition = "class X { int A; };";
+ BindableMatcher<Decl> getPattern() {
+ return cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ }
+ Language getLang() { return Lang_CXX; }
+};
+
+struct Variable {
+ using DeclTy = VarDecl;
+ static constexpr auto *Prototype = "extern int X;";
+ static constexpr auto *ConflictingPrototype = "extern float X;";
+ static constexpr auto *Definition = "int X;";
+ static constexpr auto *ConflictingDefinition = "float X;";
+ BindableMatcher<Decl> getPattern() { return varDecl(hasName("X")); }
+ Language getLang() { return Lang_CXX; }
+};
+
+struct ClassTemplate {
+ using DeclTy = ClassTemplateDecl;
+ static constexpr auto *Prototype = "template <class> class X;";
+ static constexpr auto *ConflictingPrototype = "template <int> class X;";
+ static constexpr auto *Definition = "template <class> class X {};";
+ static constexpr auto *ConflictingDefinition =
+ "template <class> class X { int A; };";
+ static constexpr auto *ConflictingProtoDef = "template <int> class X { };";
+ BindableMatcher<Decl> getPattern() {
+ return classTemplateDecl(hasName("X"), unless(isImplicit()));
+ }
+ Language getLang() { return Lang_CXX; }
+};
+
+struct FunctionTemplate {
+ using DeclTy = FunctionTemplateDecl;
+ static constexpr auto *Definition0 =
+ R"(
+ template <class T>
+ void X(T a) {};
+ )";
+ // This is actually not a conflicting definition, but another primary template.
+ static constexpr auto *Definition1 =
+ R"(
+ template <class T>
+ void X(T* a) {};
+ )";
+ BindableMatcher<Decl> getPattern() {
+ return functionTemplateDecl(hasName("X"), unless(isImplicit()));
+ }
+ static std::string getDef0() { return Definition0; }
+ static std::string getDef1() { return Definition1; }
+ Language getLang() { return Lang_CXX; }
+};
+
+static const internal::VariadicDynCastAllOfMatcher<Decl, VarTemplateDecl>
+ varTemplateDecl;
+
+struct VarTemplate {
+ using DeclTy = VarTemplateDecl;
+ static constexpr auto *Definition =
+ R"(
+ template <class T>
+ constexpr T X = 0;
+ )";
+ static constexpr auto *ConflictingDefinition =
+ R"(
+ template <int>
+ constexpr int X = 0;
+ )";
+ BindableMatcher<Decl> getPattern() { return varTemplateDecl(hasName("X")); }
+ Language getLang() { return Lang_CXX14; }
+};
+
+struct ClassTemplateSpec {
+ using DeclTy = ClassTemplateSpecializationDecl;
+ static constexpr auto *Prototype =
+ R"(
+ template <class T> class X;
+ template <> class X<int>;
+ )";
+ static constexpr auto *Definition =
+ R"(
+ template <class T> class X;
+ template <> class X<int> {};
+ )";
+ static constexpr auto *ConflictingDefinition =
+ R"(
+ template <class T> class X;
+ template <> class X<int> { int A; };
+ )";
+ BindableMatcher<Decl> getPattern() {
+ return classTemplateSpecializationDecl(hasName("X"), unless(isImplicit()));
+ }
+ Language getLang() { return Lang_CXX; }
+};
+
+// Function template specializations are all "full" specializations.
+// Structural equivalency does not check the body of functions, so we cannot
+// create conflicting function template specializations.
+struct FunctionTemplateSpec {
+ using DeclTy = FunctionDecl;
+
+ static constexpr auto *Definition0 =
+ R"(
+ template <class T>
+ void X(T a);
+ template <> void X(int a) {};
+ )";
+
+ // This is actually not a conflicting definition, but another full
+ // specialization.
+ // Thus, during the import we would create a new specialization with a
+ // different type argument.
+ static constexpr auto *Definition1 =
+ R"(
+ template <class T>
+ void X(T a);
+ template <> void X(double a) {};
+ )";
+
+ BindableMatcher<Decl> getPattern() {
+ return functionDecl(hasName("X"), isExplicitTemplateSpecialization(),
+ unless(isImplicit()));
+ }
+ static std::string getDef0() { return Definition0; }
+ static std::string getDef1() { return Definition1; }
+ Language getLang() { return Lang_CXX; }
+};
+
+static const internal::VariadicDynCastAllOfMatcher<
+ Decl, VarTemplateSpecializationDecl>
+ varTemplateSpecializationDecl;
+
+struct VarTemplateSpec {
+ using DeclTy = VarTemplateSpecializationDecl;
+ static constexpr auto *Definition =
+ R"(
+ template <class T> T X = 0;
+ template <> int X<int> = 0;
+ )";
+ static constexpr auto *ConflictingDefinition =
+ R"(
+ template <class T> T X = 0;
+ template <> float X<int> = 1.0;
+ )";
+ BindableMatcher<Decl> getPattern() {
+ return varTemplateSpecializationDecl(hasName("X"), unless(isImplicit()));
+ }
+ Language getLang() { return Lang_CXX14; }
+};
+
+template <typename TypeParam, ASTImporter::ODRHandlingType ODRHandlingParam>
+struct ODRViolation : ASTImporterOptionSpecificTestBase {
+
+ using DeclTy = typename TypeParam::DeclTy;
+
+ ODRViolation() { ODRHandling = ODRHandlingParam; }
+
+ static std::string getPrototype() { return TypeParam::Prototype; }
+ static std::string getConflictingPrototype() {
+ return TypeParam::ConflictingPrototype;
+ }
+ static std::string getDefinition() { return TypeParam::Definition; }
+ static std::string getConflictingDefinition() {
+ return TypeParam::ConflictingDefinition;
+ }
+ static std::string getConflictingProtoDef() {
+ return TypeParam::ConflictingProtoDef;
+ }
+ static BindableMatcher<Decl> getPattern() { return TypeParam().getPattern(); }
+ static Language getLang() { return TypeParam().getLang(); }
+
+ template <std::string (*ToTUContent)(), std::string (*FromTUContent)(),
+ void (*ResultChecker)(llvm::Expected<Decl *> &, Decl *, Decl *)>
+ void TypedTest_ImportAfter() {
+ Decl *ToTU = getToTuDecl(ToTUContent(), getLang());
+ auto *ToD = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern());
+
+ Decl *FromTU = getTuDecl(FromTUContent(), getLang());
+ auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern());
+
+ auto Result = importOrError(FromD, getLang());
+
+ ResultChecker(Result, ToTU, ToD);
+ }
+
+ // Check that a Decl has been successfully imported into a standalone redecl
+ // chain.
+ static void CheckImportedAsNew(llvm::Expected<Decl *> &Result, Decl *ToTU,
+ Decl *ToD) {
+ ASSERT_TRUE(isSuccess(Result));
+ Decl *ImportedD = *Result;
+ ASSERT_TRUE(ImportedD);
+ EXPECT_NE(ImportedD, ToD);
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u);
+
+ // There may be a hidden fwd spec decl before a function spec decl.
+ if (auto *ImportedF = dyn_cast<FunctionDecl>(ImportedD))
+ if (ImportedF->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization)
+ return;
+
+ EXPECT_FALSE(ImportedD->getPreviousDecl());
+ }
+
+ // Check that a Decl was not imported because of NameConflict.
+ static void CheckImportNameConflict(llvm::Expected<Decl *> &Result,
+ Decl *ToTU, Decl *ToD) {
+ EXPECT_TRUE(isImportError(Result, ImportError::NameConflict));
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u);
+ }
+
+ // Check that a Decl was not imported because lookup found the same decl.
+ static void CheckImportFoundExisting(llvm::Expected<Decl *> &Result,
+ Decl *ToTU, Decl *ToD) {
+ ASSERT_TRUE(isSuccess(Result));
+ EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u);
+ }
+
+ void TypedTest_ImportConflictingDefAfterDef() {
+ TypedTest_ImportAfter<getDefinition, getConflictingDefinition,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_ImportConflictingProtoAfterProto() {
+ TypedTest_ImportAfter<getPrototype, getConflictingPrototype,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_ImportConflictingProtoAfterDef() {
+ TypedTest_ImportAfter<getDefinition, getConflictingPrototype,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_ImportConflictingDefAfterProto() {
+ TypedTest_ImportAfter<getConflictingPrototype, getDefinition,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_ImportConflictingProtoDefAfterProto() {
+ TypedTest_ImportAfter<getPrototype, getConflictingProtoDef,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_ImportConflictingProtoAfterProtoDef() {
+ TypedTest_ImportAfter<getConflictingProtoDef, getPrototype,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_ImportConflictingProtoDefAfterDef() {
+ TypedTest_ImportAfter<getDefinition, getConflictingProtoDef,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_ImportConflictingDefAfterProtoDef() {
+ TypedTest_ImportAfter<getConflictingProtoDef, getDefinition,
+ CheckImportedAsNew>();
+ }
+
+ void TypedTest_DontImportConflictingProtoAfterProto() {
+ TypedTest_ImportAfter<getPrototype, getConflictingPrototype,
+ CheckImportNameConflict>();
+ }
+ void TypedTest_DontImportConflictingDefAfterDef() {
+ TypedTest_ImportAfter<getDefinition, getConflictingDefinition,
+ CheckImportNameConflict>();
+ }
+ void TypedTest_DontImportConflictingProtoAfterDef() {
+ TypedTest_ImportAfter<getDefinition, getConflictingPrototype,
+ CheckImportNameConflict>();
+ }
+ void TypedTest_DontImportConflictingDefAfterProto() {
+ TypedTest_ImportAfter<getConflictingPrototype, getDefinition,
+ CheckImportNameConflict>();
+ }
+ void TypedTest_DontImportConflictingProtoDefAfterProto() {
+ TypedTest_ImportAfter<getPrototype, getConflictingProtoDef,
+ CheckImportNameConflict>();
+ }
+ void TypedTest_DontImportConflictingProtoAfterProtoDef() {
+ TypedTest_ImportAfter<getConflictingProtoDef, getPrototype,
+ CheckImportNameConflict>();
+ }
+ void TypedTest_DontImportConflictingProtoDefAfterDef() {
+ TypedTest_ImportAfter<getDefinition, getConflictingProtoDef,
+ CheckImportNameConflict>();
+ }
+ void TypedTest_DontImportConflictingDefAfterProtoDef() {
+ TypedTest_ImportAfter<getConflictingProtoDef, getDefinition,
+ CheckImportNameConflict>();
+ }
+
+ // Used for function templates and function template specializations.
+ void TypedTest_ImportDifferentDefAfterDef() {
+ TypedTest_ImportAfter<TypeParam::getDef0, TypeParam::getDef1,
+ CheckImportedAsNew>();
+ }
+ void TypedTest_DontImportSameDefAfterDef() {
+ TypedTest_ImportAfter<TypeParam::getDef0, TypeParam::getDef0,
+ CheckImportFoundExisting>();
+ }
+};
+
+// ==============================
+// Define the parametrized tests.
+// ==============================
+
+#define ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE( \
+ TypeParam, ODRHandlingParam, NamePrefix, TestCase) \
+ using TypeParam##ODRHandlingParam = \
+ ODRViolation<TypeParam, ASTImporter::ODRHandlingType::ODRHandlingParam>; \
+ TEST_P(TypeParam##ODRHandlingParam, NamePrefix##TestCase) { \
+ TypedTest_##TestCase(); \
+ }
+
+// clang-format off
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Function, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Typedef, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ TypedefAlias, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Enum, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ EnumConstant, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Class, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ VarTemplate, Liberal, ,
+ ImportConflictingDefAfterDef)
+// Class and variable template specializations/instantiatons are always
+// imported conservatively, because the AST holds the specializations in a set,
+// and the key within the set is a hash calculated from the arguments of the
+// specialization.
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplateSpec, Liberal, ,
+ DontImportConflictingDefAfterDef) // Don't import !!!
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ VarTemplateSpec, Liberal, ,
+ DontImportConflictingDefAfterDef) // Don't import !!!
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Function, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Typedef, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ TypedefAlias, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Enum, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ EnumConstant, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Class, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ VarTemplate, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplateSpec, Conservative, ,
+ DontImportConflictingDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ VarTemplateSpec, Conservative, ,
+ DontImportConflictingDefAfterDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Function, Liberal, ,
+ ImportConflictingProtoAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Liberal, ,
+ ImportConflictingProtoAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingProtoAfterProto)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Function, Conservative, ,
+ DontImportConflictingProtoAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Conservative, ,
+ DontImportConflictingProtoAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingProtoAfterProto)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Liberal, ,
+ ImportConflictingProtoAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingProtoAfterDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Conservative, ,
+ DontImportConflictingProtoAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingProtoAfterDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Function, Liberal, ,
+ ImportConflictingDefAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Liberal, ,
+ ImportConflictingDefAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingDefAfterProto)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Function, Conservative, ,
+ DontImportConflictingDefAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ Variable, Conservative, ,
+ DontImportConflictingDefAfterProto)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingDefAfterProto)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingProtoDefAfterProto)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingProtoDefAfterProto)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingProtoAfterProtoDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingProtoAfterProtoDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingProtoDefAfterDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingProtoDefAfterDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Liberal, ,
+ ImportConflictingDefAfterProtoDef)
+
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ ClassTemplate, Conservative, ,
+ DontImportConflictingDefAfterProtoDef)
+
+// FunctionTemplate decls overload with each other. Thus, they are imported
+// always as a new node, independently from any ODRHandling strategy.
+//
+// Function template specializations are "full" specializations. Structural
+// equivalency does not check the body of functions, so we cannot create
+// conflicting function template specializations. Thus, ODR handling strategies
+// has nothing to do with function template specializations. Fully specialized
+// function templates are imported as new nodes if their template arguments are
+// different.
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplate, Liberal, ,
+ ImportDifferentDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplateSpec, Liberal, ,
+ ImportDifferentDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplate, Conservative, ,
+ ImportDifferentDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplateSpec, Conservative, ,
+ ImportDifferentDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplate, Liberal, ,
+ DontImportSameDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplateSpec, Liberal, ,
+ DontImportSameDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplate, Conservative, ,
+ DontImportSameDefAfterDef)
+ASTIMPORTER_ODR_INSTANTIATE_TYPED_TEST_CASE(
+ FunctionTemplateSpec, Conservative, ,
+ DontImportSameDefAfterDef)
+
+// ======================
+// Instantiate the tests.
+// ======================
+
+// FIXME: These fail on Windows.
+#if !defined(_WIN32)
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, FunctionConservative,
+ DefaultTestValuesForRunOptions, );
+#endif
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, TypedefConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, TypedefAliasConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, EnumConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, EnumConstantConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, ClassConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, VariableConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, ClassTemplateConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, FunctionTemplateConservative,
+ DefaultTestValuesForRunOptions, );
+// FIXME: Make VarTemplate tests work.
+//INSTANTIATE_TEST_CASE_P(
+ //ODRViolationTests, VarTemplateConservative,
+ //DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, FunctionTemplateSpecConservative,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, ClassTemplateSpecConservative,
+ DefaultTestValuesForRunOptions, );
+// FIXME: Make VarTemplateSpec tests work.
+//INSTANTIATE_TEST_CASE_P(
+ //ODRViolationTests, VarTemplateSpecConservative,
+ //DefaultTestValuesForRunOptions, );
+
+// FIXME: These fail on Windows.
+#if !defined(_WIN32)
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, FunctionLiberal,
+ DefaultTestValuesForRunOptions, );
+#endif
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, TypedefLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, TypedefAliasLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, EnumLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, EnumConstantLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, ClassLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, VariableLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, ClassTemplateLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, FunctionTemplateLiberal,
+ DefaultTestValuesForRunOptions, );
+// FIXME: Make VarTemplate tests work.
+// INSTANTIATE_TEST_CASE_P(
+// ODRViolationTests, VarTemplateLiberal,
+// DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, ClassTemplateSpecLiberal,
+ DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(
+ ODRViolationTests, FunctionTemplateSpecLiberal,
+ DefaultTestValuesForRunOptions, );
+// FIXME: Make VarTemplateSpec tests work.
+//INSTANTIATE_TEST_CASE_P(
+ //ODRViolationTests, VarTemplateSpecLiberal,
+ //DefaultTestValuesForRunOptions, );
+
+// clang-format on
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTImporterTest.cpp b/gnu/llvm/clang/unittests/AST/ASTImporterTest.cpp
new file mode 100644
index 00000000000..3e8f804374f
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTImporterTest.cpp
@@ -0,0 +1,5907 @@
+//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for the correct import of AST nodes from one AST context to another.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/StringMap.h"
+
+#include "clang/AST/DeclContextInternals.h"
+#include "gtest/gtest.h"
+
+#include "ASTImporterFixtures.h"
+#include "MatchVerifier.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using internal::Matcher;
+using internal::BindableMatcher;
+using llvm::StringMap;
+
+// Base class for those tests which use the family of `testImport` functions.
+class TestImportBase : public CompilerOptionSpecificTest,
+ public ::testing::WithParamInterface<ArgVector> {
+
+ template <typename NodeType>
+ llvm::Expected<NodeType> importNode(ASTUnit *From, ASTUnit *To,
+ ASTImporter &Importer, NodeType Node) {
+ ASTContext &ToCtx = To->getASTContext();
+
+ // Add 'From' file to virtual file system so importer can 'find' it
+ // while importing SourceLocations. It is safe to add same file multiple
+ // times - it just isn't replaced.
+ StringRef FromFileName = From->getMainFileName();
+ createVirtualFileIfNeeded(To, FromFileName,
+ From->getBufferForFile(FromFileName));
+
+ auto Imported = Importer.Import(Node);
+
+ if (Imported) {
+ // This should dump source locations and assert if some source locations
+ // were not imported.
+ SmallString<1024> ImportChecker;
+ llvm::raw_svector_ostream ToNothing(ImportChecker);
+ ToCtx.getTranslationUnitDecl()->print(ToNothing);
+
+ // This traverses the AST to catch certain bugs like poorly or not
+ // implemented subtrees.
+ (*Imported)->dump(ToNothing);
+ }
+
+ return Imported;
+ }
+
+ template <typename NodeType>
+ testing::AssertionResult
+ testImport(const std::string &FromCode, const ArgVector &FromArgs,
+ const std::string &ToCode, const ArgVector &ToArgs,
+ MatchVerifier<NodeType> &Verifier,
+ const BindableMatcher<NodeType> &SearchMatcher,
+ const BindableMatcher<NodeType> &VerificationMatcher) {
+ const char *const InputFileName = "input.cc";
+ const char *const OutputFileName = "output.cc";
+
+ std::unique_ptr<ASTUnit> FromAST = tooling::buildASTFromCodeWithArgs(
+ FromCode, FromArgs, InputFileName),
+ ToAST = tooling::buildASTFromCodeWithArgs(
+ ToCode, ToArgs, OutputFileName);
+
+ ASTContext &FromCtx = FromAST->getASTContext(),
+ &ToCtx = ToAST->getASTContext();
+
+ ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx,
+ FromAST->getFileManager(), false);
+
+ auto FoundNodes = match(SearchMatcher, FromCtx);
+ if (FoundNodes.size() != 1)
+ return testing::AssertionFailure()
+ << "Multiple potential nodes were found!";
+
+ auto ToImport = selectFirst<NodeType>(DeclToImportID, FoundNodes);
+ if (!ToImport)
+ return testing::AssertionFailure() << "Node type mismatch!";
+
+ // Sanity check: the node being imported should match in the same way as
+ // the result node.
+ BindableMatcher<NodeType> WrapperMatcher(VerificationMatcher);
+ EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher));
+
+ auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport);
+ if (!Imported) {
+ std::string ErrorText;
+ handleAllErrors(
+ Imported.takeError(),
+ [&ErrorText](const ImportError &Err) { ErrorText = Err.message(); });
+ return testing::AssertionFailure()
+ << "Import failed, error: \"" << ErrorText << "\"!";
+ }
+
+ return Verifier.match(*Imported, WrapperMatcher);
+ }
+
+ template <typename NodeType>
+ testing::AssertionResult
+ testImport(const std::string &FromCode, const ArgVector &FromArgs,
+ const std::string &ToCode, const ArgVector &ToArgs,
+ MatchVerifier<NodeType> &Verifier,
+ const BindableMatcher<NodeType> &VerificationMatcher) {
+ return testImport(
+ FromCode, FromArgs, ToCode, ToArgs, Verifier,
+ translationUnitDecl(
+ has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))),
+ VerificationMatcher);
+ }
+
+protected:
+ ArgVector getExtraArgs() const override { return GetParam(); }
+
+public:
+
+ /// Test how AST node named "declToImport" located in the translation unit
+ /// of "FromCode" virtual file is imported to "ToCode" virtual file.
+ /// The verification is done by running AMatcher over the imported node.
+ template <typename NodeType, typename MatcherType>
+ void testImport(const std::string &FromCode, Language FromLang,
+ const std::string &ToCode, Language ToLang,
+ MatchVerifier<NodeType> &Verifier,
+ const MatcherType &AMatcher) {
+ ArgVector FromArgs = getArgVectorForLanguage(FromLang),
+ ToArgs = getArgVectorForLanguage(ToLang);
+ EXPECT_TRUE(
+ testImport(FromCode, FromArgs, ToCode, ToArgs, Verifier, AMatcher));
+ }
+
+ struct ImportAction {
+ StringRef FromFilename;
+ StringRef ToFilename;
+ // FIXME: Generalize this to support other node kinds.
+ BindableMatcher<Decl> ImportPredicate;
+
+ ImportAction(StringRef FromFilename, StringRef ToFilename,
+ DeclarationMatcher ImportPredicate)
+ : FromFilename(FromFilename), ToFilename(ToFilename),
+ ImportPredicate(ImportPredicate) {}
+
+ ImportAction(StringRef FromFilename, StringRef ToFilename,
+ const std::string &DeclName)
+ : FromFilename(FromFilename), ToFilename(ToFilename),
+ ImportPredicate(namedDecl(hasName(DeclName))) {}
+ };
+
+ using SingleASTUnit = std::unique_ptr<ASTUnit>;
+ using AllASTUnits = StringMap<SingleASTUnit>;
+
+ struct CodeEntry {
+ std::string CodeSample;
+ Language Lang;
+ };
+
+ using CodeFiles = StringMap<CodeEntry>;
+
+ /// Builds an ASTUnit for one potential compile options set.
+ SingleASTUnit createASTUnit(StringRef FileName, const CodeEntry &CE) const {
+ ArgVector Args = getArgVectorForLanguage(CE.Lang);
+ auto AST = tooling::buildASTFromCodeWithArgs(CE.CodeSample, Args, FileName);
+ EXPECT_TRUE(AST.get());
+ return AST;
+ }
+
+ /// Test an arbitrary sequence of imports for a set of given in-memory files.
+ /// The verification is done by running VerificationMatcher against a
+ /// specified AST node inside of one of given files.
+ /// \param CodeSamples Map whose key is the file name and the value is the
+ /// file content.
+ /// \param ImportActions Sequence of imports. Each import in sequence
+ /// specifies "from file" and "to file" and a matcher that is used for
+ /// searching a declaration for import in "from file".
+ /// \param FileForFinalCheck Name of virtual file for which the final check is
+ /// applied.
+ /// \param FinalSelectPredicate Matcher that specifies the AST node in the
+ /// FileForFinalCheck for which the verification will be done.
+ /// \param VerificationMatcher Matcher that will be used for verification
+ /// after all imports in sequence are done.
+ void testImportSequence(const CodeFiles &CodeSamples,
+ const std::vector<ImportAction> &ImportActions,
+ StringRef FileForFinalCheck,
+ BindableMatcher<Decl> FinalSelectPredicate,
+ BindableMatcher<Decl> VerificationMatcher) {
+ AllASTUnits AllASTs;
+ using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>;
+ llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> Importers;
+
+ auto GenASTsIfNeeded = [this, &AllASTs, &CodeSamples](StringRef Filename) {
+ if (!AllASTs.count(Filename)) {
+ auto Found = CodeSamples.find(Filename);
+ assert(Found != CodeSamples.end() && "Wrong file for import!");
+ AllASTs[Filename] = createASTUnit(Filename, Found->getValue());
+ }
+ };
+
+ for (const ImportAction &Action : ImportActions) {
+ StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename;
+ GenASTsIfNeeded(FromFile);
+ GenASTsIfNeeded(ToFile);
+
+ ASTUnit *From = AllASTs[FromFile].get();
+ ASTUnit *To = AllASTs[ToFile].get();
+
+ // Create a new importer if needed.
+ std::unique_ptr<ASTImporter> &ImporterRef = Importers[{From, To}];
+ if (!ImporterRef)
+ ImporterRef.reset(new ASTImporter(
+ To->getASTContext(), To->getFileManager(), From->getASTContext(),
+ From->getFileManager(), false));
+
+ // Find the declaration and import it.
+ auto FoundDecl = match(Action.ImportPredicate.bind(DeclToImportID),
+ From->getASTContext());
+ EXPECT_TRUE(FoundDecl.size() == 1);
+ const Decl *ToImport = selectFirst<Decl>(DeclToImportID, FoundDecl);
+ auto Imported = importNode(From, To, *ImporterRef, ToImport);
+ EXPECT_TRUE(static_cast<bool>(Imported));
+ if (!Imported)
+ llvm::consumeError(Imported.takeError());
+ }
+
+ // Find the declaration and import it.
+ auto FoundDecl = match(FinalSelectPredicate.bind(DeclToVerifyID),
+ AllASTs[FileForFinalCheck]->getASTContext());
+ EXPECT_TRUE(FoundDecl.size() == 1);
+ const Decl *ToVerify = selectFirst<Decl>(DeclToVerifyID, FoundDecl);
+ MatchVerifier<Decl> Verifier;
+ EXPECT_TRUE(
+ Verifier.match(ToVerify, BindableMatcher<Decl>(VerificationMatcher)));
+ }
+};
+
+template <typename T> RecordDecl *getRecordDecl(T *D) {
+ auto *ET = cast<ElaboratedType>(D->getType().getTypePtr());
+ return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl();
+}
+
+static const RecordDecl *getRecordDeclOfFriend(FriendDecl *FD) {
+ QualType Ty = FD->getFriendType()->getType().getCanonicalType();
+ return cast<RecordType>(Ty)->getDecl();
+}
+
+struct ImportExpr : TestImportBase {};
+struct ImportType : TestImportBase {};
+struct ImportDecl : TestImportBase {};
+
+struct CanonicalRedeclChain : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ auto Redecls = getCanonicalForwardRedeclChain(D0);
+ ASSERT_EQ(Redecls.size(), 1u);
+ EXPECT_EQ(D0, Redecls[0]);
+}
+
+TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers2) {
+ Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ auto *D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ FunctionDecl *D1 = D2->getPreviousDecl();
+
+ auto Redecls = getCanonicalForwardRedeclChain(D0);
+ ASSERT_EQ(Redecls.size(), 3u);
+ EXPECT_EQ(D0, Redecls[0]);
+ EXPECT_EQ(D1, Redecls[1]);
+ EXPECT_EQ(D2, Redecls[2]);
+}
+
+TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) {
+ Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ auto *D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ FunctionDecl *D1 = D2->getPreviousDecl();
+
+ auto RedeclsD0 = getCanonicalForwardRedeclChain(D0);
+ auto RedeclsD1 = getCanonicalForwardRedeclChain(D1);
+ auto RedeclsD2 = getCanonicalForwardRedeclChain(D2);
+
+ EXPECT_THAT(RedeclsD0, ::testing::ContainerEq(RedeclsD1));
+ EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2));
+}
+
+namespace {
+struct RedirectingImporter : public ASTImporter {
+ using ASTImporter::ASTImporter;
+
+protected:
+ llvm::Expected<Decl *> ImportImpl(Decl *FromD) override {
+ auto *ND = dyn_cast<NamedDecl>(FromD);
+ if (!ND || ND->getName() != "shouldNotBeImported")
+ return ASTImporter::ImportImpl(FromD);
+ for (Decl *D : getToContext().getTranslationUnitDecl()->decls()) {
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ if (ND->getName() == "realDecl") {
+ RegisterImportedDecl(FromD, ND);
+ return ND;
+ }
+ }
+ return ASTImporter::ImportImpl(FromD);
+ }
+};
+
+} // namespace
+
+struct RedirectingImporterTest : ASTImporterOptionSpecificTestBase {
+ RedirectingImporterTest() {
+ Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
+ ASTContext &FromContext, FileManager &FromFileManager,
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
+ return new RedirectingImporter(ToContext, ToFileManager, FromContext,
+ FromFileManager, MinimalImport,
+ SharedState);
+ };
+ }
+};
+
+// Test that an ASTImporter subclass can intercept an import call.
+TEST_P(RedirectingImporterTest, InterceptImport) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("class shouldNotBeImported {};", Lang_CXX,
+ "class realDecl {};", Lang_CXX, "shouldNotBeImported");
+ auto *Imported = cast<CXXRecordDecl>(To);
+ EXPECT_EQ(Imported->getQualifiedNameAsString(), "realDecl");
+
+ // Make sure our importer prevented the importing of the decl.
+ auto *ToTU = Imported->getTranslationUnitDecl();
+ auto Pattern = functionDecl(hasName("shouldNotBeImported"));
+ unsigned count =
+ DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_EQ(0U, count);
+}
+
+// Test that when we indirectly import a declaration the custom ASTImporter
+// is still intercepting the import.
+TEST_P(RedirectingImporterTest, InterceptIndirectImport) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("class shouldNotBeImported {};"
+ "class F { shouldNotBeImported f; };",
+ Lang_CXX, "class realDecl {};", Lang_CXX, "F");
+
+ // Make sure our ASTImporter prevented the importing of the decl.
+ auto *ToTU = To->getTranslationUnitDecl();
+ auto Pattern = functionDecl(hasName("shouldNotBeImported"));
+ unsigned count =
+ DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_EQ(0U, count);
+}
+
+struct ImportPath : ASTImporterOptionSpecificTestBase {
+ Decl *FromTU;
+ FunctionDecl *D0, *D1, *D2;
+ ImportPath() {
+ FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ D1 = D2->getPreviousDecl();
+ }
+};
+
+TEST_P(ImportPath, Push) {
+ ASTImporter::ImportPathTy path;
+ path.push(D0);
+ EXPECT_FALSE(path.hasCycleAtBack());
+}
+
+TEST_P(ImportPath, SmallCycle) {
+ ASTImporter::ImportPathTy path;
+ path.push(D0);
+ path.push(D0);
+ EXPECT_TRUE(path.hasCycleAtBack());
+ path.pop();
+ EXPECT_FALSE(path.hasCycleAtBack());
+ path.push(D0);
+ EXPECT_TRUE(path.hasCycleAtBack());
+}
+
+TEST_P(ImportPath, GetSmallCycle) {
+ ASTImporter::ImportPathTy path;
+ path.push(D0);
+ path.push(D0);
+ EXPECT_TRUE(path.hasCycleAtBack());
+ std::array<Decl* ,2> Res;
+ int i = 0;
+ for (Decl *Di : path.getCycleAtBack()) {
+ Res[i++] = Di;
+ }
+ ASSERT_EQ(i, 2);
+ EXPECT_EQ(Res[0], D0);
+ EXPECT_EQ(Res[1], D0);
+}
+
+TEST_P(ImportPath, GetCycle) {
+ ASTImporter::ImportPathTy path;
+ path.push(D0);
+ path.push(D1);
+ path.push(D2);
+ path.push(D0);
+ EXPECT_TRUE(path.hasCycleAtBack());
+ std::array<Decl* ,4> Res;
+ int i = 0;
+ for (Decl *Di : path.getCycleAtBack()) {
+ Res[i++] = Di;
+ }
+ ASSERT_EQ(i, 4);
+ EXPECT_EQ(Res[0], D0);
+ EXPECT_EQ(Res[1], D2);
+ EXPECT_EQ(Res[2], D1);
+ EXPECT_EQ(Res[3], D0);
+}
+
+TEST_P(ImportPath, CycleAfterCycle) {
+ ASTImporter::ImportPathTy path;
+ path.push(D0);
+ path.push(D1);
+ path.push(D0);
+ path.push(D1);
+ path.push(D2);
+ path.push(D0);
+ EXPECT_TRUE(path.hasCycleAtBack());
+ std::array<Decl* ,4> Res;
+ int i = 0;
+ for (Decl *Di : path.getCycleAtBack()) {
+ Res[i++] = Di;
+ }
+ ASSERT_EQ(i, 4);
+ EXPECT_EQ(Res[0], D0);
+ EXPECT_EQ(Res[1], D2);
+ EXPECT_EQ(Res[2], D1);
+ EXPECT_EQ(Res[3], D0);
+
+ path.pop();
+ path.pop();
+ path.pop();
+ EXPECT_TRUE(path.hasCycleAtBack());
+ i = 0;
+ for (Decl *Di : path.getCycleAtBack()) {
+ Res[i++] = Di;
+ }
+ ASSERT_EQ(i, 3);
+ EXPECT_EQ(Res[0], D0);
+ EXPECT_EQ(Res[1], D1);
+ EXPECT_EQ(Res[2], D0);
+
+ path.pop();
+ EXPECT_FALSE(path.hasCycleAtBack());
+}
+
+TEST_P(ImportExpr, ImportStringLiteral) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)\"foo\"; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ stringLiteral(hasType(asString("const char [4]"))))));
+ testImport(
+ "void declToImport() { (void)L\"foo\"; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ stringLiteral(hasType(asString("const wchar_t [4]"))))));
+ testImport(
+ "void declToImport() { (void) \"foo\" \"bar\"; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ stringLiteral(hasType(asString("const char [7]"))))));
+}
+
+TEST_P(ImportExpr, ImportChooseExpr) {
+ MatchVerifier<Decl> Verifier;
+
+ // This case tests C code that is not condition-dependent and has a true
+ // condition.
+ testImport(
+ "void declToImport() { (void)__builtin_choose_expr(1, 2, 3); }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(chooseExpr())));
+}
+
+TEST_P(ImportExpr, ImportGNUNullExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)__null; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger())))));
+}
+
+TEST_P(ImportExpr, ImportCXXNullPtrLiteralExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)nullptr; }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionDecl(hasDescendant(cxxNullPtrLiteralExpr())));
+}
+
+
+TEST_P(ImportExpr, ImportFloatinglLiteralExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)1.0; }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(
+ floatLiteral(equals(1.0), hasType(asString("double"))))));
+ testImport(
+ "void declToImport() { (void)1.0e-5f; }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(
+ floatLiteral(equals(1.0e-5f), hasType(asString("float"))))));
+}
+
+TEST_P(ImportExpr, ImportImaginaryLiteralExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)1.0i; }",
+ Lang_CXX14, "", Lang_CXX14, Verifier,
+ functionDecl(hasDescendant(imaginaryLiteral())));
+}
+
+TEST_P(ImportExpr, ImportCompoundLiteralExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() {"
+ " struct s { int x; long y; unsigned z; }; "
+ " (void)(struct s){ 42, 0L, 1U }; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ compoundLiteralExpr(
+ hasType(asString("struct s")),
+ has(initListExpr(
+ hasType(asString("struct s")),
+ has(integerLiteral(
+ equals(42), hasType(asString("int")))),
+ has(integerLiteral(
+ equals(0), hasType(asString("long")))),
+ has(integerLiteral(
+ equals(1), hasType(asString("unsigned int"))))))))));
+}
+
+TEST_P(ImportExpr, ImportCXXThisExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "class declToImport { void f() { (void)this; } };",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ cxxRecordDecl(
+ hasMethod(
+ hasDescendant(
+ cxxThisExpr(
+ hasType(
+ asString("class declToImport *")))))));
+}
+
+TEST_P(ImportExpr, ImportAtomicExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { int *ptr; __atomic_load_n(ptr, 1); }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(
+ atomicExpr(
+ has(ignoringParenImpCasts(
+ declRefExpr(hasDeclaration(varDecl(hasName("ptr"))),
+ hasType(asString("int *"))))),
+ has(integerLiteral(equals(1), hasType(asString("int"))))))));
+}
+
+TEST_P(ImportExpr, ImportLabelDeclAndAddrLabelExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { loop: goto loop; (void)&&loop; }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(
+ hasDescendant(
+ labelStmt(hasDeclaration(labelDecl(hasName("loop"))))),
+ hasDescendant(
+ addrLabelExpr(hasDeclaration(labelDecl(hasName("loop")))))));
+}
+
+AST_MATCHER_P(TemplateDecl, hasTemplateDecl,
+ internal::Matcher<NamedDecl>, InnerMatcher) {
+ const NamedDecl *Template = Node.getTemplatedDecl();
+ return Template && InnerMatcher.matches(*Template, Finder, Builder);
+}
+
+TEST_P(ImportExpr, ImportParenListExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template<typename T> class dummy { void f() { dummy X(*this); } };"
+ "typedef dummy<int> declToImport;"
+ "template class dummy<int>;",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ typedefDecl(hasType(templateSpecializationType(
+ hasDeclaration(classTemplateSpecializationDecl(hasSpecializedTemplate(
+ classTemplateDecl(hasTemplateDecl(cxxRecordDecl(hasMethod(allOf(
+ hasName("f"),
+ hasBody(compoundStmt(has(declStmt(hasSingleDecl(
+ varDecl(hasInitializer(parenListExpr(has(unaryOperator(
+ hasOperatorName("*"),
+ hasUnaryOperand(cxxThisExpr())))))))))))))))))))))));
+}
+
+TEST_P(ImportExpr, ImportSwitch) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { int b; switch (b) { case 1: break; } }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(
+ switchStmt(has(compoundStmt(has(caseStmt())))))));
+}
+
+TEST_P(ImportExpr, ImportStmtExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { int b; int a = b ?: 1; int C = ({int X=4; X;}); }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(
+ varDecl(
+ hasName("C"),
+ hasType(asString("int")),
+ hasInitializer(
+ stmtExpr(
+ hasAnySubstatement(declStmt(hasSingleDecl(
+ varDecl(
+ hasName("X"),
+ hasType(asString("int")),
+ hasInitializer(
+ integerLiteral(equals(4))))))),
+ hasDescendant(
+ implicitCastExpr())))))));
+}
+
+TEST_P(ImportExpr, ImportConditionalOperator) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)(true ? 1 : -5); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ conditionalOperator(
+ hasCondition(cxxBoolLiteral(equals(true))),
+ hasTrueExpression(integerLiteral(equals(1))),
+ hasFalseExpression(
+ unaryOperator(hasUnaryOperand(integerLiteral(equals(5))))))
+ )));
+}
+
+TEST_P(ImportExpr, ImportBinaryConditionalOperator) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)(1 ?: -5); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ binaryConditionalOperator(
+ hasCondition(
+ implicitCastExpr(
+ hasSourceExpression(opaqueValueExpr(
+ hasSourceExpression(integerLiteral(equals(1))))),
+ hasType(booleanType()))),
+ hasTrueExpression(
+ opaqueValueExpr(
+ hasSourceExpression(integerLiteral(equals(1))))),
+ hasFalseExpression(
+ unaryOperator(
+ hasOperatorName("-"),
+ hasUnaryOperand(integerLiteral(equals(5)))))))));
+}
+
+TEST_P(ImportExpr, ImportDesignatedInitExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() {"
+ " struct point { double x; double y; };"
+ " struct point ptarray[10] = "
+ "{ [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(
+ initListExpr(
+ has(designatedInitExpr(
+ designatorCountIs(2),
+ hasDescendant(floatLiteral(equals(1.0))),
+ hasDescendant(integerLiteral(equals(2))))),
+ has(designatedInitExpr(
+ designatorCountIs(2),
+ hasDescendant(floatLiteral(equals(2.0))),
+ hasDescendant(integerLiteral(equals(2))))),
+ has(designatedInitExpr(
+ designatorCountIs(2),
+ hasDescendant(floatLiteral(equals(1.0))),
+ hasDescendant(integerLiteral(equals(0)))))))));
+}
+
+TEST_P(ImportExpr, ImportPredefinedExpr) {
+ MatchVerifier<Decl> Verifier;
+ // __func__ expands as StringLiteral("declToImport")
+ testImport(
+ "void declToImport() { (void)__func__; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ predefinedExpr(
+ hasType(
+ asString("const char [13]")),
+ has(stringLiteral(hasType(
+ asString("const char [13]"))))))));
+}
+
+TEST_P(ImportExpr, ImportInitListExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() {"
+ " struct point { double x; double y; };"
+ " point ptarray[10] = { [2].y = 1.0, [2].x = 2.0,"
+ " [0].x = 1.0 }; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ initListExpr(
+ has(
+ cxxConstructExpr(
+ requiresZeroInitialization())),
+ has(
+ initListExpr(
+ hasType(asString("struct point")),
+ has(floatLiteral(equals(1.0))),
+ has(implicitValueInitExpr(
+ hasType(asString("double")))))),
+ has(
+ initListExpr(
+ hasType(asString("struct point")),
+ has(floatLiteral(equals(2.0))),
+ has(floatLiteral(equals(1.0)))))))));
+}
+
+
+const internal::VariadicDynCastAllOfMatcher<Expr, VAArgExpr> vaArgExpr;
+
+TEST_P(ImportExpr, ImportVAArgExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport(__builtin_va_list list, ...) {"
+ " (void)__builtin_va_arg(list, int); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ cStyleCastExpr(hasSourceExpression(vaArgExpr())))));
+}
+
+TEST_P(ImportExpr, CXXTemporaryObjectExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "struct C {};"
+ "void declToImport() { C c = C(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ exprWithCleanups(has(cxxConstructExpr(
+ has(materializeTemporaryExpr(has(implicitCastExpr(
+ has(cxxTemporaryObjectExpr())))))))))));
+}
+
+TEST_P(ImportType, ImportAtomicType) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { typedef _Atomic(int) a_int; }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionDecl(hasDescendant(typedefDecl(has(atomicType())))));
+}
+
+TEST_P(ImportDecl, ImportFunctionTemplateDecl) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <typename T> void declToImport() { };",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl());
+}
+
+TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <typename T> struct C { T t; };"
+ "template <typename T> void declToImport() {"
+ " C<T> d;"
+ " (void)d.t;"
+ "}"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl(hasDescendant(
+ cStyleCastExpr(has(cxxDependentScopeMemberExpr())))));
+ testImport(
+ "template <typename T> struct C { T t; };"
+ "template <typename T> void declToImport() {"
+ " C<T> d;"
+ " (void)(&d)->t;"
+ "}"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl(hasDescendant(
+ cStyleCastExpr(has(cxxDependentScopeMemberExpr())))));
+}
+
+TEST_P(ImportType, ImportTypeAliasTemplate) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <int K>"
+ "struct dummy { static const int i = K; };"
+ "template <int K> using dummy2 = dummy<K>;"
+ "int declToImport() { return dummy2<3>::i; }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionDecl(
+ hasDescendant(implicitCastExpr(has(declRefExpr()))),
+ unless(hasAncestor(translationUnitDecl(has(typeAliasDecl()))))));
+}
+
+const internal::VariadicDynCastAllOfMatcher<Decl, VarTemplateSpecializationDecl>
+ varTemplateSpecializationDecl;
+
+TEST_P(ImportDecl, ImportVarTemplate) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <typename T>"
+ "T pi = T(3.1415926535897932385L);"
+ "void declToImport() { (void)pi<int>; }",
+ Lang_CXX14, "", Lang_CXX14, Verifier,
+ functionDecl(
+ hasDescendant(declRefExpr(to(varTemplateSpecializationDecl()))),
+ unless(hasAncestor(translationUnitDecl(has(varDecl(
+ hasName("pi"), unless(varTemplateSpecializationDecl()))))))));
+}
+
+TEST_P(ImportType, ImportPackExpansion) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <typename... Args>"
+ "struct dummy {"
+ " dummy(Args... args) {}"
+ " static const int i = 4;"
+ "};"
+ "int declToImport() { return dummy<int>::i; }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionDecl(hasDescendant(
+ returnStmt(has(implicitCastExpr(has(declRefExpr())))))));
+}
+
+const internal::VariadicDynCastAllOfMatcher<Type,
+ DependentTemplateSpecializationType>
+ dependentTemplateSpecializationType;
+
+TEST_P(ImportType, ImportDependentTemplateSpecialization) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template<typename T>"
+ "struct A;"
+ "template<typename T>"
+ "struct declToImport {"
+ " typename A<T>::template B<T> a;"
+ "};",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ classTemplateDecl(has(cxxRecordDecl(has(
+ fieldDecl(hasType(dependentTemplateSpecializationType())))))));
+}
+
+const internal::VariadicDynCastAllOfMatcher<Stmt, SizeOfPackExpr>
+ sizeOfPackExpr;
+
+TEST_P(ImportExpr, ImportSizeOfPackExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <typename... Ts>"
+ "void declToImport() {"
+ " const int i = sizeof...(Ts);"
+ "};"
+ "void g() { declToImport<int>(); }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionTemplateDecl(hasDescendant(sizeOfPackExpr())));
+ testImport(
+ "template <typename... Ts>"
+ "using X = int[sizeof...(Ts)];"
+ "template <typename... Us>"
+ "struct Y {"
+ " X<Us..., int, double, int, Us...> f;"
+ "};"
+ "Y<float, int> declToImport;",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ varDecl(hasType(classTemplateSpecializationDecl(has(fieldDecl(hasType(
+ hasUnqualifiedDesugaredType(constantArrayType(hasSize(7))))))))));
+}
+
+/// \brief Matches __builtin_types_compatible_p:
+/// GNU extension to check equivalent types
+/// Given
+/// \code
+/// __builtin_types_compatible_p(int, int)
+/// \endcode
+// will generate TypeTraitExpr <...> 'int'
+const internal::VariadicDynCastAllOfMatcher<Stmt, TypeTraitExpr> typeTraitExpr;
+
+TEST_P(ImportExpr, ImportTypeTraitExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { "
+ " (void)__builtin_types_compatible_p(int, int);"
+ "}",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(typeTraitExpr(hasType(asString("int"))))));
+}
+
+const internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> cxxTypeidExpr;
+
+TEST_P(ImportExpr, ImportCXXTypeidExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "namespace std { class type_info {}; }"
+ "void declToImport() {"
+ " int x;"
+ " auto a = typeid(int); auto b = typeid(x);"
+ "}",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionDecl(
+ hasDescendant(varDecl(
+ hasName("a"), hasInitializer(hasDescendant(cxxTypeidExpr())))),
+ hasDescendant(varDecl(
+ hasName("b"), hasInitializer(hasDescendant(cxxTypeidExpr()))))));
+}
+
+TEST_P(ImportExpr, ImportTypeTraitExprValDep) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template<typename T> struct declToImport {"
+ " void m() { (void)__is_pod(T); }"
+ "};"
+ "void f() { declToImport<int>().m(); }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ classTemplateDecl(has(cxxRecordDecl(has(
+ functionDecl(hasDescendant(
+ typeTraitExpr(hasType(booleanType())))))))));
+}
+
+TEST_P(ImportDecl, ImportRecordDeclInFunc) {
+ MatchVerifier<Decl> Verifier;
+ testImport("int declToImport() { "
+ " struct data_t {int a;int b;};"
+ " struct data_t d;"
+ " return 0;"
+ "}",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasBody(compoundStmt(
+ has(declStmt(hasSingleDecl(varDecl(hasName("d")))))))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordTypeInFunc) {
+ Decl *FromTU = getTuDecl("int declToImport() { "
+ " struct data_t {int a;int b;};"
+ " struct data_t d;"
+ " return 0;"
+ "}",
+ Lang_C, "input.c");
+ auto *FromVar =
+ FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("d")));
+ ASSERT_TRUE(FromVar);
+ auto ToType =
+ ImportType(FromVar->getType().getCanonicalType(), FromVar, Lang_C);
+ EXPECT_FALSE(ToType.isNull());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncParams) {
+ // This construct is not supported by ASTImporter.
+ Decl *FromTU = getTuDecl(
+ "int declToImport(struct data_t{int a;int b;} ***d){ return 0; }",
+ Lang_C, "input.c");
+ auto *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("declToImport")));
+ ASSERT_TRUE(From);
+ auto *To = Import(From, Lang_C);
+ EXPECT_EQ(To, nullptr);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncFromMacro) {
+ Decl *FromTU = getTuDecl(
+ "#define NONAME_SIZEOF(type) sizeof(struct{type *dummy;}) \n"
+ "int declToImport(){ return NONAME_SIZEOF(int); }",
+ Lang_C, "input.c");
+ auto *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("declToImport")));
+ ASSERT_TRUE(From);
+ auto *To = Import(From, Lang_C);
+ ASSERT_TRUE(To);
+ EXPECT_TRUE(MatchVerifier<FunctionDecl>().match(
+ To, functionDecl(hasName("declToImport"),
+ hasDescendant(unaryExprOrTypeTraitExpr()))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportRecordDeclInFuncParamsFromMacro) {
+ // This construct is not supported by ASTImporter.
+ Decl *FromTU = getTuDecl(
+ "#define PAIR_STRUCT(type) struct data_t{type a;type b;} \n"
+ "int declToImport(PAIR_STRUCT(int) ***d){ return 0; }",
+ Lang_C, "input.c");
+ auto *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("declToImport")));
+ ASSERT_TRUE(From);
+ auto *To = Import(From, Lang_C);
+ EXPECT_EQ(To, nullptr);
+}
+
+const internal::VariadicDynCastAllOfMatcher<Expr, CXXPseudoDestructorExpr>
+ cxxPseudoDestructorExpr;
+
+TEST_P(ImportExpr, ImportCXXPseudoDestructorExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "typedef int T;"
+ "void declToImport(int *p) {"
+ " T t;"
+ " p->T::~T();"
+ "}",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ callExpr(has(cxxPseudoDestructorExpr())))));
+}
+
+TEST_P(ImportDecl, ImportUsingDecl) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "namespace foo { int bar; }"
+ "void declToImport() { using foo::bar; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(usingDecl())));
+}
+
+/// \brief Matches shadow declarations introduced into a scope by a
+/// (resolved) using declaration.
+///
+/// Given
+/// \code
+/// namespace n { int f; }
+/// namespace declToImport { using n::f; }
+/// \endcode
+/// usingShadowDecl()
+/// matches \code f \endcode
+const internal::VariadicDynCastAllOfMatcher<Decl,
+ UsingShadowDecl> usingShadowDecl;
+
+TEST_P(ImportDecl, ImportUsingShadowDecl) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "namespace foo { int bar; }"
+ "namespace declToImport { using foo::bar; }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ namespaceDecl(has(usingShadowDecl())));
+}
+
+TEST_P(ImportExpr, ImportUnresolvedLookupExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template<typename T> int foo();"
+ "template <typename T> void declToImport() {"
+ " (void)::foo<T>;"
+ " (void)::template foo<T>;"
+ "}"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl(hasDescendant(unresolvedLookupExpr())));
+}
+
+TEST_P(ImportExpr, ImportCXXUnresolvedConstructExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <typename T> struct C { T t; };"
+ "template <typename T> void declToImport() {"
+ " C<T> d;"
+ " d.t = T();"
+ "}"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl(hasDescendant(
+ binaryOperator(has(cxxUnresolvedConstructExpr())))));
+ testImport(
+ "template <typename T> struct C { T t; };"
+ "template <typename T> void declToImport() {"
+ " C<T> d;"
+ " (&d)->t = T();"
+ "}"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl(hasDescendant(
+ binaryOperator(has(cxxUnresolvedConstructExpr())))));
+}
+
+/// Check that function "declToImport()" (which is the templated function
+/// for corresponding FunctionTemplateDecl) is not added into DeclContext.
+/// Same for class template declarations.
+TEST_P(ImportDecl, ImportTemplatedDeclForTemplate) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template <typename T> void declToImport() { T a = 1; }"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl(hasAncestor(translationUnitDecl(
+ unless(has(functionDecl(hasName("declToImport"))))))));
+ testImport(
+ "template <typename T> struct declToImport { T t; };"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ classTemplateDecl(hasAncestor(translationUnitDecl(
+ unless(has(cxxRecordDecl(hasName("declToImport"))))))));
+}
+
+TEST_P(ImportDecl, ImportClassTemplatePartialSpecialization) {
+ MatchVerifier<Decl> Verifier;
+ auto Code =
+ R"s(
+ struct declToImport {
+ template <typename T0> struct X;
+ template <typename T0> struct X<T0 *> {};
+ };
+ )s";
+ testImport(Code, Lang_CXX, "", Lang_CXX, Verifier,
+ recordDecl(has(classTemplateDecl()),
+ has(classTemplateSpecializationDecl())));
+}
+
+TEST_P(ImportExpr, CXXOperatorCallExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "class declToImport {"
+ " void f() { *this = declToImport(); }"
+ "};",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ cxxRecordDecl(has(cxxMethodDecl(hasDescendant(
+ cxxOperatorCallExpr())))));
+}
+
+TEST_P(ImportExpr, DependentSizedArrayType) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "template<typename T, int Size> class declToImport {"
+ " T data[Size];"
+ "};",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ classTemplateDecl(has(cxxRecordDecl(
+ has(fieldDecl(hasType(dependentSizedArrayType())))))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportBeginLocOfDeclRefExpr) {
+ Decl *FromTU = getTuDecl(
+ "class A { public: static int X; }; void f() { (void)A::X; }", Lang_CXX);
+ auto From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ ASSERT_TRUE(From);
+ ASSERT_TRUE(
+ cast<CStyleCastExpr>(cast<CompoundStmt>(From->getBody())->body_front())
+ ->getSubExpr()
+ ->getBeginLoc()
+ .isValid());
+ FunctionDecl *To = Import(From, Lang_CXX);
+ ASSERT_TRUE(To);
+ ASSERT_TRUE(
+ cast<CStyleCastExpr>(cast<CompoundStmt>(To->getBody())->body_front())
+ ->getSubExpr()
+ ->getBeginLoc()
+ .isValid());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportOfTemplatedDeclOfClassTemplateDecl) {
+ Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX);
+ auto From =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl());
+ ASSERT_TRUE(From);
+ auto To = cast<ClassTemplateDecl>(Import(From, Lang_CXX));
+ ASSERT_TRUE(To);
+ Decl *ToTemplated = To->getTemplatedDecl();
+ Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX);
+ EXPECT_TRUE(ToTemplated1);
+ EXPECT_EQ(ToTemplated1, ToTemplated);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportOfTemplatedDeclOfFunctionTemplateDecl) {
+ Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX);
+ auto From = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU, functionTemplateDecl());
+ ASSERT_TRUE(From);
+ auto To = cast<FunctionTemplateDecl>(Import(From, Lang_CXX));
+ ASSERT_TRUE(To);
+ Decl *ToTemplated = To->getTemplatedDecl();
+ Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX);
+ EXPECT_TRUE(ToTemplated1);
+ EXPECT_EQ(ToTemplated1, ToTemplated);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportOfTemplatedDeclShouldImportTheClassTemplateDecl) {
+ Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX);
+ auto FromFT =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl());
+ ASSERT_TRUE(FromFT);
+
+ auto ToTemplated =
+ cast<CXXRecordDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX));
+ EXPECT_TRUE(ToTemplated);
+ auto ToTU = ToTemplated->getTranslationUnitDecl();
+ auto ToFT =
+ FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, classTemplateDecl());
+ EXPECT_TRUE(ToFT);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportOfTemplatedDeclShouldImportTheFunctionTemplateDecl) {
+ Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX);
+ auto FromFT = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU, functionTemplateDecl());
+ ASSERT_TRUE(FromFT);
+
+ auto ToTemplated =
+ cast<FunctionDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX));
+ EXPECT_TRUE(ToTemplated);
+ auto ToTU = ToTemplated->getTranslationUnitDecl();
+ auto ToFT = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ ToTU, functionTemplateDecl());
+ EXPECT_TRUE(ToFT);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplatedDecl) {
+ auto Code =
+ R"(
+ namespace x {
+ template<class X> struct S1{};
+ template<class X> struct S2{};
+ template<class X> struct S3{};
+ }
+ )";
+ Decl *FromTU = getTuDecl(Code, Lang_CXX);
+ auto FromNs =
+ FirstDeclMatcher<NamespaceDecl>().match(FromTU, namespaceDecl());
+ auto ToNs = cast<NamespaceDecl>(Import(FromNs, Lang_CXX));
+ ASSERT_TRUE(ToNs);
+ auto From =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTU,
+ classTemplateDecl(
+ hasName("S2")));
+ auto To =
+ FirstDeclMatcher<ClassTemplateDecl>().match(ToNs,
+ classTemplateDecl(
+ hasName("S2")));
+ ASSERT_TRUE(From);
+ ASSERT_TRUE(To);
+ auto ToTemplated = To->getTemplatedDecl();
+ auto ToTemplated1 =
+ cast<CXXRecordDecl>(Import(From->getTemplatedDecl(), Lang_CXX));
+ EXPECT_TRUE(ToTemplated1);
+ ASSERT_EQ(ToTemplated1, ToTemplated);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportChooseExpr) {
+ // This tests the import of isConditionTrue directly to make sure the importer
+ // gets it right.
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ "void declToImport() { (void)__builtin_choose_expr(1, 0, 1); }",
+ Lang_C, "", Lang_C);
+
+ auto ToResults = match(chooseExpr().bind("choose"), To->getASTContext());
+ auto FromResults = match(chooseExpr().bind("choose"), From->getASTContext());
+
+ const ChooseExpr *FromChooseExpr =
+ selectFirst<ChooseExpr>("choose", FromResults);
+ ASSERT_TRUE(FromChooseExpr);
+
+ const ChooseExpr *ToChooseExpr = selectFirst<ChooseExpr>("choose", ToResults);
+ ASSERT_TRUE(ToChooseExpr);
+
+ EXPECT_EQ(FromChooseExpr->isConditionTrue(), ToChooseExpr->isConditionTrue());
+ EXPECT_EQ(FromChooseExpr->isConditionDependent(),
+ ToChooseExpr->isConditionDependent());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportFunctionWithBackReferringParameter) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ template <typename T> struct X {};
+
+ void declToImport(int y, X<int> &x) {}
+
+ template <> struct X<int> {
+ void g() {
+ X<int> x;
+ declToImport(0, x);
+ }
+ };
+ )",
+ Lang_CXX, "", Lang_CXX);
+
+ MatchVerifier<Decl> Verifier;
+ auto Matcher = functionDecl(hasName("declToImport"),
+ parameterCountIs(2),
+ hasParameter(0, hasName("y")),
+ hasParameter(1, hasName("x")),
+ hasParameter(1, hasType(asString("X<int> &"))));
+ ASSERT_TRUE(Verifier.match(From, Matcher));
+ EXPECT_TRUE(Verifier.match(To, Matcher));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ TUshouldNotContainTemplatedDeclOfFunctionTemplates) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("template <typename T> void declToImport() { T a = 1; }"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX);
+
+ auto Check = [](Decl *D) -> bool {
+ auto TU = D->getTranslationUnitDecl();
+ for (auto Child : TU->decls()) {
+ if (auto *FD = dyn_cast<FunctionDecl>(Child)) {
+ if (FD->getNameAsString() == "declToImport") {
+ GTEST_NONFATAL_FAILURE_(
+ "TU should not contain any FunctionDecl with name declToImport");
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ ASSERT_TRUE(Check(From));
+ EXPECT_TRUE(Check(To));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ TUshouldNotContainTemplatedDeclOfClassTemplates) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("template <typename T> struct declToImport { T t; };"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX, "", Lang_CXX);
+
+ auto Check = [](Decl *D) -> bool {
+ auto TU = D->getTranslationUnitDecl();
+ for (auto Child : TU->decls()) {
+ if (auto *RD = dyn_cast<CXXRecordDecl>(Child)) {
+ if (RD->getNameAsString() == "declToImport") {
+ GTEST_NONFATAL_FAILURE_(
+ "TU should not contain any CXXRecordDecl with name declToImport");
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ ASSERT_TRUE(Check(From));
+ EXPECT_TRUE(Check(To));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ TUshouldNotContainTemplatedDeclOfTypeAlias) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl(
+ "template <typename T> struct X {};"
+ "template <typename T> using declToImport = X<T>;"
+ "void instantiate() { declToImport<int> a; }",
+ Lang_CXX11, "", Lang_CXX11);
+
+ auto Check = [](Decl *D) -> bool {
+ auto TU = D->getTranslationUnitDecl();
+ for (auto Child : TU->decls()) {
+ if (auto *AD = dyn_cast<TypeAliasDecl>(Child)) {
+ if (AD->getNameAsString() == "declToImport") {
+ GTEST_NONFATAL_FAILURE_(
+ "TU should not contain any TypeAliasDecl with name declToImport");
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ ASSERT_TRUE(Check(From));
+ EXPECT_TRUE(Check(To));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) {
+
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ template<class T>
+ class Base {};
+ class declToImport : public Base<declToImport> {};
+ )",
+ Lang_CXX, "", Lang_CXX);
+
+ // Check that the ClassTemplateSpecializationDecl is NOT the child of the TU.
+ auto Pattern =
+ translationUnitDecl(unless(has(classTemplateSpecializationDecl())));
+ ASSERT_TRUE(
+ MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
+ EXPECT_TRUE(
+ MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
+
+ // Check that the ClassTemplateSpecializationDecl is the child of the
+ // ClassTemplateDecl.
+ Pattern = translationUnitDecl(has(classTemplateDecl(
+ hasName("Base"), has(classTemplateSpecializationDecl()))));
+ ASSERT_TRUE(
+ MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
+ EXPECT_TRUE(
+ MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
+}
+
+AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) {
+ size_t Index = 0;
+ for (Decl *D : Node.decls()) {
+ if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
+ auto *ND = cast<NamedDecl>(D);
+ if (Index == Order.size())
+ return false;
+ if (ND->getName() != Order[Index])
+ return false;
+ ++Index;
+ }
+ }
+ return Index == Order.size();
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ namespace NS {
+ template<class T>
+ class X {};
+ template class X<int>;
+ }
+ )",
+ Lang_CXX, "", Lang_CXX, "NS");
+
+ // Check that the ClassTemplateSpecializationDecl is NOT the child of the
+ // ClassTemplateDecl.
+ auto Pattern = namespaceDecl(has(classTemplateDecl(
+ hasName("X"), unless(has(classTemplateSpecializationDecl())))));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));
+
+ // Check that the ClassTemplateSpecializationDecl is the child of the
+ // NamespaceDecl.
+ Pattern = namespaceDecl(has(classTemplateSpecializationDecl(hasName("X"))));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ CXXRecordDeclFieldsShouldBeInCorrectOrder) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl(
+ "struct declToImport { int a; int b; };",
+ Lang_CXX11, "", Lang_CXX11);
+
+ MatchVerifier<Decl> Verifier;
+ ASSERT_TRUE(Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
+ EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ // The original recursive algorithm of ASTImporter first imports 'c' then
+ // 'b' and lastly 'a'. Therefore we must restore the order somehow.
+ R"s(
+ struct declToImport {
+ int a = c + b;
+ int b = 1;
+ int c = 2;
+ };
+ )s",
+ Lang_CXX11, "", Lang_CXX11);
+
+ MatchVerifier<Decl> Verifier;
+ ASSERT_TRUE(
+ Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
+ EXPECT_TRUE(
+ Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ CXXRecordDeclFieldAndIndirectFieldOrder) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ // First field is "a", then the field for unnamed union, then "b" and "c"
+ // from it (indirect fields), then "d".
+ R"s(
+ struct declToImport {
+ int a = d;
+ union {
+ int b;
+ int c;
+ };
+ int d;
+ };
+ )s",
+ Lang_CXX11, "", Lang_CXX11);
+
+ MatchVerifier<Decl> Verifier;
+ ASSERT_TRUE(Verifier.match(
+ From, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"}))));
+ EXPECT_TRUE(Verifier.match(
+ To, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"}))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ShouldImportImplicitCXXRecordDecl) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ struct declToImport {
+ };
+ )",
+ Lang_CXX, "", Lang_CXX);
+
+ MatchVerifier<Decl> Verifier;
+ // Match the implicit Decl.
+ auto Matcher = cxxRecordDecl(has(cxxRecordDecl()));
+ ASSERT_TRUE(Verifier.match(From, Matcher));
+ EXPECT_TRUE(Verifier.match(To, Matcher));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ShouldImportImplicitCXXRecordDeclOfClassTemplate) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ template <typename U>
+ struct declToImport {
+ };
+ )",
+ Lang_CXX, "", Lang_CXX);
+
+ MatchVerifier<Decl> Verifier;
+ // Match the implicit Decl.
+ auto Matcher = classTemplateDecl(has(cxxRecordDecl(has(cxxRecordDecl()))));
+ ASSERT_TRUE(Verifier.match(From, Matcher));
+ EXPECT_TRUE(Verifier.match(To, Matcher));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ template<class T>
+ class Base {};
+ class declToImport : public Base<declToImport> {};
+ )",
+ Lang_CXX, "", Lang_CXX);
+
+ auto hasImplicitClass = has(cxxRecordDecl());
+ auto Pattern = translationUnitDecl(has(classTemplateDecl(
+ hasName("Base"),
+ has(classTemplateSpecializationDecl(hasImplicitClass)))));
+ ASSERT_TRUE(
+ MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
+ EXPECT_TRUE(
+ MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, IDNSOrdinary) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("void declToImport() {}", Lang_CXX, "", Lang_CXX);
+
+ MatchVerifier<Decl> Verifier;
+ auto Matcher = functionDecl();
+ ASSERT_TRUE(Verifier.match(From, Matcher));
+ EXPECT_TRUE(Verifier.match(To, Matcher));
+ EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, IDNSOfNonmemberOperator) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct X {};
+ void operator<<(int, X);
+ )",
+ Lang_CXX);
+ Decl *From = LastDeclMatcher<Decl>{}.match(FromTU, functionDecl());
+ const Decl *To = Import(From, Lang_CXX);
+ EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ShouldImportMembersOfClassTemplateSpecializationDecl) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ template<class T>
+ class Base { int a; };
+ class declToImport : Base<declToImport> {};
+ )",
+ Lang_CXX, "", Lang_CXX);
+
+ auto Pattern = translationUnitDecl(has(classTemplateDecl(
+ hasName("Base"),
+ has(classTemplateSpecializationDecl(has(fieldDecl(hasName("a"))))))));
+ ASSERT_TRUE(
+ MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
+ EXPECT_TRUE(
+ MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportDefinitionOfClassTemplateAfterFwdDecl) {
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B;
+ )",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("B")));
+
+ Import(FromD, Lang_CXX);
+ }
+
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B {
+ void f();
+ };
+ )",
+ Lang_CXX, "input1.cc");
+ FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ Import(FromD, Lang_CXX);
+ auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("B")));
+ auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX));
+ EXPECT_TRUE(ToCTD->isThisDeclarationADefinition());
+ }
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ struct B {
+ void f();
+ };
+
+ template <typename T>
+ struct B;
+ )",
+ Lang_CXX);
+ ASSERT_EQ(1u, DeclCounterWithPredicate<ClassTemplateDecl>(
+ [](const ClassTemplateDecl *T) {
+ return T->isThisDeclarationADefinition();
+ })
+ .match(ToTU, classTemplateDecl()));
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B {
+ void f();
+ };
+ )",
+ Lang_CXX, "input1.cc");
+ ClassTemplateDecl *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("B")));
+
+ Import(FromD, Lang_CXX);
+
+ // We should have only one definition.
+ EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateDecl>(
+ [](const ClassTemplateDecl *T) {
+ return T->isThisDeclarationADefinition();
+ })
+ .match(ToTU, classTemplateDecl()));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportDefinitionOfClassIfThereIsAnExistingFwdDeclAndDefinition) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ struct B {
+ void f();
+ };
+
+ struct B;
+ )",
+ Lang_CXX);
+ ASSERT_EQ(2u, DeclCounter<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(unless(isImplicit()))));
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct B {
+ void f();
+ };
+ )",
+ Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("B")));
+
+ Import(FromD, Lang_CXX);
+
+ EXPECT_EQ(2u, DeclCounter<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(unless(isImplicit()))));
+}
+
+static void CompareSourceLocs(FullSourceLoc Loc1, FullSourceLoc Loc2) {
+ EXPECT_EQ(Loc1.getExpansionLineNumber(), Loc2.getExpansionLineNumber());
+ EXPECT_EQ(Loc1.getExpansionColumnNumber(), Loc2.getExpansionColumnNumber());
+ EXPECT_EQ(Loc1.getSpellingLineNumber(), Loc2.getSpellingLineNumber());
+ EXPECT_EQ(Loc1.getSpellingColumnNumber(), Loc2.getSpellingColumnNumber());
+}
+static void CompareSourceRanges(SourceRange Range1, SourceRange Range2,
+ SourceManager &SM1, SourceManager &SM2) {
+ CompareSourceLocs(FullSourceLoc{ Range1.getBegin(), SM1 },
+ FullSourceLoc{ Range2.getBegin(), SM2 });
+ CompareSourceLocs(FullSourceLoc{ Range1.getEnd(), SM1 },
+ FullSourceLoc{ Range2.getEnd(), SM2 });
+}
+TEST_P(ASTImporterOptionSpecificTestBase, ImportSourceLocs) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ #define MFOO(arg) arg = arg + 1
+
+ void foo() {
+ int a = 5;
+ MFOO(a);
+ }
+ )",
+ Lang_CXX);
+ auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+ auto ToD = Import(FromD, Lang_CXX);
+
+ auto ToLHS = LastDeclMatcher<DeclRefExpr>().match(ToD, declRefExpr());
+ auto FromLHS = LastDeclMatcher<DeclRefExpr>().match(FromTU, declRefExpr());
+ auto ToRHS = LastDeclMatcher<IntegerLiteral>().match(ToD, integerLiteral());
+ auto FromRHS =
+ LastDeclMatcher<IntegerLiteral>().match(FromTU, integerLiteral());
+
+ SourceManager &ToSM = ToAST->getASTContext().getSourceManager();
+ SourceManager &FromSM = FromD->getASTContext().getSourceManager();
+ CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM,
+ FromSM);
+ CompareSourceRanges(ToLHS->getSourceRange(), FromLHS->getSourceRange(), ToSM,
+ FromSM);
+ CompareSourceRanges(ToRHS->getSourceRange(), FromRHS->getSourceRange(), ToSM,
+ FromSM);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportNestedMacro) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ #define FUNC_INT void declToImport
+ #define FUNC FUNC_INT
+ FUNC(int a);
+ )",
+ Lang_CXX);
+ auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+ auto ToD = Import(FromD, Lang_CXX);
+
+ SourceManager &ToSM = ToAST->getASTContext().getSourceManager();
+ SourceManager &FromSM = FromD->getASTContext().getSourceManager();
+ CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM,
+ FromSM);
+}
+
+TEST_P(
+ ASTImporterOptionSpecificTestBase,
+ ImportDefinitionOfClassTemplateSpecIfThereIsAnExistingFwdDeclAndDefinition) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ struct B;
+
+ template <>
+ struct B<int> {};
+
+ template <>
+ struct B<int>;
+ )",
+ Lang_CXX);
+ // We should have only one definition.
+ ASSERT_EQ(1u, DeclCounterWithPredicate<ClassTemplateSpecializationDecl>(
+ [](const ClassTemplateSpecializationDecl *T) {
+ return T->isThisDeclarationADefinition();
+ })
+ .match(ToTU, classTemplateSpecializationDecl()));
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B;
+
+ template <>
+ struct B<int> {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("B")));
+
+ Import(FromD, Lang_CXX);
+
+ // We should have only one definition.
+ EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateSpecializationDecl>(
+ [](const ClassTemplateSpecializationDecl *T) {
+ return T->isThisDeclarationADefinition();
+ })
+ .match(ToTU, classTemplateSpecializationDecl()));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ObjectsWithUnnamedStructType) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct { int a; int b; } object0 = { 2, 3 };
+ struct { int x; int y; int z; } object1;
+ )",
+ Lang_CXX, "input0.cc");
+
+ auto *Obj0 =
+ FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("object0")));
+ auto *From0 = getRecordDecl(Obj0);
+ auto *Obj1 =
+ FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("object1")));
+ auto *From1 = getRecordDecl(Obj1);
+
+ auto *To0 = Import(From0, Lang_CXX);
+ auto *To1 = Import(From1, Lang_CXX);
+
+ EXPECT_TRUE(To0);
+ EXPECT_TRUE(To1);
+ EXPECT_NE(To0, To1);
+ EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecords) {
+ auto *Code =
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )";
+ Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c");
+
+ Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c");
+
+ auto *X0 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+ auto *X1 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+ Import(X0, Lang_C);
+ Import(X1, Lang_C);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We expect no (ODR) warning during the import.
+ EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ EXPECT_EQ(1u,
+ DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecordsReversed) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )",
+ Lang_C, "input0.c");
+
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ struct X { // reversed order
+ struct { int b; };
+ struct { int a; };
+ };
+ )",
+ Lang_C, "input1.c");
+
+ auto *X0 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+ auto *X1 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+ Import(X0, Lang_C);
+ Import(X1, Lang_C);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We expect one (ODR) warning during the import.
+ EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ EXPECT_EQ(1u,
+ DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag) {
+ auto Pattern = varDecl(hasName("x"));
+ VarDecl *Imported1;
+ {
+ Decl *FromTU = getTuDecl("extern int x;", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
+ Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX));
+ }
+ VarDecl *Imported2;
+ {
+ Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
+ Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX));
+ }
+ EXPECT_EQ(Imported1->getCanonicalDecl(), Imported2->getCanonicalDecl());
+ EXPECT_FALSE(Imported2->isUsed(false));
+ {
+ Decl *FromTU =
+ getTuDecl("extern int x; int f() { return x; }", Lang_CXX, "input2.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ Import(FromD, Lang_CXX);
+ }
+ EXPECT_TRUE(Imported2->isUsed(false));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag2) {
+ auto Pattern = varDecl(hasName("x"));
+ VarDecl *ExistingD;
+ {
+ Decl *ToTU = getToTuDecl("int x = 1;", Lang_CXX);
+ ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern);
+ }
+ EXPECT_FALSE(ExistingD->isUsed(false));
+ {
+ Decl *FromTU = getTuDecl(
+ "int x = 1; int f() { return x; }", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ Import(FromD, Lang_CXX);
+ }
+ EXPECT_TRUE(ExistingD->isUsed(false));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag3) {
+ auto Pattern = varDecl(hasName("a"));
+ VarDecl *ExistingD;
+ {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ struct A {
+ static const int a = 1;
+ };
+ )", Lang_CXX);
+ ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern);
+ }
+ EXPECT_FALSE(ExistingD->isUsed(false));
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct A {
+ static const int a = 1;
+ };
+ const int *f() { return &A::a; } // requires storage,
+ // thus used flag will be set
+ )", Lang_CXX, "input1.cc");
+ auto *FromFunD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
+ ASSERT_TRUE(FromD->isUsed(false));
+ Import(FromFunD, Lang_CXX);
+ }
+ EXPECT_TRUE(ExistingD->isUsed(false));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ReimportWithUsedFlag) {
+ auto Pattern = varDecl(hasName("x"));
+
+ Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
+
+ auto *Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX));
+
+ ASSERT_FALSE(Imported1->isUsed(false));
+
+ FromD->setIsUsed();
+ auto *Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX));
+
+ EXPECT_EQ(Imported1, Imported2);
+ EXPECT_TRUE(Imported2->isUsed(false));
+}
+
+struct ImportFunctions : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) {
+ Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ auto *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Proto
+
+ Decl *ImportedD = Import(From, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == To0);
+ EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
+ EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) {
+ Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ auto *From =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Def
+
+ Decl *ImportedD = Import(From, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == To1);
+ EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
+ EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) {
+ auto Code =
+ R"(
+ struct B { virtual void f(); };
+ void B::f() {}
+ struct D : B { void f(); };
+ )";
+ auto Pattern =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
+ Decl *FromTU = getTuDecl(Code, Lang_CXX);
+ CXXMethodDecl *Proto =
+ FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
+
+ ASSERT_EQ(Proto->size_overridden_methods(), 1u);
+ CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX));
+ EXPECT_EQ(To->size_overridden_methods(), 1u);
+}
+
+TEST_P(ImportFunctions, VirtualFlagShouldBePreservedWhenImportingPrototype) {
+ auto Code =
+ R"(
+ struct B { virtual void f(); };
+ void B::f() {}
+ )";
+ auto Pattern =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ Decl *FromTU = getTuDecl(Code, Lang_CXX);
+ CXXMethodDecl *Proto =
+ FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
+ CXXMethodDecl *Def = LastDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
+
+ ASSERT_TRUE(Proto->isVirtual());
+ ASSERT_TRUE(Def->isVirtual());
+ CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX));
+ EXPECT_TRUE(To->isVirtual());
+}
+
+TEST_P(ImportFunctions,
+ ImportDefinitionIfThereIsAnExistingDefinitionAndFwdDecl) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ void f() {}
+ void f();
+ )",
+ Lang_CXX);
+ ASSERT_EQ(1u,
+ DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
+ return FD->doesThisDeclarationHaveABody();
+ }).match(ToTU, functionDecl()));
+
+ Decl *FromTU = getTuDecl("void f() {}", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ Import(FromD, Lang_CXX);
+
+ EXPECT_EQ(1u,
+ DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
+ return FD->doesThisDeclarationHaveABody();
+ }).match(ToTU, functionDecl()));
+}
+
+TEST_P(ImportFunctions, ImportOverriddenMethodTwice) {
+ auto Code =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ )";
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto DFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
+
+ Decl *FromTU0 = getTuDecl(Code, Lang_CXX);
+ auto *DF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
+ Import(DF, Lang_CXX);
+
+ Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *BF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
+ Import(BF, Lang_CXX);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
+}
+
+TEST_P(ImportFunctions, ImportOverriddenMethodTwiceDefinitionFirst) {
+ auto CodeWithoutDef =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ )";
+ auto CodeWithDef =
+ R"(
+ struct B { virtual void f(){}; };
+ struct D:B { void f(){}; };
+ )";
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto DFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
+ auto BFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
+ auto DFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
+ auto FDefAllP = cxxMethodDecl(hasName("f"), isDefinition());
+
+ {
+ Decl *FromTU = getTuDecl(CodeWithDef, Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, DFP);
+ Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX, "input1.cc");
+ auto *FromB = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, BFP);
+ Import(FromB, Lang_CXX);
+ }
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FDefAllP), 2u);
+}
+
+TEST_P(ImportFunctions, ImportOverriddenMethodTwiceOutOfClassDef) {
+ auto Code =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ void B::f(){};
+ )";
+
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto BFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
+ auto DFP = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))),
+ unless(isDefinition()));
+
+ Decl *FromTU0 = getTuDecl(Code, Lang_CXX);
+ auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
+ Import(D, Lang_CXX);
+
+ Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *B = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
+ Import(B, Lang_CXX);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);
+
+ auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("B")));
+ auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
+ auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
+ ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
+
+ // The definition should be out-of-class.
+ EXPECT_NE(ToBFInClass, ToBFOutOfClass);
+ EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
+ ToBFOutOfClass->getLexicalDeclContext());
+ EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
+ EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);
+
+ // Check that the redecl chain is intact.
+ EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
+}
+
+TEST_P(ImportFunctions,
+ ImportOverriddenMethodTwiceOutOfClassDefInSeparateCode) {
+ auto CodeTU0 =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ )";
+ auto CodeTU1 =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ void B::f(){}
+ void D::f(){}
+ void foo(B &b, D &d) { b.f(); d.f(); }
+ )";
+
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto BFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
+ auto DFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
+ auto DFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
+ auto FooDef = functionDecl(hasName("foo"));
+
+ {
+ Decl *FromTU0 = getTuDecl(CodeTU0, Lang_CXX, "input0.cc");
+ auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
+ Import(D, Lang_CXX);
+ }
+
+ {
+ Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX, "input1.cc");
+ auto *Foo = FirstDeclMatcher<FunctionDecl>().match(FromTU1, FooDef);
+ Import(Foo, Lang_CXX);
+ }
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 0u);
+
+ auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("B")));
+ auto *ToD = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("D")));
+ auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
+ auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
+ ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
+ auto *ToDFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, DFP);
+ auto *ToDFOutOfClass = LastDeclMatcher<CXXMethodDecl>().match(
+ ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
+
+ // The definition should be out-of-class.
+ EXPECT_NE(ToBFInClass, ToBFOutOfClass);
+ EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
+ ToBFOutOfClass->getLexicalDeclContext());
+ EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
+ EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);
+
+ EXPECT_NE(ToDFInClass, ToDFOutOfClass);
+ EXPECT_NE(ToDFInClass->getLexicalDeclContext(),
+ ToDFOutOfClass->getLexicalDeclContext());
+ EXPECT_EQ(ToDFOutOfClass->getDeclContext(), ToD);
+ EXPECT_EQ(ToDFOutOfClass->getLexicalDeclContext(), ToTU);
+
+ // Check that the redecl chain is intact.
+ EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
+ EXPECT_EQ(ToDFOutOfClass->getPreviousDecl(), ToDFInClass);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportVariableChainInC) {
+ std::string Code = "static int v; static int v = 0;";
+ auto Pattern = varDecl(hasName("v"));
+
+ TranslationUnitDecl *FromTu = getTuDecl(Code, Lang_C, "input0.c");
+
+ auto *From0 = FirstDeclMatcher<VarDecl>().match(FromTu, Pattern);
+ auto *From1 = LastDeclMatcher<VarDecl>().match(FromTu, Pattern);
+
+ auto *To0 = Import(From0, Lang_C);
+ auto *To1 = Import(From1, Lang_C);
+
+ EXPECT_TRUE(To0);
+ ASSERT_TRUE(To1);
+ EXPECT_NE(To0, To1);
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportFunctions, ImportFromDifferentScopedAnonNamespace) {
+ TranslationUnitDecl *FromTu = getTuDecl(
+ "namespace NS0 { namespace { void f(); } }"
+ "namespace NS1 { namespace { void f(); } }",
+ Lang_CXX, "input0.cc");
+ auto Pattern = functionDecl(hasName("f"));
+
+ auto *FromF0 = FirstDeclMatcher<FunctionDecl>().match(FromTu, Pattern);
+ auto *FromF1 = LastDeclMatcher<FunctionDecl>().match(FromTu, Pattern);
+
+ auto *ToF0 = Import(FromF0, Lang_CXX);
+ auto *ToF1 = Import(FromF1, Lang_CXX);
+
+ EXPECT_TRUE(ToF0);
+ ASSERT_TRUE(ToF1);
+ EXPECT_NE(ToF0, ToF1);
+ EXPECT_FALSE(ToF1->getPreviousDecl());
+}
+
+TEST_P(ImportFunctions, ImportFunctionFromUnnamedNamespace) {
+ {
+ Decl *FromTU = getTuDecl("namespace { void f() {} } void g0() { f(); }",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("g0")));
+
+ Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU =
+ getTuDecl("namespace { void f() { int a; } } void g1() { f(); }",
+ Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("g1")));
+ Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))),
+ 2u);
+}
+
+TEST_P(ImportFunctions, ImportImplicitFunctionsInLambda) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ void foo() {
+ (void)[]() { ; };
+ }
+ )",
+ Lang_CXX11);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+ auto *ToD = Import(FromD, Lang_CXX);
+ EXPECT_TRUE(ToD);
+ CXXRecordDecl *LambdaRec =
+ cast<LambdaExpr>(cast<CStyleCastExpr>(
+ *cast<CompoundStmt>(ToD->getBody())->body_begin())
+ ->getSubExpr())
+ ->getLambdaClass();
+ EXPECT_TRUE(LambdaRec->getDestructor());
+}
+
+TEST_P(ImportFunctions,
+ CallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct X {
+ template <typename T>
+ void foo(){}
+ };
+ void f() {
+ X x;
+ x.foo<int>();
+ }
+ )",
+ Lang_CXX);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ auto *ToD = Import(FromD, Lang_CXX);
+ EXPECT_TRUE(ToD);
+ EXPECT_TRUE(MatchVerifier<FunctionDecl>().match(
+ ToD, functionDecl(hasName("f"), hasDescendant(declRefExpr()))));
+}
+
+TEST_P(ImportFunctions,
+ DependentCallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct X {
+ template <typename T>
+ void foo(){}
+ };
+ template <typename T>
+ void f() {
+ X x;
+ x.foo<T>();
+ }
+ void g() {
+ f<int>();
+ }
+ )",
+ Lang_CXX);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("g")));
+ auto *ToD = Import(FromD, Lang_CXX);
+ EXPECT_TRUE(ToD);
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_TRUE(MatchVerifier<TranslationUnitDecl>().match(
+ ToTU, translationUnitDecl(hasDescendant(
+ functionDecl(hasName("f"), hasDescendant(declRefExpr()))))));
+}
+
+struct ImportFunctionTemplates : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) {
+ auto Code =
+ R"(
+ class X {
+ template <class T>
+ void f(T t);
+ };
+ )";
+ Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU1, functionTemplateDecl(hasName("f")));
+ auto *ToD1 = Import(FromD1, Lang_CXX);
+ Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc");
+ auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU2, functionTemplateDecl(hasName("f")));
+ auto *ToD2 = Import(FromD2, Lang_CXX);
+ EXPECT_EQ(ToD1, ToD2);
+}
+
+TEST_P(ImportFunctionTemplates,
+ ImportFunctionTemplateWithDefInRecordDeclTwice) {
+ auto Code =
+ R"(
+ class X {
+ template <class T>
+ void f(T t);
+ };
+ template <class T>
+ void X::f(T t) {};
+ )";
+ Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU1, functionTemplateDecl(hasName("f")));
+ auto *ToD1 = Import(FromD1, Lang_CXX);
+ Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc");
+ auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU2, functionTemplateDecl(hasName("f")));
+ auto *ToD2 = Import(FromD2, Lang_CXX);
+ EXPECT_EQ(ToD1, ToD2);
+}
+
+TEST_P(ImportFunctionTemplates,
+ ImportFunctionWhenThereIsAFunTemplateWithSameName) {
+ getToTuDecl(
+ R"(
+ template <typename T>
+ void foo(T) {}
+ void foo();
+ )",
+ Lang_CXX);
+ Decl *FromTU = getTuDecl("void foo();", Lang_CXX);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+ auto *ImportedD = Import(FromD, Lang_CXX);
+ EXPECT_TRUE(ImportedD);
+}
+
+TEST_P(ImportFunctionTemplates,
+ ImportConstructorWhenThereIsAFunTemplateWithSameName) {
+ auto Code =
+ R"(
+ struct Foo {
+ template <typename T>
+ Foo(T) {}
+ Foo();
+ };
+ )";
+ getToTuDecl(Code, Lang_CXX);
+ Decl *FromTU = getTuDecl(Code, Lang_CXX);
+ auto *FromD =
+ LastDeclMatcher<CXXConstructorDecl>().match(FromTU, cxxConstructorDecl());
+ auto *ImportedD = Import(FromD, Lang_CXX);
+ EXPECT_TRUE(ImportedD);
+}
+
+TEST_P(ImportFunctionTemplates,
+ ImportOperatorWhenThereIsAFunTemplateWithSameName) {
+ getToTuDecl(
+ R"(
+ template <typename T>
+ void operator<(T,T) {}
+ struct X{};
+ void operator<(X, X);
+ )",
+ Lang_CXX);
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct X{};
+ void operator<(X, X);
+ )",
+ Lang_CXX);
+ auto *FromD = LastDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasOverloadedOperatorName("<")));
+ auto *ImportedD = Import(FromD, Lang_CXX);
+ EXPECT_TRUE(ImportedD);
+}
+
+struct ImportFriendFunctions : ImportFunctions {};
+
+TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *FromTU = getTuDecl("struct X { friend void f(); };"
+ "void f();",
+ Lang_CXX,
+ "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
+ auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
+}
+
+TEST_P(ImportFriendFunctions,
+ ImportFriendFunctionRedeclChainProto_OutOfClassProtoFirst) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *FromTU = getTuDecl("void f();"
+ "struct X { friend void f(); };",
+ Lang_CXX, "input0.cc");
+ auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
+ auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
+}
+
+TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDef) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *FromTU = getTuDecl("struct X { friend void f(){} };"
+ "void f();",
+ Lang_CXX,
+ "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
+ auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
+}
+
+TEST_P(ImportFriendFunctions,
+ ImportFriendFunctionRedeclChainDef_OutOfClassDef) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *FromTU = getTuDecl("struct X { friend void f(); };"
+ "void f(){}",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
+ auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
+}
+
+TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDefWithClass) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ class X;
+ void f(X *x){}
+ class X{
+ friend void f(X *x);
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
+ auto *InClassFD = cast<FunctionDecl>(FirstDeclMatcher<FriendDecl>()
+ .match(ToTU, friendDecl())
+ ->getFriendDecl());
+ EXPECT_FALSE(InClassFD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(InClassFD->getPreviousDecl(), ImportedD);
+ // The parameters must refer the same type
+ EXPECT_EQ((*InClassFD->param_begin())->getOriginalType(),
+ (*ImportedD->param_begin())->getOriginalType());
+}
+
+TEST_P(ImportFriendFunctions,
+ ImportFriendFunctionRedeclChainDefWithClass_ImportTheProto) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ class X;
+ void f(X *x){}
+ class X{
+ friend void f(X *x);
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
+ auto *OutOfClassFD = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(unless(hasParent(friendDecl()))));
+
+ EXPECT_TRUE(OutOfClassFD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(ImportedD->getPreviousDecl(), OutOfClassFD);
+ // The parameters must refer the same type
+ EXPECT_EQ((*OutOfClassFD->param_begin())->getOriginalType(),
+ (*ImportedD->param_begin())->getOriginalType());
+}
+
+TEST_P(ImportFriendFunctions, ImportFriendFunctionFromMultipleTU) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ FunctionDecl *ImportedD;
+ {
+ Decl *FromTU =
+ getTuDecl("struct X { friend void f(){} };", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ }
+ FunctionDecl *ImportedD1;
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD1 = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
+ EXPECT_FALSE(ImportedD1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD);
+}
+
+TEST_P(ImportFriendFunctions, Lookup) {
+ auto FunctionPattern = functionDecl(hasName("f"));
+ auto ClassPattern = cxxRecordDecl(hasName("X"));
+
+ TranslationUnitDecl *FromTU =
+ getTuDecl("struct X { friend void f(); };", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ {
+ auto FromName = FromD->getDeclName();
+ auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
+ auto LookupRes = Class->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 0u);
+ LookupRes = FromTU->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+ }
+
+ auto *ToD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ auto ToName = ToD->getDeclName();
+
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
+ auto LookupRes = Class->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 0u);
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 1u);
+ auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+}
+
+TEST_P(ImportFriendFunctions, LookupWithProtoAfter) {
+ auto FunctionPattern = functionDecl(hasName("f"));
+ auto ClassPattern = cxxRecordDecl(hasName("X"));
+
+ TranslationUnitDecl *FromTU = getTuDecl(
+ "struct X { friend void f(); };"
+ // This proto decl makes f available to normal
+ // lookup, otherwise it is hidden.
+ // Normal C++ lookup (implemented in
+ // `clang::Sema::CppLookupName()` and in `LookupDirect()`)
+ // returns the found `NamedDecl` only if the set IDNS is matched
+ "void f();",
+ Lang_CXX, "input0.cc");
+ auto *FromFriend =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ auto *FromNormal =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_FALSE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+
+ auto FromName = FromFriend->getDeclName();
+ auto *FromClass =
+ FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
+ auto LookupRes = FromClass->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 0u);
+ LookupRes = FromTU->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+
+ auto *ToFriend = cast<FunctionDecl>(Import(FromFriend, Lang_CXX));
+ auto ToName = ToFriend->getDeclName();
+
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
+ LookupRes = ToClass->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 0u);
+ LookupRes = ToTU->noload_lookup(ToName);
+ // Test is disabled because this result is 2.
+ EXPECT_EQ(LookupRes.size(), 1u);
+
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
+ ToFriend = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ auto *ToNormal = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_FALSE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+}
+
+TEST_P(ImportFriendFunctions, LookupWithProtoBefore) {
+ auto FunctionPattern = functionDecl(hasName("f"));
+ auto ClassPattern = cxxRecordDecl(hasName("X"));
+
+ TranslationUnitDecl *FromTU = getTuDecl(
+ "void f();"
+ "struct X { friend void f(); };",
+ Lang_CXX, "input0.cc");
+ auto *FromNormal =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ auto *FromFriend =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+
+ auto FromName = FromNormal->getDeclName();
+ auto *FromClass =
+ FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
+ auto LookupRes = FromClass->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 0u);
+ LookupRes = FromTU->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+
+ auto *ToNormal = cast<FunctionDecl>(Import(FromNormal, Lang_CXX));
+ auto ToName = ToNormal->getDeclName();
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
+ LookupRes = ToClass->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 0u);
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
+ ToNormal = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ auto *ToFriend = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+}
+
+TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ TranslationUnitDecl *FromNormalTU =
+ getTuDecl("void f();", Lang_CXX, "input0.cc");
+ auto *FromNormalF =
+ FirstDeclMatcher<FunctionDecl>().match(FromNormalTU, Pattern);
+ TranslationUnitDecl *FromFriendTU =
+ getTuDecl("class X { friend void f(); };", Lang_CXX, "input1.cc");
+ auto *FromFriendF =
+ FirstDeclMatcher<FunctionDecl>().match(FromFriendTU, Pattern);
+ auto FromNormalName = FromNormalF->getDeclName();
+ auto FromFriendName = FromFriendF->getDeclName();
+
+ ASSERT_TRUE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_FALSE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ auto LookupRes = FromNormalTU->noload_lookup(FromNormalName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+ LookupRes = FromFriendTU->noload_lookup(FromFriendName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+
+ auto *ToNormalF = cast<FunctionDecl>(Import(FromNormalF, Lang_CXX));
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto ToName = ToNormalF->getDeclName();
+ EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+
+ auto *ToFriendF = cast<FunctionDecl>(Import(FromFriendF, Lang_CXX));
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+
+ EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+
+ EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+}
+
+TEST_P(ImportFriendFunctions, ImportFriendList) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ "struct X { friend void f(); };"
+ "void f();",
+ Lang_CXX, "input0.cc");
+ auto *FromFriendF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTU, friendDecl());
+ auto FromFriends = FromClass->friends();
+ unsigned int FrN = 0;
+ for (auto Fr : FromFriends) {
+ ASSERT_EQ(Fr, FromFriend);
+ ++FrN;
+ }
+ ASSERT_EQ(FrN, 1u);
+
+ Import(FromFriendF, Lang_CXX);
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X")));
+ auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ auto ToFriends = ToClass->friends();
+ FrN = 0;
+ for (auto Fr : ToFriends) {
+ EXPECT_EQ(Fr, ToFriend);
+ ++FrN;
+ }
+ EXPECT_EQ(FrN, 1u);
+}
+
+AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>,
+ InnerMatcher) {
+ if (auto *Typedef = Node.getTypedefNameForAnonDecl())
+ return InnerMatcher.matches(*Typedef, Finder, Builder);
+ return false;
+}
+
+TEST_P(ImportDecl, ImportEnumSequential) {
+ CodeFiles Samples{{"main.c",
+ {"void foo();"
+ "void moo();"
+ "int main() { foo(); moo(); }",
+ Lang_C}},
+
+ {"foo.c",
+ {"typedef enum { THING_VALUE } thing_t;"
+ "void conflict(thing_t type);"
+ "void foo() { (void)THING_VALUE; }"
+ "void conflict(thing_t type) {}",
+ Lang_C}},
+
+ {"moo.c",
+ {"typedef enum { THING_VALUE } thing_t;"
+ "void conflict(thing_t type);"
+ "void moo() { conflict(THING_VALUE); }",
+ Lang_C}}};
+
+ auto VerificationMatcher =
+ enumDecl(has(enumConstantDecl(hasName("THING_VALUE"))),
+ hasTypedefForAnonDecl(hasName("thing_t")));
+
+ ImportAction ImportFoo{"foo.c", "main.c", functionDecl(hasName("foo"))},
+ ImportMoo{"moo.c", "main.c", functionDecl(hasName("moo"))};
+
+ testImportSequence(
+ Samples, {ImportFoo, ImportMoo}, // "foo", them "moo".
+ // Just check that there is only one enum decl in the result AST.
+ "main.c", enumDecl(), VerificationMatcher);
+
+ // For different import order, result should be the same.
+ testImportSequence(
+ Samples, {ImportMoo, ImportFoo}, // "moo", them "foo".
+ // Check that there is only one enum decl in the result AST.
+ "main.c", enumDecl(), VerificationMatcher);
+}
+
+TEST_P(ImportDecl, ImportFieldOrder) {
+ MatchVerifier<Decl> Verifier;
+ testImport("struct declToImport {"
+ " int b = a + 2;"
+ " int a = 5;"
+ "};",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ recordDecl(hasFieldOrder({"b", "a"})));
+}
+
+const internal::VariadicDynCastAllOfMatcher<Expr, DependentScopeDeclRefExpr>
+ dependentScopeDeclRefExpr;
+
+TEST_P(ImportExpr, DependentScopeDeclRefExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport("template <typename T> struct S { static T foo; };"
+ "template <typename T> void declToImport() {"
+ " (void) S<T>::foo;"
+ "}"
+ "void instantiate() { declToImport<int>(); }"
+ "template <typename T> T S<T>::foo;",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionTemplateDecl(has(functionDecl(has(compoundStmt(
+ has(cStyleCastExpr(has(dependentScopeDeclRefExpr())))))))));
+
+ testImport("template <typename T> struct S {"
+ "template<typename S> static void foo(){};"
+ "};"
+ "template <typename T> void declToImport() {"
+ " S<T>::template foo<T>();"
+ "}"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionTemplateDecl(has(functionDecl(has(compoundStmt(
+ has(callExpr(has(dependentScopeDeclRefExpr())))))))));
+}
+
+const internal::VariadicDynCastAllOfMatcher<Type, DependentNameType>
+ dependentNameType;
+
+TEST_P(ImportExpr, DependentNameType) {
+ MatchVerifier<Decl> Verifier;
+ testImport("template <typename T> struct declToImport {"
+ " typedef typename T::type dependent_name;"
+ "};",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ classTemplateDecl(has(
+ cxxRecordDecl(has(typedefDecl(has(dependentNameType())))))));
+}
+
+TEST_P(ImportExpr, UnresolvedMemberExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport("struct S { template <typename T> void mem(); };"
+ "template <typename U> void declToImport() {"
+ " S s;"
+ " s.mem<U>();"
+ "}"
+ "void instantiate() { declToImport<int>(); }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionTemplateDecl(has(functionDecl(has(
+ compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
+}
+
+class ImportImplicitMethods : public ASTImporterOptionSpecificTestBase {
+public:
+ static constexpr auto DefaultCode = R"(
+ struct A { int x; };
+ void f() {
+ A a;
+ A a1(a);
+ A a2(A{});
+ a = a1;
+ a = A{};
+ a.~A();
+ })";
+
+ template <typename MatcherType>
+ void testImportOf(
+ const MatcherType &MethodMatcher, const char *Code = DefaultCode) {
+ test(MethodMatcher, Code, /*ExpectedCount=*/1u);
+ }
+
+ template <typename MatcherType>
+ void testNoImportOf(
+ const MatcherType &MethodMatcher, const char *Code = DefaultCode) {
+ test(MethodMatcher, Code, /*ExpectedCount=*/0u);
+ }
+
+private:
+ template <typename MatcherType>
+ void test(const MatcherType &MethodMatcher,
+ const char *Code, unsigned int ExpectedCount) {
+ auto ClassMatcher = cxxRecordDecl(unless(isImplicit()));
+
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, ClassMatcher);
+
+ ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 1u);
+
+ {
+ CXXMethodDecl *Method =
+ FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher);
+ ToClass->removeDecl(Method);
+ SharedStatePtr->getLookupTable()->remove(Method);
+ }
+
+ ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u);
+
+ Decl *ImportedClass = nullptr;
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input1.cc");
+ auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, ClassMatcher);
+ ImportedClass = Import(FromClass, Lang_CXX11);
+ }
+
+ EXPECT_EQ(ToClass, ImportedClass);
+ EXPECT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher),
+ ExpectedCount);
+ }
+};
+
+TEST_P(ImportImplicitMethods, DefaultConstructor) {
+ testImportOf(cxxConstructorDecl(isDefaultConstructor()));
+}
+
+TEST_P(ImportImplicitMethods, CopyConstructor) {
+ testImportOf(cxxConstructorDecl(isCopyConstructor()));
+}
+
+TEST_P(ImportImplicitMethods, MoveConstructor) {
+ testImportOf(cxxConstructorDecl(isMoveConstructor()));
+}
+
+TEST_P(ImportImplicitMethods, Destructor) {
+ testImportOf(cxxDestructorDecl());
+}
+
+TEST_P(ImportImplicitMethods, CopyAssignment) {
+ testImportOf(cxxMethodDecl(isCopyAssignmentOperator()));
+}
+
+TEST_P(ImportImplicitMethods, MoveAssignment) {
+ testImportOf(cxxMethodDecl(isMoveAssignmentOperator()));
+}
+
+TEST_P(ImportImplicitMethods, DoNotImportUserProvided) {
+ auto Code = R"(
+ struct A { A() { int x; } };
+ )";
+ testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
+}
+
+TEST_P(ImportImplicitMethods, DoNotImportDefault) {
+ auto Code = R"(
+ struct A { A() = default; };
+ )";
+ testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
+}
+
+TEST_P(ImportImplicitMethods, DoNotImportDeleted) {
+ auto Code = R"(
+ struct A { A() = delete; };
+ )";
+ testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
+}
+
+TEST_P(ImportImplicitMethods, DoNotImportOtherMethod) {
+ auto Code = R"(
+ struct A { void f() { } };
+ )";
+ testNoImportOf(cxxMethodDecl(hasName("f")), Code);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentRecord) {
+ Decl *ToR1;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { };", Lang_CXX, "input0.cc");
+ auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A")));
+
+ ToR1 = Import(FromR, Lang_CXX);
+ }
+
+ Decl *ToR2;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { };", Lang_CXX, "input1.cc");
+ auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A")));
+
+ ToR2 = Import(FromR, Lang_CXX);
+ }
+
+ EXPECT_EQ(ToR1, ToR2);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentRecord) {
+ Decl *ToR1;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { int x; };", Lang_CXX, "input0.cc");
+ auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A")));
+ ToR1 = Import(FromR, Lang_CXX);
+ }
+ Decl *ToR2;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { unsigned x; };", Lang_CXX, "input1.cc");
+ auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A")));
+ ToR2 = Import(FromR, Lang_CXX);
+ }
+ EXPECT_NE(ToR1, ToR2);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentField) {
+ Decl *ToF1;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { int x; };", Lang_CXX, "input0.cc");
+ auto *FromF = FirstDeclMatcher<FieldDecl>().match(
+ FromTU, fieldDecl(hasName("x")));
+ ToF1 = Import(FromF, Lang_CXX);
+ }
+ Decl *ToF2;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { int x; };", Lang_CXX, "input1.cc");
+ auto *FromF = FirstDeclMatcher<FieldDecl>().match(
+ FromTU, fieldDecl(hasName("x")));
+ ToF2 = Import(FromF, Lang_CXX);
+ }
+ EXPECT_EQ(ToF1, ToF2);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentField) {
+ Decl *ToF1;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { int x; };", Lang_CXX, "input0.cc");
+ auto *FromF = FirstDeclMatcher<FieldDecl>().match(
+ FromTU, fieldDecl(hasName("x")));
+ ToF1 = Import(FromF, Lang_CXX);
+ }
+ Decl *ToF2;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { unsigned x; };", Lang_CXX, "input1.cc");
+ auto *FromF = FirstDeclMatcher<FieldDecl>().match(
+ FromTU, fieldDecl(hasName("x")));
+ ToF2 = Import(FromF, Lang_CXX);
+ }
+ EXPECT_NE(ToF1, ToF2);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentMethod) {
+ Decl *ToM1;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { void x(); }; void A::x() { }", Lang_CXX, "input0.cc");
+ auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("x"), isDefinition()));
+ ToM1 = Import(FromM, Lang_CXX);
+ }
+ Decl *ToM2;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { void x(); }; void A::x() { }", Lang_CXX, "input1.cc");
+ auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("x"), isDefinition()));
+ ToM2 = Import(FromM, Lang_CXX);
+ }
+ EXPECT_EQ(ToM1, ToM2);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentMethod) {
+ Decl *ToM1;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { void x(); }; void A::x() { }",
+ Lang_CXX, "input0.cc");
+ auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("x"), isDefinition()));
+ ToM1 = Import(FromM, Lang_CXX);
+ }
+ Decl *ToM2;
+ {
+ Decl *FromTU = getTuDecl(
+ "struct A { void x() const; }; void A::x() const { }",
+ Lang_CXX, "input1.cc");
+ auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("x"), isDefinition()));
+ ToM2 = Import(FromM, Lang_CXX);
+ }
+ EXPECT_NE(ToM1, ToM2);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportUnnamedStructsWithRecursingField) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct A {
+ struct {
+ struct A *next;
+ } entry0;
+ struct {
+ struct A *next;
+ } entry1;
+ };
+ )",
+ Lang_C, "input0.cc");
+ auto *From =
+ FirstDeclMatcher<RecordDecl>().match(FromTU, recordDecl(hasName("A")));
+
+ Import(From, Lang_C);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *Entry0 =
+ FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0")));
+ auto *Entry1 =
+ FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry1")));
+ auto *R0 = getRecordDecl(Entry0);
+ auto *R1 = getRecordDecl(Entry1);
+ EXPECT_NE(R0, R1);
+ EXPECT_TRUE(MatchVerifier<RecordDecl>().match(
+ R0, recordDecl(has(fieldDecl(hasName("next"))))));
+ EXPECT_TRUE(MatchVerifier<RecordDecl>().match(
+ R1, recordDecl(has(fieldDecl(hasName("next"))))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportUnnamedFieldsInCorrectOrder) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ void f(int X, int Y, bool Z) {
+ (void)[X, Y, Z] { (void)Z; };
+ }
+ )",
+ Lang_CXX11, "input0.cc");
+ auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ auto *ToF = cast_or_null<FunctionDecl>(Import(FromF, Lang_CXX11));
+ EXPECT_TRUE(ToF);
+
+ CXXRecordDecl *FromLambda =
+ cast<LambdaExpr>(cast<CStyleCastExpr>(cast<CompoundStmt>(
+ FromF->getBody())->body_front())->getSubExpr())->getLambdaClass();
+
+ auto *ToLambda = cast_or_null<CXXRecordDecl>(Import(FromLambda, Lang_CXX11));
+ EXPECT_TRUE(ToLambda);
+
+ // Check if the fields of the lambda class are imported in correct order.
+ unsigned FromIndex = 0u;
+ for (auto *FromField : FromLambda->fields()) {
+ ASSERT_FALSE(FromField->getDeclName());
+ auto *ToField = cast_or_null<FieldDecl>(Import(FromField, Lang_CXX11));
+ EXPECT_TRUE(ToField);
+ Optional<unsigned> ToIndex = ASTImporter::getFieldIndex(ToField);
+ EXPECT_TRUE(ToIndex);
+ EXPECT_EQ(*ToIndex, FromIndex);
+ ++FromIndex;
+ }
+
+ EXPECT_EQ(FromIndex, 3u);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ MergeFieldDeclsOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ int a{0}; // FieldDecl with InitListExpr
+ X(char) : a(3) {} // (1)
+ X(int) {} // (2)
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ // ClassTemplateSpec with ctor (1): FieldDecl without InitlistExpr
+ X<char> xc('c');
+ }
+ )", Lang_CXX11);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ // FieldDecl without InitlistExpr:
+ auto *ToField = *ToSpec->field_begin();
+ ASSERT_TRUE(ToField);
+ ASSERT_FALSE(ToField->getInClassInitializer());
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ // ClassTemplateSpec with ctor (2): FieldDecl WITH InitlistExpr
+ X<char> xc(1);
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ // FieldDecl with InitlistExpr:
+ auto *FromField = *FromSpec->field_begin();
+ ASSERT_TRUE(FromField);
+ ASSERT_TRUE(FromField->getInClassInitializer());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ // After the import, the FieldDecl has to be merged, thus it should have the
+ // InitListExpr.
+ EXPECT_TRUE(ToField->getInClassInitializer());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ MergeFunctionOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ void f() {}
+ void g() {}
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ X<char> x;
+ x.f();
+ }
+ )", Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ X<char> x;
+ x.g();
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ auto FunPattern = functionDecl(hasName("g"),
+ hasParent(classTemplateSpecializationDecl()));
+ auto *FromFun =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, FunPattern);
+ auto *ToFun =
+ FirstDeclMatcher<FunctionDecl>().match(ToTU, FunPattern);
+ ASSERT_TRUE(FromFun->hasBody());
+ ASSERT_FALSE(ToFun->hasBody());
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_TRUE(ToFun->hasBody());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ODRViolationOfClassTemplateSpecializationsShouldBeReported) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {};
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ template <>
+ struct X<char> {
+ int a;
+ };
+ void foo() {
+ X<char> x;
+ }
+ )",
+ Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ template <>
+ struct X<char> {
+ int b;
+ };
+ void foo() {
+ X<char> x;
+ }
+ )",
+ Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+
+ // We expect one (ODR) warning during the import.
+ EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+
+ // The second specialization is different from the first, thus it violates
+ // ODR, consequently we expect to keep the first specialization only, which is
+ // already in the "To" context.
+ EXPECT_FALSE(ImportedSpec);
+ EXPECT_EQ(1u,
+ DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X"))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ MergeCtorOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ X(char) {}
+ X(int) {}
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ X<char> x('c');
+ }
+ )", Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ X<char> x(1);
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ // Match the void(int) ctor.
+ auto CtorPattern =
+ cxxConstructorDecl(hasParameter(0, varDecl(hasType(asString("int")))),
+ hasParent(classTemplateSpecializationDecl()));
+ auto *FromCtor =
+ FirstDeclMatcher<CXXConstructorDecl>().match(FromTU, CtorPattern);
+ auto *ToCtor =
+ FirstDeclMatcher<CXXConstructorDecl>().match(ToTU, CtorPattern);
+ ASSERT_TRUE(FromCtor->hasBody());
+ ASSERT_FALSE(ToCtor->hasBody());
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_TRUE(ToCtor->hasBody());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ClassTemplatePartialSpecializationsShouldNotBeDuplicated) {
+ auto Code =
+ R"(
+ // primary template
+ template<class T1, class T2, int I>
+ class A {};
+
+ // partial specialization
+ template<class T, int I>
+ class A<T, T*, I> {};
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromSpec =
+ FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
+ FromTU, classTemplatePartialSpecializationDecl());
+ auto *ToSpec =
+ FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl()));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ClassTemplateSpecializationsShouldNotBeDuplicated) {
+ auto Code =
+ R"(
+ // primary template
+ template<class T1, class T2, int I>
+ class A {};
+
+ // full specialization
+ template<>
+ class A<int, int, 1> {};
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl());
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl()));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ClassTemplateFullAndPartialSpecsShouldNotBeMixed) {
+ std::string PrimaryTemplate =
+ R"(
+ template<class T1, class T2, int I>
+ class A {};
+ )";
+ auto PartialSpec =
+ R"(
+ template<class T, int I>
+ class A<T, T*, I> {};
+ )";
+ auto FullSpec =
+ R"(
+ template<>
+ class A<int, int, 1> {};
+ )";
+ Decl *ToTU = getToTuDecl(PrimaryTemplate + FullSpec, Lang_CXX11);
+ Decl *FromTU = getTuDecl(PrimaryTemplate + PartialSpec, Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_TRUE(ImportedSpec);
+ // Check the number of partial specializations.
+ EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl()));
+ // Check the number of full specializations.
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(
+ unless(classTemplatePartialSpecializationDecl()))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ InitListExprValueKindShouldBeImported) {
+ Decl *TU = getTuDecl(
+ R"(
+ const int &init();
+ void foo() { const int &a{init()}; }
+ )", Lang_CXX11, "input0.cc");
+ auto *FromD = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("a")));
+ ASSERT_TRUE(FromD->getAnyInitializer());
+ auto *InitExpr = FromD->getAnyInitializer();
+ ASSERT_TRUE(InitExpr);
+ ASSERT_TRUE(InitExpr->isGLValue());
+
+ auto *ToD = Import(FromD, Lang_CXX11);
+ EXPECT_TRUE(ToD);
+ auto *ToInitExpr = cast<VarDecl>(ToD)->getAnyInitializer();
+ EXPECT_TRUE(ToInitExpr);
+ EXPECT_TRUE(ToInitExpr->isGLValue());
+}
+
+struct ImportVariables : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportVariables, ImportOfOneDeclBringsInTheWholeChain) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct A {
+ static const int a = 1 + 2;
+ };
+ const int A::a;
+ )", Lang_CXX, "input1.cc");
+
+ auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with init
+ auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with definition
+ ASSERT_NE(FromDWithInit, FromDWithDef);
+ ASSERT_EQ(FromDWithDef->getPreviousDecl(), FromDWithInit);
+
+ auto *ToD0 = cast<VarDecl>(Import(FromDWithInit, Lang_CXX11));
+ auto *ToD1 = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
+ ASSERT_TRUE(ToD0);
+ ASSERT_TRUE(ToD1);
+ EXPECT_NE(ToD0, ToD1);
+ EXPECT_EQ(ToD1->getPreviousDecl(), ToD0);
+}
+
+TEST_P(ImportVariables, InitAndDefinitionAreInDifferentTUs) {
+ auto StructA =
+ R"(
+ struct A {
+ static const int a = 1 + 2;
+ };
+ )";
+ Decl *ToTU = getToTuDecl(StructA, Lang_CXX);
+ Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a;", Lang_CXX,
+ "input1.cc");
+
+ auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with init
+ auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with definition
+ ASSERT_EQ(FromDWithInit, FromDWithDef->getPreviousDecl());
+ ASSERT_TRUE(FromDWithInit->getInit());
+ ASSERT_FALSE(FromDWithInit->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition());
+ ASSERT_FALSE(FromDWithDef->getInit());
+
+ auto *ToD = FirstDeclMatcher<VarDecl>().match(
+ ToTU, varDecl(hasName("a"))); // Decl with init
+ ASSERT_TRUE(ToD->getInit());
+ ASSERT_FALSE(ToD->getDefinition());
+
+ auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
+ EXPECT_TRUE(ImportedD->getAnyInitializer());
+ EXPECT_TRUE(ImportedD->getDefinition());
+}
+
+TEST_P(ImportVariables, InitAndDefinitionAreInTheFromContext) {
+ auto StructA =
+ R"(
+ struct A {
+ static const int a;
+ };
+ )";
+ Decl *ToTU = getToTuDecl(StructA, Lang_CXX);
+ Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a = 1 + 2;",
+ Lang_CXX, "input1.cc");
+
+ auto *FromDDeclarationOnly = FirstDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a")));
+ auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with definition and with init.
+ ASSERT_EQ(FromDDeclarationOnly, FromDWithDef->getPreviousDecl());
+ ASSERT_FALSE(FromDDeclarationOnly->getInit());
+ ASSERT_FALSE(FromDDeclarationOnly->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDWithDef->getInit());
+
+ auto *ToD = FirstDeclMatcher<VarDecl>().match(
+ ToTU, varDecl(hasName("a")));
+ ASSERT_FALSE(ToD->getInit());
+ ASSERT_FALSE(ToD->getDefinition());
+
+ auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
+ EXPECT_TRUE(ImportedD->getAnyInitializer());
+ EXPECT_TRUE(ImportedD->getDefinition());
+}
+
+struct ImportClasses : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) {
+ Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContextCXX) {
+ Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_CXX);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) {
+ Decl *FromTU0 = getTuDecl("struct A { struct X *Xp; };", Lang_C, "input0.cc");
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto FromProto = FirstDeclMatcher<RecordDecl>().match(FromTU0, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedProto = Import(FromProto, Lang_C);
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+
+struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ class A {
+ template <int I> class F {};
+ class X {
+ template <int I> friend class F;
+ };
+ };
+ )",
+ Lang_CXX, "input0.cc");
+
+ auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
+ auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("F")));
+
+ ASSERT_TRUE(FromClass);
+ ASSERT_TRUE(FromFriendClass);
+ ASSERT_NE(FromClass, FromFriendClass);
+ ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
+ ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
+ ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+ FromClass->getDescribedClassTemplate());
+
+ auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
+ auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
+
+ EXPECT_TRUE(ToClass);
+ EXPECT_TRUE(ToFriendClass);
+ EXPECT_NE(ToClass, ToFriendClass);
+ EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
+ EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
+ EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+ ToClass->getDescribedClassTemplate());
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) {
+ Decl *FromTu = getTuDecl(
+ R"(
+ class declToImport {
+ friend class declToImport;
+ };
+ )",
+ Lang_CXX, "input.cc");
+
+ auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTu, cxxRecordDecl(hasName("declToImport")));
+ auto *ToD = Import(FromD, Lang_CXX);
+ auto Pattern = cxxRecordDecl(has(friendDecl()));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+}
+
+TEST_P(ImportFriendClasses, UndeclaredFriendClassShouldNotBeVisible) {
+ Decl *FromTu = getTuDecl("class X { friend class Y; };", Lang_CXX, "from.cc");
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTu, cxxRecordDecl(hasName("X")));
+ auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
+ RecordDecl *FromRecordOfFriend =
+ const_cast<RecordDecl *>(getRecordDeclOfFriend(FromFriend));
+
+ ASSERT_EQ(FromRecordOfFriend->getDeclContext(), cast<DeclContext>(FromTu));
+ ASSERT_EQ(FromRecordOfFriend->getLexicalDeclContext(),
+ cast<DeclContext>(FromX));
+ ASSERT_FALSE(
+ FromRecordOfFriend->getDeclContext()->containsDecl(FromRecordOfFriend));
+ ASSERT_FALSE(FromRecordOfFriend->getLexicalDeclContext()->containsDecl(
+ FromRecordOfFriend));
+ ASSERT_FALSE(FromRecordOfFriend->getLookupParent()
+ ->lookup(FromRecordOfFriend->getDeclName())
+ .empty());
+
+ auto *ToX = Import(FromX, Lang_CXX);
+ ASSERT_TRUE(ToX);
+
+ Decl *ToTu = ToX->getTranslationUnitDecl();
+ auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
+ RecordDecl *ToRecordOfFriend =
+ const_cast<RecordDecl *>(getRecordDeclOfFriend(ToFriend));
+
+ ASSERT_EQ(ToRecordOfFriend->getDeclContext(), cast<DeclContext>(ToTu));
+ ASSERT_EQ(ToRecordOfFriend->getLexicalDeclContext(), cast<DeclContext>(ToX));
+ EXPECT_FALSE(
+ ToRecordOfFriend->getDeclContext()->containsDecl(ToRecordOfFriend));
+ EXPECT_FALSE(ToRecordOfFriend->getLexicalDeclContext()->containsDecl(
+ ToRecordOfFriend));
+ EXPECT_FALSE(ToRecordOfFriend->getLookupParent()
+ ->lookup(ToRecordOfFriend->getDeclName())
+ .empty());
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
+ Decl *FromTu = getTuDecl(
+ R"(
+ template<class A> class declToImport {
+ template<class A1> friend class declToImport;
+ };
+ )",
+ Lang_CXX, "input.cc");
+
+ auto *FromD =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
+ auto *ToD = Import(FromD, Lang_CXX);
+
+ auto Pattern = classTemplateDecl(
+ has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+
+ auto *Class =
+ FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
+ auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
+ EXPECT_NE(Friend->getFriendDecl(), Class);
+ EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
+}
+
+TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {
+ auto Pattern = classTemplateSpecializationDecl(hasName("X"));
+
+ ClassTemplateSpecializationDecl *Imported1;
+ {
+ Decl *FromTU = getTuDecl("template<class T> class X;"
+ "struct Y { friend class X<int>; };",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, Pattern);
+
+ Imported1 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+ }
+ ClassTemplateSpecializationDecl *Imported2;
+ {
+ Decl *FromTU = getTuDecl("template<class T> class X;"
+ "template<> class X<int>{};"
+ "struct Z { friend class X<int>; };",
+ Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, Pattern);
+
+ Imported2 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern),
+ 2u);
+ ASSERT_TRUE(Imported2->getPreviousDecl());
+ EXPECT_EQ(Imported2->getPreviousDecl(), Imported1);
+}
+
+TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU0, classTemplateDecl(hasName("F")));
+ auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU1, classTemplateDecl(hasName("F")));
+ auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
+ Imported1->getTemplatedDecl()->getTypeForDecl());
+}
+
+TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("class declToImport {};", Lang_CXX,
+ "class Y { friend class declToImport; };", Lang_CXX);
+ auto *Imported = cast<CXXRecordDecl>(To);
+
+ EXPECT_TRUE(Imported->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+ ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX);
+ auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("F")));
+ auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ToDecl->getTemplatedDecl(),
+ ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+ ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU0, classTemplateDecl(hasName("F")));
+ auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU1, classTemplateDecl(hasName("F")));
+ auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd->getTemplatedDecl(),
+ ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl());
+ QualType FT = Friend->getFriendType()->getType();
+ FT = FromTU0->getASTContext().getCanonicalType(FT);
+ auto *Fwd = cast<TagType>(FT)->getDecl();
+ auto *ImportedFwd = Import(Fwd, Lang_CXX);
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU1, cxxRecordDecl(hasName("F")));
+ auto *ImportedDef = Import(Definition, Lang_CXX);
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) {
+ auto *Code = R"(
+ template <class T>
+ struct X {
+ friend void foo(){}
+ };
+ )";
+ TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+ auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasName("foo")));
+
+ TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc");
+ auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+ auto *ImportedFoo = Import(FromFoo, Lang_CXX);
+ EXPECT_EQ(ImportedFoo, ToFoo);
+}
+
+struct DeclContextTest : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
+ Decl *TU = getTuDecl(
+ R"(
+ namespace NS {
+
+ template <typename T>
+ struct S {};
+ template struct S<int>;
+
+ inline namespace INS {
+ template <typename T>
+ struct S {};
+ template struct S<int>;
+ }
+
+ }
+ )", Lang_CXX11, "input0.cc");
+ auto *NS = FirstDeclMatcher<NamespaceDecl>().match(
+ TU, namespaceDecl());
+ auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ TU, classTemplateSpecializationDecl());
+ ASSERT_TRUE(NS->containsDecl(Spec));
+
+ NS->removeDecl(Spec);
+ EXPECT_FALSE(NS->containsDecl(Spec));
+}
+
+TEST_P(DeclContextTest,
+ removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
+ Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
+ auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+ auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+
+ // Investigate the list.
+ auto *DC = A0->getDeclContext();
+ ASSERT_TRUE(DC->containsDecl(A0));
+ ASSERT_TRUE(DC->containsDecl(A1));
+
+ // Investigate the lookup table.
+ auto *Map = DC->getLookupPtr();
+ ASSERT_TRUE(Map);
+ auto I = Map->find(A0->getDeclName());
+ ASSERT_NE(I, Map->end());
+ StoredDeclsList &L = I->second;
+ // The lookup table contains the most recent decl of A.
+ ASSERT_NE(L.getAsDecl(), A0);
+ ASSERT_EQ(L.getAsDecl(), A1);
+
+ ASSERT_TRUE(L.getAsDecl());
+ // Simulate the private function DeclContext::reconcileExternalVisibleStorage.
+ // The point here is to have a Vec with only one element, which is not the
+ // one we are going to delete from the DC later.
+ L.setHasExternalDecls();
+ ASSERT_TRUE(L.getAsVector());
+ ASSERT_EQ(1u, L.getAsVector()->size());
+
+ // This asserts in the old implementation.
+ DC->removeDecl(A0);
+ EXPECT_FALSE(DC->containsDecl(A0));
+}
+
+struct ImportFunctionTemplateSpecializations
+ : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportFunctionTemplateSpecializations,
+ TUshouldNotContainFunctionTemplateImplicitInstantiation) {
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T>
+ int f() { return 0; }
+ void foo() { f<int>(); }
+ )",
+ Lang_CXX, "input0.cc");
+
+ // Check that the function template instantiation is NOT the child of the TU.
+ auto Pattern = translationUnitDecl(
+ unless(has(functionDecl(hasName("f"), isTemplateInstantiation()))));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));
+
+ auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+ ASSERT_TRUE(Import(Foo, Lang_CXX));
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
+}
+
+TEST_P(ImportFunctionTemplateSpecializations,
+ TUshouldNotContainFunctionTemplateExplicitInstantiation) {
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T>
+ int f() { return 0; }
+ template int f<int>();
+ )",
+ Lang_CXX, "input0.cc");
+
+ // Check that the function template instantiation is NOT the child of the TU.
+ auto Instantiation = functionDecl(hasName("f"), isTemplateInstantiation());
+ auto Pattern = translationUnitDecl(unless(has(Instantiation)));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));
+
+ ASSERT_TRUE(
+ Import(FirstDeclMatcher<Decl>().match(FromTU, Instantiation), Lang_CXX));
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
+}
+
+TEST_P(ImportFunctionTemplateSpecializations,
+ TUshouldContainFunctionTemplateSpecialization) {
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T>
+ int f() { return 0; }
+ template <> int f<int>() { return 4; }
+ )",
+ Lang_CXX, "input0.cc");
+
+ // Check that the function template specialization is the child of the TU.
+ auto Specialization =
+ functionDecl(hasName("f"), isExplicitTemplateSpecialization());
+ auto Pattern = translationUnitDecl(has(Specialization));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));
+
+ ASSERT_TRUE(
+ Import(FirstDeclMatcher<Decl>().match(FromTU, Specialization), Lang_CXX));
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
+}
+
+TEST_P(ImportFunctionTemplateSpecializations,
+ FunctionTemplateSpecializationRedeclChain) {
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T>
+ int f() { return 0; }
+ template <> int f<int>() { return 4; }
+ )",
+ Lang_CXX, "input0.cc");
+
+ auto Spec = functionDecl(hasName("f"), isExplicitTemplateSpecialization(),
+ hasParent(translationUnitDecl()));
+ auto *FromSpecD = FirstDeclMatcher<Decl>().match(FromTU, Spec);
+ {
+ auto *TU = FromTU;
+ auto *SpecD = FromSpecD;
+ auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ TU, functionTemplateDecl());
+ auto *FirstSpecD = *(TemplateD->spec_begin());
+ ASSERT_EQ(SpecD, FirstSpecD);
+ ASSERT_TRUE(SpecD->getPreviousDecl());
+ ASSERT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl())
+ ->doesThisDeclarationHaveABody());
+ }
+
+ ASSERT_TRUE(Import(FromSpecD, Lang_CXX));
+
+ {
+ auto *TU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *SpecD = FirstDeclMatcher<Decl>().match(TU, Spec);
+ auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ TU, functionTemplateDecl());
+ auto *FirstSpecD = *(TemplateD->spec_begin());
+ EXPECT_EQ(SpecD, FirstSpecD);
+ ASSERT_TRUE(SpecD->getPreviousDecl());
+ EXPECT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl())
+ ->doesThisDeclarationHaveABody());
+ }
+}
+
+TEST_P(ImportFunctionTemplateSpecializations,
+ MatchNumberOfFunctionTemplateSpecializations) {
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T> constexpr int f() { return 0; }
+ template <> constexpr int f<int>() { return 4; }
+ void foo() {
+ static_assert(f<char>() == 0, "");
+ static_assert(f<int>() == 4, "");
+ }
+ )",
+ Lang_CXX11, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+
+ Import(FromD, Lang_CXX11);
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(
+ DeclCounter<FunctionDecl>().match(FromTU, functionDecl(hasName("f"))),
+ DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))));
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) {
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B;
+ )",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("B")));
+
+ Import(FromD, Lang_CXX);
+ }
+
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B {
+ void f();
+ B* b;
+ };
+ )",
+ Lang_CXX, "input1.cc");
+ FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ Import(FromD, Lang_CXX);
+ auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("B")));
+ auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX));
+ EXPECT_TRUE(ToCTD->isThisDeclarationADefinition());
+
+ // We expect no (ODR) warning during the import.
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ }
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportingTypedefShouldImportTheCompleteType) {
+ // We already have an incomplete underlying type in the "To" context.
+ auto Code =
+ R"(
+ template <typename T>
+ struct S {
+ void foo();
+ };
+ using U = S<int>;
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ auto *ToD = FirstDeclMatcher<TypedefNameDecl>().match(ToTU,
+ typedefNameDecl(hasName("U")));
+ ASSERT_TRUE(ToD->getUnderlyingType()->isIncompleteType());
+
+ // The "From" context has the same typedef, but the underlying type is
+ // complete this time.
+ Decl *FromTU = getTuDecl(std::string(Code) +
+ R"(
+ void foo(U* u) {
+ u->foo();
+ }
+ )", Lang_CXX11);
+ auto *FromD = FirstDeclMatcher<TypedefNameDecl>().match(FromTU,
+ typedefNameDecl(hasName("U")));
+ ASSERT_FALSE(FromD->getUnderlyingType()->isIncompleteType());
+
+ // The imported type should be complete.
+ auto *ImportedD = cast<TypedefNameDecl>(Import(FromD, Lang_CXX11));
+ EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportTemplateParameterLists) {
+ auto Code =
+ R"(
+ template<class T>
+ int f() { return 0; }
+ template <> int f<int>() { return 4; }
+ )";
+
+ Decl *FromTU = getTuDecl(Code, Lang_CXX);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU,
+ functionDecl(hasName("f"), isExplicitTemplateSpecialization()));
+ ASSERT_EQ(FromD->getNumTemplateParameterLists(), 1u);
+
+ auto *ToD = Import(FromD, Lang_CXX);
+ // The template parameter list should exist.
+ EXPECT_EQ(ToD->getNumTemplateParameterLists(), 1u);
+}
+
+struct ASTImporterLookupTableTest : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ASTImporterLookupTableTest, OneDecl) {
+ auto *ToTU = getToTuDecl("int a;", Lang_CXX);
+ auto *D = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("a")));
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(ToTU, D->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), D);
+}
+
+static Decl *findInDeclListOfDC(DeclContext *DC, DeclarationName Name) {
+ for (Decl *D : DC->decls()) {
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ if (ND->getDeclName() == Name)
+ return ND;
+ }
+ return nullptr;
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) {
+ auto *Code = R"(
+ template <class T>
+ struct X {
+ friend void foo(){}
+ };
+ )";
+ TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+ auto *X = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("X")));
+ auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasName("foo")));
+ DeclContext *FooDC = Foo->getDeclContext();
+ DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+ ASSERT_EQ(cast<Decl>(FooLexicalDC), X->getTemplatedDecl());
+ ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+ DeclarationName FooName = Foo->getDeclName();
+
+ // Cannot find in the LookupTable of its DC (TUDecl)
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Cannot find in the LookupTable of its LexicalDC (X)
+ FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Can't find in the list of Decls of the DC.
+ EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+ // Can't find in the list of Decls of the LexicalDC
+ EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), nullptr);
+
+ // ASTImporter specific lookup finds it.
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(FooDC, Foo->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ FwdDeclStructShouldBeFoundByImporterSpecificLookup) {
+ TranslationUnitDecl *ToTU =
+ getToTuDecl("struct A { struct Foo *p; };", Lang_C);
+ auto *Foo =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("Foo")));
+ auto *A =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+ DeclContext *FooDC = Foo->getDeclContext();
+ DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+ ASSERT_EQ(cast<Decl>(FooLexicalDC), A);
+ ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+ DeclarationName FooName = Foo->getDeclName();
+
+ // Cannot find in the LookupTable of its DC (TUDecl).
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Cannot find in the LookupTable of its LexicalDC (A).
+ FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Can't find in the list of Decls of the DC.
+ EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+ // Can find in the list of Decls of the LexicalDC.
+ EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), Foo);
+
+ // ASTImporter specific lookup finds it.
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(FooDC, Foo->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsNamesInDifferentDC) {
+ TranslationUnitDecl *ToTU =
+ getToTuDecl("int V; struct A { int V; }; struct B { int V; };", Lang_C);
+ DeclarationName VName = FirstDeclMatcher<VarDecl>()
+ .match(ToTU, varDecl(hasName("V")))
+ ->getDeclName();
+ auto *A =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+ auto *B =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("B")));
+
+ ASTImporterLookupTable LT(*ToTU);
+
+ auto Res = LT.lookup(cast<DeclContext>(A), VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+ ToTU, fieldDecl(hasName("V"),
+ hasParent(recordDecl(hasName("A"))))));
+ Res = LT.lookup(cast<DeclContext>(B), VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+ ToTU, fieldDecl(hasName("V"),
+ hasParent(recordDecl(hasName("B"))))));
+ Res = LT.lookup(ToTU, VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<VarDecl>().match(
+ ToTU, varDecl(hasName("V"),
+ hasParent(translationUnitDecl()))));
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ void foo();
+ void foo(int);
+ void foo(int, int);
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+ auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+ DeclarationName Name = F0->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 3u);
+ EXPECT_EQ(Res.count(F0), 1u);
+ EXPECT_EQ(Res.count(F2), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ DifferentOperatorsShouldHaveDifferentResultSet) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ struct X{};
+ void operator+(X, X);
+ void operator-(X, X);
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *FPlus = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasOverloadedOperatorName("+")));
+ auto *FMinus = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasOverloadedOperatorName("-")));
+ DeclarationName NamePlus = FPlus->getDeclName();
+ auto ResPlus = LT.lookup(ToTU, NamePlus);
+ EXPECT_EQ(ResPlus.size(), 1u);
+ EXPECT_EQ(ResPlus.count(FPlus), 1u);
+ EXPECT_EQ(ResPlus.count(FMinus), 0u);
+ DeclarationName NameMinus = FMinus->getDeclName();
+ auto ResMinus = LT.lookup(ToTU, NameMinus);
+ EXPECT_EQ(ResMinus.size(), 1u);
+ EXPECT_EQ(ResMinus.count(FMinus), 1u);
+ EXPECT_EQ(ResMinus.count(FPlus), 0u);
+ EXPECT_NE(*ResMinus.begin(), *ResPlus.begin());
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupDeclNamesFromDifferentTUs) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ struct X {};
+ void operator+(X, X);
+ )",
+ Lang_CXX);
+ auto *ToPlus = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasOverloadedOperatorName("+")));
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct X {};
+ void operator+(X, X);
+ )",
+ Lang_CXX);
+ auto *FromPlus = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasOverloadedOperatorName("+")));
+
+ // FromPlus have a different TU, thus its DeclarationName is different too.
+ ASSERT_NE(ToPlus->getDeclName(), FromPlus->getDeclName());
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(ToTU, ToPlus->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), ToPlus);
+
+ // FromPlus have a different TU, thus its DeclarationName is different too.
+ Res = LT.lookup(ToTU, FromPlus->getDeclName());
+ ASSERT_EQ(Res.size(), 0u);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ LookupFindsFwdFriendClassDeclWithElaboratedType) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { friend class F; };
+ )",
+ Lang_CXX);
+
+ // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
+ // So we must dig up the underlying CXXRecordDecl.
+ ASTImporterLookupTable LT(*ToTU);
+ auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
+ auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("Y")));
+
+ DeclarationName Name = RD->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), RD);
+
+ Res = LT.lookup(Y, Name);
+ EXPECT_EQ(Res.size(), 0u);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ LookupFindsFwdFriendClassDeclWithUnelaboratedType) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class F;
+ class Y { friend F; };
+ )",
+ Lang_CXX11);
+
+ // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
+ // So we must dig up the underlying CXXRecordDecl.
+ ASTImporterLookupTable LT(*ToTU);
+ auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
+ auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, cxxRecordDecl(hasName("Y")));
+
+ DeclarationName Name = RD->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), RD);
+
+ Res = LT.lookup(Y, Name);
+ EXPECT_EQ(Res.size(), 0u);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ LookupFindsFriendClassDeclWithTypeAliasDoesNotAssert) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class F;
+ using alias_of_f = F;
+ class Y { friend alias_of_f; };
+ )",
+ Lang_CXX11);
+
+ // ASTImporterLookupTable constructor handles using declarations correctly,
+ // no assert is expected.
+ ASTImporterLookupTable LT(*ToTU);
+
+ auto *Alias = FirstDeclMatcher<TypeAliasDecl>().match(
+ ToTU, typeAliasDecl(hasName("alias_of_f")));
+ DeclarationName Name = Alias->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.count(Alias), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { template <class T> friend class F; };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, DependentFriendClass) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ class F;
+
+ template <typename T>
+ class Y {
+ friend class F<T>;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, FriendClassTemplateSpecialization) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ class F;
+
+ class Y {
+ friend class F<int>;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ ASSERT_EQ(Res.size(), 3u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+ EXPECT_EQ(Res.count(*F->spec_begin()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { friend void F(); };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F =
+ FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), F);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ LookupFindsDeclsInClassTemplateSpecialization) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ struct X {
+ int F;
+ };
+ void foo() {
+ X<char> xc;
+ }
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+
+ auto *Template = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("X")));
+ auto *FieldInTemplate = FirstDeclMatcher<FieldDecl>().match(
+ ToTU,
+ fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl())))));
+
+ auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ FieldDecl *FieldInSpec = *Spec->field_begin();
+ ASSERT_TRUE(FieldInSpec);
+
+ DeclarationName Name = FieldInSpec->getDeclName();
+ auto TemplateDC = cast<DeclContext>(Template->getTemplatedDecl());
+
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ TemplateDC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+ EXPECT_EQ(FoundDecls[0], FieldInTemplate);
+
+ auto Res = LT.lookup(TemplateDC, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FieldInTemplate);
+
+ cast<DeclContext>(Spec)->getRedeclContext()->localUncachedLookup(Name,
+ FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+ EXPECT_EQ(FoundDecls[0], FieldInSpec);
+
+ Res = LT.lookup(cast<DeclContext>(Spec), Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FieldInSpec);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { template <class T> friend void F(); };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ ToTU, functionTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, MultipleBefriendingClasses) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ struct X;
+ struct A {
+ friend struct X;
+ };
+ struct B {
+ friend struct X;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *X = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X")));
+ auto *FriendD0 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ auto *FriendD1 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ const RecordDecl *RD0 = getRecordDeclOfFriend(FriendD0);
+ const RecordDecl *RD1 = getRecordDeclOfFriend(FriendD1);
+ ASSERT_EQ(RD0, RD1);
+ ASSERT_EQ(RD1, X);
+
+ DeclarationName Name = X->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), X);
+}
+
+TEST_P(ASTImporterLookupTableTest, EnumConstantDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ enum E {
+ A,
+ B
+ };
+ )",
+ Lang_C);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *E = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(hasName("E")));
+ auto *A = FirstDeclMatcher<EnumConstantDecl>().match(
+ ToTU, enumConstantDecl(hasName("A")));
+
+ DeclarationName Name = A->getDeclName();
+ // Redecl context is the TU.
+ ASSERT_EQ(E->getRedeclContext(), ToTU);
+
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ // Normal lookup finds in the DC.
+ E->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+
+ // Normal lookup finds in the Redecl context.
+ ToTU->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+
+ // Import specific lookup finds in the DC.
+ auto Res = LT.lookup(E, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+
+ // Import specific lookup finds in the Redecl context.
+ Res = LT.lookup(ToTU, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ namespace N {
+ int A;
+ }
+ namespace N {
+ }
+ )",
+ Lang_CXX);
+ auto *N1 =
+ LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N")));
+ auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A")));
+ DeclarationName Name = A->getDeclName();
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(N1, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ RedeclChainShouldBeCorrectAmongstNamespaces) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ namespace NS {
+ struct X;
+ struct Y {
+ static const int I = 3;
+ };
+ }
+ namespace NS {
+ struct X { // <--- To be imported
+ void method(int i = Y::I) {}
+ int f;
+ };
+ }
+ )",
+ Lang_CXX);
+ auto *FromFwd = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit())));
+ auto *FromDef = LastDeclMatcher<CXXRecordDecl>().match(
+ FromTU,
+ cxxRecordDecl(hasName("X"), isDefinition(), unless(isImplicit())));
+ ASSERT_NE(FromFwd, FromDef);
+ ASSERT_FALSE(FromFwd->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDef->isThisDeclarationADefinition());
+ ASSERT_EQ(FromFwd->getCanonicalDecl(), FromDef->getCanonicalDecl());
+
+ auto *ToDef = cast_or_null<CXXRecordDecl>(Import(FromDef, Lang_CXX));
+ auto *ToFwd = cast_or_null<CXXRecordDecl>(Import(FromFwd, Lang_CXX));
+ EXPECT_NE(ToFwd, ToDef);
+ EXPECT_FALSE(ToFwd->isThisDeclarationADefinition());
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_EQ(ToFwd->getCanonicalDecl(), ToDef->getCanonicalDecl());
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We expect no (ODR) warning during the import.
+ EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+}
+
+struct ImportFriendFunctionTemplates : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportFriendFunctionTemplates, LookupShouldFindPreviousFriend) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ class X {
+ template <typename T> friend void foo();
+ };
+ )",
+ Lang_CXX);
+ auto *Friend = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ ToTU, functionTemplateDecl(hasName("foo")));
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T> void foo();
+ )",
+ Lang_CXX);
+ auto *FromFoo = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU, functionTemplateDecl(hasName("foo")));
+ auto *Imported = Import(FromFoo, Lang_CXX);
+
+ EXPECT_EQ(Imported->getPreviousDecl(), Friend);
+}
+
+struct ASTImporterWithFakeErrors : ASTImporter {
+ using ASTImporter::ASTImporter;
+ bool returnWithErrorInTest() override { return true; }
+};
+
+struct ErrorHandlingTest : ASTImporterOptionSpecificTestBase {
+ ErrorHandlingTest() {
+ Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
+ ASTContext &FromContext, FileManager &FromFileManager,
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
+ return new ASTImporterWithFakeErrors(ToContext, ToFileManager,
+ FromContext, FromFileManager,
+ MinimalImport, SharedState);
+ };
+ }
+ // In this test we purposely report an error (UnsupportedConstruct) when
+ // importing the below stmt.
+ static constexpr auto* ErroneousStmt = R"( asm(""); )";
+};
+
+// Check a case when no new AST node is created in the AST before encountering
+// the error.
+TEST_P(ErrorHandlingTest, ErrorHappensBeforeCreatingANewNode) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ class X {};
+ template <>
+ class X<int> { int a; };
+ )",
+ Lang_CXX);
+ TranslationUnitDecl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ class X {};
+ template <>
+ class X<int> { double b; };
+ )",
+ Lang_CXX);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ ClassTemplateSpecializationDecl *ImportedSpec = Import(FromSpec, Lang_CXX);
+ EXPECT_FALSE(ImportedSpec);
+
+ // The original Decl is kept, no new decl is created.
+ EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X"))),
+ 1u);
+
+ // But an error is set to the counterpart in the "from" context.
+ ASTImporter *Importer = findFromTU(FromSpec)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromSpec);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::NameConflict);
+}
+
+// Check a case when a new AST node is created but not linked to the AST before
+// encountering the error.
+TEST_P(ErrorHandlingTest,
+ ErrorHappensAfterCreatingTheNodeButBeforeLinkingThatToTheAST) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ std::string("void foo() { ") + ErroneousStmt + " }",
+ Lang_CXX);
+ auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+
+ FunctionDecl *ImportedFoo = Import(FromFoo, Lang_CXX);
+ EXPECT_FALSE(ImportedFoo);
+
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // Created, but not linked.
+ EXPECT_EQ(
+ DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("foo"))),
+ 0u);
+
+ ASTImporter *Importer = findFromTU(FromFoo)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFoo);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+}
+
+// Check a case when a new AST node is created and linked to the AST before
+// encountering the error. The error is set for the counterpart of the nodes in
+// the "from" context.
+TEST_P(ErrorHandlingTest, ErrorHappensAfterNodeIsCreatedAndLinked) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ std::string(R"(
+ void f();
+ void f() { )") + ErroneousStmt + R"( }
+ )",
+ Lang_CXX);
+ auto *FromProto = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ auto *FromDef =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, functionDecl(hasName("f")));
+ FunctionDecl *ImportedProto = Import(FromProto, Lang_CXX);
+ EXPECT_FALSE(ImportedProto); // Could not import.
+ // However, we created two nodes in the AST. 1) the fwd decl 2) the
+ // definition. The definition is not added to its DC, but the fwd decl is
+ // there.
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))),
+ 1u);
+ // Match the fwd decl.
+ auto *ToProto =
+ FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("f")));
+ EXPECT_TRUE(ToProto);
+ // An error is set to the counterpart in the "from" context both for the fwd
+ // decl and the definition.
+ ASTImporter *Importer = findFromTU(FromProto)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromProto);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ OptErr = Importer->getImportDeclErrorIfAny(FromDef);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+}
+
+// An error should be set for a class if we cannot import one member.
+TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ std::string(R"(
+ class X {
+ void f() { )") + ErroneousStmt + R"( } // This member has the error
+ // during import.
+ void ok(); // The error should not prevent importing this.
+ }; // An error will be set for X too.
+ )",
+ Lang_CXX);
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // An error is set for X.
+ EXPECT_FALSE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+
+ // An error is set for f().
+ auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
+ FromTU, cxxMethodDecl(hasName("f")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromF);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ // And any subsequent import should fail.
+ CXXMethodDecl *ImportedF = Import(FromF, Lang_CXX);
+ EXPECT_FALSE(ImportedF);
+
+ // There is an error set for the other member too.
+ auto *FromOK = FirstDeclMatcher<CXXMethodDecl>().match(
+ FromTU, cxxMethodDecl(hasName("ok")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromOK);
+ EXPECT_TRUE(OptErr);
+ // Cannot import the other member.
+ CXXMethodDecl *ImportedOK = Import(FromOK, Lang_CXX);
+ EXPECT_FALSE(ImportedOK);
+}
+
+// Check that an error propagates to the dependent AST nodes.
+// In the below code it means that an error in X should propagate to A.
+// And even to F since the containing A is erroneous.
+// And to all AST nodes which we visit during the import process which finally
+// ends up in a failure (in the error() function).
+TEST_P(ErrorHandlingTest, ErrorPropagatesThroughImportCycles) {
+ Decl *FromTU = getTuDecl(
+ std::string(R"(
+ namespace NS {
+ class A {
+ template <int I> class F {};
+ class X {
+ template <int I> friend class F;
+ void error() { )") + ErroneousStmt + R"( }
+ };
+ };
+
+ class B {};
+ } // NS
+ )",
+ Lang_CXX, "input0.cc");
+
+ auto *FromFRD = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
+ auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A"), isDefinition()));
+ auto *FromB = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("B"), isDefinition()));
+ auto *FromNS = FirstDeclMatcher<NamespaceDecl>().match(
+ FromTU, namespaceDecl(hasName("NS")));
+
+ // Start by importing the templated CXXRecordDecl of F.
+ // Import fails for that.
+ EXPECT_FALSE(Import(FromFRD, Lang_CXX));
+ // Import fails for A.
+ EXPECT_FALSE(Import(FromA, Lang_CXX));
+ // But we should be able to import the independent B.
+ EXPECT_TRUE(Import(FromB, Lang_CXX));
+ // And the namespace.
+ EXPECT_TRUE(Import(FromNS, Lang_CXX));
+
+ // An error is set to the templated CXXRecordDecl of F.
+ ASTImporter *Importer = findFromTU(FromFRD)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFRD);
+ EXPECT_TRUE(OptErr);
+
+ // An error is set to A.
+ OptErr = Importer->getImportDeclErrorIfAny(FromA);
+ EXPECT_TRUE(OptErr);
+
+ // There is no error set to B.
+ OptErr = Importer->getImportDeclErrorIfAny(FromB);
+ EXPECT_FALSE(OptErr);
+
+ // There is no error set to NS.
+ OptErr = Importer->getImportDeclErrorIfAny(FromNS);
+ EXPECT_FALSE(OptErr);
+
+ // Check some of those decls whose ancestor is X, they all should have an
+ // error set if we visited them during an import process which finally failed.
+ // These decls are part of a cycle in an ImportPath.
+ // There would not be any error set for these decls if we hadn't follow the
+ // ImportPaths and the cycles.
+ OptErr = Importer->getImportDeclErrorIfAny(
+ FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("F"))));
+ // An error is set to the 'F' ClassTemplateDecl.
+ EXPECT_TRUE(OptErr);
+ // An error is set to the FriendDecl.
+ OptErr = Importer->getImportDeclErrorIfAny(
+ FirstDeclMatcher<FriendDecl>().match(
+ FromTU, friendDecl()));
+ EXPECT_TRUE(OptErr);
+ // An error is set to the implicit class of A.
+ OptErr =
+ Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A"), isImplicit())));
+ EXPECT_TRUE(OptErr);
+ // An error is set to the implicit class of X.
+ OptErr =
+ Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X"), isImplicit())));
+ EXPECT_TRUE(OptErr);
+}
+
+TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ std::string(R"(
+ namespace X {
+ void f() { )") + ErroneousStmt + R"( } // This member has the error
+ // during import.
+ void ok(); // The error should not prevent importing this.
+ }; // An error will be set for X too.
+ )",
+ Lang_CXX);
+ auto *FromX = FirstDeclMatcher<NamespaceDecl>().match(
+ FromTU, namespaceDecl(hasName("X")));
+ NamespaceDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // There is no error set for X.
+ EXPECT_TRUE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ ASSERT_FALSE(OptErr);
+
+ // An error is set for f().
+ auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromF);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ // And any subsequent import should fail.
+ FunctionDecl *ImportedF = Import(FromF, Lang_CXX);
+ EXPECT_FALSE(ImportedF);
+
+ // There is no error set for ok().
+ auto *FromOK = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("ok")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromOK);
+ EXPECT_FALSE(OptErr);
+ // And we should be able to import.
+ FunctionDecl *ImportedOK = Import(FromOK, Lang_CXX);
+ EXPECT_TRUE(ImportedOK);
+}
+
+// An error should be set for a class if it had a previous import with an error
+// from another TU.
+TEST_P(ErrorHandlingTest,
+ ImportedDeclWithErrorShouldFailTheImportOfDeclWhichMapToIt) {
+ // We already have a fwd decl.
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ "class X;", Lang_CXX);
+ // Then we import a definition.
+ {
+ TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
+ class X {
+ void f() { )") + ErroneousStmt + R"( }
+ void ok();
+ };
+ )",
+ Lang_CXX);
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // An error is set for X ...
+ EXPECT_FALSE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ }
+ // ... but the node had been created.
+ auto *ToXDef = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X"), isDefinition()));
+ // An error is set for "ToXDef" in the shared state.
+ Optional<ImportError> OptErr =
+ SharedStatePtr->getImportDeclErrorIfAny(ToXDef);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+
+ auto *ToXFwd = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X"), unless(isDefinition())));
+ // An error is NOT set for the fwd Decl of X in the shared state.
+ OptErr = SharedStatePtr->getImportDeclErrorIfAny(ToXFwd);
+ ASSERT_FALSE(OptErr);
+
+ // Try to import X again but from another TU.
+ {
+ TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
+ class X {
+ void f() { )") + ErroneousStmt + R"( }
+ void ok();
+ };
+ )",
+ Lang_CXX, "input1.cc");
+
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // If we did not save the errors for the "to" context then the below checks
+ // would fail, because the lookup finds the fwd Decl of the existing
+ // definition in the "to" context. We can reach the existing definition via
+ // the found fwd Decl. That existing definition is structurally equivalent
+ // (we check only the fields) with this one we want to import, so we return
+ // with the existing definition, which is erroneous (one method is missing).
+
+ // The import should fail.
+ EXPECT_FALSE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ // And an error is set for this new X in the "from" ctx.
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ }
+}
+
+TEST_P(ErrorHandlingTest, ImportOfOverriddenMethods) {
+ auto MatchFooA =
+ functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("A"))));
+ auto MatchFooB =
+ functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("B"))));
+ auto MatchFooC =
+ functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("C"))));
+
+ // Provoke import of a method that has overridden methods with import error.
+ TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
+ struct C;
+ struct A {
+ virtual void foo();
+ void f1(C *);
+ };
+ void A::foo() {
+ )") + ErroneousStmt + R"(
+ }
+ struct B : public A {
+ void foo() override;
+ };
+ struct C : public B {
+ void foo() override;
+ };
+ )",
+ Lang_CXX11);
+ auto *FromFooA = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooA);
+ auto *FromFooB = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooB);
+ auto *FromFooC = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooC);
+
+ EXPECT_FALSE(Import(FromFooA, Lang_CXX11));
+ ASTImporter *Importer = findFromTU(FromFooA)->Importer.get();
+ auto CheckError = [&Importer](Decl *FromD) {
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromD);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ };
+ CheckError(FromFooA);
+ EXPECT_FALSE(Import(FromFooB, Lang_CXX11));
+ CheckError(FromFooB);
+ EXPECT_FALSE(Import(FromFooC, Lang_CXX11));
+ CheckError(FromFooC);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionBody) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ void f() {
+ auto L = [](){};
+ }
+ )",
+ Lang_CXX11, "input0.cc");
+ auto Pattern = lambdaExpr();
+ CXXRecordDecl *FromL =
+ FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
+
+ auto ToL = Import(FromL, Lang_CXX11);
+ unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end());
+ unsigned FromLSize =
+ std::distance(FromL->decls().begin(), FromL->decls().end());
+ EXPECT_NE(ToLSize, 0u);
+ EXPECT_EQ(ToLSize, FromLSize);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionParam) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename F>
+ void f(F L = [](){}) {}
+ )",
+ Lang_CXX11, "input0.cc");
+ auto Pattern = lambdaExpr();
+ CXXRecordDecl *FromL =
+ FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
+
+ auto ToL = Import(FromL, Lang_CXX11);
+ unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end());
+ unsigned FromLSize =
+ std::distance(FromL->decls().begin(), FromL->decls().end());
+ EXPECT_NE(ToLSize, 0u);
+ EXPECT_EQ(ToLSize, FromLSize);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, LambdaInGlobalScope) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto l1 = [](unsigned lp) { return 1; };
+ auto l2 = [](int lp) { return 2; };
+ int f(int p) {
+ return l1(p) + l2(p);
+ }
+ )",
+ Lang_CXX11, "input0.cc");
+ FunctionDecl *FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ FunctionDecl *ToF = Import(FromF, Lang_CXX11);
+ EXPECT_TRUE(ToF);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportExistingFriendClassTemplateDef) {
+ auto Code =
+ R"(
+ template <class T1, class T2>
+ struct Base {
+ template <class U1, class U2>
+ friend struct Class;
+ };
+ template <class T1, class T2>
+ struct Class { };
+ )";
+
+ TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+ TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc");
+
+ auto *ToClassProto = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Class")));
+ auto *ToClassDef = LastDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Class")));
+ ASSERT_FALSE(ToClassProto->isThisDeclarationADefinition());
+ ASSERT_TRUE(ToClassDef->isThisDeclarationADefinition());
+ // Previous friend decl is not linked to it!
+ ASSERT_FALSE(ToClassDef->getPreviousDecl());
+ ASSERT_EQ(ToClassDef->getMostRecentDecl(), ToClassDef);
+ ASSERT_EQ(ToClassProto->getMostRecentDecl(), ToClassProto);
+
+ auto *FromClassProto = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Class")));
+ auto *FromClassDef = LastDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Class")));
+ ASSERT_FALSE(FromClassProto->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromClassDef->isThisDeclarationADefinition());
+ ASSERT_FALSE(FromClassDef->getPreviousDecl());
+ ASSERT_EQ(FromClassDef->getMostRecentDecl(), FromClassDef);
+ ASSERT_EQ(FromClassProto->getMostRecentDecl(), FromClassProto);
+
+ auto *ImportedDef = Import(FromClassDef, Lang_CXX);
+ // At import we should find the definition for 'Class' even if the
+ // prototype (inside 'friend') for it comes first in the AST and is not
+ // linked to the definition.
+ EXPECT_EQ(ImportedDef, ToClassDef);
+}
+
+struct LLDBLookupTest : ASTImporterOptionSpecificTestBase {
+ LLDBLookupTest() {
+ Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
+ ASTContext &FromContext, FileManager &FromFileManager,
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
+ return new ASTImporter(ToContext, ToFileManager, FromContext,
+ FromFileManager, MinimalImport,
+ // We use the regular lookup.
+ /*SharedState=*/nullptr);
+ };
+ }
+};
+
+TEST_P(LLDBLookupTest, ImporterShouldFindInTransparentContext) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ extern "C" {
+ class X{};
+ };
+ )",
+ Lang_CXX);
+ auto *ToX = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X")));
+
+ // Set up a stub external storage.
+ ToTU->setHasExternalLexicalStorage(true);
+ // Set up DeclContextBits.HasLazyExternalLexicalLookups to true.
+ ToTU->setMustBuildLookupTable();
+ struct TestExternalASTSource : ExternalASTSource {};
+ ToTU->getASTContext().setExternalSource(new TestExternalASTSource());
+
+ Decl *FromTU = getTuDecl(
+ R"(
+ class X;
+ )",
+ Lang_CXX);
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ auto *ImportedX = Import(FromX, Lang_CXX);
+ // The lookup must find the existing class definition in the LinkageSpecDecl.
+ // Then the importer renders the existing and the new decl into one chain.
+ EXPECT_EQ(ImportedX->getCanonicalDecl(), ToX->getCanonicalDecl());
+}
+
+struct SVEBuiltins : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(SVEBuiltins, ImportTypes) {
+ static const char *const TypeNames[] = {
+ "__SVInt8_t",
+ "__SVInt16_t",
+ "__SVInt32_t",
+ "__SVInt64_t",
+ "__SVUint8_t",
+ "__SVUint16_t",
+ "__SVUint32_t",
+ "__SVUint64_t",
+ "__SVFloat16_t",
+ "__SVFloat32_t",
+ "__SVFloat64_t",
+ "__SVBool_t"
+ };
+
+ TranslationUnitDecl *ToTU = getToTuDecl("", Lang_CXX);
+ TranslationUnitDecl *FromTU = getTuDecl("", Lang_CXX, "input.cc");
+ for (auto *TypeName : TypeNames) {
+ auto *ToTypedef = FirstDeclMatcher<TypedefDecl>().match(
+ ToTU, typedefDecl(hasName(TypeName)));
+ QualType ToType = ToTypedef->getUnderlyingType();
+
+ auto *FromTypedef = FirstDeclMatcher<TypedefDecl>().match(
+ FromTU, typedefDecl(hasName(TypeName)));
+ QualType FromType = FromTypedef->getUnderlyingType();
+
+ QualType ImportedType = ImportType(FromType, FromTypedef, Lang_CXX);
+ EXPECT_EQ(ImportedType, ToType);
+ }
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportOfDefaultImplicitFunctions) {
+ // Test that import of implicit functions works and the functions
+ // are merged into one chain.
+ auto GetDeclToImport = [this](StringRef File) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct X { };
+ // Force generating some implicit operator definitions for X.
+ void f() { X x1, x2; x1 = x2; X *x3 = new X; delete x3; }
+ )",
+ Lang_CXX11, File);
+ auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit())));
+ // Destructor is picked as one example of implicit function.
+ return FromD->getDestructor();
+ };
+
+ auto *ToD1 = Import(GetDeclToImport("input1.cc"), Lang_CXX11);
+ ASSERT_TRUE(ToD1);
+
+ auto *ToD2 = Import(GetDeclToImport("input2.cc"), Lang_CXX11);
+ ASSERT_TRUE(ToD2);
+
+ EXPECT_EQ(ToD1->getCanonicalDecl(), ToD2->getCanonicalDecl());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ ImportOfExplicitlyDefaultedOrDeleted) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct X { X() = default; X(const X&) = delete; };
+ )",
+ Lang_CXX11);
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ auto *ImportedX = Import(FromX, Lang_CXX11);
+ auto *Constr1 = FirstDeclMatcher<CXXConstructorDecl>().match(
+ ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit())));
+ auto *Constr2 = LastDeclMatcher<CXXConstructorDecl>().match(
+ ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit())));
+
+ ASSERT_TRUE(ImportedX);
+ EXPECT_TRUE(Constr1->isDefaulted());
+ EXPECT_TRUE(Constr1->isExplicitlyDefaulted());
+ EXPECT_TRUE(Constr2->isDeletedAsWritten());
+ EXPECT_EQ(ImportedX->isAggregate(), FromX->isAggregate());
+}
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, SVEBuiltins,
+ ::testing::Values(ArgVector{"-target",
+ "aarch64-linux-gnu"}), );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
+ ::testing::Values(ArgVector()), );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, CanonicalRedeclChain,
+ ::testing::Values(ArgVector()), );
+
+TEST_P(ASTImporterOptionSpecificTestBase, LambdasAreDifferentiated) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ void f() {
+ auto L0 = [](){};
+ auto L1 = [](){};
+ }
+ )",
+ Lang_CXX11, "input0.cc");
+ auto Pattern = lambdaExpr();
+ CXXRecordDecl *FromL0 =
+ FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
+ CXXRecordDecl *FromL1 =
+ LastDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
+ ASSERT_NE(FromL0, FromL1);
+
+ CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
+ CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
+ EXPECT_NE(ToL0, ToL1);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ LambdasInFunctionParamsAreDifferentiated) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename F0, typename F1>
+ void f(F0 L0 = [](){}, F1 L1 = [](){}) {}
+ )",
+ Lang_CXX11, "input0.cc");
+ auto Pattern = cxxRecordDecl(isLambda());
+ CXXRecordDecl *FromL0 =
+ FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+ CXXRecordDecl *FromL1 =
+ LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+ ASSERT_NE(FromL0, FromL1);
+
+ CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
+ CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
+ ASSERT_NE(ToL0, ToL1);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase,
+ LambdasInFunctionParamsAreDifferentiatedWhenMacroIsUsed) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ #define LAMBDA [](){}
+ template <typename F0, typename F1>
+ void f(F0 L0 = LAMBDA, F1 L1 = LAMBDA) {}
+ )",
+ Lang_CXX11, "input0.cc");
+ auto Pattern = cxxRecordDecl(isLambda());
+ CXXRecordDecl *FromL0 =
+ FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+ CXXRecordDecl *FromL1 =
+ LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+ ASSERT_NE(FromL0, FromL1);
+
+ Import(FromL0, Lang_CXX11);
+ Import(FromL1, Lang_CXX11);
+ CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
+ CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
+ ASSERT_NE(ToL0, ToL1);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportAssignedLambda) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ void f() {
+ auto x = []{} = {}; auto x2 = x;
+ }
+ )",
+ Lang_CXX2a, "input0.cc");
+ auto FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ // We have only one lambda class.
+ ASSERT_EQ(
+ DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())),
+ 1u);
+
+ FunctionDecl *ToF = Import(FromF, Lang_CXX2a);
+ EXPECT_TRUE(ToF);
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We have only one lambda class after the import.
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())),
+ 1u);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportDefaultConstructibleLambdas) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ void f() {
+ auto x = []{} = {};
+ auto xb = []{} = {};
+ }
+ )",
+ Lang_CXX2a, "input0.cc");
+ auto FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ // We have two lambda classes.
+ ASSERT_EQ(
+ DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())),
+ 2u);
+
+ FunctionDecl *ToF = Import(FromF, Lang_CXX2a);
+ EXPECT_TRUE(ToF);
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We have two lambda classes after the import.
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())),
+ 2u);
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImplicitlyDeclareSelf) {
+ Decl *FromTU = getTuDecl(R"(
+ __attribute__((objc_root_class))
+ @interface Root
+ @end
+ @interface C : Root
+ -(void)method;
+ @end
+ @implementation C
+ -(void)method {}
+ @end
+ )",
+ Lang_OBJCXX, "input.mm");
+ auto *FromMethod = LastDeclMatcher<ObjCMethodDecl>().match(
+ FromTU, namedDecl(hasName("method")));
+ ASSERT_TRUE(FromMethod);
+ auto ToMethod = Import(FromMethod, Lang_OBJCXX);
+ ASSERT_TRUE(ToMethod);
+
+ // Both methods should have their implicit parameters.
+ EXPECT_TRUE(FromMethod->getSelfDecl() != nullptr);
+ EXPECT_TRUE(ToMethod->getSelfDecl() != nullptr);
+}
+
+struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportAutoFunctions, ReturnWithTypedefDeclaredInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto X = [](long l) {
+ using int_type = long;
+ auto dur = 13;
+ return static_cast<int_type>(dur);
+ };
+ )",
+ Lang_CXX14, "input0.cc");
+ CXXMethodDecl *From =
+ FirstDeclMatcher<CXXMethodDecl>().match(FromTU, cxxMethodDecl());
+
+ // Explicitly set the return type of the lambda's operator() to the TypeAlias.
+ // Normally the return type would be the built-in 'long' type. However, there
+ // are cases when Clang does not use the canonical type and the TypeAlias is
+ // used. I could not create such an AST from regular source code, it requires
+ // some special state in the preprocessor. I've found such an AST when Clang
+ // parsed libcxx/src/filesystem/directory_iterator.cpp, but could not reduce
+ // that with creduce, because after preprocessing, the AST no longer
+ // contained the TypeAlias as a return type of the lambda.
+ ASTContext &Ctx = From->getASTContext();
+ TypeAliasDecl *FromTA =
+ FirstDeclMatcher<TypeAliasDecl>().match(FromTU, typeAliasDecl());
+ QualType TT = Ctx.getTypedefType(FromTA);
+ const FunctionProtoType *FPT = cast<FunctionProtoType>(From->getType());
+ QualType NewFunType =
+ Ctx.getFunctionType(TT, FPT->getParamTypes(), FPT->getExtProtoInfo());
+ From->setType(NewFunType);
+
+ CXXMethodDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<TypedefType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X {};
+ return X();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ FunctionDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X {};
+ return X();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ // This time import the type directly.
+ QualType ToT = ImportType(From->getType(), From, Lang_CXX14);
+ const FunctionProtoType *FPT = cast<FunctionProtoType>(ToT);
+ EXPECT_TRUE(isa<AutoType>(FPT->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X {};
+ using Y = X;
+ return Y();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ FunctionDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X { struct Y{}; };
+ return X::Y();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ FunctionDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ auto l = []() {
+ struct X {};
+ return X();
+ };
+ return l();
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ if (struct X {} x; true)
+ return X();
+ else
+ return X();
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ for (struct X {} x;;)
+ return X();
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ switch (struct X {} x; 10) {
+ case 10:
+ return X();
+ }
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportPath,
+ ::testing::Values(ArgVector()), );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportType,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportDecl,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterOptionSpecificTestBase,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedirectingImporterTest,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportAutoFunctions,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionTemplates,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctionTemplates,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests,
+ ImportFunctionTemplateSpecializations,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportImplicitMethods,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportVariables,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, LLDBLookupTest,
+ DefaultTestValuesForRunOptions, );
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTImporterVisibilityTest.cpp b/gnu/llvm/clang/unittests/AST/ASTImporterVisibilityTest.cpp
new file mode 100644
index 00000000000..d00829f5cfe
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTImporterVisibilityTest.cpp
@@ -0,0 +1,446 @@
+//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Type-parameterized tests for the correct import of Decls with different
+// visibility.
+//
+//===----------------------------------------------------------------------===//
+
+// Define this to have ::testing::Combine available.
+// FIXME: Better solution for this?
+#define GTEST_HAS_COMBINE 1
+
+#include "ASTImporterFixtures.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using internal::BindableMatcher;
+
+// Type parameters for type-parameterized test fixtures.
+struct GetFunPattern {
+ using DeclTy = FunctionDecl;
+ BindableMatcher<Decl> operator()() { return functionDecl(hasName("f")); }
+};
+struct GetVarPattern {
+ using DeclTy = VarDecl;
+ BindableMatcher<Decl> operator()() { return varDecl(hasName("v")); }
+};
+struct GetClassPattern {
+ using DeclTy = CXXRecordDecl;
+ BindableMatcher<Decl> operator()() { return cxxRecordDecl(hasName("X")); }
+};
+struct GetEnumPattern {
+ using DeclTy = EnumDecl;
+ BindableMatcher<Decl> operator()() { return enumDecl(hasName("E")); }
+};
+struct GetTypedefNamePattern {
+ using DeclTy = TypedefNameDecl;
+ BindableMatcher<Decl> operator()() { return typedefNameDecl(hasName("T")); }
+};
+struct GetFunTemplPattern {
+ using DeclTy = FunctionTemplateDecl;
+ BindableMatcher<Decl> operator()() {
+ return functionTemplateDecl(hasName("f"));
+ }
+};
+struct GetClassTemplPattern {
+ using DeclTy = ClassTemplateDecl;
+ BindableMatcher<Decl> operator()() { return classTemplateDecl(hasName("X")); }
+};
+
+// Values for the value-parameterized test fixtures.
+// FunctionDecl:
+const auto *ExternF = "void f();";
+const auto *StaticF = "static void f();";
+const auto *AnonF = "namespace { void f(); }";
+// VarDecl:
+const auto *ExternV = "extern int v;";
+const auto *StaticV = "static int v;";
+const auto *AnonV = "namespace { extern int v; }";
+// CXXRecordDecl:
+const auto *ExternC = "class X;";
+const auto *AnonC = "namespace { class X; }";
+// EnumDecl:
+const auto *ExternE = "enum E {};";
+const auto *AnonE = "namespace { enum E {}; }";
+// TypedefNameDecl:
+const auto *ExternTypedef = "typedef int T;";
+const auto *AnonTypedef = "namespace { typedef int T; }";
+const auto *ExternUsing = "using T = int;";
+const auto *AnonUsing = "namespace { using T = int; }";
+// FunctionTemplateDecl:
+const auto *ExternFT = "template <class> void f();";
+const auto *StaticFT = "template <class> static void f();";
+const auto *AnonFT = "namespace { template <class> void f(); }";
+// ClassTemplateDecl:
+const auto *ExternCT = "template <class> class X;";
+const auto *AnonCT = "namespace { template <class> class X; }";
+
+// First value in tuple: Compile options.
+// Second value in tuple: Source code to be used in the test.
+using ImportVisibilityChainParams =
+ ::testing::WithParamInterface<std::tuple<ArgVector, const char *>>;
+// Fixture to test the redecl chain of Decls with the same visibility. Gtest
+// makes it possible to have either value-parameterized or type-parameterized
+// fixtures. However, we cannot have both value- and type-parameterized test
+// fixtures. This is a value-parameterized test fixture in the gtest sense. We
+// intend to mimic gtest's type-parameters via the PatternFactory template
+// parameter. We manually instantiate the different tests with the each types.
+template <typename PatternFactory>
+class ImportVisibilityChain
+ : public ASTImporterTestBase, public ImportVisibilityChainParams {
+protected:
+ using DeclTy = typename PatternFactory::DeclTy;
+ ArgVector getExtraArgs() const override { return std::get<0>(GetParam()); }
+ std::string getCode() const { return std::get<1>(GetParam()); }
+ BindableMatcher<Decl> getPattern() const { return PatternFactory()(); }
+
+ // Type-parameterized test.
+ void TypedTest_ImportChain() {
+ std::string Code = getCode() + getCode();
+ auto Pattern = getPattern();
+
+ TranslationUnitDecl *FromTu = getTuDecl(Code, Lang_CXX14, "input0.cc");
+
+ auto *FromD0 = FirstDeclMatcher<DeclTy>().match(FromTu, Pattern);
+ auto *FromD1 = LastDeclMatcher<DeclTy>().match(FromTu, Pattern);
+
+ auto *ToD0 = Import(FromD0, Lang_CXX14);
+ auto *ToD1 = Import(FromD1, Lang_CXX14);
+
+ EXPECT_TRUE(ToD0);
+ ASSERT_TRUE(ToD1);
+ EXPECT_NE(ToD0, ToD1);
+ EXPECT_EQ(ToD1->getPreviousDecl(), ToD0);
+ }
+};
+
+// Manual instantiation of the fixture with each type.
+using ImportFunctionsVisibilityChain = ImportVisibilityChain<GetFunPattern>;
+using ImportVariablesVisibilityChain = ImportVisibilityChain<GetVarPattern>;
+using ImportClassesVisibilityChain = ImportVisibilityChain<GetClassPattern>;
+using ImportFunctionTemplatesVisibilityChain =
+ ImportVisibilityChain<GetFunTemplPattern>;
+using ImportClassTemplatesVisibilityChain =
+ ImportVisibilityChain<GetClassTemplPattern>;
+
+// Value-parameterized test for functions.
+TEST_P(ImportFunctionsVisibilityChain, ImportChain) {
+ TypedTest_ImportChain();
+}
+// Value-parameterized test for variables.
+TEST_P(ImportVariablesVisibilityChain, ImportChain) {
+ TypedTest_ImportChain();
+}
+// Value-parameterized test for classes.
+TEST_P(ImportClassesVisibilityChain, ImportChain) {
+ TypedTest_ImportChain();
+}
+// Value-parameterized test for function templates.
+TEST_P(ImportFunctionTemplatesVisibilityChain, ImportChain) {
+ TypedTest_ImportChain();
+}
+// Value-parameterized test for class templates.
+TEST_P(ImportClassTemplatesVisibilityChain, ImportChain) {
+ TypedTest_ImportChain();
+}
+
+// Automatic instantiation of the value-parameterized tests.
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionsVisibilityChain,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(ExternF, StaticF, AnonF)), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportVariablesVisibilityChain,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ // There is no point to instantiate with StaticV, because in C++ we can
+ // forward declare a variable only with the 'extern' keyword.
+ // Consequently, each fwd declared variable has external linkage. This
+ // is different in the C language where any declaration without an
+ // initializer is a tentative definition, subsequent definitions may be
+ // provided but they must have the same linkage. See also the test
+ // ImportVariableChainInC which test for this special C Lang case.
+ ::testing::Values(ExternV, AnonV)), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportClassesVisibilityChain,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(ExternC, AnonC)), );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests,
+ ImportFunctionTemplatesVisibilityChain,
+ ::testing::Combine(DefaultTestValuesForRunOptions,
+ ::testing::Values(ExternFT, StaticFT,
+ AnonFT)), );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplatesVisibilityChain,
+ ::testing::Combine(DefaultTestValuesForRunOptions,
+ ::testing::Values(ExternCT,
+ AnonCT)), );
+
+// First value in tuple: Compile options.
+// Second value in tuple: Tuple with informations for the test.
+// Code for first import (or initial code), code to import, whether the `f`
+// functions are expected to be linked in a declaration chain.
+// One value of this tuple is combined with every value of compile options.
+// The test can have a single tuple as parameter only.
+using ImportVisibilityParams = ::testing::WithParamInterface<
+ std::tuple<ArgVector, std::tuple<const char *, const char *, bool>>>;
+
+template <typename PatternFactory>
+class ImportVisibility
+ : public ASTImporterTestBase,
+ public ImportVisibilityParams {
+protected:
+ using DeclTy = typename PatternFactory::DeclTy;
+ ArgVector getExtraArgs() const override { return std::get<0>(GetParam()); }
+ std::string getCode0() const { return std::get<0>(std::get<1>(GetParam())); }
+ std::string getCode1() const { return std::get<1>(std::get<1>(GetParam())); }
+ bool shouldBeLinked() const { return std::get<2>(std::get<1>(GetParam())); }
+ BindableMatcher<Decl> getPattern() const { return PatternFactory()(); }
+
+ void TypedTest_ImportAfter() {
+ TranslationUnitDecl *ToTu = getToTuDecl(getCode0(), Lang_CXX14);
+ TranslationUnitDecl *FromTu =
+ getTuDecl(getCode1(), Lang_CXX14, "input1.cc");
+
+ auto *ToD0 = FirstDeclMatcher<DeclTy>().match(ToTu, getPattern());
+ auto *FromD1 = FirstDeclMatcher<DeclTy>().match(FromTu, getPattern());
+
+ auto *ToD1 = Import(FromD1, Lang_CXX14);
+
+ ASSERT_TRUE(ToD0);
+ ASSERT_TRUE(ToD1);
+ EXPECT_NE(ToD0, ToD1);
+
+ if (shouldBeLinked())
+ EXPECT_EQ(ToD1->getPreviousDecl(), ToD0);
+ else
+ EXPECT_FALSE(ToD1->getPreviousDecl());
+ }
+
+ void TypedTest_ImportAfterImport() {
+ TranslationUnitDecl *FromTu0 =
+ getTuDecl(getCode0(), Lang_CXX14, "input0.cc");
+ TranslationUnitDecl *FromTu1 =
+ getTuDecl(getCode1(), Lang_CXX14, "input1.cc");
+ auto *FromD0 = FirstDeclMatcher<DeclTy>().match(FromTu0, getPattern());
+ auto *FromD1 = FirstDeclMatcher<DeclTy>().match(FromTu1, getPattern());
+ auto *ToD0 = Import(FromD0, Lang_CXX14);
+ auto *ToD1 = Import(FromD1, Lang_CXX14);
+ ASSERT_TRUE(ToD0);
+ ASSERT_TRUE(ToD1);
+ EXPECT_NE(ToD0, ToD1);
+ if (shouldBeLinked())
+ EXPECT_EQ(ToD1->getPreviousDecl(), ToD0);
+ else
+ EXPECT_FALSE(ToD1->getPreviousDecl());
+ }
+
+ void TypedTest_ImportAfterWithMerge() {
+ TranslationUnitDecl *ToTu = getToTuDecl(getCode0(), Lang_CXX14);
+ TranslationUnitDecl *FromTu =
+ getTuDecl(getCode1(), Lang_CXX14, "input1.cc");
+
+ auto *ToF0 = FirstDeclMatcher<DeclTy>().match(ToTu, getPattern());
+ auto *FromF1 = FirstDeclMatcher<DeclTy>().match(FromTu, getPattern());
+
+ auto *ToF1 = Import(FromF1, Lang_CXX14);
+
+ ASSERT_TRUE(ToF0);
+ ASSERT_TRUE(ToF1);
+
+ if (shouldBeLinked())
+ EXPECT_EQ(ToF0, ToF1);
+ else
+ EXPECT_NE(ToF0, ToF1);
+
+ // We expect no (ODR) warning during the import.
+ EXPECT_EQ(0u, ToTu->getASTContext().getDiagnostics().getNumWarnings());
+ }
+
+ void TypedTest_ImportAfterImportWithMerge() {
+ TranslationUnitDecl *FromTu0 =
+ getTuDecl(getCode0(), Lang_CXX14, "input0.cc");
+ TranslationUnitDecl *FromTu1 =
+ getTuDecl(getCode1(), Lang_CXX14, "input1.cc");
+ auto *FromF0 = FirstDeclMatcher<DeclTy>().match(FromTu0, getPattern());
+ auto *FromF1 = FirstDeclMatcher<DeclTy>().match(FromTu1, getPattern());
+ auto *ToF0 = Import(FromF0, Lang_CXX14);
+ auto *ToF1 = Import(FromF1, Lang_CXX14);
+ ASSERT_TRUE(ToF0);
+ ASSERT_TRUE(ToF1);
+ if (shouldBeLinked())
+ EXPECT_EQ(ToF0, ToF1);
+ else
+ EXPECT_NE(ToF0, ToF1);
+
+ // We expect no (ODR) warning during the import.
+ EXPECT_EQ(0u, ToF0->getTranslationUnitDecl()
+ ->getASTContext()
+ .getDiagnostics()
+ .getNumWarnings());
+ }
+};
+using ImportFunctionsVisibility = ImportVisibility<GetFunPattern>;
+using ImportVariablesVisibility = ImportVisibility<GetVarPattern>;
+using ImportClassesVisibility = ImportVisibility<GetClassPattern>;
+using ImportEnumsVisibility = ImportVisibility<GetEnumPattern>;
+using ImportTypedefNameVisibility = ImportVisibility<GetTypedefNamePattern>;
+using ImportFunctionTemplatesVisibility = ImportVisibility<GetFunTemplPattern>;
+using ImportClassTemplatesVisibility = ImportVisibility<GetClassTemplPattern>;
+
+// FunctionDecl.
+TEST_P(ImportFunctionsVisibility, ImportAfter) {
+ TypedTest_ImportAfter();
+}
+TEST_P(ImportFunctionsVisibility, ImportAfterImport) {
+ TypedTest_ImportAfterImport();
+}
+// VarDecl.
+TEST_P(ImportVariablesVisibility, ImportAfter) {
+ TypedTest_ImportAfter();
+}
+TEST_P(ImportVariablesVisibility, ImportAfterImport) {
+ TypedTest_ImportAfterImport();
+}
+// CXXRecordDecl.
+TEST_P(ImportClassesVisibility, ImportAfter) {
+ TypedTest_ImportAfter();
+}
+TEST_P(ImportClassesVisibility, ImportAfterImport) {
+ TypedTest_ImportAfterImport();
+}
+// EnumDecl.
+TEST_P(ImportEnumsVisibility, ImportAfter) {
+ TypedTest_ImportAfterWithMerge();
+}
+TEST_P(ImportEnumsVisibility, ImportAfterImport) {
+ TypedTest_ImportAfterImportWithMerge();
+}
+// TypedefNameDecl.
+TEST_P(ImportTypedefNameVisibility, ImportAfter) {
+ TypedTest_ImportAfterWithMerge();
+}
+TEST_P(ImportTypedefNameVisibility, ImportAfterImport) {
+ TypedTest_ImportAfterImportWithMerge();
+}
+// FunctionTemplateDecl.
+TEST_P(ImportFunctionTemplatesVisibility, ImportAfter) {
+ TypedTest_ImportAfter();
+}
+TEST_P(ImportFunctionTemplatesVisibility, ImportAfterImport) {
+ TypedTest_ImportAfterImport();
+}
+// ClassTemplateDecl.
+TEST_P(ImportClassTemplatesVisibility, ImportAfter) { TypedTest_ImportAfter(); }
+TEST_P(ImportClassTemplatesVisibility, ImportAfterImport) {
+ TypedTest_ImportAfterImport();
+}
+
+const bool ExpectLinkedDeclChain = true;
+const bool ExpectUnlinkedDeclChain = false;
+
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportFunctionsVisibility,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(
+ std::make_tuple(ExternF, ExternF, ExpectLinkedDeclChain),
+ std::make_tuple(ExternF, StaticF, ExpectUnlinkedDeclChain),
+ std::make_tuple(ExternF, AnonF, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticF, ExternF, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticF, StaticF, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticF, AnonF, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonF, ExternF, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonF, StaticF, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonF, AnonF, ExpectUnlinkedDeclChain))), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportVariablesVisibility,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(
+ std::make_tuple(ExternV, ExternV, ExpectLinkedDeclChain),
+ std::make_tuple(ExternV, StaticV, ExpectUnlinkedDeclChain),
+ std::make_tuple(ExternV, AnonV, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticV, ExternV, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticV, StaticV, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticV, AnonV, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonV, ExternV, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonV, StaticV, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonV, AnonV, ExpectUnlinkedDeclChain))), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportClassesVisibility,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(
+ std::make_tuple(ExternC, ExternC, ExpectLinkedDeclChain),
+ std::make_tuple(ExternC, AnonC, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonC, ExternC, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonC, AnonC, ExpectUnlinkedDeclChain))), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportEnumsVisibility,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(
+ std::make_tuple(ExternE, ExternE, ExpectLinkedDeclChain),
+ std::make_tuple(ExternE, AnonE, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonE, ExternE, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonE, AnonE, ExpectUnlinkedDeclChain))), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportTypedefNameVisibility,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(
+ std::make_tuple(ExternTypedef, ExternTypedef,
+ ExpectLinkedDeclChain),
+ std::make_tuple(ExternTypedef, AnonTypedef,
+ ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonTypedef, ExternTypedef,
+ ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonTypedef, AnonTypedef, ExpectUnlinkedDeclChain),
+
+ std::make_tuple(ExternUsing, ExternUsing, ExpectLinkedDeclChain),
+ std::make_tuple(ExternUsing, AnonUsing, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonUsing, ExternUsing, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonUsing, AnonUsing, ExpectUnlinkedDeclChain),
+
+ std::make_tuple(ExternUsing, ExternTypedef, ExpectLinkedDeclChain),
+ std::make_tuple(ExternUsing, AnonTypedef, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonUsing, ExternTypedef, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonUsing, AnonTypedef, ExpectUnlinkedDeclChain),
+
+ std::make_tuple(ExternTypedef, ExternUsing, ExpectLinkedDeclChain),
+ std::make_tuple(ExternTypedef, AnonUsing, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonTypedef, ExternUsing, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonTypedef, AnonUsing,
+ ExpectUnlinkedDeclChain))), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportFunctionTemplatesVisibility,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(
+ std::make_tuple(ExternFT, ExternFT, ExpectLinkedDeclChain),
+ std::make_tuple(ExternFT, StaticFT, ExpectUnlinkedDeclChain),
+ std::make_tuple(ExternFT, AnonFT, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticFT, ExternFT, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticFT, StaticFT, ExpectUnlinkedDeclChain),
+ std::make_tuple(StaticFT, AnonFT, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonFT, ExternFT, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonFT, StaticFT, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonFT, AnonFT, ExpectUnlinkedDeclChain))), );
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportClassTemplatesVisibility,
+ ::testing::Combine(
+ DefaultTestValuesForRunOptions,
+ ::testing::Values(std::make_tuple(ExternCT, ExternCT, ExpectLinkedDeclChain),
+ std::make_tuple(ExternCT, AnonCT, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonCT, ExternCT, ExpectUnlinkedDeclChain),
+ std::make_tuple(AnonCT, AnonCT, ExpectUnlinkedDeclChain))), );
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTPrint.h b/gnu/llvm/clang/unittests/AST/ASTPrint.h
new file mode 100644
index 00000000000..c3b6b842316
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTPrint.h
@@ -0,0 +1,92 @@
+//===- unittests/AST/ASTPrint.h ------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers to simplify testing of printing of AST constructs provided in the/
+// form of the source code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+
+using PolicyAdjusterType =
+ Optional<llvm::function_ref<void(PrintingPolicy &Policy)>>;
+
+static void PrintStmt(raw_ostream &Out, const ASTContext *Context,
+ const Stmt *S, PolicyAdjusterType PolicyAdjuster) {
+ assert(S != nullptr && "Expected non-null Stmt");
+ PrintingPolicy Policy = Context->getPrintingPolicy();
+ if (PolicyAdjuster)
+ (*PolicyAdjuster)(Policy);
+ S->printPretty(Out, /*Helper*/ nullptr, Policy);
+}
+
+class PrintMatch : public ast_matchers::MatchFinder::MatchCallback {
+ SmallString<1024> Printed;
+ unsigned NumFoundStmts;
+ PolicyAdjusterType PolicyAdjuster;
+
+public:
+ PrintMatch(PolicyAdjusterType PolicyAdjuster)
+ : NumFoundStmts(0), PolicyAdjuster(PolicyAdjuster) {}
+
+ void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
+ const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id");
+ if (!S)
+ return;
+ NumFoundStmts++;
+ if (NumFoundStmts > 1)
+ return;
+
+ llvm::raw_svector_ostream Out(Printed);
+ PrintStmt(Out, Result.Context, S, PolicyAdjuster);
+ }
+
+ StringRef getPrinted() const { return Printed; }
+
+ unsigned getNumFoundStmts() const { return NumFoundStmts; }
+};
+
+template <typename T>
+::testing::AssertionResult
+PrintedStmtMatches(StringRef Code, const std::vector<std::string> &Args,
+ const T &NodeMatch, StringRef ExpectedPrinted,
+ PolicyAdjusterType PolicyAdjuster = None) {
+
+ PrintMatch Printer(PolicyAdjuster);
+ ast_matchers::MatchFinder Finder;
+ Finder.addMatcher(NodeMatch, &Printer);
+ std::unique_ptr<tooling::FrontendActionFactory> Factory(
+ tooling::newFrontendActionFactory(&Finder));
+
+ if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args))
+ return testing::AssertionFailure()
+ << "Parsing error in \"" << Code.str() << "\"";
+
+ if (Printer.getNumFoundStmts() == 0)
+ return testing::AssertionFailure() << "Matcher didn't find any statements";
+
+ if (Printer.getNumFoundStmts() > 1)
+ return testing::AssertionFailure()
+ << "Matcher should match only one statement (found "
+ << Printer.getNumFoundStmts() << ")";
+
+ if (Printer.getPrinted() != ExpectedPrinted)
+ return ::testing::AssertionFailure()
+ << "Expected \"" << ExpectedPrinted.str() << "\", got \""
+ << Printer.getPrinted().str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+} // namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTTraverserTest.cpp b/gnu/llvm/clang/unittests/AST/ASTTraverserTest.cpp
new file mode 100644
index 00000000000..88921a00205
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTTraverserTest.cpp
@@ -0,0 +1,617 @@
+//===- unittests/AST/ASTTraverserTest.h------------------------------------===//
+//
+// 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/AST/ASTContext.h"
+#include "clang/AST/ASTNodeTraverser.h"
+#include "clang/AST/TextNodeDumper.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace clang::tooling;
+using namespace clang::ast_matchers;
+
+namespace clang {
+
+class NodeTreePrinter : public TextTreeStructure {
+ llvm::raw_ostream &OS;
+
+public:
+ NodeTreePrinter(llvm::raw_ostream &OS)
+ : TextTreeStructure(OS, /* showColors */ false), OS(OS) {}
+
+ void Visit(const Decl *D) {
+ OS << D->getDeclKindName() << "Decl";
+ if (auto *ND = dyn_cast<NamedDecl>(D)) {
+ OS << " '" << ND->getDeclName() << "'";
+ }
+ }
+
+ void Visit(const Stmt *S) {
+ OS << S->getStmtClassName();
+ if (auto *E = dyn_cast<DeclRefExpr>(S)) {
+ OS << " '" << E->getDecl()->getDeclName() << "'";
+ }
+ }
+
+ void Visit(QualType QT) {
+ OS << "QualType " << QT.split().Quals.getAsString();
+ }
+
+ void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; }
+
+ void Visit(const comments::Comment *C, const comments::FullComment *FC) {
+ OS << C->getCommentKindName();
+ }
+
+ void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; }
+
+ void Visit(const Attr *A) {
+ switch (A->getKind()) {
+#define ATTR(X) \
+ case attr::X: \
+ OS << #X; \
+ break;
+#include "clang/Basic/AttrList.inc"
+ }
+ OS << "Attr";
+ }
+
+ void Visit(const OMPClause *C) { OS << "OMPClause"; }
+ void Visit(const TemplateArgument &A, SourceRange R = {},
+ const Decl *From = nullptr, const char *Label = nullptr) {
+ OS << "TemplateArgument";
+ }
+
+ template <typename... T> void Visit(T...) {}
+};
+
+class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> {
+
+ NodeTreePrinter MyNodeRecorder;
+
+public:
+ TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {}
+ NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; }
+};
+
+template <typename... NodeType> std::string dumpASTString(NodeType &&... N) {
+ std::string Buffer;
+ llvm::raw_string_ostream OS(Buffer);
+
+ TestASTDumper Dumper(OS);
+
+ OS << "\n";
+
+ Dumper.Visit(std::forward<NodeType &&>(N)...);
+
+ return OS.str();
+}
+
+template <typename... NodeType>
+std::string dumpASTString(ast_type_traits::TraversalKind TK, NodeType &&... N) {
+ std::string Buffer;
+ llvm::raw_string_ostream OS(Buffer);
+
+ TestASTDumper Dumper(OS);
+ Dumper.SetTraversalKind(TK);
+
+ OS << "\n";
+
+ Dumper.Visit(std::forward<NodeType &&>(N)...);
+
+ return OS.str();
+}
+
+const FunctionDecl *getFunctionNode(clang::ASTUnit *AST,
+ const std::string &Name) {
+ auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"),
+ AST->getASTContext());
+ EXPECT_EQ(Result.size(), 1u);
+ return Result[0].getNodeAs<FunctionDecl>("fn");
+}
+
+template <typename T> struct Verifier {
+ static void withDynNode(T Node, const std::string &DumpString) {
+ EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(Node)),
+ DumpString);
+ }
+};
+
+template <typename T> struct Verifier<T *> {
+ static void withDynNode(T *Node, const std::string &DumpString) {
+ EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(*Node)),
+ DumpString);
+ }
+};
+
+template <typename T>
+void verifyWithDynNode(T Node, const std::string &DumpString) {
+ EXPECT_EQ(dumpASTString(Node), DumpString);
+
+ Verifier<T>::withDynNode(Node, DumpString);
+}
+
+TEST(Traverse, Dump) {
+
+ auto AST = buildASTFromCode(R"cpp(
+struct A {
+ int m_number;
+
+ /// CTor
+ A() : m_number(42) {}
+
+ [[nodiscard]] const int func() {
+ return 42;
+ }
+
+};
+
+template<typename T>
+struct templ
+{
+};
+
+template<>
+struct templ<int>
+{
+};
+
+void parmvardecl_attr(struct A __attribute__((address_space(19)))*);
+
+)cpp");
+
+ const FunctionDecl *Func = getFunctionNode(AST.get(), "func");
+
+ verifyWithDynNode(Func,
+ R"cpp(
+CXXMethodDecl 'func'
+|-CompoundStmt
+| `-ReturnStmt
+| `-IntegerLiteral
+`-WarnUnusedResultAttr
+)cpp");
+
+ Stmt *Body = Func->getBody();
+
+ verifyWithDynNode(Body,
+ R"cpp(
+CompoundStmt
+`-ReturnStmt
+ `-IntegerLiteral
+)cpp");
+
+ QualType QT = Func->getType();
+
+ verifyWithDynNode(QT,
+ R"cpp(
+FunctionProtoType
+`-QualType const
+ `-BuiltinType
+)cpp");
+
+ const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A");
+
+ verifyWithDynNode(CTorFunc->getType(),
+ R"cpp(
+FunctionProtoType
+`-BuiltinType
+)cpp");
+
+ Attr *A = *Func->attr_begin();
+
+ {
+ std::string expectedString = R"cpp(
+WarnUnusedResultAttr
+)cpp";
+
+ EXPECT_EQ(dumpASTString(A), expectedString);
+ }
+
+ auto *CTor = dyn_cast<CXXConstructorDecl>(CTorFunc);
+ const CXXCtorInitializer *Init = *CTor->init_begin();
+
+ verifyWithDynNode(Init,
+ R"cpp(
+CXXCtorInitializer
+`-IntegerLiteral
+)cpp");
+
+ const comments::FullComment *Comment =
+ AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc);
+ {
+ std::string expectedString = R"cpp(
+FullComment
+`-ParagraphComment
+ `-TextComment
+)cpp";
+ EXPECT_EQ(dumpASTString(Comment, Comment), expectedString);
+ }
+
+ auto Result = ast_matchers::match(
+ classTemplateSpecializationDecl(hasName("templ")).bind("fn"),
+ AST->getASTContext());
+ EXPECT_EQ(Result.size(), 1u);
+ auto Templ = Result[0].getNodeAs<ClassTemplateSpecializationDecl>("fn");
+
+ TemplateArgument TA = Templ->getTemplateArgs()[0];
+
+ verifyWithDynNode(TA,
+ R"cpp(
+TemplateArgument
+)cpp");
+
+ Func = getFunctionNode(AST.get(), "parmvardecl_attr");
+
+ const auto *Parm = Func->getParamDecl(0);
+ const auto TL = Parm->getTypeSourceInfo()->getTypeLoc();
+ ASSERT_TRUE(TL.getType()->isPointerType());
+
+ const auto ATL = TL.getNextTypeLoc().getAs<AttributedTypeLoc>();
+ const auto *AS = cast<AddressSpaceAttr>(ATL.getAttr());
+ EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(AS->getAddressSpace())),
+ 19u);
+}
+
+TEST(Traverse, IgnoreUnlessSpelledInSource) {
+
+ auto AST = buildASTFromCode(R"cpp(
+
+struct A
+{
+};
+
+struct B
+{
+ B(int);
+ B(A const& a);
+ B();
+};
+
+struct C
+{
+ operator B();
+};
+
+B func1() {
+ return 42;
+}
+
+B func2() {
+ return B{42};
+}
+
+B func3() {
+ return B(42);
+}
+
+B func4() {
+ return B();
+}
+
+B func5() {
+ return B{};
+}
+
+B func6() {
+ return C();
+}
+
+B func7() {
+ return A();
+}
+
+B func8() {
+ return C{};
+}
+
+B func9() {
+ return A{};
+}
+
+B func10() {
+ A a;
+ return a;
+}
+
+B func11() {
+ B b;
+ return b;
+}
+
+B func12() {
+ C c;
+ return c;
+}
+
+)cpp");
+
+ auto getFunctionNode = [&AST](const std::string &name) {
+ auto BN = ast_matchers::match(functionDecl(hasName(name)).bind("fn"),
+ AST->getASTContext());
+ EXPECT_EQ(BN.size(), 1u);
+ return BN[0].getNodeAs<Decl>("fn");
+ };
+
+ {
+ auto FN = getFunctionNode("func1");
+ llvm::StringRef Expected = R"cpp(
+FunctionDecl 'func1'
+`-CompoundStmt
+ `-ReturnStmt
+ `-ExprWithCleanups
+ `-CXXConstructExpr
+ `-MaterializeTemporaryExpr
+ `-ImplicitCastExpr
+ `-CXXConstructExpr
+ `-IntegerLiteral
+)cpp";
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, FN), Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func1'
+`-CompoundStmt
+ `-ReturnStmt
+ `-IntegerLiteral
+)cpp";
+ EXPECT_EQ(
+ dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, FN),
+ Expected);
+ }
+
+ llvm::StringRef Expected = R"cpp(
+FunctionDecl 'func2'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+ `-IntegerLiteral
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func2")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func3'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXFunctionalCastExpr
+ `-IntegerLiteral
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func3")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func4'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func4")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func5'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func5")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func6'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func6")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func7'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func7")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func8'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXFunctionalCastExpr
+ `-InitListExpr
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func8")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func9'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXFunctionalCastExpr
+ `-InitListExpr
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func9")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func10'
+`-CompoundStmt
+ |-DeclStmt
+ | `-VarDecl 'a'
+ | `-CXXConstructExpr
+ `-ReturnStmt
+ `-DeclRefExpr 'a'
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func10")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func11'
+`-CompoundStmt
+ |-DeclStmt
+ | `-VarDecl 'b'
+ | `-CXXConstructExpr
+ `-ReturnStmt
+ `-DeclRefExpr 'b'
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func11")),
+ Expected);
+
+ Expected = R"cpp(
+FunctionDecl 'func12'
+`-CompoundStmt
+ |-DeclStmt
+ | `-VarDecl 'c'
+ | `-CXXConstructExpr
+ `-ReturnStmt
+ `-DeclRefExpr 'c'
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func12")),
+ Expected);
+}
+
+TEST(Traverse, LambdaUnlessSpelledInSource) {
+
+ auto AST =
+ buildASTFromCodeWithArgs(R"cpp(
+
+void captures() {
+ int a = 0;
+ int b = 0;
+ int d = 0;
+ int f = 0;
+
+ [a, &b, c = d, &e = f](int g, int h = 42) {};
+}
+
+void templated() {
+ int a = 0;
+ [a]<typename T>(T t) {};
+}
+
+struct SomeStruct {
+ int a = 0;
+ void capture_this() {
+ [this]() {};
+ }
+ void capture_this_copy() {
+ [self = *this]() {};
+ }
+};
+)cpp",
+ {"-Wno-unused-value", "-Wno-c++2a-extensions"});
+
+ auto getLambdaNode = [&AST](const std::string &name) {
+ auto BN = ast_matchers::match(
+ lambdaExpr(hasAncestor(functionDecl(hasName(name)))).bind("lambda"),
+ AST->getASTContext());
+ EXPECT_EQ(BN.size(), 1u);
+ return BN[0].getNodeAs<LambdaExpr>("lambda");
+ };
+
+ {
+ auto L = getLambdaNode("captures");
+
+ llvm::StringRef Expected = R"cpp(
+LambdaExpr
+|-DeclRefExpr 'a'
+|-DeclRefExpr 'b'
+|-VarDecl 'c'
+| `-DeclRefExpr 'd'
+|-VarDecl 'e'
+| `-DeclRefExpr 'f'
+|-ParmVarDecl 'g'
+|-ParmVarDecl 'h'
+| `-IntegerLiteral
+`-CompoundStmt
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
+ Expected);
+
+ Expected = R"cpp(
+LambdaExpr
+|-CXXRecordDecl ''
+| |-CXXMethodDecl 'operator()'
+| | |-ParmVarDecl 'g'
+| | |-ParmVarDecl 'h'
+| | | `-IntegerLiteral
+| | `-CompoundStmt
+| |-FieldDecl ''
+| |-FieldDecl ''
+| |-FieldDecl ''
+| |-FieldDecl ''
+| `-CXXDestructorDecl '~'
+|-ImplicitCastExpr
+| `-DeclRefExpr 'a'
+|-DeclRefExpr 'b'
+|-ImplicitCastExpr
+| `-DeclRefExpr 'd'
+|-DeclRefExpr 'f'
+`-CompoundStmt
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, L), Expected);
+ }
+
+ {
+ auto L = getLambdaNode("templated");
+
+ llvm::StringRef Expected = R"cpp(
+LambdaExpr
+|-DeclRefExpr 'a'
+|-TemplateTypeParmDecl 'T'
+|-ParmVarDecl 't'
+`-CompoundStmt
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
+ Expected);
+ }
+
+ {
+ auto L = getLambdaNode("capture_this");
+
+ llvm::StringRef Expected = R"cpp(
+LambdaExpr
+|-CXXThisExpr
+`-CompoundStmt
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
+ Expected);
+ }
+
+ {
+ auto L = getLambdaNode("capture_this_copy");
+
+ llvm::StringRef Expected = R"cpp(
+LambdaExpr
+|-VarDecl 'self'
+| `-UnaryOperator
+| `-CXXThisExpr
+`-CompoundStmt
+)cpp";
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
+ Expected);
+ }
+}
+
+} // namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTTypeTraitsTest.cpp b/gnu/llvm/clang/unittests/AST/ASTTypeTraitsTest.cpp
new file mode 100644
index 00000000000..2313f9f6dfa
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTTypeTraitsTest.cpp
@@ -0,0 +1,183 @@
+//===- unittest/AST/ASTTypeTraits.cpp - AST type traits unit tests ------===//
+//
+// 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/AST/ASTTypeTraits.h"
+#include "MatchVerifier.h"
+#include "gtest/gtest.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace ast_type_traits {
+
+TEST(ASTNodeKind, NoKind) {
+ EXPECT_FALSE(ASTNodeKind().isBaseOf(ASTNodeKind()));
+ EXPECT_FALSE(ASTNodeKind().isSame(ASTNodeKind()));
+}
+
+template <typename T> static ASTNodeKind DNT() {
+ return ASTNodeKind::getFromNodeKind<T>();
+}
+
+TEST(ASTNodeKind, IsNone) {
+ EXPECT_TRUE(ASTNodeKind().isNone());
+ EXPECT_FALSE(DNT<Decl>().isNone());
+ EXPECT_FALSE(DNT<VarDecl>().isNone());
+}
+
+TEST(ASTNodeKind, Bases) {
+ EXPECT_TRUE(DNT<Decl>().isBaseOf(DNT<VarDecl>()));
+ EXPECT_FALSE(DNT<Decl>().isSame(DNT<VarDecl>()));
+ EXPECT_FALSE(DNT<VarDecl>().isBaseOf(DNT<Decl>()));
+
+ EXPECT_TRUE(DNT<Decl>().isSame(DNT<Decl>()));
+}
+
+TEST(ASTNodeKind, BaseDistances) {
+ unsigned Distance = 1;
+ EXPECT_TRUE(DNT<Expr>().isBaseOf(DNT<Expr>(), &Distance));
+ EXPECT_EQ(0u, Distance);
+
+ EXPECT_TRUE(DNT<Stmt>().isBaseOf(DNT<IfStmt>(), &Distance));
+ EXPECT_EQ(1u, Distance);
+
+ Distance = 3;
+ EXPECT_TRUE(DNT<DeclaratorDecl>().isBaseOf(DNT<ParmVarDecl>(), &Distance));
+ EXPECT_EQ(2u, Distance);
+}
+
+TEST(ASTNodeKind, SameBase) {
+ EXPECT_TRUE(DNT<Expr>().isBaseOf(DNT<CallExpr>()));
+ EXPECT_TRUE(DNT<Expr>().isBaseOf(DNT<BinaryOperator>()));
+ EXPECT_FALSE(DNT<CallExpr>().isBaseOf(DNT<BinaryOperator>()));
+ EXPECT_FALSE(DNT<BinaryOperator>().isBaseOf(DNT<CallExpr>()));
+}
+
+TEST(ASTNodeKind, DiffBase) {
+ EXPECT_FALSE(DNT<Expr>().isBaseOf(DNT<ArrayType>()));
+ EXPECT_FALSE(DNT<QualType>().isBaseOf(DNT<FunctionDecl>()));
+ EXPECT_FALSE(DNT<Type>().isSame(DNT<QualType>()));
+}
+
+TEST(ASTNodeKind, MostDerivedType) {
+ EXPECT_TRUE(DNT<BinaryOperator>().isSame(
+ ASTNodeKind::getMostDerivedType(DNT<Expr>(), DNT<BinaryOperator>())));
+ EXPECT_TRUE(DNT<BinaryOperator>().isSame(
+ ASTNodeKind::getMostDerivedType(DNT<BinaryOperator>(), DNT<Expr>())));
+ EXPECT_TRUE(DNT<VarDecl>().isSame(
+ ASTNodeKind::getMostDerivedType(DNT<VarDecl>(), DNT<VarDecl>())));
+
+ // Not related. Returns nothing.
+ EXPECT_TRUE(
+ ASTNodeKind::getMostDerivedType(DNT<IfStmt>(), DNT<VarDecl>()).isNone());
+ EXPECT_TRUE(ASTNodeKind::getMostDerivedType(DNT<IfStmt>(),
+ DNT<BinaryOperator>()).isNone());
+}
+
+TEST(ASTNodeKind, MostDerivedCommonAncestor) {
+ EXPECT_TRUE(DNT<Expr>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<Expr>(), DNT<BinaryOperator>())));
+ EXPECT_TRUE(DNT<Expr>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<BinaryOperator>(), DNT<Expr>())));
+ EXPECT_TRUE(DNT<VarDecl>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<VarDecl>(), DNT<VarDecl>())));
+
+ // A little related. Returns the ancestor.
+ EXPECT_TRUE(
+ DNT<NamedDecl>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<CXXMethodDecl>(), DNT<RecordDecl>())));
+
+ // Not related. Returns nothing.
+ EXPECT_TRUE(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<IfStmt>(), DNT<VarDecl>()).isNone());
+}
+
+struct Foo {};
+
+TEST(ASTNodeKind, UnknownKind) {
+ // We can construct one, but it is nowhere in the hierarchy.
+ EXPECT_FALSE(DNT<Foo>().isSame(DNT<Foo>()));
+}
+
+TEST(ASTNodeKind, Name) {
+ EXPECT_EQ("<None>", ASTNodeKind().asStringRef());
+#define VERIFY_NAME(Node) EXPECT_EQ(#Node, DNT<Node>().asStringRef());
+ VERIFY_NAME(TemplateArgument);
+ VERIFY_NAME(NestedNameSpecifierLoc);
+ VERIFY_NAME(QualType);
+ VERIFY_NAME(TypeLoc);
+ VERIFY_NAME(CXXCtorInitializer);
+ VERIFY_NAME(NestedNameSpecifier);
+ VERIFY_NAME(Decl);
+ VERIFY_NAME(CXXRecordDecl);
+ VERIFY_NAME(Stmt);
+ VERIFY_NAME(CallExpr);
+ VERIFY_NAME(Type);
+ VERIFY_NAME(ConstantArrayType);
+#undef VERIFY_NAME
+}
+
+TEST(DynTypedNode, DeclSourceRange) {
+ RangeVerifier<DynTypedNode> Verifier;
+ Verifier.expectRange(1, 1, 1, 11);
+ EXPECT_TRUE(Verifier.match("void f() {}", decl()));
+}
+
+TEST(DynTypedNode, StmtSourceRange) {
+ RangeVerifier<DynTypedNode> Verifier;
+ Verifier.expectRange(1, 10, 1, 11);
+ EXPECT_TRUE(Verifier.match("void f() {}", stmt()));
+}
+
+TEST(DynTypedNode, TypeLocSourceRange) {
+ RangeVerifier<DynTypedNode> Verifier;
+ Verifier.expectRange(1, 1, 1, 8);
+ EXPECT_TRUE(Verifier.match("void f() {}", typeLoc(loc(functionType()))));
+}
+
+TEST(DynTypedNode, NNSLocSourceRange) {
+ RangeVerifier<DynTypedNode> Verifier;
+ Verifier.expectRange(1, 33, 1, 34);
+ EXPECT_TRUE(Verifier.match("namespace N { typedef void T; } N::T f() {}",
+ nestedNameSpecifierLoc()));
+}
+
+TEST(DynTypedNode, DeclDump) {
+ DumpVerifier Verifier;
+ Verifier.expectSubstring("FunctionDecl");
+ EXPECT_TRUE(Verifier.match("void f() {}", functionDecl()));
+}
+
+TEST(DynTypedNode, StmtDump) {
+ DumpVerifier Verifier;
+ Verifier.expectSubstring("CompoundStmt");
+ EXPECT_TRUE(Verifier.match("void f() {}", stmt()));
+}
+
+TEST(DynTypedNode, DeclPrint) {
+ PrintVerifier Verifier;
+ Verifier.expectString("void f() {\n}\n");
+ EXPECT_TRUE(Verifier.match("void f() {}", functionDecl()));
+}
+
+TEST(DynTypedNode, StmtPrint) {
+ PrintVerifier Verifier;
+ Verifier.expectString("{\n}\n");
+ EXPECT_TRUE(Verifier.match("void f() {}", stmt()));
+}
+
+TEST(DynTypedNode, QualType) {
+ QualType Q;
+ DynTypedNode Node = DynTypedNode::create(Q);
+ EXPECT_TRUE(Node == Node);
+ EXPECT_FALSE(Node < Node);
+}
+
+} // namespace ast_type_traits
+} // namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/ASTVectorTest.cpp b/gnu/llvm/clang/unittests/AST/ASTVectorTest.cpp
new file mode 100644
index 00000000000..f5b208ab168
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ASTVectorTest.cpp
@@ -0,0 +1,90 @@
+//===- unittests/AST/DeclTest.cpp --- Declaration tests -------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Unit tests for the ASTVector container.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTVector.h"
+#include "clang/Basic/Builtins.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+
+namespace clang {
+namespace ast {
+
+namespace {
+class ASTVectorTest : public ::testing::Test {
+protected:
+ ASTVectorTest()
+ : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr), Idents(LangOpts, nullptr),
+ Ctxt(LangOpts, SourceMgr, Idents, Sels, Builtins) {}
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ LangOptions LangOpts;
+ IdentifierTable Idents;
+ SelectorTable Sels;
+ Builtin::Context Builtins;
+ ASTContext Ctxt;
+};
+} // unnamed namespace
+
+TEST_F(ASTVectorTest, Compile) {
+ ASTVector<int> V;
+ V.insert(Ctxt, V.begin(), 0);
+}
+
+TEST_F(ASTVectorTest, InsertFill) {
+ ASTVector<double> V;
+
+ // Ensure returned iterator points to first of inserted elements
+ auto I = V.insert(Ctxt, V.begin(), 5, 1.0);
+ ASSERT_EQ(V.begin(), I);
+
+ // Check non-empty case as well
+ I = V.insert(Ctxt, V.begin() + 1, 5, 1.0);
+ ASSERT_EQ(V.begin() + 1, I);
+
+ // And insert-at-end
+ I = V.insert(Ctxt, V.end(), 5, 1.0);
+ ASSERT_EQ(V.end() - 5, I);
+}
+
+TEST_F(ASTVectorTest, InsertEmpty) {
+ ASTVector<double> V;
+
+ // Ensure no pointer overflow when inserting empty range
+ int Values[] = { 0, 1, 2, 3 };
+ ArrayRef<int> IntVec(Values);
+ auto I = V.insert(Ctxt, V.begin(), IntVec.begin(), IntVec.begin());
+ ASSERT_EQ(V.begin(), I);
+ ASSERT_TRUE(V.empty());
+
+ // Non-empty range
+ I = V.insert(Ctxt, V.begin(), IntVec.begin(), IntVec.end());
+ ASSERT_EQ(V.begin(), I);
+
+ // Non-Empty Vector, empty range
+ I = V.insert(Ctxt, V.end(), IntVec.begin(), IntVec.begin());
+ ASSERT_EQ(V.begin() + IntVec.size(), I);
+
+ // Non-Empty Vector, non-empty range
+ I = V.insert(Ctxt, V.end(), IntVec.begin(), IntVec.end());
+ ASSERT_EQ(V.begin() + IntVec.size(), I);
+}
+
+} // end namespace ast
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/CMakeLists.txt b/gnu/llvm/clang/unittests/AST/CMakeLists.txt
new file mode 100644
index 00000000000..be585efc7a2
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/CMakeLists.txt
@@ -0,0 +1,45 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+if (MSVC)
+ set_source_files_properties(ASTImporterTest.cpp PROPERTIES COMPILE_FLAGS /bigobj)
+endif()
+
+add_clang_unittest(ASTTests
+ ASTContextParentMapTest.cpp
+ ASTImporterFixtures.cpp
+ ASTImporterTest.cpp
+ ASTImporterGenericRedeclTest.cpp
+ ASTImporterODRStrategiesTest.cpp
+ ASTImporterVisibilityTest.cpp
+ ASTTraverserTest.cpp
+ ASTTypeTraitsTest.cpp
+ ASTTraverserTest.cpp
+ ASTVectorTest.cpp
+ CommentLexer.cpp
+ CommentParser.cpp
+ CommentTextTest.cpp
+ DataCollectionTest.cpp
+ DeclPrinterTest.cpp
+ DeclTest.cpp
+ EvaluateAsRValueTest.cpp
+ ExternalASTSourceTest.cpp
+ Language.cpp
+ NamedDeclPrinterTest.cpp
+ OMPStructuredBlockTest.cpp
+ RecursiveASTVisitorTest.cpp
+ SourceLocationTest.cpp
+ StmtPrinterTest.cpp
+ StructuralEquivalenceTest.cpp
+ )
+
+clang_target_link_libraries(ASTTests
+ PRIVATE
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangSerialization
+ clangTooling
+ )
diff --git a/gnu/llvm/clang/unittests/AST/CommentLexer.cpp b/gnu/llvm/clang/unittests/AST/CommentLexer.cpp
new file mode 100644
index 00000000000..1883050658a
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/CommentLexer.cpp
@@ -0,0 +1,1845 @@
+//===- unittests/AST/CommentLexer.cpp ------ Comment lexer tests ----------===//
+//
+// 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/AST/CommentLexer.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/Basic/CommentOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/STLExtras.h"
+#include "gtest/gtest.h"
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+
+namespace clang {
+namespace comments {
+
+namespace {
+class CommentLexerTest : public ::testing::Test {
+protected:
+ CommentLexerTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr),
+ Traits(Allocator, CommentOptions()) {
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ llvm::BumpPtrAllocator Allocator;
+ CommandTraits Traits;
+
+ void lexString(const char *Source, std::vector<Token> &Toks);
+
+ StringRef getCommandName(const Token &Tok) {
+ return Traits.getCommandInfo(Tok.getCommandID())->Name;
+ }
+
+ StringRef getVerbatimBlockName(const Token &Tok) {
+ return Traits.getCommandInfo(Tok.getVerbatimBlockID())->Name;
+ }
+
+ StringRef getVerbatimLineName(const Token &Tok) {
+ return Traits.getCommandInfo(Tok.getVerbatimLineID())->Name;
+ }
+};
+
+void CommentLexerTest::lexString(const char *Source,
+ std::vector<Token> &Toks) {
+ std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID File = SourceMgr.createFileID(std::move(Buf));
+ SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
+
+ Lexer L(Allocator, Diags, Traits, Begin, Source, Source + strlen(Source));
+
+ while (1) {
+ Token Tok;
+ L.lex(Tok);
+ if (Tok.is(tok::eof))
+ break;
+ Toks.push_back(Tok);
+ }
+}
+
+} // unnamed namespace
+
+// Empty source range should be handled.
+TEST_F(CommentLexerTest, Basic1) {
+ const char *Source = "";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(0U, Toks.size());
+}
+
+// Empty comments should be handled.
+TEST_F(CommentLexerTest, Basic2) {
+ const char *Sources[] = {
+ "//", "///", "//!", "///<", "//!<"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(1U, Toks.size());
+
+ ASSERT_EQ(tok::newline, Toks[0].getKind());
+ }
+}
+
+// Empty comments should be handled.
+TEST_F(CommentLexerTest, Basic3) {
+ const char *Sources[] = {
+ "/**/", "/***/", "/*!*/", "/**<*/", "/*!<*/"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::newline, Toks[0].getKind());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// Single comment with plain text.
+TEST_F(CommentLexerTest, Basic4) {
+ const char *Sources[] = {
+ "// Meow", "/// Meow", "//! Meow",
+ "// Meow\n", "// Meow\r\n", "//! Meow\r",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// Single comment with plain text.
+TEST_F(CommentLexerTest, Basic5) {
+ const char *Sources[] = {
+ "/* Meow*/", "/** Meow*/", "/*! Meow*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// Test newline escaping.
+TEST_F(CommentLexerTest, Basic6) {
+ const char *Sources[] = {
+ "// Aaa\\\n" " Bbb\\ \n" " Ccc?" "?/\n",
+ "// Aaa\\\r\n" " Bbb\\ \r\n" " Ccc?" "?/\r\n",
+ "// Aaa\\\r" " Bbb\\ \r" " Ccc?" "?/\r"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[1].getText());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[3].getText());
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[4].getText());
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[5].getText());
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" Ccc?" "?/"), Toks[7].getText());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+ }
+}
+
+// Check that we skip C-style aligned stars correctly.
+TEST_F(CommentLexerTest, Basic7) {
+ const char *Source =
+ "/* Aaa\n"
+ " * Bbb\r\n"
+ "\t* Ccc\n"
+ " ! Ddd\n"
+ " * Eee\n"
+ " ** Fff\n"
+ " */";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(15U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[2].getText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Ccc"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" ! Ddd"), Toks[6].getText());
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+
+ ASSERT_EQ(tok::text, Toks[8].getKind());
+ ASSERT_EQ(StringRef(" Eee"), Toks[8].getText());
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+
+ ASSERT_EQ(tok::text, Toks[10].getKind());
+ ASSERT_EQ(StringRef("* Fff"), Toks[10].getText());
+ ASSERT_EQ(tok::newline, Toks[11].getKind());
+
+ ASSERT_EQ(tok::text, Toks[12].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[12].getText());
+
+ ASSERT_EQ(tok::newline, Toks[13].getKind());
+ ASSERT_EQ(tok::newline, Toks[14].getKind());
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand1) {
+ const char *Sources[] = { "//@", "///@", "//!@" };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand2) {
+ const char *Sources[] = { "/*@*/", "/**@*/", "/*!@*/"};
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand3) {
+ const char *Sources[] = { "/*\\*/", "/**\\*/" };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// Doxygen escape sequences.
+TEST_F(CommentLexerTest, DoxygenCommand4) {
+ const char *Sources[] = {
+ "/// \\\\ \\@ \\& \\$ \\# \\< \\> \\% \\\" \\. \\::",
+ "/// @\\ @@ @& @$ @# @< @> @% @\" @. @::"
+ };
+ const char *Text[] = {
+ " ",
+ "\\", " ", "@", " ", "&", " ", "$", " ", "#", " ",
+ "<", " ", ">", " ", "%", " ", "\"", " ", ".", " ",
+ "::", ""
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(array_lengthof(Text), Toks.size());
+
+ for (size_t j = 0, e = Toks.size(); j != e; j++) {
+ if(Toks[j].is(tok::text)) {
+ ASSERT_EQ(StringRef(Text[j]), Toks[j].getText())
+ << "index " << i;
+ }
+ }
+ }
+}
+
+// A command marker followed by a non-letter that is not a part of an escape
+// sequence.
+TEST_F(CommentLexerTest, DoxygenCommand5) {
+ const char *Source = "/// \\^ \\0";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("^ "), Toks[2].getText());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[3].getText());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef("0"), Toks[4].getText());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand6) {
+ const char *Source = "/// \\brief Aaa.";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("brief"), getCommandName(Toks[1]));
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa."), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand7) {
+ const char *Source = "/// \\em\\em \\em\t\\em\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(8U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[1]));
+
+ ASSERT_EQ(tok::backslash_command, Toks[2].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[2]));
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[3].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[4].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[4]));
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef("\t"), Toks[5].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[6].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[6]));
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand8) {
+ const char *Source = "/// @em@em @em\t@em\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(8U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::at_command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[1]));
+
+ ASSERT_EQ(tok::at_command, Toks[2].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[2]));
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[3].getText());
+
+ ASSERT_EQ(tok::at_command, Toks[4].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[4]));
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef("\t"), Toks[5].getText());
+
+ ASSERT_EQ(tok::at_command, Toks[6].getKind());
+ ASSERT_EQ(StringRef("em"), getCommandName(Toks[6]));
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand9) {
+ const char *Source = "/// \\aaa\\bbb \\ccc\t\\ddd\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(8U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::unknown_command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("aaa"), Toks[1].getUnknownCommandName());
+
+ ASSERT_EQ(tok::unknown_command, Toks[2].getKind());
+ ASSERT_EQ(StringRef("bbb"), Toks[2].getUnknownCommandName());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[3].getText());
+
+ ASSERT_EQ(tok::unknown_command, Toks[4].getKind());
+ ASSERT_EQ(StringRef("ccc"), Toks[4].getUnknownCommandName());
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef("\t"), Toks[5].getText());
+
+ ASSERT_EQ(tok::unknown_command, Toks[6].getKind());
+ ASSERT_EQ(StringRef("ddd"), Toks[6].getUnknownCommandName());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand10) {
+ const char *Source = "// \\c\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("c"), getCommandName(Toks[1]));
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, RegisterCustomBlockCommand) {
+ const char *Source =
+ "/// \\NewBlockCommand Aaa.\n"
+ "/// @NewBlockCommand Aaa.\n";
+
+ Traits.registerBlockCommand(StringRef("NewBlockCommand"));
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(8U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("NewBlockCommand"), getCommandName(Toks[1]));
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa."), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[4].getText());
+
+ ASSERT_EQ(tok::at_command, Toks[5].getKind());
+ ASSERT_EQ(StringRef("NewBlockCommand"), getCommandName(Toks[5]));
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Aaa."), Toks[6].getText());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+}
+
+TEST_F(CommentLexerTest, RegisterMultipleBlockCommands) {
+ const char *Source =
+ "/// \\Foo\n"
+ "/// \\Bar Baz\n"
+ "/// \\Blech quux=corge\n";
+
+ Traits.registerBlockCommand(StringRef("Foo"));
+ Traits.registerBlockCommand(StringRef("Bar"));
+ Traits.registerBlockCommand(StringRef("Blech"));
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(11U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("Foo"), getCommandName(Toks[1]));
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[3].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[4].getKind());
+ ASSERT_EQ(StringRef("Bar"), getCommandName(Toks[4]));
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef(" Baz"), Toks[5].getText());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[7].getText());
+
+ ASSERT_EQ(tok::backslash_command, Toks[8].getKind());
+ ASSERT_EQ(StringRef("Blech"), getCommandName(Toks[8]));
+
+ ASSERT_EQ(tok::text, Toks[9].getKind());
+ ASSERT_EQ(StringRef(" quux=corge"), Toks[9].getText());
+
+ ASSERT_EQ(tok::newline, Toks[10].getKind());
+}
+
+// Empty verbatim block.
+TEST_F(CommentLexerTest, VerbatimBlock1) {
+ const char *Sources[] = {
+ "/// \\verbatim\\endverbatim\n//",
+ "/** \\verbatim\\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[2].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), getVerbatimBlockName(Toks[2]));
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+// Empty verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock2) {
+ const char *Source = "/// \\verbatim";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+// Empty verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock3) {
+ const char *Source = "/** \\verbatim*/";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+// Single-line verbatim block.
+TEST_F(CommentLexerTest, VerbatimBlock4) {
+ const char *Sources[] = {
+ "/// Meow \\verbatim aaa \\endverbatim\n//",
+ "/** Meow \\verbatim aaa \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[3].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), getVerbatimBlockName(Toks[3]));
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+// Single-line verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock5) {
+ const char *Sources[] = {
+ "/// Meow \\verbatim aaa \n//",
+ "/** Meow \\verbatim aaa */"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, VerbatimBlock6) {
+ const char *Source =
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "//\n"
+ "// Bbb\n"
+ "// \\endverbatim\n";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[6].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[8].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), getVerbatimBlockName(Toks[8]));
+
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+}
+
+TEST_F(CommentLexerTest, VerbatimBlock7) {
+ const char *Source =
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " *\n"
+ " * Bbb\n"
+ " * \\endverbatim\n"
+ " */";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef(""), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[4].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[5].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), getVerbatimBlockName(Toks[5]));
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[7].getText());
+
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+}
+
+// Complex test for verbatim blocks.
+TEST_F(CommentLexerTest, VerbatimBlock8) {
+ const char *Source =
+ "/* Meow \\verbatim aaa\\$\\@\n"
+ "bbb \\endverbati\r"
+ "ccc\r\n"
+ "ddd \\endverbatim Blah \\verbatim eee\n"
+ "\\endverbatim BlahBlah*/";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(14U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa\\$\\@"), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef("bbb \\endverbati"), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[4].getKind());
+ ASSERT_EQ(StringRef("ccc"), Toks[4].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[5].getKind());
+ ASSERT_EQ(StringRef("ddd "), Toks[5].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[6].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), getVerbatimBlockName(Toks[6]));
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" Blah "), Toks[7].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[8].getKind());
+ ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[8]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[9].getKind());
+ ASSERT_EQ(StringRef(" eee"), Toks[9].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[10].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), getVerbatimBlockName(Toks[10]));
+
+ ASSERT_EQ(tok::text, Toks[11].getKind());
+ ASSERT_EQ(StringRef(" BlahBlah"), Toks[11].getText());
+
+ ASSERT_EQ(tok::newline, Toks[12].getKind());
+ ASSERT_EQ(tok::newline, Toks[13].getKind());
+}
+
+// LaTeX verbatim blocks.
+TEST_F(CommentLexerTest, VerbatimBlock9) {
+ const char *Source =
+ "/// \\f$ Aaa \\f$ \\f[ Bbb \\f] \\f{ Ccc \\f}";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(13U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("f$"), getVerbatimBlockName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[3].getKind());
+ ASSERT_EQ(StringRef("f$"), getVerbatimBlockName(Toks[3]));
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[4].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[5].getKind());
+ ASSERT_EQ(StringRef("f["), getVerbatimBlockName(Toks[5]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Bbb "), Toks[6].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[7].getKind());
+ ASSERT_EQ(StringRef("f]"), getVerbatimBlockName(Toks[7]));
+
+ ASSERT_EQ(tok::text, Toks[8].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[8].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[9].getKind());
+ ASSERT_EQ(StringRef("f{"), getVerbatimBlockName(Toks[9]));
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[10].getKind());
+ ASSERT_EQ(StringRef(" Ccc "), Toks[10].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[11].getKind());
+ ASSERT_EQ(StringRef("f}"), getVerbatimBlockName(Toks[11]));
+
+ ASSERT_EQ(tok::newline, Toks[12].getKind());
+}
+
+// Empty verbatim line.
+TEST_F(CommentLexerTest, VerbatimLine1) {
+ const char *Sources[] = {
+ "/// \\fn\n//",
+ "/** \\fn*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), getVerbatimLineName(Toks[1]));
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+// Verbatim line with Doxygen escape sequences, which should not be expanded.
+TEST_F(CommentLexerTest, VerbatimLine2) {
+ const char *Sources[] = {
+ "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
+ "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), getVerbatimLineName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"),
+ Toks[2].getVerbatimLineText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+// Verbatim line should not eat anything from next source line.
+TEST_F(CommentLexerTest, VerbatimLine3) {
+ const char *Source =
+ "/** \\fn void *foo(const char *zzz = \"\\$\");\n"
+ " * Meow\n"
+ " */";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(9U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), getVerbatimLineName(Toks[1]));
+
+ ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"),
+ Toks[2].getVerbatimLineText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[6].getText());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML1) {
+ const char *Source =
+ "// <";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML2) {
+ const char *Source =
+ "// a<2";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" a"), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("2"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML3) {
+ const char *Source =
+ "// < img";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" img"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML4) {
+ const char *Sources[] = {
+ "// <img",
+ "// <img "
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML5) {
+ const char *Source =
+ "// <img 42";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("42"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML6) {
+ const char *Source = "// <img> Meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[3].getText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML7) {
+ const char *Source = "// <img=";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("="), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML8) {
+ const char *Source = "// <img src=> Meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(7U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_greater, Toks[4].getKind());
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[5].getText());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML9) {
+ const char *Sources[] = {
+ "// <img src",
+ "// <img src "
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML10) {
+ const char *Sources[] = {
+ "// <img src=",
+ "// <img src ="
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML11) {
+ const char *Sources[] = {
+ "// <img src=\"",
+ "// <img src = \"",
+ "// <img src=\'",
+ "// <img src = \'"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef(""), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML12) {
+ const char *Source = "// <img src=@";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[4].getText());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML13) {
+ const char *Sources[] = {
+ "// <img src=\"val\\\"\\'val",
+ "// <img src=\"val\\\"\\'val\"",
+ "// <img src=\'val\\\"\\'val",
+ "// <img src=\'val\\\"\\'val\'"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef("val\\\"\\'val"), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML14) {
+ const char *Sources[] = {
+ "// <img src=\"val\\\"\\'val\">",
+ "// <img src=\'val\\\"\\'val\'>"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(7U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef("val\\\"\\'val"), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::html_greater, Toks[5].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML15) {
+ const char *Sources[] = {
+ "// <img/>",
+ "// <img />"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_slash_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML16) {
+ const char *Sources[] = {
+ "// <img/ Aaa",
+ "// <img / Aaa"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("/"), Toks[2].getText());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[3].getText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML17) {
+ const char *Source = "// </";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("</"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML18) {
+ const char *Source = "// </@";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("</"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML19) {
+ const char *Source = "// </img";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, NotAKnownHTMLTag1) {
+ const char *Source = "// <tag>";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<tag"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(">"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, NotAKnownHTMLTag2) {
+ const char *Source = "// </tag>";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("</tag"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(">"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences1) {
+ const char *Source = "// &";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences2) {
+ const char *Source = "// &!";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("!"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences3) {
+ const char *Source = "// &amp";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&amp"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences4) {
+ const char *Source = "// &amp!";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&amp"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("!"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences5) {
+ const char *Source = "// &#";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences6) {
+ const char *Source = "// &#a";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("a"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences7) {
+ const char *Source = "// &#42";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#42"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences8) {
+ const char *Source = "// &#42a";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#42"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("a"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences9) {
+ const char *Source = "// &#x";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#x"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences10) {
+ const char *Source = "// &#xz";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#x"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("z"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences11) {
+ const char *Source = "// &#xab";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#xab"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences12) {
+ const char *Source = "// &#xaBz";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#xaB"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("z"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences13) {
+ const char *Source = "// &amp;";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences14) {
+ const char *Source = "// &amp;&lt;";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences15) {
+ const char *Source = "// &amp; meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" meow"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences16) {
+ const char *Sources[] = {
+ "// &#61;",
+ "// &#x3d;",
+ "// &#X3d;",
+ "// &#X3D;"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("="), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, MultipleComments) {
+ const char *Source =
+ "// Aaa\n"
+ "/// Bbb\n"
+ "/* Ccc\n"
+ " * Ddd*/\n"
+ "/** Eee*/";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(12U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[2].getText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Ccc"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Ddd"), Toks[6].getText());
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+
+ ASSERT_EQ(tok::text, Toks[9].getKind());
+ ASSERT_EQ(StringRef(" Eee"), Toks[9].getText());
+
+ ASSERT_EQ(tok::newline, Toks[10].getKind());
+ ASSERT_EQ(tok::newline, Toks[11].getKind());
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/gnu/llvm/clang/unittests/AST/CommentParser.cpp b/gnu/llvm/clang/unittests/AST/CommentParser.cpp
new file mode 100644
index 00000000000..327cabd619b
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/CommentParser.cpp
@@ -0,0 +1,1445 @@
+//===- unittests/AST/CommentParser.cpp ------ Comment parser tests --------===//
+//
+// 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/AST/CommentParser.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/CommentSema.h"
+#include "clang/Basic/CommentOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Allocator.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace clang {
+namespace comments {
+
+namespace {
+
+const bool MY_DEBUG = true;
+
+class CommentParserTest : public ::testing::Test {
+protected:
+ CommentParserTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr),
+ Traits(Allocator, CommentOptions()) {
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ llvm::BumpPtrAllocator Allocator;
+ CommandTraits Traits;
+
+ FullComment *parseString(const char *Source);
+};
+
+FullComment *CommentParserTest::parseString(const char *Source) {
+ std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID File = SourceMgr.createFileID(std::move(Buf));
+ SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
+
+ Lexer L(Allocator, Diags, Traits, Begin, Source, Source + strlen(Source));
+
+ Sema S(Allocator, SourceMgr, Diags, Traits, /*PP=*/ nullptr);
+ Parser P(L, S, Allocator, SourceMgr, Diags, Traits);
+ FullComment *FC = P.parseFullComment();
+
+ if (MY_DEBUG) {
+ llvm::errs() << "=== Source:\n" << Source << "\n=== AST:\n";
+ FC->dump(llvm::errs(), &Traits, &SourceMgr);
+ }
+
+ Token Tok;
+ L.lex(Tok);
+ if (Tok.is(tok::eof))
+ return FC;
+ else
+ return nullptr;
+}
+
+::testing::AssertionResult HasChildCount(const Comment *C, size_t Count) {
+ if (!C)
+ return ::testing::AssertionFailure() << "Comment is NULL";
+
+ if (Count != C->child_count())
+ return ::testing::AssertionFailure()
+ << "Count = " << Count
+ << ", child_count = " << C->child_count();
+
+ return ::testing::AssertionSuccess();
+}
+
+template <typename T>
+::testing::AssertionResult GetChildAt(const Comment *C,
+ size_t Idx,
+ T *&Child) {
+ if (!C)
+ return ::testing::AssertionFailure() << "Comment is NULL";
+
+ if (Idx >= C->child_count())
+ return ::testing::AssertionFailure()
+ << "Idx out of range. Idx = " << Idx
+ << ", child_count = " << C->child_count();
+
+ Comment::child_iterator I = C->child_begin() + Idx;
+ Comment *CommentChild = *I;
+ if (!CommentChild)
+ return ::testing::AssertionFailure() << "Child is NULL";
+
+ Child = dyn_cast<T>(CommentChild);
+ if (!Child)
+ return ::testing::AssertionFailure()
+ << "Child is not of requested type, but a "
+ << CommentChild->getCommentKindName();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTextAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ TextComment *TC;
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualText = TC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "TextComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ if (TC->hasTrailingNewline())
+ return ::testing::AssertionFailure()
+ << "TextComment has a trailing newline";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTextWithNewlineAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ TextComment *TC;
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualText = TC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "TextComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ if (!TC->hasTrailingNewline())
+ return ::testing::AssertionFailure()
+ << "TextComment has no trailing newline";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasBlockCommandAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ BlockCommandComment *&BCC,
+ StringRef Name,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, BCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = BCC->getCommandName(Traits);
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "BlockCommandComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ Paragraph = BCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasParamCommandAt(
+ const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ ParamCommandComment *&PCC,
+ StringRef CommandName,
+ ParamCommandComment::PassDirection Direction,
+ bool IsDirectionExplicit,
+ StringRef ParamName,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, PCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualCommandName = PCC->getCommandName(Traits);
+ if (ActualCommandName != CommandName)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has name \"" << ActualCommandName.str() << "\", "
+ "expected \"" << CommandName.str() << "\"";
+
+ if (PCC->getDirection() != Direction)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has direction " << PCC->getDirection() << ", "
+ "expected " << Direction;
+
+ if (PCC->isDirectionExplicit() != IsDirectionExplicit)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has "
+ << (PCC->isDirectionExplicit() ? "explicit" : "implicit")
+ << " direction, "
+ "expected " << (IsDirectionExplicit ? "explicit" : "implicit");
+
+ if (!ParamName.empty() && !PCC->hasParamName())
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has no parameter name";
+
+ StringRef ActualParamName = PCC->hasParamName() ? PCC->getParamNameAsWritten() : "";
+ if (ActualParamName != ParamName)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has parameter name \"" << ActualParamName.str()
+ << "\", "
+ "expected \"" << ParamName.str() << "\"";
+
+ Paragraph = PCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTParamCommandAt(
+ const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ TParamCommandComment *&TPCC,
+ StringRef CommandName,
+ StringRef ParamName,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TPCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualCommandName = TPCC->getCommandName(Traits);
+ if (ActualCommandName != CommandName)
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has name \"" << ActualCommandName.str() << "\", "
+ "expected \"" << CommandName.str() << "\"";
+
+ if (!ParamName.empty() && !TPCC->hasParamName())
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has no parameter name";
+
+ StringRef ActualParamName = TPCC->hasParamName() ? TPCC->getParamNameAsWritten() : "";
+ if (ActualParamName != ParamName)
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has parameter name \"" << ActualParamName.str()
+ << "\", "
+ "expected \"" << ParamName.str() << "\"";
+
+ Paragraph = TPCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, ICC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = ICC->getCommandName(Traits);
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct NoArgs {};
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name,
+ NoArgs) {
+ ::testing::AssertionResult AR = HasInlineCommandAt(C, Traits, Idx, ICC, Name);
+ if (!AR)
+ return AR;
+
+ if (ICC->getNumArgs() != 0)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name,
+ StringRef Arg) {
+ ::testing::AssertionResult AR = HasInlineCommandAt(C, Traits, Idx, ICC, Name);
+ if (!AR)
+ return AR;
+
+ if (ICC->getNumArgs() != 1)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
+ "expected 1";
+
+ StringRef ActualArg = ICC->getArgText(0);
+ if (ActualArg != Arg)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has argument \"" << ActualArg.str() << "\", "
+ "expected \"" << Arg.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, HST);
+ if (!AR)
+ return AR;
+
+ StringRef ActualTagName = HST->getTagName();
+ if (ActualTagName != TagName)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has name \"" << ActualTagName.str() << "\", "
+ "expected \"" << TagName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct SelfClosing {};
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ SelfClosing) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (!HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is not self-closing";
+
+ return ::testing::AssertionSuccess();
+}
+
+
+struct NoAttrs {};
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ NoAttrs) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is self-closing";
+
+ if (HST->getNumAttrs() != 0)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ StringRef AttrName,
+ StringRef AttrValue) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is self-closing";
+
+ if (HST->getNumAttrs() != 1)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
+ "expected 1";
+
+ StringRef ActualName = HST->getAttr(0).Name;
+ if (ActualName != AttrName)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has attr \"" << ActualName.str() << "\", "
+ "expected \"" << AttrName.str() << "\"";
+
+ StringRef ActualValue = HST->getAttr(0).Value;
+ if (ActualValue != AttrValue)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has attr value \"" << ActualValue.str() << "\", "
+ "expected \"" << AttrValue.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLEndTagAt(const Comment *C,
+ size_t Idx,
+ HTMLEndTagComment *&HET,
+ StringRef TagName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, HET);
+ if (!AR)
+ return AR;
+
+ StringRef ActualTagName = HET->getTagName();
+ if (ActualTagName != TagName)
+ return ::testing::AssertionFailure()
+ << "HTMLEndTagComment has name \"" << ActualTagName.str() << "\", "
+ "expected \"" << TagName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasParagraphCommentAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ ParagraphComment *PC;
+
+ {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, PC);
+ if (!AR)
+ return AR;
+ }
+
+ {
+ ::testing::AssertionResult AR = HasChildCount(PC, 1);
+ if (!AR)
+ return AR;
+ }
+
+ {
+ ::testing::AssertionResult AR = HasTextAt(PC, 0, Text);
+ if (!AR)
+ return AR;
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, VBC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = VBC->getCommandName(Traits);
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ StringRef ActualCloseName = VBC->getCloseName();
+ if (ActualCloseName != CloseName)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has closing command name \""
+ << ActualCloseName.str() << "\", "
+ "expected \"" << CloseName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct NoLines {};
+struct Lines {};
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ NoLines) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ Lines,
+ StringRef Line0) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 1)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 1";
+
+ StringRef ActualLine0 = VBC->getText(0);
+ if (ActualLine0 != Line0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
+ "expected \"" << Line0.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ Lines,
+ StringRef Line0,
+ StringRef Line1) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 2)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 2";
+
+ StringRef ActualLine0 = VBC->getText(0);
+ if (ActualLine0 != Line0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
+ "expected \"" << Line0.str() << "\"";
+
+ StringRef ActualLine1 = VBC->getText(1);
+ if (ActualLine1 != Line1)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[1] \"" << ActualLine1.str() << "\", "
+ "expected \"" << Line1.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimLineAt(const Comment *C,
+ const CommandTraits &Traits,
+ size_t Idx,
+ VerbatimLineComment *&VLC,
+ StringRef Name,
+ StringRef Text) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, VLC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = VLC->getCommandName(Traits);
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "VerbatimLineComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ StringRef ActualText = VLC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "VerbatimLineComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+
+TEST_F(CommentParserTest, Basic1) {
+ const char *Source = "//";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 0));
+}
+
+TEST_F(CommentParserTest, Basic2) {
+ const char *Source = "// Meow";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Meow"));
+}
+
+TEST_F(CommentParserTest, Basic3) {
+ const char *Source =
+ "// Aaa\n"
+ "// Bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 1, " Bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, ParagraphSplitting1) {
+ const char *Sources[] = {
+ "// Aaa\n"
+ "//\n"
+ "// Bbb",
+
+ "// Aaa\n"
+ "// \n"
+ "// Bbb",
+
+ "// Aaa\n"
+ "//\t\n"
+ "// Bbb",
+
+ "// Aaa\n"
+ "//\n"
+ "//\n"
+ "// Bbb",
+
+ "/**\n"
+ " Aaa\n"
+ "\n"
+ " Bbb\n"
+ "*/",
+
+ "/**\n"
+ " Aaa\n"
+ " \n"
+ " Bbb\n"
+ "*/",
+
+ "/**\n"
+ " Aaa\n"
+ "\t \n"
+ " Bbb\n"
+ "*/",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Aaa"));
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 1, " Bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph1) {
+ const char *Source =
+ "// \\brief Aaa\n"
+ "//\n"
+ "// Bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Aaa"));
+ }
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 2, " Bbb"));
+}
+
+TEST_F(CommentParserTest, Paragraph2) {
+ const char *Source = "// \\brief \\author";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " "));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "author", PC));
+
+ ASSERT_TRUE(GetChildAt(BCC, 0, PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph3) {
+ const char *Source =
+ "// \\brief Aaa\n"
+ "// Bbb \\author\n"
+ "// Ccc";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(GetChildAt(BCC, 0, PC));
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 1, " Bbb "));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "author", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Ccc"));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand1) {
+ const char *Source = "// \\param aaa";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand2) {
+ const char *Source = "// \\param\\brief";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "brief", PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand3) {
+ const char *Sources[] = {
+ "// \\param aaa Bbb\n",
+ "// \\param\n"
+ "// aaa Bbb\n",
+ "// \\param \n"
+ "// aaa Bbb\n",
+ "// \\param aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand4) {
+ const char *Sources[] = {
+ "// \\param [in] aaa Bbb\n",
+ "// \\param[in] aaa Bbb\n",
+ "// \\param\n"
+ "// [in] aaa Bbb\n",
+ "// \\param [in]\n"
+ "// aaa Bbb\n",
+ "// \\param [in] aaa\n"
+ "// Bbb\n",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand5) {
+ const char *Sources[] = {
+ "// \\param [out] aaa Bbb\n",
+ "// \\param[out] aaa Bbb\n",
+ "// \\param\n"
+ "// [out] aaa Bbb\n",
+ "// \\param [out]\n"
+ "// aaa Bbb\n",
+ "// \\param [out] aaa\n"
+ "// Bbb\n",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
+ ParamCommandComment::Out,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand6) {
+ const char *Sources[] = {
+ "// \\param [in,out] aaa Bbb\n",
+ "// \\param[in,out] aaa Bbb\n",
+ "// \\param [in, out] aaa Bbb\n",
+ "// \\param [in,\n"
+ "// out] aaa Bbb\n",
+ "// \\param [in,out]\n"
+ "// aaa Bbb\n",
+ "// \\param [in,out] aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
+ ParamCommandComment::InOut,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand7) {
+ const char *Source =
+ "// \\param aaa \\% Bbb \\$ ccc\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+
+ ASSERT_TRUE(HasChildCount(PC, 5));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasTextAt(PC, 1, "%"));
+ ASSERT_TRUE(HasTextAt(PC, 2, " Bbb "));
+ ASSERT_TRUE(HasTextAt(PC, 3, "$"));
+ ASSERT_TRUE(HasTextAt(PC, 4, " ccc"));
+ }
+}
+
+TEST_F(CommentParserTest, TParamCommand1) {
+ const char *Sources[] = {
+ "// \\tparam aaa Bbb\n",
+ "// \\tparam\n"
+ "// aaa Bbb\n",
+ "// \\tparam \n"
+ "// aaa Bbb\n",
+ "// \\tparam aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ TParamCommandComment *TPCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasTParamCommandAt(FC, Traits, 1, TPCC, "tparam",
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(TPCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, TParamCommand2) {
+ const char *Source = "// \\tparam\\brief";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ TParamCommandComment *TPCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasTParamCommandAt(FC, Traits, 1, TPCC, "tparam", "", PC));
+ ASSERT_TRUE(HasChildCount(TPCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "brief", PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+
+TEST_F(CommentParserTest, InlineCommand1) {
+ const char *Source = "// \\c";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", NoArgs()));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand2) {
+ const char *Source = "// \\c ";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", NoArgs()));
+ ASSERT_TRUE(HasTextAt(PC, 2, " "));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand3) {
+ const char *Source = "// \\c aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", "aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand4) {
+ const char *Source = "// \\c aaa bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", "aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 2, " bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand5) {
+ const char *Source = "// \\unknown aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "unknown", NoArgs()));
+ ASSERT_TRUE(HasTextAt(PC, 2, " aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, HTML1) {
+ const char *Sources[] = {
+ "// <a",
+ "// <a>",
+ "// <a >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", NoAttrs()));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML2) {
+ const char *Sources[] = {
+ "// <br/>",
+ "// <br />"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "br", SelfClosing()));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML3) {
+ const char *Sources[] = {
+ "// <a href",
+ "// <a href ",
+ "// <a href>",
+ "// <a href >",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", ""));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML4) {
+ const char *Sources[] = {
+ "// <a href=\"bbb\"",
+ "// <a href=\"bbb\">",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", "bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML5) {
+ const char *Sources[] = {
+ "// </a",
+ "// </a>",
+ "// </a >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLEndTagComment *HET;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLEndTagAt(PC, 1, HET, "a"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML6) {
+ const char *Source =
+ "// <pre>\n"
+ "// Aaa\n"
+ "// Bbb\n"
+ "// </pre>\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ HTMLEndTagComment *HET;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 6));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "pre", NoAttrs()));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 2, " Aaa"));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 3, " Bbb"));
+ ASSERT_TRUE(HasTextAt(PC, 4, " "));
+ ASSERT_TRUE(HasHTMLEndTagAt(PC, 5, HET, "pre"));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock1) {
+ const char *Source = "// \\verbatim\\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VCC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VCC,
+ "verbatim", "endverbatim",
+ NoLines()));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock2) {
+ const char *Source = "// \\verbatim Aaa \\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
+ "verbatim", "endverbatim",
+ Lines(), " Aaa "));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock3) {
+ const char *Source = "// \\verbatim Aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC, "verbatim", "",
+ Lines(), " Aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock4) {
+ const char *Source =
+ "//\\verbatim\n"
+ "//\\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 0, VBC,
+ "verbatim", "endverbatim",
+ NoLines()));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock5) {
+ const char *Sources[] = {
+ "//\\verbatim\n"
+ "// Aaa\n"
+ "//\\endverbatim\n",
+
+ "/*\\verbatim\n"
+ " * Aaa\n"
+ " *\\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 0, VBC,
+ "verbatim", "endverbatim",
+ Lines(), " Aaa"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock6) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " * \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
+ "verbatim", "endverbatim",
+ Lines(), " Aaa"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock7) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "// Bbb\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " * Bbb\n"
+ " * \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
+ "verbatim", "endverbatim",
+ Lines(), " Aaa", " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock8) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "//\n"
+ "// Bbb\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " *\n"
+ " * Bbb\n"
+ " * \\endverbatim*/"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
+ "verbatim", "endverbatim"));
+ ASSERT_EQ(3U, VBC->getNumLines());
+ ASSERT_EQ(" Aaa", VBC->getText(0));
+ ASSERT_EQ("", VBC->getText(1));
+ ASSERT_EQ(" Bbb", VBC->getText(2));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimLine1) {
+ const char *Sources[] = {
+ "// \\fn",
+ "// \\fn\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimLineComment *VLC;
+ ASSERT_TRUE(HasVerbatimLineAt(FC, Traits, 1, VLC, "fn", ""));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimLine2) {
+ const char *Sources[] = {
+ "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
+ "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimLineComment *VLC;
+ ASSERT_TRUE(HasVerbatimLineAt(FC, Traits, 1, VLC, "fn",
+ " void *foo(const char *zzz = \"\\$\");"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, Deprecated) {
+ const char *Sources[] = {
+ "/** @deprecated*/",
+ "/// @deprecated\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "deprecated", PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+ }
+}
+
+} // unnamed namespace
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/gnu/llvm/clang/unittests/AST/CommentTextTest.cpp b/gnu/llvm/clang/unittests/AST/CommentTextTest.cpp
new file mode 100644
index 00000000000..3de6758e45b
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/CommentTextTest.cpp
@@ -0,0 +1,127 @@
+//===- unittest/AST/CommentTextTest.cpp - Comment text extraction test ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for user-friendly output formatting of comments, i.e.
+// RawComment::getFormattedText().
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/RawCommentList.h"
+#include "clang/Basic/CommentOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include <gtest/gtest.h>
+
+namespace clang {
+
+class CommentTextTest : public ::testing::Test {
+protected:
+ std::string formatComment(llvm::StringRef CommentText) {
+ SourceManagerForFile FileSourceMgr("comment-test.cpp", CommentText);
+ SourceManager& SourceMgr = FileSourceMgr.get();
+
+ auto CommentStartOffset = CommentText.find("/");
+ assert(CommentStartOffset != llvm::StringRef::npos);
+ FileID File = SourceMgr.getMainFileID();
+
+ SourceRange CommentRange(
+ SourceMgr.getLocForStartOfFile(File).getLocWithOffset(
+ CommentStartOffset),
+ SourceMgr.getLocForEndOfFile(File));
+ CommentOptions EmptyOpts;
+ // FIXME: technically, merged that we set here is incorrect, but that
+ // shouldn't matter.
+ RawComment Comment(SourceMgr, CommentRange, EmptyOpts, /*Merged=*/true);
+ DiagnosticsEngine Diags(new DiagnosticIDs, new DiagnosticOptions);
+ return Comment.getFormattedText(SourceMgr, Diags);
+ }
+};
+
+TEST_F(CommentTextTest, FormattedText) {
+ // clang-format off
+ auto ExpectedOutput =
+R"(This function does this and that.
+For example,
+ Runnning it in that case will give you
+ this result.
+That's about it.)";
+ // Two-slash comments.
+ auto Formatted = formatComment(
+R"cpp(
+// This function does this and that.
+// For example,
+// Runnning it in that case will give you
+// this result.
+// That's about it.)cpp");
+ EXPECT_EQ(ExpectedOutput, Formatted);
+
+ // Three-slash comments.
+ Formatted = formatComment(
+R"cpp(
+/// This function does this and that.
+/// For example,
+/// Runnning it in that case will give you
+/// this result.
+/// That's about it.)cpp");
+ EXPECT_EQ(ExpectedOutput, Formatted);
+
+ // Block comments.
+ Formatted = formatComment(
+R"cpp(
+/* This function does this and that.
+ * For example,
+ * Runnning it in that case will give you
+ * this result.
+ * That's about it.*/)cpp");
+ EXPECT_EQ(ExpectedOutput, Formatted);
+
+ // Doxygen-style block comments.
+ Formatted = formatComment(
+R"cpp(
+/** This function does this and that.
+ * For example,
+ * Runnning it in that case will give you
+ * this result.
+ * That's about it.*/)cpp");
+ EXPECT_EQ(ExpectedOutput, Formatted);
+
+ // Weird indentation.
+ Formatted = formatComment(
+R"cpp(
+ // This function does this and that.
+ // For example,
+ // Runnning it in that case will give you
+ // this result.
+ // That's about it.)cpp");
+ EXPECT_EQ(ExpectedOutput, Formatted);
+ // clang-format on
+}
+
+TEST_F(CommentTextTest, KeepsDoxygenControlSeqs) {
+ // clang-format off
+ auto ExpectedOutput =
+R"(\brief This is the brief part of the comment.
+\param a something about a.
+@param b something about b.)";
+
+ auto Formatted = formatComment(
+R"cpp(
+/// \brief This is the brief part of the comment.
+/// \param a something about a.
+/// @param b something about b.)cpp");
+ EXPECT_EQ(ExpectedOutput, Formatted);
+ // clang-format on
+}
+
+} // namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/DataCollectionTest.cpp b/gnu/llvm/clang/unittests/AST/DataCollectionTest.cpp
new file mode 100644
index 00000000000..b732a445d99
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/DataCollectionTest.cpp
@@ -0,0 +1,172 @@
+//===- unittests/AST/DataCollectionTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for the DataCollection module.
+//
+// They work by hashing the collected data of two nodes and asserting that the
+// hash values are equal iff the nodes are considered equal.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DataCollection.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace ast_matchers;
+
+namespace {
+class StmtDataCollector : public ConstStmtVisitor<StmtDataCollector> {
+ ASTContext &Context;
+ llvm::MD5 &DataConsumer;
+
+ template <class T> void addData(const T &Data) {
+ data_collection::addDataToConsumer(DataConsumer, Data);
+ }
+
+public:
+ StmtDataCollector(const Stmt *S, ASTContext &Context, llvm::MD5 &DataConsumer)
+ : Context(Context), DataConsumer(DataConsumer) {
+ this->Visit(S);
+ }
+
+#define DEF_ADD_DATA(CLASS, CODE) \
+ template <class Dummy = void> Dummy Visit##CLASS(const CLASS *S) { \
+ CODE; \
+ ConstStmtVisitor<StmtDataCollector>::Visit##CLASS(S); \
+ }
+
+#include "clang/AST/StmtDataCollectors.inc"
+};
+} // end anonymous namespace
+
+namespace {
+struct StmtHashMatch : public MatchFinder::MatchCallback {
+ unsigned NumFound;
+ llvm::MD5::MD5Result &Hash;
+ StmtHashMatch(llvm::MD5::MD5Result &Hash) : NumFound(0), Hash(Hash) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id");
+ if (!S)
+ return;
+ ++NumFound;
+ if (NumFound > 1)
+ return;
+ llvm::MD5 MD5;
+ StmtDataCollector(S, *Result.Context, MD5);
+ MD5.final(Hash);
+ }
+};
+} // end anonymous namespace
+
+static testing::AssertionResult hashStmt(llvm::MD5::MD5Result &Hash,
+ const StatementMatcher &StmtMatch,
+ StringRef Code) {
+ StmtHashMatch Hasher(Hash);
+ MatchFinder Finder;
+ Finder.addMatcher(StmtMatch, &Hasher);
+ std::unique_ptr<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Finder));
+ if (!runToolOnCode(Factory->create(), Code))
+ return testing::AssertionFailure()
+ << "Parsing error in \"" << Code.str() << "\"";
+ if (Hasher.NumFound == 0)
+ return testing::AssertionFailure() << "Matcher didn't find any statements";
+ if (Hasher.NumFound > 1)
+ return testing::AssertionFailure()
+ << "Matcher should match only one statement "
+ "(found "
+ << Hasher.NumFound << ")";
+ return testing::AssertionSuccess();
+}
+
+static testing::AssertionResult
+isStmtHashEqual(const StatementMatcher &StmtMatch, StringRef Code1,
+ StringRef Code2) {
+ llvm::MD5::MD5Result Hash1, Hash2;
+ testing::AssertionResult Result = hashStmt(Hash1, StmtMatch, Code1);
+ if (!Result)
+ return Result;
+ if (!(Result = hashStmt(Hash2, StmtMatch, Code2)))
+ return Result;
+
+ return testing::AssertionResult(Hash1 == Hash2);
+}
+
+TEST(StmtDataCollector, TestDeclRefExpr) {
+ ASSERT_TRUE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;",
+ "int x, r = x;"));
+ ASSERT_FALSE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;",
+ "int y, r = y;"));
+ ASSERT_FALSE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;",
+ "namespace n { int x, r = x; };"));
+}
+
+TEST(StmtDataCollector, TestMemberExpr) {
+ ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"),
+ "struct { int x; } X; int r = X.x;",
+ "struct { int x; } X; int r = (&X)->x;"));
+ ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"),
+ "struct { int x; } X; int r = X.x;",
+ "struct { int x; } Y; int r = Y.x;"));
+ ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"),
+ "struct { int x; } X; int r = X.x;",
+ "struct C { int x; } X; int r = X.C::x;"));
+ ASSERT_FALSE(isStmtHashEqual(memberExpr().bind("id"),
+ "struct { int x; } X; int r = X.x;",
+ "struct { int y; } X; int r = X.y;"));
+}
+
+TEST(StmtDataCollector, TestIntegerLiteral) {
+ ASSERT_TRUE(
+ isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x = 0;"));
+ ASSERT_TRUE(
+ isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x =00;"));
+ ASSERT_FALSE(
+ isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x = 1;"));
+}
+
+TEST(StmtDataCollector, TestFloatingLiteral) {
+ ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .0;",
+ "double x = .0;"));
+ ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .10;",
+ "double x = .1;"));
+ ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .1;",
+ "double x = 1e-1;"));
+ ASSERT_FALSE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .0;",
+ "double x = .1;"));
+}
+
+TEST(StmtDataCollector, TestStringLiteral) {
+ ASSERT_TRUE(isStmtHashEqual(stringLiteral().bind("id"), R"(char x[] = "0";)",
+ R"(char x[] = "0";)"));
+ ASSERT_FALSE(isStmtHashEqual(stringLiteral().bind("id"), R"(char x[] = "0";)",
+ R"(char x[] = "1";)"));
+}
+
+TEST(StmtDataCollector, TestCXXBoolLiteral) {
+ ASSERT_TRUE(isStmtHashEqual(cxxBoolLiteral().bind("id"), "bool x = false;",
+ "bool x = false;"));
+ ASSERT_FALSE(isStmtHashEqual(cxxBoolLiteral().bind("id"), "bool x = false;",
+ "bool x = true;"));
+}
+
+TEST(StmtDataCollector, TestCharacterLiteral) {
+ ASSERT_TRUE(isStmtHashEqual(characterLiteral().bind("id"), "char x = '0';",
+ "char x = '0';"));
+ ASSERT_TRUE(isStmtHashEqual(characterLiteral().bind("id"),
+ R"(char x = '\0';)",
+ R"(char x = '\x00';)"));
+ ASSERT_FALSE(isStmtHashEqual(characterLiteral().bind("id"), "char x = '0';",
+ "char x = '1';"));
+}
diff --git a/gnu/llvm/clang/unittests/AST/DeclMatcher.h b/gnu/llvm/clang/unittests/AST/DeclMatcher.h
new file mode 100644
index 00000000000..a7698aab76b
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/DeclMatcher.h
@@ -0,0 +1,78 @@
+//===- unittest/AST/DeclMatcher.h - AST unit test support ---------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_AST_DECLMATCHER_H
+#define LLVM_CLANG_UNITTESTS_AST_DECLMATCHER_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace ast_matchers {
+
+enum class DeclMatcherKind { First, Last };
+
+// Matcher class to retrieve the first/last matched node under a given AST.
+template <typename NodeType, DeclMatcherKind MatcherKind>
+class DeclMatcher : public MatchFinder::MatchCallback {
+ NodeType *Node = nullptr;
+ void run(const MatchFinder::MatchResult &Result) override {
+ if ((MatcherKind == DeclMatcherKind::First && Node == nullptr) ||
+ MatcherKind == DeclMatcherKind::Last) {
+ Node = const_cast<NodeType *>(Result.Nodes.getNodeAs<NodeType>(""));
+ }
+ }
+public:
+ // Returns the first/last matched node under the tree rooted in `D`.
+ template <typename MatcherType>
+ NodeType *match(const Decl *D, const MatcherType &AMatcher) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher.bind(""), this);
+ Finder.matchAST(D->getASTContext());
+ assert(Node);
+ return Node;
+ }
+};
+template <typename NodeType>
+using LastDeclMatcher = DeclMatcher<NodeType, DeclMatcherKind::Last>;
+template <typename NodeType>
+using FirstDeclMatcher = DeclMatcher<NodeType, DeclMatcherKind::First>;
+
+template <typename NodeType>
+class DeclCounterWithPredicate : public MatchFinder::MatchCallback {
+ using UnaryPredicate = std::function<bool(const NodeType *)>;
+ UnaryPredicate Predicate;
+ unsigned Count = 0;
+ void run(const MatchFinder::MatchResult &Result) override {
+ if (auto N = Result.Nodes.getNodeAs<NodeType>("")) {
+ if (Predicate(N))
+ ++Count;
+ }
+ }
+
+public:
+ DeclCounterWithPredicate()
+ : Predicate([](const NodeType *) { return true; }) {}
+ DeclCounterWithPredicate(UnaryPredicate P) : Predicate(P) {}
+ // Returns the number of matched nodes which satisfy the predicate under the
+ // tree rooted in `D`.
+ template <typename MatcherType>
+ unsigned match(const Decl *D, const MatcherType &AMatcher) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher.bind(""), this);
+ Finder.matchAST(D->getASTContext());
+ return Count;
+ }
+};
+
+template <typename NodeType>
+using DeclCounter = DeclCounterWithPredicate<NodeType>;
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif
diff --git a/gnu/llvm/clang/unittests/AST/DeclPrinterTest.cpp b/gnu/llvm/clang/unittests/AST/DeclPrinterTest.cpp
new file mode 100644
index 00000000000..5a0a80421d0
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/DeclPrinterTest.cpp
@@ -0,0 +1,1320 @@
+//===- unittests/AST/DeclPrinterTest.cpp --- Declaration printer tests ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for Decl::print() and related methods.
+//
+// Search this file for WRONG to see test cases that are producing something
+// completely wrong, invalid C++ or just misleading.
+//
+// These tests have a coding convention:
+// * declaration to be printed is named 'A' unless it should have some special
+// name (e.g., 'operator+');
+// * additional helper declarations are 'Z', 'Y', 'X' and so on.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace ast_matchers;
+using namespace tooling;
+
+namespace {
+
+using PrintingPolicyModifier = void (*)(PrintingPolicy &policy);
+
+void PrintDecl(raw_ostream &Out, const ASTContext *Context, const Decl *D,
+ PrintingPolicyModifier PolicyModifier) {
+ PrintingPolicy Policy = Context->getPrintingPolicy();
+ Policy.TerseOutput = true;
+ if (PolicyModifier)
+ PolicyModifier(Policy);
+ D->print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ false);
+}
+
+class PrintMatch : public MatchFinder::MatchCallback {
+ SmallString<1024> Printed;
+ unsigned NumFoundDecls;
+ PrintingPolicyModifier PolicyModifier;
+
+public:
+ PrintMatch(PrintingPolicyModifier PolicyModifier)
+ : NumFoundDecls(0), PolicyModifier(PolicyModifier) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const Decl *D = Result.Nodes.getNodeAs<Decl>("id");
+ if (!D || D->isImplicit())
+ return;
+ NumFoundDecls++;
+ if (NumFoundDecls > 1)
+ return;
+
+ llvm::raw_svector_ostream Out(Printed);
+ PrintDecl(Out, Result.Context, D, PolicyModifier);
+ }
+
+ StringRef getPrinted() const {
+ return Printed;
+ }
+
+ unsigned getNumFoundDecls() const {
+ return NumFoundDecls;
+ }
+};
+
+::testing::AssertionResult
+PrintedDeclMatches(StringRef Code, const std::vector<std::string> &Args,
+ const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted, StringRef FileName,
+ PrintingPolicyModifier PolicyModifier = nullptr) {
+ PrintMatch Printer(PolicyModifier);
+ MatchFinder Finder;
+ Finder.addMatcher(NodeMatch, &Printer);
+ std::unique_ptr<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Finder));
+
+ if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
+ return testing::AssertionFailure()
+ << "Parsing error in \"" << Code.str() << "\"";
+
+ if (Printer.getNumFoundDecls() == 0)
+ return testing::AssertionFailure()
+ << "Matcher didn't find any declarations";
+
+ if (Printer.getNumFoundDecls() > 1)
+ return testing::AssertionFailure()
+ << "Matcher should match only one declaration "
+ "(found " << Printer.getNumFoundDecls() << ")";
+
+ if (Printer.getPrinted() != ExpectedPrinted)
+ return ::testing::AssertionFailure()
+ << "Expected \"" << ExpectedPrinted.str() << "\", "
+ "got \"" << Printer.getPrinted().str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult
+PrintedDeclCXX98Matches(StringRef Code, StringRef DeclName,
+ StringRef ExpectedPrinted,
+ PrintingPolicyModifier PolicyModifier = nullptr) {
+ std::vector<std::string> Args(1, "-std=c++98");
+ return PrintedDeclMatches(Code,
+ Args,
+ namedDecl(hasName(DeclName)).bind("id"),
+ ExpectedPrinted,
+ "input.cc",
+ PolicyModifier);
+}
+
+::testing::AssertionResult
+PrintedDeclCXX98Matches(StringRef Code, const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted,
+ PrintingPolicyModifier PolicyModifier = nullptr) {
+ std::vector<std::string> Args(1, "-std=c++98");
+ return PrintedDeclMatches(Code,
+ Args,
+ NodeMatch,
+ ExpectedPrinted,
+ "input.cc",
+ PolicyModifier);
+}
+
+::testing::AssertionResult PrintedDeclCXX11Matches(StringRef Code,
+ StringRef DeclName,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args(1, "-std=c++11");
+ return PrintedDeclMatches(Code,
+ Args,
+ namedDecl(hasName(DeclName)).bind("id"),
+ ExpectedPrinted,
+ "input.cc");
+}
+
+::testing::AssertionResult PrintedDeclCXX11Matches(
+ StringRef Code,
+ const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args(1, "-std=c++11");
+ return PrintedDeclMatches(Code,
+ Args,
+ NodeMatch,
+ ExpectedPrinted,
+ "input.cc");
+}
+
+::testing::AssertionResult PrintedDeclCXX11nonMSCMatches(
+ StringRef Code,
+ const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args(1, "-std=c++11");
+ Args.push_back("-fno-delayed-template-parsing");
+ return PrintedDeclMatches(Code,
+ Args,
+ NodeMatch,
+ ExpectedPrinted,
+ "input.cc");
+}
+
+::testing::AssertionResult
+PrintedDeclCXX1ZMatches(StringRef Code, const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args(1, "-std=c++1z");
+ return PrintedDeclMatches(Code,
+ Args,
+ NodeMatch,
+ ExpectedPrinted,
+ "input.cc");
+}
+
+::testing::AssertionResult PrintedDeclObjCMatches(
+ StringRef Code,
+ const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args(1, "");
+ return PrintedDeclMatches(Code,
+ Args,
+ NodeMatch,
+ ExpectedPrinted,
+ "input.m");
+}
+
+} // unnamed namespace
+
+TEST(DeclPrinter, TestTypedef1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "typedef int A;",
+ "A",
+ "typedef int A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTypedef2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "typedef const char *A;",
+ "A",
+ "typedef const char *A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTypedef3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template <typename Y> class X {};"
+ "typedef X<int> A;",
+ "A",
+ "typedef X<int> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTypedef4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "namespace X { class Y {}; }"
+ "typedef X::Y A;",
+ "A",
+ "typedef X::Y A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestNamespace1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "namespace A { int B; }",
+ "A",
+ "namespace A {\n}"));
+ // Should be: with { ... }
+}
+
+TEST(DeclPrinter, TestNamespace2) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "inline namespace A { int B; }",
+ "A",
+ "inline namespace A {\n}"));
+ // Should be: with { ... }
+}
+
+TEST(DeclPrinter, TestNamespaceAlias1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "namespace Z { }"
+ "namespace A = Z;",
+ "A",
+ "namespace A = Z"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestNamespaceAlias2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "namespace X { namespace Y {} }"
+ "namespace A = X::Y;",
+ "A",
+ "namespace A = X::Y"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class A { int a; };",
+ "A",
+ "class A {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A { int a; };",
+ "A",
+ "struct A {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "union A { int a; };",
+ "A",
+ "union A {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class Z { int a; };"
+ "class A : Z { int b; };",
+ "A",
+ "class A : Z {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl5) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z { int a; };"
+ "struct A : Z { int b; };",
+ "A",
+ "struct A : Z {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl6) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class Z { int a; };"
+ "class A : public Z { int b; };",
+ "A",
+ "class A : public Z {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl7) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class Z { int a; };"
+ "class A : protected Z { int b; };",
+ "A",
+ "class A : protected Z {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl8) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class Z { int a; };"
+ "class A : private Z { int b; };",
+ "A",
+ "class A : private Z {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl9) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class Z { int a; };"
+ "class A : virtual Z { int b; };",
+ "A",
+ "class A : virtual Z {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl10) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class Z { int a; };"
+ "class A : virtual public Z { int b; };",
+ "A",
+ "class A : virtual public Z {}"));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl11) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class Z { int a; };"
+ "class Y : virtual public Z { int b; };"
+ "class A : virtual public Z, private Y { int c; };",
+ "A",
+ "class A : virtual public Z, private Y {}"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void A();",
+ "A",
+ "void A()"));
+}
+
+TEST(DeclPrinter, TestFreeFunctionDecl_FullyQualifiedName) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void A();",
+ "A",
+ "void A()",
+ [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; }));
+}
+
+TEST(DeclPrinter, TestFreeFunctionDeclInNamespace_FullyQualifiedName) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "namespace X { void A(); };",
+ "A",
+ "void X::A()",
+ [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; }));
+}
+
+TEST(DeclPrinter, TestMemberFunction_FullyQualifiedName) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct X { void A(); };",
+ "A",
+ "void X::A()",
+ [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; }));
+}
+
+TEST(DeclPrinter, TestMemberFunctionInNamespace_FullyQualifiedName) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "namespace Z { struct X { void A(); }; }",
+ "A",
+ "void Z::X::A()",
+ [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; }));
+}
+
+TEST(DeclPrinter, TestMemberFunctionOutside_FullyQualifiedName) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct X { void A(); };"
+ "void X::A() {}",
+ functionDecl(hasName("A"), isDefinition()).bind("id"),
+ "void X::A()",
+ [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; }));
+}
+
+TEST(DeclPrinter, TestFunctionDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void A() {}",
+ "A",
+ "void A()"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void Z();"
+ "void A() { Z(); }",
+ "A",
+ "void A()"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "extern void A();",
+ "A",
+ "extern void A()"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl5) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "static void A();",
+ "A",
+ "static void A()"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl6) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "inline void A();",
+ "A",
+ "inline void A()"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl7) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "constexpr int A(int a);",
+ "A",
+ "constexpr int A(int a)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl8) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void A(int a);",
+ "A",
+ "void A(int a)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl9) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void A(...);",
+ "A",
+ "void A(...)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl10) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void A(int a, ...);",
+ "A",
+ "void A(int a, ...)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl11) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "typedef long ssize_t;"
+ "typedef int *pInt;"
+ "void A(int a, pInt b, ssize_t c);",
+ "A",
+ "void A(int a, pInt b, ssize_t c)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl12) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void A(int a, int b = 0);",
+ "A",
+ "void A(int a, int b = 0)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl13) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void (*A(int a))(int b);",
+ "A",
+ "void (*A(int a))(int)"));
+ // Should be: with parameter name (?)
+}
+
+TEST(DeclPrinter, TestFunctionDecl14) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T>"
+ "void A(T t) { }"
+ "template<>"
+ "void A(int N) { }",
+ functionDecl(hasName("A"), isExplicitTemplateSpecialization()).bind("id"),
+ "template<> void A<int>(int N)"));
+}
+
+
+TEST(DeclPrinter, TestCXXConstructorDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " A();"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A()"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " A(int a);"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A(int a)"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " A(const A &a);"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A(const A &a)"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " A(const A &a, int = 0);"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A(const A &a, int = 0)"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDeclWithMemberInitializer) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " int m;"
+ " A() : m(2) {}"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A()"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDeclWithMemberInitializer_NoTerseOutput) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " int m;"
+ " A() : m(2) {}"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A() : m(2) {\n}\n",
+ [](PrintingPolicy &Policy){ Policy.TerseOutput = false; }));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl5) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct A {"
+ " A(const A &&a);"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A(const A &&a)"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl6) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " explicit A(int a);"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "explicit A(int a)"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl7) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct A {"
+ " constexpr A();"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "constexpr A()"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl8) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct A {"
+ " A() = default;"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A() = default"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl9) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct A {"
+ " A() = delete;"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A() = delete"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl10) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename... T>"
+ "struct A {"
+ " A(const A &a);"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A<T...>(const A<T...> &a)"));
+}
+
+TEST(DeclPrinter, TestCXXConstructorDecl11) {
+ ASSERT_TRUE(PrintedDeclCXX11nonMSCMatches(
+ "template<typename... T>"
+ "struct A : public T... {"
+ " A(T&&... ts) : T(ts)... {}"
+ "};",
+ cxxConstructorDecl(ofClass(hasName("A"))).bind("id"),
+ "A<T...>(T &&...ts)"));
+}
+
+TEST(DeclPrinter, TestCXXDestructorDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " ~A();"
+ "};",
+ cxxDestructorDecl(ofClass(hasName("A"))).bind("id"),
+ "~A()"));
+}
+
+TEST(DeclPrinter, TestCXXDestructorDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " virtual ~A();"
+ "};",
+ cxxDestructorDecl(ofClass(hasName("A"))).bind("id"),
+ "virtual ~A()"));
+}
+
+TEST(DeclPrinter, TestCXXConversionDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " operator int();"
+ "};",
+ cxxMethodDecl(ofClass(hasName("A"))).bind("id"),
+ "operator int()"));
+}
+
+TEST(DeclPrinter, TestCXXConversionDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct A {"
+ " operator bool();"
+ "};",
+ cxxMethodDecl(ofClass(hasName("A"))).bind("id"),
+ "operator bool()"));
+}
+
+TEST(DeclPrinter, TestCXXConversionDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {};"
+ "struct A {"
+ " operator Z();"
+ "};",
+ cxxMethodDecl(ofClass(hasName("A"))).bind("id"),
+ "operator Z()"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction1) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "namespace std { typedef decltype(sizeof(int)) size_t; }"
+ "struct Z {"
+ " void *operator new(std::size_t);"
+ "};",
+ cxxMethodDecl(ofClass(hasName("Z"))).bind("id"),
+ "void *operator new(std::size_t)"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction2) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "namespace std { typedef decltype(sizeof(int)) size_t; }"
+ "struct Z {"
+ " void *operator new[](std::size_t);"
+ "};",
+ cxxMethodDecl(ofClass(hasName("Z"))).bind("id"),
+ "void *operator new[](std::size_t)"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction3) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct Z {"
+ " void operator delete(void *);"
+ "};",
+ cxxMethodDecl(ofClass(hasName("Z"))).bind("id"),
+ "void operator delete(void *) noexcept"));
+ // Should be: without noexcept?
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " void operator delete(void *);"
+ "};",
+ cxxMethodDecl(ofClass(hasName("Z"))).bind("id"),
+ "void operator delete(void *)"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction5) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct Z {"
+ " void operator delete[](void *);"
+ "};",
+ cxxMethodDecl(ofClass(hasName("Z"))).bind("id"),
+ "void operator delete[](void *) noexcept"));
+ // Should be: without noexcept?
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_Operator1) {
+ const char *OperatorNames[] = {
+ "+", "-", "*", "/", "%", "^", "&", "|",
+ "=", "<", ">", "+=", "-=", "*=", "/=", "%=",
+ "^=", "&=", "|=", "<<", ">>", ">>=", "<<=", "==", "!=",
+ "<=", ">=", "&&", "||", ",", "->*",
+ "()", "[]"
+ };
+
+ for (unsigned i = 0, e = llvm::array_lengthof(OperatorNames); i != e; ++i) {
+ SmallString<128> Code;
+ Code.append("struct Z { void operator");
+ Code.append(OperatorNames[i]);
+ Code.append("(Z z); };");
+
+ SmallString<128> Expected;
+ Expected.append("void operator");
+ Expected.append(OperatorNames[i]);
+ Expected.append("(Z z)");
+
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ Code,
+ cxxMethodDecl(ofClass(hasName("Z"))).bind("id"),
+ Expected));
+ }
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_Operator2) {
+ const char *OperatorNames[] = {
+ "~", "!", "++", "--", "->"
+ };
+
+ for (unsigned i = 0, e = llvm::array_lengthof(OperatorNames); i != e; ++i) {
+ SmallString<128> Code;
+ Code.append("struct Z { void operator");
+ Code.append(OperatorNames[i]);
+ Code.append("(); };");
+
+ SmallString<128> Expected;
+ Expected.append("void operator");
+ Expected.append(OperatorNames[i]);
+ Expected.append("()");
+
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ Code,
+ cxxMethodDecl(ofClass(hasName("Z"))).bind("id"),
+ Expected));
+ }
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " void A(int a);"
+ "};",
+ "A",
+ "void A(int a)"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " virtual void A(int a);"
+ "};",
+ "A",
+ "virtual void A(int a)"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " virtual void A(int a);"
+ "};"
+ "struct ZZ : Z {"
+ " void A(int a);"
+ "};",
+ "ZZ::A",
+ "void A(int a)"));
+ // TODO: should we print "virtual"?
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " inline void A(int a);"
+ "};",
+ "A",
+ "inline void A(int a)"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl5) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " virtual void A(int a) = 0;"
+ "};",
+ "A",
+ "virtual void A(int a) = 0"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " void A(int a) const;"
+ "};",
+ "A",
+ "void A(int a) const"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " void A(int a) volatile;"
+ "};",
+ "A",
+ "void A(int a) volatile"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " void A(int a) const volatile;"
+ "};",
+ "A",
+ "void A(int a) const volatile"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier1) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct Z {"
+ " void A(int a) &;"
+ "};",
+ "A",
+ "void A(int a) &"));
+}
+
+TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier2) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct Z {"
+ " void A(int a) &&;"
+ "};",
+ "A",
+ "void A(int a) &&"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " void A(int a) throw();"
+ "};",
+ "A",
+ "void A(int a) throw()"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z {"
+ " void A(int a) throw(int);"
+ "};",
+ "A",
+ "void A(int a) throw(int)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "class ZZ {};"
+ "struct Z {"
+ " void A(int a) throw(ZZ, int);"
+ "};",
+ "A",
+ "void A(int a) throw(ZZ, int)"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification4) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct Z {"
+ " void A(int a) noexcept;"
+ "};",
+ "A",
+ "void A(int a) noexcept"));
+}
+
+TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification5) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct Z {"
+ " void A(int a) noexcept(true);"
+ "};",
+ "A",
+ "void A(int a) noexcept(trueA(int a) noexcept(true)"));
+ // WRONG; Should be: "void A(int a) noexcept(true);"
+}
+
+TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification6) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "struct Z {"
+ " void A(int a) noexcept(1 < 2);"
+ "};",
+ "A",
+ "void A(int a) noexcept(1 < 2A(int a) noexcept(1 < 2)"));
+ // WRONG; Should be: "void A(int a) noexcept(1 < 2);"
+}
+
+TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification7) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<int N>"
+ "struct Z {"
+ " void A(int a) noexcept(N < 2);"
+ "};",
+ "A",
+ "void A(int a) noexcept(N < 2A(int a) noexcept(N < 2)"));
+ // WRONG; Should be: "void A(int a) noexcept(N < 2);"
+}
+
+TEST(DeclPrinter, TestVarDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "char *const (*(*A)[5])(int);",
+ "A",
+ "char *const (*(*A)[5])(int)"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestVarDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "void (*A)() throw(int);",
+ "A",
+ "void (*A)() throw(int)"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestVarDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "void (*A)() noexcept;",
+ "A",
+ "void (*A)() noexcept"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestFieldDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T>"
+ "struct Z { T A; };",
+ "A",
+ "T A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestFieldDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<int N>"
+ "struct Z { int A[N]; };",
+ "A",
+ "int A[N]"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T>"
+ "struct A { T a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T = int>"
+ "struct A { T a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T = int> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<class T>"
+ "struct A { T a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <class T> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T, typename U>"
+ "struct A { T a; U b; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T, typename U> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl5) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<int N>"
+ "struct A { int a[N]; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <int N> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl6) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<int N = 42>"
+ "struct A { int a[N]; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <int N = 42> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl7) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "typedef int MyInt;"
+ "template<MyInt N>"
+ "struct A { int a[N]; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <MyInt N> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl8) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<template<typename U> class T> struct A { };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <template <typename U> class T> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl9) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T> struct Z { };"
+ "template<template<typename U> class T = Z> struct A { };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <template <typename U> class T> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl10) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename... T>"
+ "struct A { int a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename ...T> struct A {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateDecl11) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename... T>"
+ "struct A : public T... { int a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename ...T> struct A : public T... {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T, typename U>"
+ "struct A { T a; U b; };"
+ "template<typename T>"
+ "struct A<T, int> { T a; };",
+ classTemplateSpecializationDecl().bind("id"),
+ "template <typename T> struct A<T, int> {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T>"
+ "struct A { T a; };"
+ "template<typename T>"
+ "struct A<T *> { T a; };",
+ classTemplateSpecializationDecl().bind("id"),
+ "template <typename T> struct A<T *> {}"));
+}
+
+TEST(DeclPrinter, TestClassTemplateSpecializationDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T>"
+ "struct A { T a; };"
+ "template<>"
+ "struct A<int> { int a; };",
+ classTemplateSpecializationDecl().bind("id"),
+ "template<> struct A<int> {}"));
+}
+
+TEST(DeclPrinter, TestFunctionTemplateDecl1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T>"
+ "void A(T &t);",
+ functionTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T> void A(T &t)"));
+}
+
+TEST(DeclPrinter, TestFunctionTemplateDecl2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T>"
+ "void A(T &t) { }",
+ functionTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T> void A(T &t)"));
+}
+
+TEST(DeclPrinter, TestFunctionTemplateDecl3) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename... T>"
+ "void A(T... a);",
+ functionTemplateDecl(hasName("A")).bind("id"),
+ "template <typename ...T> void A(T ...a)"));
+}
+
+TEST(DeclPrinter, TestFunctionTemplateDecl4) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z { template<typename T> void A(T t); };",
+ functionTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T> void A(T t)"));
+}
+
+TEST(DeclPrinter, TestFunctionTemplateDecl5) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct Z { template<typename T> void A(T t) {} };",
+ functionTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T> void A(T t)"));
+}
+
+TEST(DeclPrinter, TestFunctionTemplateDecl6) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T >struct Z {"
+ " template<typename U> void A(U t) {}"
+ "};",
+ functionTemplateDecl(hasName("A")).bind("id"),
+ "template <typename U> void A(U t)"));
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList1) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T> struct Z {};"
+ "struct X {};"
+ "Z<X> A;",
+ "A",
+ "Z<X> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList2) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T, typename U> struct Z {};"
+ "struct X {};"
+ "typedef int Y;"
+ "Z<X, Y> A;",
+ "A",
+ "Z<X, Y> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList3) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T> struct Z {};"
+ "template<typename T> struct X {};"
+ "Z<X<int> > A;",
+ "A",
+ "Z<X<int> > A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList4) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename T> struct Z {};"
+ "template<typename T> struct X {};"
+ "Z<X<int>> A;",
+ "A",
+ "Z<X<int> > A"));
+ // Should be: with semicolon, without extra space in "> >"
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList5) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T> struct Z {};"
+ "template<typename T> struct X { Z<T> A; };",
+ "A",
+ "Z<T> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList6) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<template<typename T> class U> struct Z {};"
+ "template<typename T> struct X {};"
+ "Z<X> A;",
+ "A",
+ "Z<X> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList7) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<template<typename T> class U> struct Z {};"
+ "template<template<typename T> class U> struct Y {"
+ " Z<U> A;"
+ "};",
+ "A",
+ "Z<U> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList8) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<typename T> struct Z {};"
+ "template<template<typename T> class U> struct Y {"
+ " Z<U<int> > A;"
+ "};",
+ "A",
+ "Z<U<int> > A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList9) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<unsigned I> struct Z {};"
+ "Z<0> A;",
+ "A",
+ "Z<0> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList10) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<unsigned I> struct Z {};"
+ "template<unsigned I> struct X { Z<I> A; };",
+ "A",
+ "Z<I> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList11) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<int I> struct Z {};"
+ "Z<42 * 10 - 420 / 1> A;",
+ "A",
+ "Z<42 * 10 - 420 / 1> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList12) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "template<const char *p> struct Z {};"
+ "extern const char X[] = \"aaa\";"
+ "Z<X> A;",
+ "A",
+ "Z<X> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList13) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename... T> struct Z {};"
+ "template<typename... T> struct X {"
+ " Z<T...> A;"
+ "};",
+ "A",
+ "Z<T...> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList14) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<typename... T> struct Z {};"
+ "template<typename T> struct Y {};"
+ "template<typename... T> struct X {"
+ " Z<Y<T>...> A;"
+ "};",
+ "A",
+ "Z<Y<T>...> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestTemplateArgumentList15) {
+ ASSERT_TRUE(PrintedDeclCXX11Matches(
+ "template<unsigned I> struct Z {};"
+ "template<typename... T> struct X {"
+ " Z<sizeof...(T)> A;"
+ "};",
+ "A",
+ "Z<sizeof...(T)> A"));
+ // Should be: with semicolon
+}
+
+TEST(DeclPrinter, TestStaticAssert1) {
+ ASSERT_TRUE(PrintedDeclCXX1ZMatches(
+ "static_assert(true);",
+ staticAssertDecl().bind("id"),
+ "static_assert(true)"));
+}
+
+TEST(DeclPrinter, TestObjCMethod1) {
+ ASSERT_TRUE(PrintedDeclObjCMatches(
+ "__attribute__((objc_root_class)) @interface X\n"
+ "- (int)A:(id)anObject inRange:(long)range;\n"
+ "@end\n"
+ "@implementation X\n"
+ "- (int)A:(id)anObject inRange:(long)range { int printThis; return 0; }\n"
+ "@end\n",
+ namedDecl(hasName("A:inRange:"),
+ hasDescendant(namedDecl(hasName("printThis")))).bind("id"),
+ "- (int)A:(id)anObject inRange:(long)range"));
+}
+
+TEST(DeclPrinter, TestObjCProtocol1) {
+ ASSERT_TRUE(PrintedDeclObjCMatches(
+ "@protocol P1, P2;",
+ namedDecl(hasName("P1")).bind("id"),
+ "@protocol P1;\n"));
+ ASSERT_TRUE(PrintedDeclObjCMatches(
+ "@protocol P1, P2;",
+ namedDecl(hasName("P2")).bind("id"),
+ "@protocol P2;\n"));
+}
+
+TEST(DeclPrinter, TestObjCProtocol2) {
+ ASSERT_TRUE(PrintedDeclObjCMatches(
+ "@protocol P2 @end"
+ "@protocol P1<P2> @end",
+ namedDecl(hasName("P1")).bind("id"),
+ "@protocol P1<P2>\n@end"));
+}
diff --git a/gnu/llvm/clang/unittests/AST/DeclTest.cpp b/gnu/llvm/clang/unittests/AST/DeclTest.cpp
new file mode 100644
index 00000000000..e095c4518ed
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/DeclTest.cpp
@@ -0,0 +1,109 @@
+//===- unittests/AST/DeclTest.cpp --- Declaration tests -------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Unit tests for Decl nodes in the AST.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Mangle.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+#include "llvm/IR/DataLayout.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace clang;
+
+TEST(Decl, CleansUpAPValues) {
+ MatchFinder Finder;
+ std::unique_ptr<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Finder));
+
+ // This is a regression test for a memory leak in APValues for structs that
+ // allocate memory. This test only fails if run under valgrind with full leak
+ // checking enabled.
+ std::vector<std::string> Args(1, "-std=c++11");
+ Args.push_back("-fno-ms-extensions");
+ ASSERT_TRUE(runToolOnCodeWithArgs(
+ Factory->create(),
+ "struct X { int a; }; constexpr X x = { 42 };"
+ "union Y { constexpr Y(int a) : a(a) {} int a; }; constexpr Y y = { 42 };"
+ "constexpr int z[2] = { 42, 43 };"
+ "constexpr int __attribute__((vector_size(16))) v1 = {};"
+ "\n#ifdef __SIZEOF_INT128__\n"
+ "constexpr __uint128_t large_int = 0xffffffffffffffff;"
+ "constexpr __uint128_t small_int = 1;"
+ "\n#endif\n"
+ "constexpr double d1 = 42.42;"
+ "constexpr long double d2 = 42.42;"
+ "constexpr _Complex long double c1 = 42.0i;"
+ "constexpr _Complex long double c2 = 42.0;"
+ "template<int N> struct A : A<N-1> {};"
+ "template<> struct A<0> { int n; }; A<50> a;"
+ "constexpr int &r = a.n;"
+ "constexpr int A<50>::*p = &A<50>::n;"
+ "void f() { foo: bar: constexpr int k = __builtin_constant_p(0) ?"
+ " (char*)&&foo - (char*)&&bar : 0; }",
+ Args));
+
+ // FIXME: Once this test starts breaking we can test APValue::needsCleanup
+ // for ComplexInt.
+ ASSERT_FALSE(runToolOnCodeWithArgs(
+ Factory->create(),
+ "constexpr _Complex __uint128_t c = 0xffffffffffffffff;",
+ Args));
+}
+
+TEST(Decl, AsmLabelAttr) {
+ // Create two method decls: `f` and `g`.
+ StringRef Code = R"(
+ struct S {
+ void f() {}
+ void g() {}
+ };
+ )";
+ auto AST =
+ tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"});
+ ASTContext &Ctx = AST->getASTContext();
+ assert(Ctx.getTargetInfo().getDataLayout().getGlobalPrefix() &&
+ "Expected target to have a global prefix");
+ DiagnosticsEngine &Diags = AST->getDiagnostics();
+ SourceManager &SM = AST->getSourceManager();
+ FileID MainFileID = SM.getMainFileID();
+
+ // Find the method decls within the AST.
+ SmallVector<Decl *, 1> Decls;
+ AST->findFileRegionDecls(MainFileID, Code.find('{'), 0, Decls);
+ ASSERT_TRUE(Decls.size() == 1);
+ CXXRecordDecl *DeclS = cast<CXXRecordDecl>(Decls[0]);
+ NamedDecl *DeclF = *DeclS->method_begin();
+ NamedDecl *DeclG = *(++DeclS->method_begin());
+
+ // Attach asm labels to the decls: one literal, and one not.
+ DeclF->addAttr(::new (Ctx) AsmLabelAttr(Ctx, SourceLocation(), "foo",
+ /*LiteralLabel=*/true));
+ DeclG->addAttr(::new (Ctx) AsmLabelAttr(Ctx, SourceLocation(), "goo",
+ /*LiteralLabel=*/false));
+
+ // Mangle the decl names.
+ std::string MangleF, MangleG;
+ std::unique_ptr<ItaniumMangleContext> MC(
+ ItaniumMangleContext::create(Ctx, Diags));
+ {
+ llvm::raw_string_ostream OS_F(MangleF);
+ llvm::raw_string_ostream OS_G(MangleG);
+ MC->mangleName(DeclF, OS_F);
+ MC->mangleName(DeclG, OS_G);
+ }
+
+ ASSERT_TRUE(0 == MangleF.compare("\x01" "foo"));
+ ASSERT_TRUE(0 == MangleG.compare("goo"));
+}
diff --git a/gnu/llvm/clang/unittests/AST/EvaluateAsRValueTest.cpp b/gnu/llvm/clang/unittests/AST/EvaluateAsRValueTest.cpp
new file mode 100644
index 00000000000..0475330796d
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/EvaluateAsRValueTest.cpp
@@ -0,0 +1,110 @@
+//===- unittests/AST/EvaluateAsRValueTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// \file
+// \brief Unit tests for evaluation of constant initializers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+#include <map>
+#include <string>
+
+using namespace clang::tooling;
+
+namespace {
+// For each variable name encountered, whether its initializer was a
+// constant.
+typedef std::map<std::string, bool> VarInfoMap;
+
+/// \brief Records information on variable initializers to a map.
+class EvaluateConstantInitializersVisitor
+ : public clang::RecursiveASTVisitor<EvaluateConstantInitializersVisitor> {
+ public:
+ explicit EvaluateConstantInitializersVisitor(VarInfoMap &VarInfo)
+ : VarInfo(VarInfo) {}
+
+ /// \brief Checks that isConstantInitializer and EvaluateAsRValue agree
+ /// and don't crash.
+ ///
+ /// For each VarDecl with an initializer this also records in VarInfo
+ /// whether the initializer could be evaluated as a constant.
+ bool VisitVarDecl(const clang::VarDecl *VD) {
+ if (const clang::Expr *Init = VD->getInit()) {
+ clang::Expr::EvalResult Result;
+ bool WasEvaluated = Init->EvaluateAsRValue(Result, VD->getASTContext());
+ VarInfo[VD->getNameAsString()] = WasEvaluated;
+ EXPECT_EQ(WasEvaluated, Init->isConstantInitializer(VD->getASTContext(),
+ false /*ForRef*/));
+ }
+ return true;
+ }
+
+ private:
+ VarInfoMap &VarInfo;
+};
+
+class EvaluateConstantInitializersAction : public clang::ASTFrontendAction {
+ public:
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ llvm::StringRef FilePath) override {
+ return std::make_unique<Consumer>();
+ }
+
+ private:
+ class Consumer : public clang::ASTConsumer {
+ public:
+ ~Consumer() override {}
+
+ void HandleTranslationUnit(clang::ASTContext &Ctx) override {
+ VarInfoMap VarInfo;
+ EvaluateConstantInitializersVisitor Evaluator(VarInfo);
+ Evaluator.TraverseDecl(Ctx.getTranslationUnitDecl());
+ EXPECT_EQ(2u, VarInfo.size());
+ EXPECT_FALSE(VarInfo["Dependent"]);
+ EXPECT_TRUE(VarInfo["Constant"]);
+ EXPECT_EQ(2u, VarInfo.size());
+ }
+ };
+};
+}
+
+TEST(EvaluateAsRValue, FailsGracefullyForUnknownTypes) {
+ // This is a regression test; the AST library used to trigger assertion
+ // failures because it assumed that the type of initializers was always
+ // known (which is true only after template instantiation).
+ std::string ModesToTest[] = {"-std=c++03", "-std=c++11", "-std=c++1y"};
+ for (std::string const &Mode : ModesToTest) {
+ std::vector<std::string> Args(1, Mode);
+ Args.push_back("-fno-delayed-template-parsing");
+ ASSERT_TRUE(runToolOnCodeWithArgs(
+ std::make_unique<EvaluateConstantInitializersAction>(),
+ "template <typename T>"
+ "struct vector {"
+ " explicit vector(int size);"
+ "};"
+ "template <typename R>"
+ "struct S {"
+ " vector<R> intervals() const {"
+ " vector<R> Dependent(2);"
+ " return Dependent;"
+ " }"
+ "};"
+ "void doSomething() {"
+ " int Constant = 2 + 2;"
+ " (void) Constant;"
+ "}",
+ Args));
+ }
+}
diff --git a/gnu/llvm/clang/unittests/AST/ExternalASTSourceTest.cpp b/gnu/llvm/clang/unittests/AST/ExternalASTSourceTest.cpp
new file mode 100644
index 00000000000..8b70be664e0
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/ExternalASTSourceTest.cpp
@@ -0,0 +1,82 @@
+//===- unittest/AST/ExternalASTSourceTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for Clang's ExternalASTSource.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ExternalASTSource.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace llvm;
+
+
+class TestFrontendAction : public ASTFrontendAction {
+public:
+ TestFrontendAction(ExternalASTSource *Source) : Source(Source) {}
+
+private:
+ void ExecuteAction() override {
+ getCompilerInstance().getASTContext().setExternalSource(Source);
+ getCompilerInstance().getASTContext().getTranslationUnitDecl()
+ ->setHasExternalVisibleStorage();
+ return ASTFrontendAction::ExecuteAction();
+ }
+
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override {
+ return std::make_unique<ASTConsumer>();
+ }
+
+ IntrusiveRefCntPtr<ExternalASTSource> Source;
+};
+
+bool testExternalASTSource(ExternalASTSource *Source,
+ StringRef FileContents) {
+ CompilerInstance Compiler;
+ Compiler.createDiagnostics();
+
+ auto Invocation = std::make_shared<CompilerInvocation>();
+ Invocation->getPreprocessorOpts().addRemappedFile(
+ "test.cc", MemoryBuffer::getMemBuffer(FileContents).release());
+ const char *Args[] = { "test.cc" };
+ CompilerInvocation::CreateFromArgs(*Invocation, Args,
+ Compiler.getDiagnostics());
+ Compiler.setInvocation(std::move(Invocation));
+
+ TestFrontendAction Action(Source);
+ return Compiler.ExecuteAction(Action);
+}
+
+
+// Ensure that a failed name lookup into an external source only occurs once.
+TEST(ExternalASTSourceTest, FailedLookupOccursOnce) {
+ struct TestSource : ExternalASTSource {
+ TestSource(unsigned &Calls) : Calls(Calls) {}
+
+ bool FindExternalVisibleDeclsByName(const DeclContext *,
+ DeclarationName Name) override {
+ if (Name.getAsString() == "j")
+ ++Calls;
+ return false;
+ }
+
+ unsigned &Calls;
+ };
+
+ unsigned Calls = 0;
+ ASSERT_TRUE(testExternalASTSource(new TestSource(Calls), "int j, k = j;"));
+ EXPECT_EQ(1u, Calls);
+}
diff --git a/gnu/llvm/clang/unittests/AST/Language.cpp b/gnu/llvm/clang/unittests/AST/Language.cpp
new file mode 100644
index 00000000000..eeb3303a127
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/Language.cpp
@@ -0,0 +1,53 @@
+//===------ unittest/AST/Language.cpp - AST unit test support -------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines language options for AST unittests.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Language.h"
+
+namespace clang {
+namespace ast_matchers {
+
+ArgVector getBasicRunOptionsForLanguage(Language Lang) {
+ ArgVector BasicArgs;
+ // Test with basic arguments.
+ switch (Lang) {
+ case Lang_C:
+ BasicArgs = {"-x", "c", "-std=c99"};
+ break;
+ case Lang_C89:
+ BasicArgs = {"-x", "c", "-std=c89"};
+ break;
+ case Lang_CXX:
+ BasicArgs = {"-std=c++98", "-frtti"};
+ break;
+ case Lang_CXX11:
+ BasicArgs = {"-std=c++11", "-frtti"};
+ break;
+ case Lang_CXX14:
+ BasicArgs = {"-std=c++14", "-frtti"};
+ break;
+ case Lang_CXX17:
+ BasicArgs = {"-std=c++17", "-frtti"};
+ break;
+ case Lang_CXX2a:
+ BasicArgs = {"-std=c++2a", "-frtti"};
+ break;
+ case Lang_OBJCXX:
+ BasicArgs = {"-x", "objective-c++", "-frtti"};
+ break;
+ case Lang_OpenCL:
+ llvm_unreachable("Not implemented yet!");
+ }
+ return BasicArgs;
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/Language.h b/gnu/llvm/clang/unittests/AST/Language.h
new file mode 100644
index 00000000000..6ba40be743b
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/Language.h
@@ -0,0 +1,42 @@
+//===------ unittest/AST/Language.h - AST unit test support ---------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines language options for AST unittests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_AST_LANGUAGE_H
+#define LLVM_CLANG_UNITTESTS_AST_LANGUAGE_H
+
+#include "llvm/Support/ErrorHandling.h"
+#include <vector>
+#include <string>
+
+namespace clang {
+namespace ast_matchers {
+
+typedef std::vector<std::string> ArgVector;
+
+enum Language {
+ Lang_C,
+ Lang_C89,
+ Lang_CXX,
+ Lang_CXX11,
+ Lang_CXX14,
+ Lang_CXX17,
+ Lang_CXX2a,
+ Lang_OpenCL,
+ Lang_OBJCXX
+};
+
+ArgVector getBasicRunOptionsForLanguage(Language Lang);
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif
diff --git a/gnu/llvm/clang/unittests/AST/MatchVerifier.h b/gnu/llvm/clang/unittests/AST/MatchVerifier.h
new file mode 100644
index 00000000000..dd0569a5824
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/MatchVerifier.h
@@ -0,0 +1,319 @@
+//===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides MatchVerifier, a base class to implement gtest matchers that
+// verify things that can be matched on the AST.
+//
+// Also implements matchers based on MatchVerifier:
+// LocationVerifier and RangeVerifier to verify whether a matched node has
+// the expected source location or source range.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
+#define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "Language.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+/// \brief Base class for verifying some property of nodes found by a matcher.
+template <typename NodeType>
+class MatchVerifier : public MatchFinder::MatchCallback {
+public:
+ template <typename MatcherType>
+ testing::AssertionResult match(const std::string &Code,
+ const MatcherType &AMatcher) {
+ std::vector<std::string> Args;
+ return match(Code, AMatcher, Args, Lang_CXX);
+ }
+
+ template <typename MatcherType>
+ testing::AssertionResult match(const std::string &Code,
+ const MatcherType &AMatcher,
+ Language L) {
+ std::vector<std::string> Args;
+ return match(Code, AMatcher, Args, L);
+ }
+
+ template <typename MatcherType>
+ testing::AssertionResult match(const std::string &Code,
+ const MatcherType &AMatcher,
+ std::vector<std::string>& Args,
+ Language L);
+
+ template <typename MatcherType>
+ testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher);
+
+protected:
+ void run(const MatchFinder::MatchResult &Result) override;
+ virtual void verify(const MatchFinder::MatchResult &Result,
+ const NodeType &Node) {}
+
+ void setFailure(const Twine &Result) {
+ Verified = false;
+ VerifyResult = Result.str();
+ }
+
+ void setSuccess() {
+ Verified = true;
+ }
+
+private:
+ bool Verified;
+ std::string VerifyResult;
+};
+
+/// \brief Runs a matcher over some code, and returns the result of the
+/// verifier for the matched node.
+template <typename NodeType> template <typename MatcherType>
+testing::AssertionResult MatchVerifier<NodeType>::match(
+ const std::string &Code, const MatcherType &AMatcher,
+ std::vector<std::string>& Args, Language L) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher.bind(""), this);
+ std::unique_ptr<tooling::FrontendActionFactory> Factory(
+ tooling::newFrontendActionFactory(&Finder));
+
+ StringRef FileName;
+ switch (L) {
+ case Lang_C:
+ Args.push_back("-std=c99");
+ FileName = "input.c";
+ break;
+ case Lang_C89:
+ Args.push_back("-std=c89");
+ FileName = "input.c";
+ break;
+ case Lang_CXX:
+ Args.push_back("-std=c++98");
+ FileName = "input.cc";
+ break;
+ case Lang_CXX11:
+ Args.push_back("-std=c++11");
+ FileName = "input.cc";
+ break;
+ case Lang_CXX14:
+ Args.push_back("-std=c++14");
+ FileName = "input.cc";
+ break;
+ case Lang_CXX17:
+ Args.push_back("-std=c++17");
+ FileName = "input.cc";
+ break;
+ case Lang_CXX2a:
+ Args.push_back("-std=c++2a");
+ FileName = "input.cc";
+ break;
+ case Lang_OpenCL:
+ FileName = "input.cl";
+ break;
+ case Lang_OBJCXX:
+ FileName = "input.mm";
+ break;
+ }
+
+ // Default to failure in case callback is never called
+ setFailure("Could not find match");
+ if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
+ return testing::AssertionFailure() << "Parsing error";
+ if (!Verified)
+ return testing::AssertionFailure() << VerifyResult;
+ return testing::AssertionSuccess();
+}
+
+/// \brief Runs a matcher over some AST, and returns the result of the
+/// verifier for the matched node.
+template <typename NodeType> template <typename MatcherType>
+testing::AssertionResult MatchVerifier<NodeType>::match(
+ const Decl *D, const MatcherType &AMatcher) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher.bind(""), this);
+
+ setFailure("Could not find match");
+ Finder.match(*D, D->getASTContext());
+
+ if (!Verified)
+ return testing::AssertionFailure() << VerifyResult;
+ return testing::AssertionSuccess();
+}
+
+template <typename NodeType>
+void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
+ const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
+ if (!Node) {
+ setFailure("Matched node has wrong type");
+ } else {
+ // Callback has been called, default to success.
+ setSuccess();
+ verify(Result, *Node);
+ }
+}
+
+template <>
+inline void MatchVerifier<ast_type_traits::DynTypedNode>::run(
+ const MatchFinder::MatchResult &Result) {
+ BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
+ BoundNodes::IDToNodeMap::const_iterator I = M.find("");
+ if (I == M.end()) {
+ setFailure("Node was not bound");
+ } else {
+ // Callback has been called, default to success.
+ setSuccess();
+ verify(Result, I->second);
+ }
+}
+
+/// \brief Verify whether a node has the correct source location.
+///
+/// By default, Node.getSourceLocation() is checked. This can be changed
+/// by overriding getLocation().
+template <typename NodeType>
+class LocationVerifier : public MatchVerifier<NodeType> {
+public:
+ void expectLocation(unsigned Line, unsigned Column) {
+ ExpectLine = Line;
+ ExpectColumn = Column;
+ }
+
+protected:
+ void verify(const MatchFinder::MatchResult &Result,
+ const NodeType &Node) override {
+ SourceLocation Loc = getLocation(Node);
+ unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
+ unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
+ if (Line != ExpectLine || Column != ExpectColumn) {
+ std::string MsgStr;
+ llvm::raw_string_ostream Msg(MsgStr);
+ Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
+ << ">, found <";
+ Loc.print(Msg, *Result.SourceManager);
+ Msg << '>';
+ this->setFailure(Msg.str());
+ }
+ }
+
+ virtual SourceLocation getLocation(const NodeType &Node) {
+ return Node.getLocation();
+ }
+
+private:
+ unsigned ExpectLine, ExpectColumn;
+};
+
+/// \brief Verify whether a node has the correct source range.
+///
+/// By default, Node.getSourceRange() is checked. This can be changed
+/// by overriding getRange().
+template <typename NodeType>
+class RangeVerifier : public MatchVerifier<NodeType> {
+public:
+ void expectRange(unsigned BeginLine, unsigned BeginColumn,
+ unsigned EndLine, unsigned EndColumn) {
+ ExpectBeginLine = BeginLine;
+ ExpectBeginColumn = BeginColumn;
+ ExpectEndLine = EndLine;
+ ExpectEndColumn = EndColumn;
+ }
+
+protected:
+ void verify(const MatchFinder::MatchResult &Result,
+ const NodeType &Node) override {
+ SourceRange R = getRange(Node);
+ SourceLocation Begin = R.getBegin();
+ SourceLocation End = R.getEnd();
+ unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
+ unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
+ unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
+ unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
+ if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
+ EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
+ std::string MsgStr;
+ llvm::raw_string_ostream Msg(MsgStr);
+ Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
+ << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
+ Begin.print(Msg, *Result.SourceManager);
+ Msg << '-';
+ End.print(Msg, *Result.SourceManager);
+ Msg << '>';
+ this->setFailure(Msg.str());
+ }
+ }
+
+ virtual SourceRange getRange(const NodeType &Node) {
+ return Node.getSourceRange();
+ }
+
+private:
+ unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
+};
+
+/// \brief Verify whether a node's dump contains a given substring.
+class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
+public:
+ void expectSubstring(const std::string &Str) {
+ ExpectSubstring = Str;
+ }
+
+protected:
+ void verify(const MatchFinder::MatchResult &Result,
+ const ast_type_traits::DynTypedNode &Node) override {
+ std::string DumpStr;
+ llvm::raw_string_ostream Dump(DumpStr);
+ Node.dump(Dump, *Result.SourceManager);
+
+ if (Dump.str().find(ExpectSubstring) == std::string::npos) {
+ std::string MsgStr;
+ llvm::raw_string_ostream Msg(MsgStr);
+ Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
+ << Dump.str() << '>';
+ this->setFailure(Msg.str());
+ }
+ }
+
+private:
+ std::string ExpectSubstring;
+};
+
+/// \brief Verify whether a node's pretty print matches a given string.
+class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
+public:
+ void expectString(const std::string &Str) {
+ ExpectString = Str;
+ }
+
+protected:
+ void verify(const MatchFinder::MatchResult &Result,
+ const ast_type_traits::DynTypedNode &Node) override {
+ std::string PrintStr;
+ llvm::raw_string_ostream Print(PrintStr);
+ Node.print(Print, Result.Context->getPrintingPolicy());
+
+ if (Print.str() != ExpectString) {
+ std::string MsgStr;
+ llvm::raw_string_ostream Msg(MsgStr);
+ Msg << "Expected pretty print <" << ExpectString << ">, found <"
+ << Print.str() << '>';
+ this->setFailure(Msg.str());
+ }
+ }
+
+private:
+ std::string ExpectString;
+};
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif
diff --git a/gnu/llvm/clang/unittests/AST/NamedDeclPrinterTest.cpp b/gnu/llvm/clang/unittests/AST/NamedDeclPrinterTest.cpp
new file mode 100644
index 00000000000..a5c3e19055f
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/NamedDeclPrinterTest.cpp
@@ -0,0 +1,271 @@
+//===- unittests/AST/NamedDeclPrinterTest.cpp --- NamedDecl printer tests -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for NamedDecl::printQualifiedName().
+//
+// These tests have a coding convention:
+// * declaration to be printed is named 'A' unless it should have some special
+// name (e.g., 'operator+');
+// * additional helper declarations are 'Z', 'Y', 'X' and so on.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace ast_matchers;
+using namespace tooling;
+
+namespace {
+
+class PrintMatch : public MatchFinder::MatchCallback {
+ SmallString<1024> Printed;
+ unsigned NumFoundDecls;
+ std::function<void(llvm::raw_ostream &OS, const NamedDecl *)> Printer;
+
+public:
+ explicit PrintMatch(
+ std::function<void(llvm::raw_ostream &OS, const NamedDecl *)> Printer)
+ : NumFoundDecls(0), Printer(std::move(Printer)) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const NamedDecl *ND = Result.Nodes.getNodeAs<NamedDecl>("id");
+ if (!ND)
+ return;
+ NumFoundDecls++;
+ if (NumFoundDecls > 1)
+ return;
+
+ llvm::raw_svector_ostream Out(Printed);
+ Printer(Out, ND);
+ }
+
+ StringRef getPrinted() const {
+ return Printed;
+ }
+
+ unsigned getNumFoundDecls() const {
+ return NumFoundDecls;
+ }
+};
+
+::testing::AssertionResult PrintedDeclMatches(
+ StringRef Code, const std::vector<std::string> &Args,
+ const DeclarationMatcher &NodeMatch, StringRef ExpectedPrinted,
+ StringRef FileName,
+ std::function<void(llvm::raw_ostream &, const NamedDecl *)> Print) {
+ PrintMatch Printer(std::move(Print));
+ MatchFinder Finder;
+ Finder.addMatcher(NodeMatch, &Printer);
+ std::unique_ptr<FrontendActionFactory> Factory =
+ newFrontendActionFactory(&Finder);
+
+ if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
+ return testing::AssertionFailure()
+ << "Parsing error in \"" << Code.str() << "\"";
+
+ if (Printer.getNumFoundDecls() == 0)
+ return testing::AssertionFailure()
+ << "Matcher didn't find any named declarations";
+
+ if (Printer.getNumFoundDecls() > 1)
+ return testing::AssertionFailure()
+ << "Matcher should match only one named declaration "
+ "(found " << Printer.getNumFoundDecls() << ")";
+
+ if (Printer.getPrinted() != ExpectedPrinted)
+ return ::testing::AssertionFailure()
+ << "Expected \"" << ExpectedPrinted.str() << "\", "
+ "got \"" << Printer.getPrinted().str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult
+PrintedNamedDeclMatches(StringRef Code, const std::vector<std::string> &Args,
+ bool SuppressUnwrittenScope,
+ const DeclarationMatcher &NodeMatch,
+ StringRef ExpectedPrinted, StringRef FileName) {
+ return PrintedDeclMatches(Code, Args, NodeMatch, ExpectedPrinted, FileName,
+ [=](llvm::raw_ostream &Out, const NamedDecl *ND) {
+ auto Policy =
+ ND->getASTContext().getPrintingPolicy();
+ Policy.SuppressUnwrittenScope =
+ SuppressUnwrittenScope;
+ ND->printQualifiedName(Out, Policy);
+ });
+}
+
+::testing::AssertionResult
+PrintedNamedDeclCXX98Matches(StringRef Code, StringRef DeclName,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args(1, "-std=c++98");
+ return PrintedNamedDeclMatches(Code,
+ Args,
+ /*SuppressUnwrittenScope*/ false,
+ namedDecl(hasName(DeclName)).bind("id"),
+ ExpectedPrinted,
+ "input.cc");
+}
+
+::testing::AssertionResult
+PrintedWrittenNamedDeclCXX11Matches(StringRef Code, StringRef DeclName,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args(1, "-std=c++11");
+ return PrintedNamedDeclMatches(Code,
+ Args,
+ /*SuppressUnwrittenScope*/ true,
+ namedDecl(hasName(DeclName)).bind("id"),
+ ExpectedPrinted,
+ "input.cc");
+}
+
+::testing::AssertionResult
+PrintedWrittenPropertyDeclObjCMatches(StringRef Code, StringRef DeclName,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args{"-std=c++11", "-xobjective-c++"};
+ return PrintedNamedDeclMatches(Code,
+ Args,
+ /*SuppressUnwrittenScope*/ true,
+ objcPropertyDecl(hasName(DeclName)).bind("id"),
+ ExpectedPrinted,
+ "input.m");
+}
+
+::testing::AssertionResult
+PrintedNestedNameSpecifierMatches(StringRef Code, StringRef DeclName,
+ StringRef ExpectedPrinted) {
+ std::vector<std::string> Args{"-std=c++11"};
+ return PrintedDeclMatches(Code, Args, namedDecl(hasName(DeclName)).bind("id"),
+ ExpectedPrinted, "input.cc",
+ [](llvm::raw_ostream &Out, const NamedDecl *D) {
+ D->printNestedNameSpecifier(Out);
+ });
+}
+
+} // unnamed namespace
+
+TEST(NamedDeclPrinter, TestNamespace1) {
+ ASSERT_TRUE(PrintedNamedDeclCXX98Matches(
+ "namespace { int A; }",
+ "A",
+ "(anonymous namespace)::A"));
+}
+
+TEST(NamedDeclPrinter, TestNamespace2) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "inline namespace Z { namespace { int A; } }",
+ "A",
+ "A"));
+}
+
+TEST(NamedDeclPrinter, TestUnscopedUnnamedEnum) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "enum { A };",
+ "A",
+ "A"));
+}
+
+TEST(NamedDeclPrinter, TestNamedEnum) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "enum X { A };",
+ "A",
+ "A"));
+}
+
+TEST(NamedDeclPrinter, TestScopedNamedEnum) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "enum class X { A };",
+ "A",
+ "X::A"));
+}
+
+TEST(NamedDeclPrinter, TestClassWithUnscopedUnnamedEnum) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "class X { enum { A }; };",
+ "A",
+ "X::A"));
+}
+
+TEST(NamedDeclPrinter, TestClassWithUnscopedNamedEnum) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "class X { enum Y { A }; };",
+ "A",
+ "X::A"));
+}
+
+TEST(NamedDeclPrinter, TestClassWithScopedNamedEnum) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "class X { enum class Y { A }; };",
+ "A",
+ "X::Y::A"));
+}
+
+TEST(NamedDeclPrinter, TestLinkageInNamespace) {
+ ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
+ "namespace X { extern \"C\" { int A; } }",
+ "A",
+ "X::A"));
+}
+
+TEST(NamedDeclPrinter, TestObjCClassExtension) {
+ const char *Code =
+R"(
+ @interface Obj
+ @end
+
+ @interface Obj ()
+ @property(nonatomic) int property;
+ @end
+)";
+ ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches(
+ Code,
+ "property",
+ "Obj::property"));
+}
+
+TEST(NamedDeclPrinter, TestObjCClassExtensionWithGetter) {
+ const char *Code =
+R"(
+ @interface Obj
+ @end
+
+ @interface Obj ()
+ @property(nonatomic, getter=myPropertyGetter) int property;
+ @end
+)";
+ ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches(
+ Code,
+ "property",
+ "Obj::property"));
+}
+
+TEST(NamedDeclPrinter, NestedNameSpecifierSimple) {
+ const char *Code =
+ R"(
+ namespace foo { namespace bar { void func(); } }
+)";
+ ASSERT_TRUE(PrintedNestedNameSpecifierMatches(Code, "func", "foo::bar::"));
+}
+
+TEST(NamedDeclPrinter, NestedNameSpecifierTemplateArgs) {
+ const char *Code =
+ R"(
+ template <class T> struct vector;
+ template <> struct vector<int> { int method(); };
+)";
+ ASSERT_TRUE(
+ PrintedNestedNameSpecifierMatches(Code, "method", "vector<int>::"));
+}
diff --git a/gnu/llvm/clang/unittests/AST/OMPStructuredBlockTest.cpp b/gnu/llvm/clang/unittests/AST/OMPStructuredBlockTest.cpp
new file mode 100644
index 00000000000..f4a3fad4a1b
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/OMPStructuredBlockTest.cpp
@@ -0,0 +1,540 @@
+//===- unittests/AST/OMPStructuredBlockTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Fine-grained tests for IsOMPStructuredBlock bit of Stmt.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTPrint.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/StmtOpenMP.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace ast_matchers;
+using namespace tooling;
+
+namespace {
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher<
+ OMPExecutableDirective, OMPTargetDirective>
+ ompTargetDirective;
+
+StatementMatcher OMPInnermostStructuredBlockMatcher() {
+ return stmt(isOMPStructuredBlock(),
+ unless(hasDescendant(stmt(isOMPStructuredBlock()))))
+ .bind("id");
+}
+
+StatementMatcher OMPStandaloneDirectiveMatcher() {
+ return stmt(ompExecutableDirective(isStandaloneDirective())).bind("id");
+}
+
+template <typename T>
+::testing::AssertionResult
+PrintedOMPStmtMatches(StringRef Code, const T &NodeMatch,
+ StringRef ExpectedPrinted,
+ PolicyAdjusterType PolicyAdjuster = None) {
+ std::vector<std::string> Args = {
+ "-fopenmp=libomp",
+ };
+ return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
+ PolicyAdjuster);
+}
+
+static testing::AssertionResult NoMatches(StringRef Code,
+ const StatementMatcher &StmtMatch) {
+ PrintMatch Printer((PolicyAdjusterType()));
+ MatchFinder Finder;
+ Finder.addMatcher(StmtMatch, &Printer);
+ std::unique_ptr<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Finder));
+ if (!runToolOnCode(Factory->create(), Code))
+ return testing::AssertionFailure()
+ << "Parsing error in \"" << Code.str() << "\"";
+ if (Printer.getNumFoundStmts() == 0)
+ return testing::AssertionSuccess();
+ return testing::AssertionFailure()
+ << "Matcher should match only zero statements (found "
+ << Printer.getNumFoundStmts() << ")";
+}
+
+} // unnamed namespace
+
+TEST(OMPStructuredBlock, TestAtomic) {
+ const char *Source =
+ R"(
+void test(int i) {
+#pragma omp atomic
+++i;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), "++i"));
+}
+
+TEST(OMPStructuredBlock, TestBarrier) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp barrier
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp barrier\n"));
+ ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher()));
+}
+
+TEST(OMPStructuredBlock, TestCancel) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp parallel
+{
+ #pragma omp cancel parallel
+}
+})";
+ const char *Expected = R"({
+ #pragma omp cancel parallel
+}
+)";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), Expected));
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp cancel parallel\n"));
+}
+
+TEST(OMPStructuredBlock, TestCancellationPoint) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp parallel
+{
+ #pragma omp cancellation point parallel
+}
+})";
+ const char *Expected = R"({
+ #pragma omp cancellation point parallel
+}
+)";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), Expected));
+ ASSERT_TRUE(
+ PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp cancellation point parallel\n"));
+}
+
+TEST(OMPStructuredBlock, TestCritical) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp critical
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+//----------------------------------------------------------------------------//
+// Loop tests
+//----------------------------------------------------------------------------//
+
+class OMPStructuredBlockLoop : public ::testing::TestWithParam<const char *> {};
+
+TEST_P(OMPStructuredBlockLoop, TestDirective0) {
+ const std::string Source =
+ R"(
+void test(int x) {
+#pragma omp )" +
+ std::string(GetParam()) + R"(
+for (int i = 0; i < x; i++)
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST_P(OMPStructuredBlockLoop, TestDirective1) {
+ const std::string Source =
+ R"(
+void test(int x, int y) {
+#pragma omp )" +
+ std::string(GetParam()) + R"(
+for (int i = 0; i < x; i++)
+for (int i = 0; i < y; i++)
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source,
+ OMPInnermostStructuredBlockMatcher(),
+ "for (int i = 0; i < y; i++)\n ;\n"));
+}
+
+TEST_P(OMPStructuredBlockLoop, TestDirectiveCollapse1) {
+ const std::string Source =
+ R"(
+void test(int x, int y) {
+#pragma omp )" +
+ std::string(GetParam()) + R"( collapse(1)
+for (int i = 0; i < x; i++)
+for (int i = 0; i < y; i++)
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source,
+ OMPInnermostStructuredBlockMatcher(),
+ "for (int i = 0; i < y; i++)\n ;\n"));
+}
+
+TEST_P(OMPStructuredBlockLoop, TestDirectiveCollapse2) {
+ const std::string Source =
+ R"(
+void test(int x, int y) {
+#pragma omp )" +
+ std::string(GetParam()) + R"( collapse(2)
+for (int i = 0; i < x; i++)
+for (int i = 0; i < y; i++)
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST_P(OMPStructuredBlockLoop, TestDirectiveCollapse22) {
+ const std::string Source =
+ R"(
+void test(int x, int y, int z) {
+#pragma omp )" +
+ std::string(GetParam()) + R"( collapse(2)
+for (int i = 0; i < x; i++)
+for (int i = 0; i < y; i++)
+for (int i = 0; i < z; i++)
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source,
+ OMPInnermostStructuredBlockMatcher(),
+ "for (int i = 0; i < z; i++)\n ;\n"));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ OMPStructuredBlockLoopDirectives, OMPStructuredBlockLoop,
+ ::testing::Values("simd", "for", "for simd", "parallel for",
+ "parallel for simd", "target parallel for", "taskloop",
+ "taskloop simd", "distribute", "distribute parallel for",
+ "distribute parallel for simd", "distribute simd",
+ "target parallel for simd", "target simd",
+ "target\n#pragma omp teams distribute",
+ "target\n#pragma omp teams distribute simd",
+ "target\n#pragma omp teams distribute parallel for simd",
+ "target\n#pragma omp teams distribute parallel for",
+ "target teams distribute",
+ "target teams distribute parallel for",
+ "target teams distribute parallel for simd",
+ "target teams distribute simd"), );
+
+//----------------------------------------------------------------------------//
+// End Loop tests
+//----------------------------------------------------------------------------//
+
+TEST(OMPStructuredBlock, TestFlush) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp flush
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp flush\n"));
+ ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher()));
+}
+
+TEST(OMPStructuredBlock, TestMaster) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp master
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestOrdered0) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp ordered
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestOrdered1) {
+ const char *Source =
+ R"(
+void test(int x) {
+#pragma omp for ordered
+for (int i = 0; i < x; i++)
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestOrdered2) {
+ const char *Source =
+ R"(
+void test(int x) {
+#pragma omp for ordered(1)
+for (int i = 0; i < x; i++) {
+#pragma omp ordered depend(source)
+}
+})";
+ ASSERT_TRUE(
+ PrintedOMPStmtMatches(Source, OMPInnermostStructuredBlockMatcher(),
+ "{\n #pragma omp ordered depend(source)\n}\n"));
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp ordered depend(source)\n"));
+}
+
+TEST(OMPStructuredBlock, DISABLED_TestParallelMaster0XFAIL) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp parallel master
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, DISABLED_TestParallelMaster1XFAIL) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp parallel master
+{ ; }
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), "{\n ;\n}\n"));
+}
+
+TEST(OMPStructuredBlock, TestParallelSections) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp parallel sections
+{ ; }
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), "{\n ;\n}\n"));
+}
+
+TEST(OMPStructuredBlock, TestParallelDirective) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp parallel
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher<
+ OMPExecutableDirective, OMPSectionsDirective>
+ ompSectionsDirective;
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher<
+ OMPExecutableDirective, OMPSectionDirective>
+ ompSectionDirective;
+
+StatementMatcher OMPSectionsDirectiveMatcher() {
+ return stmt(
+ isOMPStructuredBlock(),
+ hasAncestor(ompExecutableDirective(ompSectionsDirective())),
+ unless(hasAncestor(ompExecutableDirective(ompSectionDirective()))))
+ .bind("id");
+}
+
+TEST(OMPStructuredBlock, TestSectionDirective) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp sections
+{
+#pragma omp section
+;
+}
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPSectionsDirectiveMatcher(),
+ "{\n"
+ " #pragma omp section\n"
+ " ;\n"
+ "}\n"));
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestSections) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp sections
+{ ; }
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), "{\n ;\n}\n"));
+}
+
+TEST(OMPStructuredBlock, TestSingleDirective) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp single
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TesTargetDataDirective) {
+ const char *Source =
+ R"(
+void test(int x) {
+#pragma omp target data map(x)
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TesTargetEnterDataDirective) {
+ const char *Source =
+ R"(
+void test(int x) {
+#pragma omp target enter data map(to : x)
+})";
+ ASSERT_TRUE(
+ PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp target enter data map(to: x)\n"));
+ ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher()));
+}
+
+TEST(OMPStructuredBlock, TesTargetExitDataDirective) {
+ const char *Source =
+ R"(
+void test(int x) {
+#pragma omp target exit data map(from : x)
+})";
+ ASSERT_TRUE(
+ PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp target exit data map(from: x)\n"));
+ ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher()));
+}
+
+TEST(OMPStructuredBlock, TestTargetParallelDirective) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp target parallel
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestTargetTeams) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp target teams
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestTargetUpdateDirective) {
+ const char *Source =
+ R"(
+void test(int x) {
+#pragma omp target update to(x)
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp target update to(x)\n"));
+ ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher()));
+}
+
+TEST(OMPStructuredBlock, TestTarget) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp target
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestTask) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp task
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestTaskgroup) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp taskgroup
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
+
+TEST(OMPStructuredBlock, TestTaskwaitDirective) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp taskwait
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp taskwait\n"));
+ ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher()));
+}
+
+TEST(OMPStructuredBlock, TestTaskyieldDirective) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp taskyield
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(),
+ "#pragma omp taskyield\n"));
+ ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher()));
+}
+
+TEST(OMPStructuredBlock, TestTeams) {
+ const char *Source =
+ R"(
+void test() {
+#pragma omp target
+#pragma omp teams
+;
+})";
+ ASSERT_TRUE(PrintedOMPStmtMatches(
+ Source, OMPInnermostStructuredBlockMatcher(), ";\n"));
+}
diff --git a/gnu/llvm/clang/unittests/AST/RecursiveASTVisitorTest.cpp b/gnu/llvm/clang/unittests/AST/RecursiveASTVisitorTest.cpp
new file mode 100644
index 00000000000..aa8756527d3
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/RecursiveASTVisitorTest.cpp
@@ -0,0 +1,105 @@
+//===- unittest/AST/RecursiveASTVisitorTest.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/AST/RecursiveASTVisitor.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ADT/STLExtras.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <cassert>
+
+using namespace clang;
+using ::testing::ElementsAre;
+
+namespace {
+class ProcessASTAction : public clang::ASTFrontendAction {
+public:
+ ProcessASTAction(llvm::unique_function<void(clang::ASTContext &)> Process)
+ : Process(std::move(Process)) {
+ assert(this->Process);
+ }
+
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) {
+ class Consumer : public ASTConsumer {
+ public:
+ Consumer(llvm::function_ref<void(ASTContext &CTx)> Process)
+ : Process(Process) {}
+
+ void HandleTranslationUnit(ASTContext &Ctx) override { Process(Ctx); }
+
+ private:
+ llvm::function_ref<void(ASTContext &CTx)> Process;
+ };
+
+ return std::make_unique<Consumer>(Process);
+ }
+
+private:
+ llvm::unique_function<void(clang::ASTContext &)> Process;
+};
+
+enum class VisitEvent {
+ StartTraverseFunction,
+ EndTraverseFunction,
+ StartTraverseAttr,
+ EndTraverseAttr
+};
+
+class CollectInterestingEvents
+ : public RecursiveASTVisitor<CollectInterestingEvents> {
+public:
+ bool TraverseFunctionDecl(FunctionDecl *D) {
+ Events.push_back(VisitEvent::StartTraverseFunction);
+ bool Ret = RecursiveASTVisitor::TraverseFunctionDecl(D);
+ Events.push_back(VisitEvent::EndTraverseFunction);
+
+ return Ret;
+ }
+
+ bool TraverseAttr(Attr *A) {
+ Events.push_back(VisitEvent::StartTraverseAttr);
+ bool Ret = RecursiveASTVisitor::TraverseAttr(A);
+ Events.push_back(VisitEvent::EndTraverseAttr);
+
+ return Ret;
+ }
+
+ std::vector<VisitEvent> takeEvents() && { return std::move(Events); }
+
+private:
+ std::vector<VisitEvent> Events;
+};
+
+std::vector<VisitEvent> collectEvents(llvm::StringRef Code) {
+ CollectInterestingEvents Visitor;
+ clang::tooling::runToolOnCode(
+ std::make_unique<ProcessASTAction>(
+ [&](clang::ASTContext &Ctx) { Visitor.TraverseAST(Ctx); }),
+ Code);
+ return std::move(Visitor).takeEvents();
+}
+} // namespace
+
+TEST(RecursiveASTVisitorTest, AttributesInsideDecls) {
+ /// Check attributes are traversed inside TraverseFunctionDecl.
+ llvm::StringRef Code = R"cpp(
+__attribute__((annotate("something"))) int foo() { return 10; }
+ )cpp";
+
+ EXPECT_THAT(collectEvents(Code),
+ ElementsAre(VisitEvent::StartTraverseFunction,
+ VisitEvent::StartTraverseAttr,
+ VisitEvent::EndTraverseAttr,
+ VisitEvent::EndTraverseFunction));
+}
diff --git a/gnu/llvm/clang/unittests/AST/SourceLocationTest.cpp b/gnu/llvm/clang/unittests/AST/SourceLocationTest.cpp
new file mode 100644
index 00000000000..d104497974f
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/SourceLocationTest.cpp
@@ -0,0 +1,846 @@
+//===- unittest/AST/SourceLocationTest.cpp - AST source loc unit tests ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for SourceLocation and SourceRange fields
+// in AST nodes.
+//
+// FIXME: In the long-term, when we test more than source locations, we may
+// want to have a unit test file for an AST node (or group of related nodes),
+// rather than a unit test file for source locations for all AST nodes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "MatchVerifier.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+// FIXME: Pull the *Verifier tests into their own test file.
+
+TEST(MatchVerifier, ParseError) {
+ LocationVerifier<VarDecl> Verifier;
+ Verifier.expectLocation(1, 1);
+ EXPECT_FALSE(Verifier.match("int i", varDecl()));
+}
+
+TEST(MatchVerifier, NoMatch) {
+ LocationVerifier<VarDecl> Verifier;
+ Verifier.expectLocation(1, 1);
+ EXPECT_FALSE(Verifier.match("int i;", recordDecl()));
+}
+
+TEST(MatchVerifier, WrongType) {
+ LocationVerifier<RecordDecl> Verifier;
+ Verifier.expectLocation(1, 1);
+ EXPECT_FALSE(Verifier.match("int i;", varDecl()));
+}
+
+TEST(LocationVerifier, WrongLocation) {
+ LocationVerifier<VarDecl> Verifier;
+ Verifier.expectLocation(1, 1);
+ EXPECT_FALSE(Verifier.match("int i;", varDecl()));
+}
+
+TEST(RangeVerifier, WrongRange) {
+ RangeVerifier<VarDecl> Verifier;
+ Verifier.expectRange(1, 1, 1, 1);
+ EXPECT_FALSE(Verifier.match("int i;", varDecl()));
+}
+
+class LabelDeclRangeVerifier : public RangeVerifier<LabelStmt> {
+protected:
+ SourceRange getRange(const LabelStmt &Node) override {
+ return Node.getDecl()->getSourceRange();
+ }
+};
+
+TEST(LabelDecl, Range) {
+ LabelDeclRangeVerifier Verifier;
+ Verifier.expectRange(1, 12, 1, 12);
+ EXPECT_TRUE(Verifier.match("void f() { l: return; }", labelStmt()));
+}
+
+TEST(LabelStmt, Range) {
+ RangeVerifier<LabelStmt> Verifier;
+ Verifier.expectRange(1, 12, 1, 15);
+ EXPECT_TRUE(Verifier.match("void f() { l: return; }", labelStmt()));
+}
+
+TEST(ParmVarDecl, KNRLocation) {
+ LocationVerifier<ParmVarDecl> Verifier;
+ Verifier.expectLocation(1, 8);
+ EXPECT_TRUE(Verifier.match("void f(i) {}", varDecl(), Lang_C));
+}
+
+TEST(ParmVarDecl, KNRRange) {
+ RangeVerifier<ParmVarDecl> Verifier;
+ Verifier.expectRange(1, 8, 1, 8);
+ EXPECT_TRUE(Verifier.match("void f(i) {}", varDecl(), Lang_C));
+}
+
+TEST(CXXNewExpr, ArrayRange) {
+ RangeVerifier<CXXNewExpr> Verifier;
+ Verifier.expectRange(1, 12, 1, 22);
+ EXPECT_TRUE(Verifier.match("void f() { new int[10]; }", cxxNewExpr()));
+}
+
+TEST(CXXNewExpr, ParenRange) {
+ RangeVerifier<CXXNewExpr> Verifier;
+ Verifier.expectRange(1, 12, 1, 20);
+ EXPECT_TRUE(Verifier.match("void f() { new int(); }", cxxNewExpr()));
+}
+
+TEST(MemberExpr, ImplicitMemberRange) {
+ RangeVerifier<MemberExpr> Verifier;
+ Verifier.expectRange(2, 30, 2, 30);
+ EXPECT_TRUE(Verifier.match("struct S { operator int() const; };\n"
+ "int foo(const S& s) { return s; }",
+ memberExpr()));
+}
+
+class MemberExprArrowLocVerifier : public RangeVerifier<MemberExpr> {
+protected:
+ SourceRange getRange(const MemberExpr &Node) override {
+ return Node.getOperatorLoc();
+ }
+};
+
+TEST(MemberExpr, ArrowRange) {
+ MemberExprArrowLocVerifier Verifier;
+ Verifier.expectRange(2, 19, 2, 19);
+ EXPECT_TRUE(Verifier.match("struct S { int x; };\n"
+ "void foo(S *s) { s->x = 0; }",
+ memberExpr()));
+}
+
+TEST(MemberExpr, MacroArrowRange) {
+ MemberExprArrowLocVerifier Verifier;
+ Verifier.expectRange(1, 24, 1, 24);
+ EXPECT_TRUE(Verifier.match("#define MEMBER(a, b) (a->b)\n"
+ "struct S { int x; };\n"
+ "void foo(S *s) { MEMBER(s, x) = 0; }",
+ memberExpr()));
+}
+
+TEST(MemberExpr, ImplicitArrowRange) {
+ MemberExprArrowLocVerifier Verifier;
+ Verifier.expectRange(0, 0, 0, 0);
+ EXPECT_TRUE(Verifier.match("struct S { int x; void Test(); };\n"
+ "void S::Test() { x = 1; }",
+ memberExpr()));
+}
+
+TEST(VarDecl, VMTypeFixedVarDeclRange) {
+ RangeVerifier<VarDecl> Verifier;
+ Verifier.expectRange(1, 1, 1, 23);
+ EXPECT_TRUE(Verifier.match("int a[(int)(void*)1234];",
+ varDecl(), Lang_C89));
+}
+
+TEST(TypeLoc, IntRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 1);
+ EXPECT_TRUE(Verifier.match("int a;", typeLoc()));
+}
+
+TEST(TypeLoc, LongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 1);
+ EXPECT_TRUE(Verifier.match("long a;", typeLoc()));
+}
+
+TEST(TypeLoc, LongDoubleRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 6);
+ EXPECT_TRUE(Verifier.match("long double a;", typeLoc()));
+}
+
+TEST(TypeLoc, DoubleLongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 8);
+ EXPECT_TRUE(Verifier.match("double long a;", typeLoc()));
+}
+
+TEST(TypeLoc, LongIntRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 6);
+ EXPECT_TRUE(Verifier.match("long int a;", typeLoc()));
+}
+
+TEST(TypeLoc, IntLongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 5);
+ EXPECT_TRUE(Verifier.match("int long a;", typeLoc()));
+}
+
+TEST(TypeLoc, UnsignedIntRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 10);
+ EXPECT_TRUE(Verifier.match("unsigned int a;", typeLoc()));
+}
+
+TEST(TypeLoc, IntUnsignedRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 5);
+ EXPECT_TRUE(Verifier.match("int unsigned a;", typeLoc()));
+}
+
+TEST(TypeLoc, LongLongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 6);
+ EXPECT_TRUE(Verifier.match("long long a;", typeLoc()));
+}
+
+TEST(TypeLoc, UnsignedLongLongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 15);
+ EXPECT_TRUE(Verifier.match("unsigned long long a;", typeLoc()));
+}
+
+TEST(TypeLoc, LongUnsignedLongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 15);
+ EXPECT_TRUE(Verifier.match("long unsigned long a;", typeLoc()));
+}
+
+TEST(TypeLoc, LongLongUnsignedRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 11);
+ EXPECT_TRUE(Verifier.match("long long unsigned a;", typeLoc()));
+}
+
+TEST(TypeLoc, ConstLongLongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 7, 1, 12);
+ EXPECT_TRUE(Verifier.match("const long long a = 0;", typeLoc()));
+}
+
+TEST(TypeLoc, LongConstLongRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 12);
+ EXPECT_TRUE(Verifier.match("long const long a = 0;", typeLoc()));
+}
+
+TEST(TypeLoc, LongLongConstRange) {
+ RangeVerifier<TypeLoc> Verifier;
+ Verifier.expectRange(1, 1, 1, 6);
+ EXPECT_TRUE(Verifier.match("long long const a = 0;", typeLoc()));
+}
+
+TEST(CXXConstructorDecl, NoRetFunTypeLocRange) {
+ RangeVerifier<CXXConstructorDecl> Verifier;
+ Verifier.expectRange(1, 11, 1, 13);
+ EXPECT_TRUE(Verifier.match("class C { C(); };", functionDecl()));
+}
+
+TEST(CXXConstructorDecl, DefaultedCtorLocRange) {
+ RangeVerifier<CXXConstructorDecl> Verifier;
+ Verifier.expectRange(1, 11, 1, 23);
+ EXPECT_TRUE(Verifier.match("class C { C() = default; };", functionDecl()));
+}
+
+TEST(CXXConstructorDecl, DeletedCtorLocRange) {
+ RangeVerifier<CXXConstructorDecl> Verifier;
+ Verifier.expectRange(1, 11, 1, 22);
+ EXPECT_TRUE(Verifier.match("class C { C() = delete; };", functionDecl()));
+}
+
+TEST(CompoundLiteralExpr, CompoundVectorLiteralRange) {
+ RangeVerifier<CompoundLiteralExpr> Verifier;
+ Verifier.expectRange(2, 11, 2, 22);
+ EXPECT_TRUE(Verifier.match(
+ "typedef int int2 __attribute__((ext_vector_type(2)));\n"
+ "int2 i2 = (int2){1, 2};", compoundLiteralExpr()));
+}
+
+TEST(CompoundLiteralExpr, ParensCompoundVectorLiteralRange) {
+ RangeVerifier<CompoundLiteralExpr> Verifier;
+ Verifier.expectRange(2, 20, 2, 31);
+ EXPECT_TRUE(Verifier.match(
+ "typedef int int2 __attribute__((ext_vector_type(2)));\n"
+ "constant int2 i2 = (int2)(1, 2);",
+ compoundLiteralExpr(), Lang_OpenCL));
+}
+
+TEST(InitListExpr, VectorLiteralListBraceRange) {
+ RangeVerifier<InitListExpr> Verifier;
+ Verifier.expectRange(2, 17, 2, 22);
+ EXPECT_TRUE(Verifier.match(
+ "typedef int int2 __attribute__((ext_vector_type(2)));\n"
+ "int2 i2 = (int2){1, 2};", initListExpr()));
+}
+
+TEST(InitListExpr, VectorLiteralInitListParens) {
+ RangeVerifier<InitListExpr> Verifier;
+ Verifier.expectRange(2, 26, 2, 31);
+ EXPECT_TRUE(Verifier.match(
+ "typedef int int2 __attribute__((ext_vector_type(2)));\n"
+ "constant int2 i2 = (int2)(1, 2);", initListExpr(), Lang_OpenCL));
+}
+
+class TemplateAngleBracketLocRangeVerifier : public RangeVerifier<TypeLoc> {
+protected:
+ SourceRange getRange(const TypeLoc &Node) override {
+ TemplateSpecializationTypeLoc T =
+ Node.getUnqualifiedLoc().castAs<TemplateSpecializationTypeLoc>();
+ assert(!T.isNull());
+ return SourceRange(T.getLAngleLoc(), T.getRAngleLoc());
+ }
+};
+
+TEST(TemplateSpecializationTypeLoc, AngleBracketLocations) {
+ TemplateAngleBracketLocRangeVerifier Verifier;
+ Verifier.expectRange(2, 8, 2, 10);
+ EXPECT_TRUE(Verifier.match(
+ "template<typename T> struct A {}; struct B{}; void f(\n"
+ "const A<B>&);",
+ loc(templateSpecializationType())));
+}
+
+TEST(CXXNewExpr, TypeParenRange) {
+ RangeVerifier<CXXNewExpr> Verifier;
+ Verifier.expectRange(1, 10, 1, 18);
+ EXPECT_TRUE(Verifier.match("int* a = new (int);", cxxNewExpr()));
+}
+
+class UnaryTransformTypeLocParensRangeVerifier : public RangeVerifier<TypeLoc> {
+protected:
+ SourceRange getRange(const TypeLoc &Node) override {
+ UnaryTransformTypeLoc T =
+ Node.getUnqualifiedLoc().castAs<UnaryTransformTypeLoc>();
+ assert(!T.isNull());
+ return SourceRange(T.getLParenLoc(), T.getRParenLoc());
+ }
+};
+
+TEST(UnaryTransformTypeLoc, ParensRange) {
+ UnaryTransformTypeLocParensRangeVerifier Verifier;
+ Verifier.expectRange(3, 26, 3, 28);
+ EXPECT_TRUE(Verifier.match(
+ "template <typename T>\n"
+ "struct S {\n"
+ "typedef __underlying_type(T) type;\n"
+ "};",
+ loc(unaryTransformType())));
+}
+
+TEST(CXXFunctionalCastExpr, SourceRange) {
+ RangeVerifier<CXXFunctionalCastExpr> Verifier;
+ Verifier.expectRange(2, 10, 2, 14);
+ EXPECT_TRUE(Verifier.match(
+ "int foo() {\n"
+ " return int{};\n"
+ "}",
+ cxxFunctionalCastExpr(), Lang_CXX11));
+}
+
+TEST(CXXConstructExpr, SourceRange) {
+ RangeVerifier<CXXConstructExpr> Verifier;
+ Verifier.expectRange(3, 14, 3, 19);
+ EXPECT_TRUE(Verifier.match(
+ "struct A { A(int, int); };\n"
+ "void f(A a);\n"
+ "void g() { f({0, 0}); }",
+ cxxConstructExpr(), Lang_CXX11));
+}
+
+TEST(CXXTemporaryObjectExpr, SourceRange) {
+ RangeVerifier<CXXTemporaryObjectExpr> Verifier;
+ Verifier.expectRange(2, 6, 2, 12);
+ EXPECT_TRUE(Verifier.match(
+ "struct A { A(int, int); };\n"
+ "A a( A{0, 0} );",
+ cxxTemporaryObjectExpr(), Lang_CXX11));
+}
+
+TEST(CXXUnresolvedConstructExpr, SourceRange) {
+ RangeVerifier<CXXUnresolvedConstructExpr> Verifier;
+ Verifier.expectRange(3, 10, 3, 12);
+ std::vector<std::string> Args;
+ Args.push_back("-fno-delayed-template-parsing");
+ EXPECT_TRUE(Verifier.match(
+ "template <typename U>\n"
+ "U foo() {\n"
+ " return U{};\n"
+ "}",
+ cxxUnresolvedConstructExpr(), Args, Lang_CXX11));
+}
+
+TEST(UsingDecl, SourceRange) {
+ RangeVerifier<UsingDecl> Verifier;
+ Verifier.expectRange(2, 22, 2, 25);
+ EXPECT_TRUE(Verifier.match(
+ "class B { protected: int i; };\n"
+ "class D : public B { B::i; };",
+ usingDecl()));
+}
+
+TEST(UnresolvedUsingValueDecl, SourceRange) {
+ RangeVerifier<UnresolvedUsingValueDecl> Verifier;
+ Verifier.expectRange(3, 3, 3, 6);
+ EXPECT_TRUE(Verifier.match(
+ "template <typename B>\n"
+ "class D : public B {\n"
+ " B::i;\n"
+ "};",
+ unresolvedUsingValueDecl()));
+}
+
+TEST(FriendDecl, FriendNonMemberFunctionLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(2, 13);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "friend void f();\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendNonMemberFunctionRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(2, 1, 2, 15);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "friend void f();\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendNonMemberFunctionDefinitionLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(2, 12);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "friend int f() { return 0; }\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendNonMemberFunctionDefinitionRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(2, 1, 2, 28);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "friend int f() { return 0; }\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendElaboratedTypeLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(2, 8);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "friend class B;\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendElaboratedTypeRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(2, 1, 2, 14);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "friend class B;\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendSimpleTypeLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(3, 8);
+ EXPECT_TRUE(Verifier.match("class B;\n"
+ "struct A {\n"
+ "friend B;\n"
+ "};\n",
+ friendDecl(), Lang_CXX11));
+}
+
+TEST(FriendDecl, FriendSimpleTypeRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(3, 1, 3, 8);
+ EXPECT_TRUE(Verifier.match("class B;\n"
+ "struct A {\n"
+ "friend B;\n"
+ "};\n",
+ friendDecl(), Lang_CXX11));
+}
+
+TEST(FriendDecl, FriendTemplateParameterLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(3, 8);
+ EXPECT_TRUE(Verifier.match("template <typename T>\n"
+ "struct A {\n"
+ "friend T;\n"
+ "};\n",
+ friendDecl(), Lang_CXX11));
+}
+
+TEST(FriendDecl, FriendTemplateParameterRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(3, 1, 3, 8);
+ EXPECT_TRUE(Verifier.match("template <typename T>\n"
+ "struct A {\n"
+ "friend T;\n"
+ "};\n",
+ friendDecl(), Lang_CXX11));
+}
+
+TEST(FriendDecl, FriendDecltypeLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(4, 8);
+ EXPECT_TRUE(Verifier.match("struct A;\n"
+ "A foo();\n"
+ "struct A {\n"
+ "friend decltype(foo());\n"
+ "};\n",
+ friendDecl(), Lang_CXX11));
+}
+
+TEST(FriendDecl, FriendDecltypeRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(4, 1, 4, 8);
+ EXPECT_TRUE(Verifier.match("struct A;\n"
+ "A foo();\n"
+ "struct A {\n"
+ "friend decltype(foo());\n"
+ "};\n",
+ friendDecl(), Lang_CXX11));
+}
+
+TEST(FriendDecl, FriendConstructorDestructorLocation) {
+ const std::string Code = "struct B {\n"
+ "B();\n"
+ "~B();\n"
+ "};\n"
+ "struct A {\n"
+ "friend B::B(), B::~B();\n"
+ "};\n";
+ LocationVerifier<FriendDecl> ConstructorVerifier;
+ ConstructorVerifier.expectLocation(6, 11);
+ EXPECT_TRUE(ConstructorVerifier.match(
+ Code, friendDecl(has(cxxConstructorDecl(ofClass(hasName("B")))))));
+ LocationVerifier<FriendDecl> DestructorVerifier;
+ DestructorVerifier.expectLocation(6, 19);
+ EXPECT_TRUE(DestructorVerifier.match(
+ Code, friendDecl(has(cxxDestructorDecl(ofClass(hasName("B")))))));
+}
+
+TEST(FriendDecl, FriendConstructorDestructorRange) {
+ const std::string Code = "struct B {\n"
+ "B();\n"
+ "~B();\n"
+ "};\n"
+ "struct A {\n"
+ "friend B::B(), B::~B();\n"
+ "};\n";
+ RangeVerifier<FriendDecl> ConstructorVerifier;
+ ConstructorVerifier.expectRange(6, 1, 6, 13);
+ EXPECT_TRUE(ConstructorVerifier.match(
+ Code, friendDecl(has(cxxConstructorDecl(ofClass(hasName("B")))))));
+ RangeVerifier<FriendDecl> DestructorVerifier;
+ DestructorVerifier.expectRange(6, 1, 6, 22);
+ EXPECT_TRUE(DestructorVerifier.match(
+ Code, friendDecl(has(cxxDestructorDecl(ofClass(hasName("B")))))));
+}
+
+TEST(FriendDecl, FriendTemplateFunctionLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(3, 13);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "template <typename T>\n"
+ "friend void f();\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendTemplateFunctionRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(2, 1, 3, 15);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "template <typename T>\n"
+ "friend void f();\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendTemplateClassLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(3, 14);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "template <typename T>\n"
+ "friend class B;\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendTemplateClassRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(2, 1, 3, 14);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "template <typename T>\n"
+ "friend class B;\n"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendInlineFunctionLocation) {
+ LocationVerifier<FriendDecl> Verifier;
+ Verifier.expectLocation(2, 19);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "int inline friend f() { return 0; }"
+ "};\n",
+ friendDecl()));
+}
+
+TEST(FriendDecl, FriendInlineFunctionRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(2, 1, 2, 35);
+ EXPECT_TRUE(Verifier.match("struct A {\n"
+ "int inline friend f() { return 0; }"
+ "};\n",
+ friendDecl(), Lang_CXX11));
+}
+
+TEST(FriendDecl, InstantiationSourceRange) {
+ RangeVerifier<FriendDecl> Verifier;
+ Verifier.expectRange(4, 3, 4, 35);
+ EXPECT_TRUE(Verifier.match(
+ "template <typename T> class S;\n"
+ "template<class T> void operator+(S<T> x);\n"
+ "template<class T> struct S {\n"
+ " friend void operator+<>(S<T> src);\n"
+ "};\n"
+ "void test(S<double> s) { +s; }",
+ friendDecl(hasParent(cxxRecordDecl(isTemplateInstantiation())))));
+}
+
+TEST(ObjCMessageExpr, CXXConstructExprRange) {
+ RangeVerifier<CXXConstructExpr> Verifier;
+ Verifier.expectRange(5, 25, 5, 27);
+ EXPECT_TRUE(Verifier.match(
+ "struct A { int a; };\n"
+ "@interface B {}\n"
+ "+ (void) f1: (A)arg;\n"
+ "@end\n"
+ "void f2() { A a; [B f1: (a)]; }\n",
+ cxxConstructExpr(), Lang_OBJCXX));
+}
+
+TEST(FunctionDecl, FunctionDeclWithThrowSpecification) {
+ RangeVerifier<FunctionDecl> Verifier;
+ Verifier.expectRange(1, 1, 1, 16);
+ EXPECT_TRUE(Verifier.match(
+ "void f() throw();\n",
+ functionDecl()));
+}
+
+TEST(FunctionDecl, FunctionDeclWithNoExceptSpecification) {
+ RangeVerifier<FunctionDecl> Verifier;
+ Verifier.expectRange(1, 1, 1, 24);
+ EXPECT_TRUE(Verifier.match(
+ "void f() noexcept(false);\n",
+ functionDecl(),
+ Language::Lang_CXX11));
+}
+
+class FunctionDeclParametersRangeVerifier : public RangeVerifier<FunctionDecl> {
+protected:
+ SourceRange getRange(const FunctionDecl &Function) override {
+ return Function.getParametersSourceRange();
+ }
+};
+
+TEST(FunctionDeclParameters, FunctionDeclOnlyVariadic) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 8);
+ EXPECT_TRUE(Verifier.match("void f(...);\n", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclVariadic) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 15);
+ EXPECT_TRUE(Verifier.match("void f(int a, ...);\n", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclMacroVariadic) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(2, 8, 1, 18);
+ EXPECT_TRUE(Verifier.match("#define VARIADIC ...\n"
+ "void f(int a, VARIADIC);\n",
+ functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclMacroParams) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 16, 2, 20);
+ EXPECT_TRUE(Verifier.match("#define PARAMS int a, int b\n"
+ "void f(PARAMS, int c);",
+ functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclSingleParameter) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 12);
+ EXPECT_TRUE(Verifier.match("void f(int a);\n", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, MemberFunctionDecl) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(2, 8, 2, 12);
+ EXPECT_TRUE(Verifier.match("class A{\n"
+ "void f(int a);\n"
+ "};",
+ functionDecl()));
+}
+
+TEST(FunctionDeclParameters, MemberFunctionDeclVariadic) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(2, 8, 2, 15);
+ EXPECT_TRUE(Verifier.match("class A{\n"
+ "void f(int a, ...);\n"
+ "};",
+ functionDecl()));
+}
+
+TEST(FunctionDeclParameters, StaticFunctionDecl) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(2, 15, 2, 19);
+ EXPECT_TRUE(Verifier.match("class A{\n"
+ "static void f(int a);\n"
+ "};",
+ functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclMultipleParameters) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 28);
+ EXPECT_TRUE(
+ Verifier.match("void f(int a, int b, char *c);\n", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclWithDefaultValue) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 16);
+ EXPECT_TRUE(Verifier.match("void f(int a = 5);\n", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclWithVolatile) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 22);
+ EXPECT_TRUE(Verifier.match("void f(volatile int *i);", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclWithConstParam) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 19);
+ EXPECT_TRUE(Verifier.match("void f(const int *i);", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclWithConstVolatileParam) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 28);
+ EXPECT_TRUE(Verifier.match("void f(const volatile int *i);", functionDecl()));
+}
+
+TEST(FunctionDeclParameters, FunctionDeclWithParamAttribute) {
+ FunctionDeclParametersRangeVerifier Verifier;
+ Verifier.expectRange(1, 8, 1, 36);
+ EXPECT_TRUE(Verifier.match("void f(__attribute__((unused)) int a) {}",
+ functionDecl()));
+}
+
+TEST(CXXMethodDecl, CXXMethodDeclWithThrowSpecification) {
+ RangeVerifier<FunctionDecl> Verifier;
+ Verifier.expectRange(2, 1, 2, 16);
+ EXPECT_TRUE(Verifier.match(
+ "class A {\n"
+ "void f() throw();\n"
+ "};\n",
+ functionDecl()));
+}
+
+TEST(CXXMethodDecl, CXXMethodDeclWithNoExceptSpecification) {
+ RangeVerifier<FunctionDecl> Verifier;
+ Verifier.expectRange(2, 1, 2, 24);
+ EXPECT_TRUE(Verifier.match(
+ "class A {\n"
+ "void f() noexcept(false);\n"
+ "};\n",
+ functionDecl(),
+ Language::Lang_CXX11));
+}
+
+class ExceptionSpecRangeVerifier : public RangeVerifier<TypeLoc> {
+protected:
+ SourceRange getRange(const TypeLoc &Node) override {
+ auto T =
+ Node.getUnqualifiedLoc().castAs<FunctionProtoTypeLoc>();
+ assert(!T.isNull());
+ return T.getExceptionSpecRange();
+ }
+};
+
+class ParmVarExceptionSpecRangeVerifier : public RangeVerifier<ParmVarDecl> {
+protected:
+ SourceRange getRange(const ParmVarDecl &Node) override {
+ if (const TypeSourceInfo *TSI = Node.getTypeSourceInfo()) {
+ TypeLoc TL = TSI->getTypeLoc();
+ if (TL.getType()->isPointerType()) {
+ TL = TL.getNextTypeLoc().IgnoreParens();
+ if (auto FPTL = TL.getAs<FunctionProtoTypeLoc>()) {
+ return FPTL.getExceptionSpecRange();
+ }
+ }
+ }
+ return SourceRange();
+ }
+};
+
+TEST(FunctionDecl, ExceptionSpecifications) {
+ ExceptionSpecRangeVerifier Verifier;
+
+ Verifier.expectRange(1, 10, 1, 16);
+ EXPECT_TRUE(Verifier.match("void f() throw();\n", loc(functionType())));
+
+ Verifier.expectRange(1, 10, 1, 34);
+ EXPECT_TRUE(Verifier.match("void f() throw(void(void) throw());\n",
+ loc(functionType())));
+
+ Verifier.expectRange(1, 10, 1, 19);
+ std::vector<std::string> Args;
+ Args.push_back("-fms-extensions");
+ EXPECT_TRUE(Verifier.match("void f() throw(...);\n", loc(functionType()),
+ Args, Language::Lang_CXX));
+
+ Verifier.expectRange(1, 10, 1, 10);
+ EXPECT_TRUE(Verifier.match("void f() noexcept;\n", loc(functionType()),
+ Language::Lang_CXX11));
+
+ Verifier.expectRange(1, 10, 1, 24);
+ EXPECT_TRUE(Verifier.match("void f() noexcept(false);\n", loc(functionType()),
+ Language::Lang_CXX11));
+
+ Verifier.expectRange(1, 10, 1, 32);
+ EXPECT_TRUE(Verifier.match("void f() noexcept(noexcept(1+1));\n",
+ loc(functionType()), Language::Lang_CXX11));
+
+ ParmVarExceptionSpecRangeVerifier Verifier2;
+ Verifier2.expectRange(1, 25, 1, 31);
+ EXPECT_TRUE(Verifier2.match("void g(void (*fp)(void) throw());\n",
+ parmVarDecl(hasType(pointerType(pointee(
+ parenType(innerType(functionType()))))))));
+
+ Verifier2.expectRange(1, 25, 1, 38);
+ EXPECT_TRUE(Verifier2.match("void g(void (*fp)(void) noexcept(true));\n",
+ parmVarDecl(hasType(pointerType(pointee(
+ parenType(innerType(functionType())))))),
+ Language::Lang_CXX11));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/gnu/llvm/clang/unittests/AST/StmtPrinterTest.cpp b/gnu/llvm/clang/unittests/AST/StmtPrinterTest.cpp
new file mode 100644
index 00000000000..080c18b0737
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/StmtPrinterTest.cpp
@@ -0,0 +1,247 @@
+//===- unittests/AST/StmtPrinterTest.cpp --- Statement printer tests ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for Stmt::printPretty() and related methods.
+//
+// Search this file for WRONG to see test cases that are producing something
+// completely wrong, invalid C++ or just misleading.
+//
+// These tests have a coding convention:
+// * statements to be printed should be contained within a function named 'A'
+// unless it should have some special name (e.g., 'operator+');
+// * additional helper declarations are 'Z', 'Y', 'X' and so on.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTPrint.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace ast_matchers;
+using namespace tooling;
+
+namespace {
+
+enum class StdVer { CXX98, CXX11, CXX14, CXX17, CXX2a };
+
+DeclarationMatcher FunctionBodyMatcher(StringRef ContainingFunction) {
+ return functionDecl(hasName(ContainingFunction),
+ has(compoundStmt(has(stmt().bind("id")))));
+}
+
+template <typename T>
+::testing::AssertionResult
+PrintedStmtCXXMatches(StdVer Standard, StringRef Code, const T &NodeMatch,
+ StringRef ExpectedPrinted,
+ PolicyAdjusterType PolicyAdjuster = None) {
+ const char *StdOpt;
+ switch (Standard) {
+ case StdVer::CXX98: StdOpt = "-std=c++98"; break;
+ case StdVer::CXX11: StdOpt = "-std=c++11"; break;
+ case StdVer::CXX14: StdOpt = "-std=c++14"; break;
+ case StdVer::CXX17: StdOpt = "-std=c++17"; break;
+ case StdVer::CXX2a: StdOpt = "-std=c++2a"; break;
+ }
+
+ std::vector<std::string> Args = {
+ StdOpt,
+ "-Wno-unused-value",
+ };
+ return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
+ PolicyAdjuster);
+}
+
+template <typename T>
+::testing::AssertionResult
+PrintedStmtMSMatches(StringRef Code, const T &NodeMatch,
+ StringRef ExpectedPrinted,
+ PolicyAdjusterType PolicyAdjuster = None) {
+ std::vector<std::string> Args = {
+ "-std=c++98",
+ "-target", "i686-pc-win32",
+ "-fms-extensions",
+ "-Wno-unused-value",
+ };
+ return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
+ PolicyAdjuster);
+}
+
+template <typename T>
+::testing::AssertionResult
+PrintedStmtObjCMatches(StringRef Code, const T &NodeMatch,
+ StringRef ExpectedPrinted,
+ PolicyAdjusterType PolicyAdjuster = None) {
+ std::vector<std::string> Args = {
+ "-ObjC",
+ "-fobjc-runtime=macosx-10.12.0",
+ };
+ return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
+ PolicyAdjuster);
+}
+
+} // unnamed namespace
+
+TEST(StmtPrinter, TestIntegerLiteral) {
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98,
+ "void A() {"
+ " 1, -1, 1U, 1u,"
+ " 1L, 1l, -1L, 1UL, 1ul,"
+ " 1LL, -1LL, 1ULL;"
+ "}",
+ FunctionBodyMatcher("A"),
+ "1 , -1 , 1U , 1U , "
+ "1L , 1L , -1L , 1UL , 1UL , "
+ "1LL , -1LL , 1ULL"));
+ // Should be: with semicolon
+}
+
+TEST(StmtPrinter, TestMSIntegerLiteral) {
+ ASSERT_TRUE(PrintedStmtMSMatches(
+ "void A() {"
+ " 1i8, -1i8, 1ui8, "
+ " 1i16, -1i16, 1ui16, "
+ " 1i32, -1i32, 1ui32, "
+ " 1i64, -1i64, 1ui64;"
+ "}",
+ FunctionBodyMatcher("A"),
+ "1i8 , -1i8 , 1Ui8 , "
+ "1i16 , -1i16 , 1Ui16 , "
+ "1 , -1 , 1U , "
+ "1LL , -1LL , 1ULL"));
+ // Should be: with semicolon
+}
+
+TEST(StmtPrinter, TestFloatingPointLiteral) {
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98,
+ "void A() { 1.0f, -1.0f, 1.0, -1.0, 1.0l, -1.0l; }",
+ FunctionBodyMatcher("A"),
+ "1.F , -1.F , 1. , -1. , 1.L , -1.L"));
+ // Should be: with semicolon
+}
+
+TEST(StmtPrinter, TestCXXConversionDeclImplicit) {
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98,
+ "struct A {"
+ "operator void *();"
+ "A operator&(A);"
+ "};"
+ "void bar(void *);"
+ "void foo(A a, A b) {"
+ " bar(a & b);"
+ "}",
+ cxxMemberCallExpr(anything()).bind("id"),
+ "a & b"));
+}
+
+TEST(StmtPrinter, TestCXXConversionDeclExplicit) {
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
+ "struct A {"
+ "operator void *();"
+ "A operator&(A);"
+ "};"
+ "void bar(void *);"
+ "void foo(A a, A b) {"
+ " auto x = (a & b).operator void *();"
+ "}",
+ cxxMemberCallExpr(anything()).bind("id"),
+ "(a & b)"));
+ // WRONG; Should be: (a & b).operator void *()
+}
+
+TEST(StmtPrinter, TestCXXLamda) {
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
+ "void A() {"
+ " auto l = [] { };"
+ "}",
+ lambdaExpr(anything()).bind("id"),
+ "[] {\n"
+ "}"));
+
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
+ "void A() {"
+ " int a = 0, b = 1;"
+ " auto l = [a,b](int c, float d) { };"
+ "}",
+ lambdaExpr(anything()).bind("id"),
+ "[a, b](int c, float d) {\n"
+ "}"));
+
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14,
+ "void A() {"
+ " auto l = [](auto a, int b, auto c, int, auto) { };"
+ "}",
+ lambdaExpr(anything()).bind("id"),
+ "[](auto a, int b, auto c, int, auto) {\n"
+ "}"));
+
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX2a,
+ "void A() {"
+ " auto l = []<typename T1, class T2, int I,"
+ " template<class, typename> class T3>"
+ " (int a, auto, int, auto d) { };"
+ "}",
+ lambdaExpr(anything()).bind("id"),
+ "[]<typename T1, class T2, int I, template <class, typename> class T3>(int a, auto, int, auto d) {\n"
+ "}"));
+}
+
+TEST(StmtPrinter, TestNoImplicitBases) {
+ const char *CPPSource = R"(
+class A {
+ int field;
+ int member() { return field; }
+};
+)";
+ // No implicit 'this'.
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
+ CPPSource, memberExpr(anything()).bind("id"), "field",
+ PolicyAdjusterType(
+ [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; })));
+ // Print implicit 'this'.
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
+ CPPSource, memberExpr(anything()).bind("id"), "this->field"));
+
+ const char *ObjCSource = R"(
+@interface I {
+ int ivar;
+}
+@end
+@implementation I
+- (int) method {
+ return ivar;
+}
+@end
+ )";
+ // No implicit 'self'.
+ ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"),
+ "return ivar;\n",
+ PolicyAdjusterType([](PrintingPolicy &PP) {
+ PP.SuppressImplicitBase = true;
+ })));
+ // Print implicit 'self'.
+ ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"),
+ "return self->ivar;\n"));
+}
+
+TEST(StmtPrinter, TerseOutputWithLambdas) {
+ const char *CPPSource = "auto lamb = []{ return 0; };";
+
+ // body is printed when TerseOutput is off(default).
+ ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, CPPSource,
+ lambdaExpr(anything()).bind("id"),
+ "[] {\n return 0;\n}"));
+
+ // body not printed when TerseOutput is on.
+ ASSERT_TRUE(PrintedStmtCXXMatches(
+ StdVer::CXX11, CPPSource, lambdaExpr(anything()).bind("id"), "[] {}",
+ PolicyAdjusterType([](PrintingPolicy &PP) { PP.TerseOutput = true; })));
+}
diff --git a/gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp b/gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp
new file mode 100644
index 00000000000..8e467527ead
--- /dev/null
+++ b/gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp
@@ -0,0 +1,1400 @@
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/AST/ASTStructuralEquivalence.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Tooling.h"
+
+#include "Language.h"
+#include "DeclMatcher.h"
+
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using std::get;
+
+struct StructuralEquivalenceTest : ::testing::Test {
+ std::unique_ptr<ASTUnit> AST0, AST1;
+ std::string Code0, Code1; // Buffers for SourceManager
+
+ // Get a pair of node pointers into the synthesized AST from the given code
+ // snippets. To determine the returned node, a separate matcher is specified
+ // for both snippets. The first matching node is returned.
+ template <typename NodeType, typename MatcherType>
+ std::tuple<NodeType *, NodeType *> makeDecls(
+ const std::string &SrcCode0, const std::string &SrcCode1, Language Lang,
+ const MatcherType &Matcher0, const MatcherType &Matcher1) {
+ this->Code0 = SrcCode0;
+ this->Code1 = SrcCode1;
+ ArgVector Args = getBasicRunOptionsForLanguage(Lang);
+
+ const char *const InputFileName = "input.cc";
+
+ AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName);
+ AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName);
+
+ NodeType *D0 = FirstDeclMatcher<NodeType>().match(
+ AST0->getASTContext().getTranslationUnitDecl(), Matcher0);
+ NodeType *D1 = FirstDeclMatcher<NodeType>().match(
+ AST1->getASTContext().getTranslationUnitDecl(), Matcher1);
+
+ return std::make_tuple(D0, D1);
+ }
+
+ std::tuple<TranslationUnitDecl *, TranslationUnitDecl *> makeTuDecls(
+ const std::string &SrcCode0, const std::string &SrcCode1, Language Lang) {
+ this->Code0 = SrcCode0;
+ this->Code1 = SrcCode1;
+ ArgVector Args = getBasicRunOptionsForLanguage(Lang);
+
+ const char *const InputFileName = "input.cc";
+
+ AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName);
+ AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName);
+
+ return std::make_tuple(AST0->getASTContext().getTranslationUnitDecl(),
+ AST1->getASTContext().getTranslationUnitDecl());
+ }
+
+ // Get a pair of node pointers into the synthesized AST from the given code
+ // snippets. The same matcher is used for both snippets.
+ template <typename NodeType, typename MatcherType>
+ std::tuple<NodeType *, NodeType *> makeDecls(
+ const std::string &SrcCode0, const std::string &SrcCode1, Language Lang,
+ const MatcherType &AMatcher) {
+ return makeDecls<NodeType, MatcherType>(
+ SrcCode0, SrcCode1, Lang, AMatcher, AMatcher);
+ }
+
+ // Get a pair of Decl pointers to the synthesized declarations from the given
+ // code snippets. We search for the first NamedDecl with given name in both
+ // snippets.
+ std::tuple<NamedDecl *, NamedDecl *> makeNamedDecls(
+ const std::string &SrcCode0, const std::string &SrcCode1,
+ Language Lang, const char *const Identifier = "foo") {
+ auto Matcher = namedDecl(hasName(Identifier));
+ return makeDecls<NamedDecl>(SrcCode0, SrcCode1, Lang, Matcher);
+ }
+
+ bool testStructuralMatch(Decl *D0, Decl *D1) {
+ llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01;
+ llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10;
+ StructuralEquivalenceContext Ctx01(
+ D0->getASTContext(), D1->getASTContext(),
+ NonEquivalentDecls01, StructuralEquivalenceKind::Default, false, false);
+ StructuralEquivalenceContext Ctx10(
+ D1->getASTContext(), D0->getASTContext(),
+ NonEquivalentDecls10, StructuralEquivalenceKind::Default, false, false);
+ bool Eq01 = Ctx01.IsEquivalent(D0, D1);
+ bool Eq10 = Ctx10.IsEquivalent(D1, D0);
+ EXPECT_EQ(Eq01, Eq10);
+ return Eq01;
+ }
+
+ bool testStructuralMatch(std::tuple<Decl *, Decl *> t) {
+ return testStructuralMatch(get<0>(t), get<1>(t));
+ }
+};
+
+TEST_F(StructuralEquivalenceTest, Int) {
+ auto Decls = makeNamedDecls("int foo;", "int foo;", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, IntVsSignedInt) {
+ auto Decls = makeNamedDecls("int foo;", "signed int foo;", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, Char) {
+ auto Decls = makeNamedDecls("char foo;", "char foo;", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+// This test is disabled for now.
+// FIXME Whether this is equivalent is dependendant on the target.
+TEST_F(StructuralEquivalenceTest, DISABLED_CharVsSignedChar) {
+ auto Decls = makeNamedDecls("char foo;", "signed char foo;", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, ForwardRecordDecl) {
+ auto Decls = makeNamedDecls("struct foo;", "struct foo;", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, IntVsSignedIntInStruct) {
+ auto Decls = makeNamedDecls("struct foo { int x; };",
+ "struct foo { signed int x; };", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, CharVsSignedCharInStruct) {
+ auto Decls = makeNamedDecls("struct foo { char x; };",
+ "struct foo { signed char x; };", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) {
+ auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(template <class T> struct foo; template<> struct foo<int>{};)",
+ R"(template <class T> struct foo; template<> struct foo<signed int>{};)",
+ Lang_CXX,
+ classTemplateSpecializationDecl());
+ auto Spec0 = get<0>(Decls);
+ auto Spec1 = get<1>(Decls);
+ EXPECT_TRUE(testStructuralMatch(Spec0, Spec1));
+}
+
+TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) {
+ auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(template <class T> struct foo; template<> struct foo<char>{};)",
+ R"(template <class T> struct foo; template<> struct foo<signed char>{};)",
+ Lang_CXX,
+ classTemplateSpecializationDecl());
+ auto Spec0 = get<0>(Decls);
+ auto Spec1 = get<1>(Decls);
+ EXPECT_FALSE(testStructuralMatch(Spec0, Spec1));
+}
+
+TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) {
+ auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+ struct true_type{};
+ template <class T> struct foo;
+ template<> struct foo<char> : true_type {};
+ )",
+ R"(
+ struct true_type{};
+ template <class T> struct foo;
+ template<> struct foo<signed char> : true_type {};
+ )",
+ Lang_CXX,
+ classTemplateSpecializationDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+// This test is disabled for now.
+// FIXME Enable it, once the check is implemented.
+TEST_F(StructuralEquivalenceTest, DISABLED_WrongOrderInNamespace) {
+ auto Code =
+ R"(
+ namespace NS {
+ template <class T> class Base {
+ int a;
+ };
+ class Derived : Base<Derived> {
+ };
+ }
+ void foo(NS::Derived &);
+ )";
+ auto Decls = makeNamedDecls(Code, Code, Lang_CXX);
+
+ NamespaceDecl *NS =
+ LastDeclMatcher<NamespaceDecl>().match(get<1>(Decls), namespaceDecl());
+ ClassTemplateDecl *TD = LastDeclMatcher<ClassTemplateDecl>().match(
+ get<1>(Decls), classTemplateDecl(hasName("Base")));
+
+ // Reorder the decls, move the TD to the last place in the DC.
+ NS->removeDecl(TD);
+ NS->addDeclInternal(TD);
+
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, WrongOrderOfFieldsInClass) {
+ auto Code = "class X { int a; int b; };";
+ auto Decls = makeNamedDecls(Code, Code, Lang_CXX, "X");
+
+ CXXRecordDecl *RD = FirstDeclMatcher<CXXRecordDecl>().match(
+ get<1>(Decls), cxxRecordDecl(hasName("X")));
+ FieldDecl *FD =
+ FirstDeclMatcher<FieldDecl>().match(get<1>(Decls), fieldDecl(hasName("a")));
+
+ // Reorder the FieldDecls
+ RD->removeDecl(FD);
+ RD->addDeclInternal(FD);
+
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+struct StructuralEquivalenceFunctionTest : StructuralEquivalenceTest {
+};
+
+TEST_F(StructuralEquivalenceFunctionTest, TemplateVsNonTemplate) {
+ auto t = makeNamedDecls(
+ "void foo();",
+ "template<class T> void foo();",
+ Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, DifferentOperators) {
+ auto t = makeDecls<FunctionDecl>(
+ "struct X{}; bool operator<(X, X);",
+ "struct X{}; bool operator==(X, X);", Lang_CXX,
+ functionDecl(hasOverloadedOperatorName("<")),
+ functionDecl(hasOverloadedOperatorName("==")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, SameOperators) {
+ auto t = makeDecls<FunctionDecl>(
+ "struct X{}; bool operator<(X, X);",
+ "struct X{}; bool operator<(X, X);", Lang_CXX,
+ functionDecl(hasOverloadedOperatorName("<")),
+ functionDecl(hasOverloadedOperatorName("<")));
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, CtorVsDtor) {
+ auto t = makeDecls<FunctionDecl>(
+ "struct X{ X(); };",
+ "struct X{ ~X(); };", Lang_CXX,
+ cxxConstructorDecl(),
+ cxxDestructorDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ParamConstWithRef) {
+ auto t = makeNamedDecls("void foo(int&);",
+ "void foo(const int&);", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ParamConstSimple) {
+ auto t = makeNamedDecls("void foo(int);",
+ "void foo(const int);", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(t));
+ // consider this OK
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, Throw) {
+ auto t = makeNamedDecls("void foo();",
+ "void foo() throw();", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, Noexcept) {
+ auto t = makeNamedDecls("void foo();",
+ "void foo() noexcept;", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexcept) {
+ auto t = makeNamedDecls("void foo() throw();",
+ "void foo() noexcept;", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptFalse) {
+ auto t = makeNamedDecls("void foo() throw();",
+ "void foo() noexcept(false);", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptTrue) {
+ auto t = makeNamedDecls("void foo() throw();",
+ "void foo() noexcept(true);", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, NoexceptNonMatch) {
+ auto t = makeNamedDecls("void foo() noexcept(false);",
+ "void foo() noexcept(true);", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, NoexceptMatch) {
+ auto t = makeNamedDecls("void foo() noexcept(false);",
+ "void foo() noexcept(false);", Lang_CXX11);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptFalse) {
+ auto t = makeNamedDecls("void foo() noexcept;",
+ "void foo() noexcept(false);", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptTrue) {
+ auto t = makeNamedDecls("void foo() noexcept;",
+ "void foo() noexcept(true);", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ReturnType) {
+ auto t = makeNamedDecls("char foo();",
+ "int foo();", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ReturnConst) {
+ auto t = makeNamedDecls("char foo();",
+ "const char foo();", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ReturnRef) {
+ auto t = makeNamedDecls("char &foo();",
+ "char &&foo();", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ParamCount) {
+ auto t = makeNamedDecls("void foo(int);",
+ "void foo(int, int);", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ParamType) {
+ auto t = makeNamedDecls("void foo(int);",
+ "void foo(char);", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ParamName) {
+ auto t = makeNamedDecls("void foo(int a);",
+ "void foo(int b);", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, Variadic) {
+ auto t = makeNamedDecls("void foo(int x...);",
+ "void foo(int x);", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, ParamPtr) {
+ auto t = makeNamedDecls("void foo(int *);",
+ "void foo(int);", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, NameInParen) {
+ auto t = makeNamedDecls(
+ "void ((foo))();",
+ "void foo();",
+ Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithExceptionSpec) {
+ auto t = makeNamedDecls(
+ "void (foo)() throw(int);",
+ "void (foo)() noexcept;",
+ Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithConst) {
+ auto t = makeNamedDecls(
+ "struct A { void (foo)() const; };",
+ "struct A { void (foo)(); };",
+ Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentNoreturnAttr) {
+ auto t = makeNamedDecls(
+ "__attribute__((noreturn)) void foo();",
+ " void foo();",
+ Lang_C);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest,
+ FunctionsWithDifferentCallingConventions) {
+ // These attributes may not be available on certain platforms.
+ if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() !=
+ llvm::Triple::x86_64)
+ return;
+ auto t = makeNamedDecls(
+ "__attribute__((preserve_all)) void foo();",
+ "__attribute__((ms_abi)) void foo();",
+ Lang_C);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentSavedRegsAttr) {
+ if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() !=
+ llvm::Triple::x86_64)
+ return;
+ auto t = makeNamedDecls(
+ "__attribute__((no_caller_saved_registers)) void foo();",
+ " void foo();",
+ Lang_C);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+struct StructuralEquivalenceCXXMethodTest : StructuralEquivalenceTest {
+};
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Virtual) {
+ auto t = makeDecls<CXXMethodDecl>(
+ "struct X { void foo(); };",
+ "struct X { virtual void foo(); };", Lang_CXX,
+ cxxMethodDecl(hasName("foo")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Pure) {
+ auto t = makeNamedDecls("struct X { virtual void foo(); };",
+ "struct X { virtual void foo() = 0; };", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, DISABLED_Final) {
+ // The final-ness is not checked yet.
+ auto t = makeNamedDecls("struct X { virtual void foo(); };",
+ "struct X { virtual void foo() final; };", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Const) {
+ auto t = makeNamedDecls("struct X { void foo(); };",
+ "struct X { void foo() const; };", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Static) {
+ auto t = makeNamedDecls("struct X { void foo(); };",
+ "struct X { static void foo(); };", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Ref1) {
+ auto t = makeNamedDecls("struct X { void foo(); };",
+ "struct X { void foo() &&; };", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Ref2) {
+ auto t = makeNamedDecls("struct X { void foo() &; };",
+ "struct X { void foo() &&; };", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, AccessSpecifier) {
+ auto t = makeDecls<CXXMethodDecl>(
+ "struct X { public: void foo(); };",
+ "struct X { private: void foo(); };", Lang_CXX,
+ cxxMethodDecl(hasName("foo")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Delete) {
+ auto t = makeNamedDecls("struct X { void foo(); };",
+ "struct X { void foo() = delete; };", Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Constructor) {
+ auto t = makeDecls<FunctionDecl>(
+ "void foo();", "struct foo { foo(); };", Lang_CXX,
+ functionDecl(hasName("foo")), cxxConstructorDecl(hasName("foo")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorParam) {
+ auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
+ "struct X { X(int); };", Lang_CXX,
+ cxxConstructorDecl(hasName("X")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorExplicit) {
+ auto t = makeDecls<CXXConstructorDecl>("struct X { X(int); };",
+ "struct X { explicit X(int); };",
+ Lang_CXX11,
+ cxxConstructorDecl(hasName("X")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorDefault) {
+ auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
+ "struct X { X() = default; };",
+ Lang_CXX11,
+ cxxConstructorDecl(hasName("X")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Conversion) {
+ auto t = makeDecls<CXXConversionDecl>("struct X { operator bool(); };",
+ "struct X { operator char(); };",
+ Lang_CXX11,
+ cxxConversionDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, Operator) {
+ auto t = makeDecls<FunctionDecl>(
+ "struct X { int operator +(int); };",
+ "struct X { int operator -(int); };", Lang_CXX,
+ functionDecl(hasOverloadedOperatorName("+")),
+ functionDecl(hasOverloadedOperatorName("-")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass1) {
+ auto t = makeDecls<FunctionDecl>(
+ "struct X { virtual void f(); }; void X::f() { }",
+ "struct X { virtual void f() { }; };",
+ Lang_CXX,
+ functionDecl(allOf(hasName("f"), isDefinition())));
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass2) {
+ auto t = makeDecls<FunctionDecl>(
+ "struct X { virtual void f(); }; void X::f() { }",
+ "struct X { void f(); }; void X::f() { }",
+ Lang_CXX,
+ functionDecl(allOf(hasName("f"), isDefinition())));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+struct StructuralEquivalenceRecordTest : StructuralEquivalenceTest {
+ // FIXME Use a common getRecordDecl with ASTImporterTest.cpp!
+ RecordDecl *getRecordDecl(FieldDecl *FD) {
+ auto *ET = cast<ElaboratedType>(FD->getType().getTypePtr());
+ return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl();
+ };
+};
+
+TEST_F(StructuralEquivalenceRecordTest, Name) {
+ auto t = makeDecls<CXXRecordDecl>(
+ "struct A{ };",
+ "struct B{ };",
+ Lang_CXX,
+ cxxRecordDecl(hasName("A")),
+ cxxRecordDecl(hasName("B")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, Fields) {
+ auto t = makeNamedDecls(
+ "struct foo{ int x; };",
+ "struct foo{ char x; };",
+ Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, DISABLED_Methods) {
+ // Currently, methods of a class are not checked at class equivalence.
+ auto t = makeNamedDecls(
+ "struct foo{ int x(); };",
+ "struct foo{ char x(); };",
+ Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, Bases) {
+ auto t = makeNamedDecls(
+ "struct A{ }; struct foo: A { };",
+ "struct B{ }; struct foo: B { };",
+ Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, InheritanceVirtual) {
+ auto t = makeNamedDecls(
+ "struct A{ }; struct foo: A { };",
+ "struct A{ }; struct foo: virtual A { };",
+ Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, DISABLED_InheritanceType) {
+ // Access specifier in inheritance is not checked yet.
+ auto t = makeNamedDecls(
+ "struct A{ }; struct foo: public A { };",
+ "struct A{ }; struct foo: private A { };",
+ Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, Match) {
+ auto Code = R"(
+ struct A{ };
+ struct B{ };
+ struct foo: A, virtual B {
+ void x();
+ int a;
+ };
+ )";
+ auto t = makeNamedDecls(Code, Code, Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) {
+ auto t = makeTuDecls(
+ R"(
+ struct A {
+ struct {
+ struct A *next;
+ } entry0;
+ struct {
+ struct A *next;
+ } entry1;
+ };
+ )",
+ "", Lang_C);
+ auto *TU = get<0>(t);
+ auto *Entry0 =
+ FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry0")));
+ auto *Entry1 =
+ FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry1")));
+ auto *R0 = getRecordDecl(Entry0);
+ auto *R1 = getRecordDecl(Entry1);
+
+ ASSERT_NE(R0, R1);
+ EXPECT_TRUE(testStructuralMatch(R0, R0));
+ EXPECT_TRUE(testStructuralMatch(R1, R1));
+ EXPECT_FALSE(testStructuralMatch(R0, R1));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, AnonymousRecordsShouldBeInequivalent) {
+ auto t = makeTuDecls(
+ R"(
+ struct X {
+ struct {
+ int a;
+ };
+ struct {
+ int b;
+ };
+ };
+ )",
+ "", Lang_C);
+ auto *TU = get<0>(t);
+ auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU, indirectFieldDecl(hasName("a")));
+ auto *FA = cast<FieldDecl>(A->chain().front());
+ RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
+ auto *B = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU, indirectFieldDecl(hasName("b")));
+ auto *FB = cast<FieldDecl>(B->chain().front());
+ RecordDecl *RB = cast<RecordType>(FB->getType().getTypePtr())->getDecl();
+
+ ASSERT_NE(RA, RB);
+ EXPECT_TRUE(testStructuralMatch(RA, RA));
+ EXPECT_TRUE(testStructuralMatch(RB, RB));
+ EXPECT_FALSE(testStructuralMatch(RA, RB));
+}
+
+TEST_F(StructuralEquivalenceRecordTest,
+ RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) {
+ auto t = makeTuDecls(
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )",
+ R"(
+ struct X { // The order is reversed.
+ struct { int b; };
+ struct { int a; };
+ };
+ )",
+ Lang_C);
+
+ auto *TU = get<0>(t);
+ auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU, indirectFieldDecl(hasName("a")));
+ auto *FA = cast<FieldDecl>(A->chain().front());
+ RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
+
+ auto *TU1 = get<1>(t);
+ auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU1, indirectFieldDecl(hasName("a")));
+ auto *FA1 = cast<FieldDecl>(A1->chain().front());
+ RecordDecl *RA1 = cast<RecordType>(FA1->getType().getTypePtr())->getDecl();
+
+ RecordDecl *X =
+ FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X")));
+ RecordDecl *X1 =
+ FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X")));
+ ASSERT_NE(X, X1);
+ EXPECT_FALSE(testStructuralMatch(X, X1));
+
+ ASSERT_NE(RA, RA1);
+ EXPECT_TRUE(testStructuralMatch(RA, RA));
+ EXPECT_TRUE(testStructuralMatch(RA1, RA1));
+ EXPECT_FALSE(testStructuralMatch(RA1, RA));
+}
+
+TEST_F(StructuralEquivalenceRecordTest,
+ UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) {
+ auto Code =
+ R"(
+ struct A {
+ struct {
+ struct A *next;
+ } entry0;
+ struct {
+ struct A *next;
+ } entry1;
+ };
+ )";
+ auto t = makeTuDecls(Code, Code, Lang_C);
+
+ auto *FromTU = get<0>(t);
+ auto *Entry1 =
+ FirstDeclMatcher<FieldDecl>().match(FromTU, fieldDecl(hasName("entry1")));
+
+ auto *ToTU = get<1>(t);
+ auto *Entry0 =
+ FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0")));
+ auto *A =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+ A->startDefinition(); // Set isBeingDefined, getDefinition() will return a
+ // nullptr. This may be the case during ASTImport.
+
+ auto *R0 = getRecordDecl(Entry0);
+ auto *R1 = getRecordDecl(Entry1);
+
+ ASSERT_NE(R0, R1);
+ EXPECT_TRUE(testStructuralMatch(R0, R0));
+ EXPECT_TRUE(testStructuralMatch(R1, R1));
+ EXPECT_FALSE(testStructuralMatch(R0, R1));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, TemplateVsNonTemplate) {
+ auto t = makeDecls<CXXRecordDecl>(
+ "struct A { };",
+ "template<class T> struct A { };",
+ Lang_CXX,
+ cxxRecordDecl(hasName("A")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest,
+ FwdDeclRecordShouldBeEqualWithFwdDeclRecord) {
+ auto t = makeNamedDecls("class foo;", "class foo;", Lang_CXX11);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest,
+ FwdDeclRecordShouldBeEqualWithRecordWhichHasDefinition) {
+ auto t =
+ makeNamedDecls("class foo;", "class foo { int A; };", Lang_CXX11);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest,
+ RecordShouldBeEqualWithRecordWhichHasDefinition) {
+ auto t = makeNamedDecls("class foo { int A; };", "class foo { int A; };",
+ Lang_CXX11);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceRecordTest, RecordsWithDifferentBody) {
+ auto t = makeNamedDecls("class foo { int B; };", "class foo { int A; };",
+ Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+struct StructuralEquivalenceLambdaTest : StructuralEquivalenceTest {};
+
+TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentMethods) {
+ // Get the LambdaExprs, unfortunately we can't match directly the underlying
+ // implicit CXXRecordDecl of the Lambda classes.
+ auto t = makeDecls<LambdaExpr>(
+ "void f() { auto L0 = [](int){}; }",
+ "void f() { auto L1 = [](){}; }",
+ Lang_CXX11,
+ lambdaExpr(),
+ lambdaExpr());
+ CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
+ CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
+ EXPECT_FALSE(testStructuralMatch(L0, L1));
+}
+
+TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithEqMethods) {
+ auto t = makeDecls<LambdaExpr>(
+ "void f() { auto L0 = [](int){}; }",
+ "void f() { auto L1 = [](int){}; }",
+ Lang_CXX11,
+ lambdaExpr(),
+ lambdaExpr());
+ CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
+ CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
+ EXPECT_TRUE(testStructuralMatch(L0, L1));
+}
+
+TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentFields) {
+ auto t = makeDecls<LambdaExpr>(
+ "void f() { char* X; auto L0 = [X](){}; }",
+ "void f() { float X; auto L1 = [X](){}; }",
+ Lang_CXX11,
+ lambdaExpr(),
+ lambdaExpr());
+ CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
+ CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
+ EXPECT_FALSE(testStructuralMatch(L0, L1));
+}
+
+TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithEqFields) {
+ auto t = makeDecls<LambdaExpr>(
+ "void f() { float X; auto L0 = [X](){}; }",
+ "void f() { float X; auto L1 = [X](){}; }",
+ Lang_CXX11,
+ lambdaExpr(),
+ lambdaExpr());
+ CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
+ CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
+ EXPECT_TRUE(testStructuralMatch(L0, L1));
+}
+
+TEST_F(StructuralEquivalenceTest, CompareSameDeclWithMultiple) {
+ auto t = makeNamedDecls(
+ "struct A{ }; struct B{ }; void foo(A a, A b);",
+ "struct A{ }; struct B{ }; void foo(A a, B b);",
+ Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceTest, ExplicitBoolDifferent) {
+ auto Decls = makeNamedDecls("struct foo {explicit(false) foo(int);};",
+ "struct foo {explicit(true) foo(int);};", Lang_CXX2a);
+ CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<0>(Decls), cxxConstructorDecl(hasName("foo")));
+ CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<1>(Decls), cxxConstructorDecl(hasName("foo")));
+ EXPECT_FALSE(testStructuralMatch(First, Second));
+}
+
+TEST_F(StructuralEquivalenceTest, ExplicitBoolSame) {
+ auto Decls = makeNamedDecls("struct foo {explicit(true) foo(int);};",
+ "struct foo {explicit(true) foo(int);};", Lang_CXX2a);
+ CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<0>(Decls), cxxConstructorDecl(hasName("foo")));
+ CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<1>(Decls), cxxConstructorDecl(hasName("foo")));
+ EXPECT_TRUE(testStructuralMatch(First, Second));
+}
+
+struct StructuralEquivalenceEnumTest : StructuralEquivalenceTest {};
+
+TEST_F(StructuralEquivalenceEnumTest, FwdDeclEnumShouldBeEqualWithFwdDeclEnum) {
+ auto t = makeNamedDecls("enum class foo;", "enum class foo;", Lang_CXX11);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceEnumTest,
+ FwdDeclEnumShouldBeEqualWithEnumWhichHasDefinition) {
+ auto t =
+ makeNamedDecls("enum class foo;", "enum class foo { A };", Lang_CXX11);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceEnumTest,
+ EnumShouldBeEqualWithEnumWhichHasDefinition) {
+ auto t = makeNamedDecls("enum class foo { A };", "enum class foo { A };",
+ Lang_CXX11);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceEnumTest, EnumsWithDifferentBody) {
+ auto t = makeNamedDecls("enum class foo { B };", "enum class foo { A };",
+ Lang_CXX11);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+struct StructuralEquivalenceTemplateTest : StructuralEquivalenceTest {};
+
+TEST_F(StructuralEquivalenceTemplateTest, ExactlySameTemplates) {
+ auto t = makeNamedDecls("template <class T> struct foo;",
+ "template <class T> struct foo;", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgName) {
+ auto t = makeNamedDecls("template <class T> struct foo;",
+ "template <class U> struct foo;", Lang_CXX);
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgKind) {
+ auto t = makeNamedDecls("template <class T> struct foo;",
+ "template <int T> struct foo;", Lang_CXX);
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceTemplateTest, ExplicitBoolSame) {
+ auto Decls = makeNamedDecls("template <bool b> struct foo {explicit(b) foo(int);};",
+ "template <bool b> struct foo {explicit(b) foo(int);};", Lang_CXX2a);
+ CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<0>(Decls), cxxConstructorDecl(hasName("foo<b>")));
+ CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<1>(Decls), cxxConstructorDecl(hasName("foo<b>")));
+ EXPECT_TRUE(testStructuralMatch(First, Second));
+}
+
+TEST_F(StructuralEquivalenceTemplateTest, ExplicitBoolDifference) {
+ auto Decls = makeNamedDecls("template <bool b> struct foo {explicit(b) foo(int);};",
+ "template <bool b> struct foo {explicit(!b) foo(int);};", Lang_CXX2a);
+ CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<0>(Decls), cxxConstructorDecl(hasName("foo<b>")));
+ CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
+ get<1>(Decls), cxxConstructorDecl(hasName("foo<b>")));
+ EXPECT_FALSE(testStructuralMatch(First, Second));
+}
+
+TEST_F(StructuralEquivalenceTemplateTest,
+ TemplateVsSubstTemplateTemplateParmInArgEq) {
+ auto t = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+template <typename P1> class Arg { };
+template <template <typename PP1> class P1> class Primary { };
+
+void f() {
+ // Make specialization with simple template.
+ Primary <Arg> A;
+}
+ )",
+ R"(
+template <typename P1> class Arg { };
+template <template <typename PP1> class P1> class Primary { };
+
+template <template <typename PP1> class P1> class Templ {
+ void f() {
+ // Make specialization with substituted template template param.
+ Primary <P1> A;
+ };
+};
+
+// Instantiate with substitution Arg into P1.
+template class Templ <Arg>;
+ )",
+ Lang_CXX, classTemplateSpecializationDecl(hasName("Primary")));
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceTemplateTest,
+ TemplateVsSubstTemplateTemplateParmInArgNotEq) {
+ auto t = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+template <typename P1> class Arg { };
+template <template <typename PP1> class P1> class Primary { };
+
+void f() {
+ // Make specialization with simple template.
+ Primary <Arg> A;
+}
+ )",
+ R"(
+// Arg is different from the other, this should cause non-equivalence.
+template <typename P1> class Arg { int X; };
+template <template <typename PP1> class P1> class Primary { };
+
+template <template <typename PP1> class P1> class Templ {
+ void f() {
+ // Make specialization with substituted template template param.
+ Primary <P1> A;
+ };
+};
+
+// Instantiate with substitution Arg into P1.
+template class Templ <Arg>;
+ )",
+ Lang_CXX, classTemplateSpecializationDecl(hasName("Primary")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+struct StructuralEquivalenceDependentTemplateArgsTest
+ : StructuralEquivalenceTemplateTest {};
+
+TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
+ SameStructsInDependentArgs) {
+ std::string Code =
+ R"(
+ template <typename>
+ struct S1;
+
+ template <typename>
+ struct enable_if;
+
+ struct S
+ {
+ template <typename T, typename enable_if<S1<T>>::type>
+ void f();
+ };
+ )";
+ auto t = makeDecls<FunctionTemplateDecl>(Code, Code, Lang_CXX11,
+ functionTemplateDecl(hasName("f")));
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
+ DifferentStructsInDependentArgs) {
+ std::string Code =
+ R"(
+ template <typename>
+ struct S1;
+
+ template <typename>
+ struct S2;
+
+ template <typename>
+ struct enable_if;
+ )";
+ auto t = makeDecls<FunctionTemplateDecl>(Code + R"(
+ struct S
+ {
+ template <typename T, typename enable_if<S1<T>>::type>
+ void f();
+ };
+ )",
+ Code + R"(
+ struct S
+ {
+ template <typename T, typename enable_if<S2<T>>::type>
+ void f();
+ };
+ )",
+ Lang_CXX11,
+ functionTemplateDecl(hasName("f")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
+ SameStructsInDependentScopeDeclRefExpr) {
+ std::string Code =
+ R"(
+ template <typename>
+ struct S1;
+
+ template <bool>
+ struct enable_if;
+
+ struct S
+ {
+ template <typename T, typename enable_if<S1<T>::value>::type>
+ void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^
+ };
+ )";
+ auto t = makeDecls<FunctionTemplateDecl>(Code, Code, Lang_CXX11,
+ functionTemplateDecl(hasName("f")));
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
+ DifferentStructsInDependentScopeDeclRefExpr) {
+ std::string Code =
+ R"(
+ template <typename>
+ struct S1;
+
+ template <typename>
+ struct S2;
+
+ template <bool>
+ struct enable_if;
+ )";
+ auto t = makeDecls<FunctionTemplateDecl>(Code + R"(
+ struct S
+ {
+ template <typename T, typename enable_if<S1<T>::value>::type>
+ void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^
+ };
+ )",
+ Code + R"(
+ struct S
+ {
+ template <typename T, typename enable_if<S2<T>::value>::type>
+ void f();
+ };
+ )",
+ Lang_CXX,
+ functionTemplateDecl(hasName("f")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
+ DifferentValueInDependentScopeDeclRefExpr) {
+ std::string Code =
+ R"(
+ template <typename>
+ struct S1;
+
+ template <bool>
+ struct enable_if;
+ )";
+ auto t = makeDecls<FunctionTemplateDecl>(Code + R"(
+ struct S
+ {
+ template <typename T, typename enable_if<S1<T>::value1>::type>
+ void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^
+ };
+ )",
+ Code + R"(
+ struct S
+ {
+ template <typename T, typename enable_if<S1<T>::value2>::type>
+ void f();
+ };
+ )",
+ Lang_CXX,
+ functionTemplateDecl(hasName("f")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(
+ StructuralEquivalenceTemplateTest,
+ ClassTemplSpecWithQualifiedAndNonQualifiedTypeArgsShouldBeEqual) {
+ auto t = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+ template <class T> struct Primary {};
+ namespace N {
+ struct Arg;
+ }
+ // Explicit instantiation with qualified name.
+ template struct Primary<N::Arg>;
+ )",
+ R"(
+ template <class T> struct Primary {};
+ namespace N {
+ struct Arg;
+ }
+ using namespace N;
+ // Explicit instantiation with UNqualified name.
+ template struct Primary<Arg>;
+ )",
+ Lang_CXX,
+ classTemplateSpecializationDecl(hasName("Primary")));
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(
+ StructuralEquivalenceTemplateTest,
+ ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTypeArgs) {
+ auto t = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+ template <class T> struct Primary {};
+ namespace N {
+ struct Arg { int a; };
+ }
+ // Explicit instantiation with qualified name.
+ template struct Primary<N::Arg>;
+ )",
+ R"(
+ template <class T> struct Primary {};
+ namespace N {
+ // This struct is not equivalent with the other in the prev TU.
+ struct Arg { double b; }; // -- Field mismatch.
+ }
+ using namespace N;
+ // Explicit instantiation with UNqualified name.
+ template struct Primary<Arg>;
+ )",
+ Lang_CXX,
+ classTemplateSpecializationDecl(hasName("Primary")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(
+ StructuralEquivalenceTemplateTest,
+ ClassTemplSpecWithQualifiedAndNonQualifiedTemplArgsShouldBeEqual) {
+ auto t = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+ template <template <class> class T> struct Primary {};
+ namespace N {
+ template <class T> struct Arg;
+ }
+ // Explicit instantiation with qualified name.
+ template struct Primary<N::Arg>;
+ )",
+ R"(
+ template <template <class> class T> struct Primary {};
+ namespace N {
+ template <class T> struct Arg;
+ }
+ using namespace N;
+ // Explicit instantiation with UNqualified name.
+ template struct Primary<Arg>;
+ )",
+ Lang_CXX,
+ classTemplateSpecializationDecl(hasName("Primary")));
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(
+ StructuralEquivalenceTemplateTest,
+ ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTemplArgs) {
+ auto t = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+ template <template <class> class T> struct Primary {};
+ namespace N {
+ template <class T> struct Arg { int a; };
+ }
+ // Explicit instantiation with qualified name.
+ template struct Primary<N::Arg>;
+ )",
+ R"(
+ template <template <class> class T> struct Primary {};
+ namespace N {
+ // This template is not equivalent with the other in the prev TU.
+ template <class T> struct Arg { double b; }; // -- Field mismatch.
+ }
+ using namespace N;
+ // Explicit instantiation with UNqualified name.
+ template struct Primary<Arg>;
+ )",
+ Lang_CXX,
+ classTemplateSpecializationDecl(hasName("Primary")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(
+ StructuralEquivalenceTemplateTest,
+ ClassTemplSpecWithInequivalentShadowedTemplArg) {
+ auto t = makeDecls<ClassTemplateSpecializationDecl>(
+ R"(
+ template <template <class> class T> struct Primary {};
+ template <class T> struct Arg { int a; };
+ // Explicit instantiation with ::Arg
+ template struct Primary<Arg>;
+ )",
+ R"(
+ template <template <class> class T> struct Primary {};
+ template <class T> struct Arg { int a; };
+ namespace N {
+ // This template is not equivalent with the other in the global scope.
+ template <class T> struct Arg { double b; }; // -- Field mismatch.
+ // Explicit instantiation with N::Arg which shadows ::Arg
+ template struct Primary<Arg>;
+ }
+ )",
+ Lang_CXX,
+ classTemplateSpecializationDecl(hasName("Primary")));
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
+ llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
+
+ template <typename NodeType, typename MatcherType>
+ std::pair<NodeType *, NodeType *>
+ findDeclPair(std::tuple<TranslationUnitDecl *, TranslationUnitDecl *> TU,
+ MatcherType M) {
+ NodeType *D0 = FirstDeclMatcher<NodeType>().match(get<0>(TU), M);
+ NodeType *D1 = FirstDeclMatcher<NodeType>().match(get<1>(TU), M);
+ return {D0, D1};
+ }
+
+ template <typename NodeType>
+ bool isInNonEqCache(std::pair<NodeType *, NodeType *> D) {
+ return NonEquivalentDecls.count(D) > 0;
+ }
+};
+
+TEST_F(StructuralEquivalenceCacheTest, SimpleNonEq) {
+ auto TU = makeTuDecls(
+ R"(
+ class A {};
+ class B {};
+ void x(A, A);
+ )",
+ R"(
+ class A {};
+ class B {};
+ void x(A, B);
+ )",
+ Lang_CXX);
+
+ StructuralEquivalenceContext Ctx(
+ get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(),
+ NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false);
+
+ auto X = findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")));
+ EXPECT_FALSE(Ctx.IsEquivalent(X.first, X.second));
+
+ EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
+ TU, cxxRecordDecl(hasName("A"), unless(isImplicit())))));
+ EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
+ TU, cxxRecordDecl(hasName("B"), unless(isImplicit())))));
+}
+
+TEST_F(StructuralEquivalenceCacheTest, SpecialNonEq) {
+ auto TU = makeTuDecls(
+ R"(
+ class A {};
+ class B { int i; };
+ void x(A *);
+ void y(A *);
+ class C {
+ friend void x(A *);
+ friend void y(A *);
+ };
+ )",
+ R"(
+ class A {};
+ class B { int i; };
+ void x(A *);
+ void y(B *);
+ class C {
+ friend void x(A *);
+ friend void y(B *);
+ };
+ )",
+ Lang_CXX);
+
+ StructuralEquivalenceContext Ctx(
+ get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(),
+ NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false);
+
+ auto C = findDeclPair<CXXRecordDecl>(
+ TU, cxxRecordDecl(hasName("C"), unless(isImplicit())));
+ EXPECT_FALSE(Ctx.IsEquivalent(C.first, C.second));
+
+ EXPECT_FALSE(isInNonEqCache(C));
+ EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
+ TU, cxxRecordDecl(hasName("A"), unless(isImplicit())))));
+ EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
+ TU, cxxRecordDecl(hasName("B"), unless(isImplicit())))));
+ EXPECT_FALSE(isInNonEqCache(
+ findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")))));
+ EXPECT_FALSE(isInNonEqCache(
+ findDeclPair<FunctionDecl>(TU, functionDecl(hasName("y")))));
+}
+
+TEST_F(StructuralEquivalenceCacheTest, Cycle) {
+ auto TU = makeTuDecls(
+ R"(
+ class C;
+ class A { C *c; };
+ void x(A *);
+ class C {
+ friend void x(A *);
+ };
+ )",
+ R"(
+ class C;
+ class A { C *c; };
+ void x(A *);
+ class C {
+ friend void x(A *);
+ };
+ )",
+ Lang_CXX);
+
+ StructuralEquivalenceContext Ctx(
+ get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(),
+ NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false);
+
+ auto C = findDeclPair<CXXRecordDecl>(
+ TU, cxxRecordDecl(hasName("C"), unless(isImplicit())));
+ EXPECT_TRUE(Ctx.IsEquivalent(C.first, C.second));
+
+ EXPECT_FALSE(isInNonEqCache(C));
+ EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
+ TU, cxxRecordDecl(hasName("A"), unless(isImplicit())))));
+ EXPECT_FALSE(isInNonEqCache(
+ findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")))));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang