diff options
author | 2020-08-03 14:31:31 +0000 | |
---|---|---|
committer | 2020-08-03 14:31:31 +0000 | |
commit | e5dd70708596ae51455a0ffa086a00c5b29f8583 (patch) | |
tree | 5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/unittests/AST | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-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')
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 = "// &"; + + 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, HTMLCharacterReferences4) { + 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, 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 = "// *"; + + 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, HTMLCharacterReferences8) { + 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, 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 = "// «"; + + 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, HTMLCharacterReferences12) { + const char *Source = "// «z"; + + 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("z"), Toks[2].getText()); + + ASSERT_EQ(tok::newline, Toks[3].getKind()); +} + +TEST_F(CommentLexerTest, HTMLCharacterReferences13) { + 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, HTMLCharacterReferences14) { + 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, HTMLCharacterReferences15) { + const char *Source = "// & 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[] = { + "// =", + "// =", + "// =", + "// =" + }; + + 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 |