summaryrefslogtreecommitdiff
path: root/unittests/AST/ASTImporterTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/AST/ASTImporterTest.cpp')
-rw-r--r--unittests/AST/ASTImporterTest.cpp3351
1 files changed, 2843 insertions, 508 deletions
diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp
index a6ec304ff1a11..21b76173c1057 100644
--- a/unittests/AST/ASTImporterTest.cpp
+++ b/unittests/AST/ASTImporterTest.cpp
@@ -11,259 +11,604 @@
//
//===----------------------------------------------------------------------===//
+#include "MatchVerifier.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTImporter.h"
-#include "MatchVerifier.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
-#include "gtest/gtest.h"
+
+#include "DeclMatcher.h"
+#include "Language.h"
+#include "gmock/gmock.h"
+#include "llvm/ADT/StringMap.h"
namespace clang {
namespace ast_matchers {
-typedef std::vector<std::string> ArgVector;
-typedef std::vector<ArgVector> RunOptions;
-
-static bool isCXX(Language Lang) {
- return Lang == Lang_CXX || Lang == Lang_CXX11;
-}
-
-static RunOptions getRunOptionsForLanguage(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"};
- break;
- case Lang_CXX11:
- BasicArgs = {"-std=c++11"};
- break;
- case Lang_OpenCL:
- case Lang_OBJCXX:
- llvm_unreachable("Not implemented yet!");
- }
-
- // For C++, test with "-fdelayed-template-parsing" enabled to handle MSVC
- // default behaviour.
- if (isCXX(Lang)) {
- ArgVector ArgsForDelayedTemplateParse = BasicArgs;
- ArgsForDelayedTemplateParse.emplace_back("-fdelayed-template-parsing");
- return {BasicArgs, ArgsForDelayedTemplateParse};
- }
-
- return {BasicArgs};
-}
-
-template<typename NodeType, typename MatcherType>
-testing::AssertionResult
-testImport(const std::string &FromCode, const ArgVector &FromArgs,
- const std::string &ToCode, const ArgVector &ToArgs,
- MatchVerifier<NodeType> &Verifier, const MatcherType &AMatcher) {
+using internal::Matcher;
+using internal::BindableMatcher;
+using llvm::StringMap;
+
+// 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.
+static void
+createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {
+ assert(ToAST);
+ ASTContext &ToCtx = ToAST->getASTContext();
+ auto *OFS = static_cast<vfs::OverlayFileSystem *>(
+ ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get());
+ auto *MFS =
+ static_cast<vfs::InMemoryFileSystem *>(OFS->overlays_begin()->get());
+ MFS->addFile(FileName, 0, std::move(Buffer));
+}
+
+static void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
+ StringRef Code) {
+ return createVirtualFileIfNeeded(ToAST, FileName,
+ llvm::MemoryBuffer::getMemBuffer(Code));
+}
+
+const StringRef DeclToImportID = "declToImport";
+const StringRef DeclToVerifyID = "declToVerify";
+
+// 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.
+struct ParameterizedTestsFixture : ::testing::TestWithParam<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 = GetParam();
+ for (const auto &Arg : ExtraArgs) {
+ Args.push_back(Arg);
+ }
+ return Args;
+ }
+
+};
+
+// Base class for those tests which use the family of `testImport` functions.
+class TestImportBase : public ParameterizedTestsFixture {
+
+ template <typename NodeType>
+ 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);
+
+ // 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)
+ return testing::AssertionFailure() << "Import failed, nullptr returned!";
+
+ 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);
+ }
+
+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(Imported);
+ }
+
+ // 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();
+}
+
+// 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 ParameterizedTestsFixture {
+
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();
-
- // Add input.cc to virtual file system so importer can 'find' it
- // while importing SourceLocations.
- vfs::OverlayFileSystem *OFS = static_cast<vfs::OverlayFileSystem *>(
- ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get());
- vfs::InMemoryFileSystem *MFS = static_cast<vfs::InMemoryFileSystem *>(
- OFS->overlays_begin()->get());
- MFS->addFile(InputFileName, 0, llvm::MemoryBuffer::getMemBuffer(FromCode));
-
- ASTImporter Importer(ToCtx, ToAST->getFileManager(),
- FromCtx, FromAST->getFileManager(), false);
-
- IdentifierInfo *ImportedII = &FromCtx.Idents.get("declToImport");
- assert(ImportedII && "Declaration with 'declToImport' name"
- "should be specified in test!");
- DeclarationName ImportDeclName(ImportedII);
- SmallVector<NamedDecl *, 4> FoundDecls;
- FromCtx.getTranslationUnitDecl()->localUncachedLookup(
- ImportDeclName, FoundDecls);
-
- if (FoundDecls.size() != 1)
- return testing::AssertionFailure() << "Multiple declarations were found!";
-
- auto Imported = Importer.Import(*FoundDecls.begin());
- if (!Imported)
- return testing::AssertionFailure() << "Import failed, nullptr returned!";
-
- // 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 Verifier.match(Imported, AMatcher);
-}
-
-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) {
- auto RunOptsFrom = getRunOptionsForLanguage(FromLang);
- auto RunOptsTo = getRunOptionsForLanguage(ToLang);
- for (const auto &FromArgs : RunOptsFrom)
- for (const auto &ToArgs : RunOptsTo)
- EXPECT_TRUE(testImport(FromCode, FromArgs, ToCode, ToArgs,
- Verifier, AMatcher));
-}
-
-
-TEST(ImportExpr, ImportStringLiteral) {
- MatchVerifier<Decl> Verifier;
- testImport("void declToImport() { \"foo\"; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- stringLiteral(
- hasType(
- asString("const char [4]"))))))));
- testImport("void declToImport() { L\"foo\"; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- stringLiteral(
- hasType(
- asString("const wchar_t [4]"))))))));
- testImport("void declToImport() { \"foo\" \"bar\"; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- stringLiteral(
- hasType(
- asString("const char [7]"))))))));
-}
-
-TEST(ImportExpr, ImportGNUNullExpr) {
- MatchVerifier<Decl> Verifier;
- testImport("void declToImport() { __null; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- gnuNullExpr(
- hasType(isInteger())))))));
-}
-
-TEST(ImportExpr, ImportCXXNullPtrLiteralExpr) {
- MatchVerifier<Decl> Verifier;
- testImport("void declToImport() { nullptr; }",
- Lang_CXX11, "", Lang_CXX11, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- cxxNullPtrLiteralExpr())))));
+ // 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;
+ TU(StringRef Code, StringRef FileName, ArgVector Args)
+ : Code(Code), FileName(FileName),
+ Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args,
+ this->FileName)),
+ TUDecl(Unit->getASTContext().getTranslationUnitDecl()) {
+ Unit->enableSourceFileDiagnostics();
+ }
+
+ void lazyInitImporter(ASTUnit *ToAST) {
+ assert(ToAST);
+ if (!Importer) {
+ Importer.reset(new ASTImporter(
+ ToAST->getASTContext(), ToAST->getFileManager(),
+ Unit->getASTContext(), Unit->getFileManager(), false));
+ }
+ assert(&ToAST->getASTContext() == &Importer->getToContext());
+ createVirtualFileIfNeeded(ToAST, FileName, Code);
+ }
+
+ Decl *import(ASTUnit *ToAST, Decl *FromDecl) {
+ lazyInitImporter(ToAST);
+ return Importer->Import(FromDecl);
+ }
+
+ QualType import(ASTUnit *ToAST, QualType FromType) {
+ lazyInitImporter(ToAST);
+ return Importer->Import(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;
+
+ void lazyInitToAST(Language ToLang) {
+ if (ToAST)
+ return;
+ ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+ // Build the AST from an empty file.
+ ToAST = tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc");
+ ToAST->enableSourceFileDiagnostics();
+ }
+
+ TU *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 = std::find_if(FromTUs.begin(), FromTUs.end(), [From](const TU &E) {
+ return E.TUDecl == From->getTranslationUnitDecl();
+ });
+ assert(It != FromTUs.end());
+ return &*It;
+ }
+
+public:
+ // We may have several From context but only one To context.
+ std::unique_ptr<ASTUnit> ToAST;
+
+ // 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) {
+ ArgVector FromArgs = getArgVectorForLanguage(FromLang),
+ ToArgs = getArgVectorForLanguage(ToLang);
+
+ FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs);
+ TU &FromTU = FromTUs.back();
+
+ ToCode = ToSrcCode;
+ assert(!ToAST);
+ ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
+ ToAST->enableSourceFileDiagnostics();
+
+ ASTContext &FromCtx = FromTU.Unit->getASTContext();
+
+ createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code);
+
+ IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
+ assert(ImportedII && "Declaration with the given identifier "
+ "should be specified in test!");
+ DeclarationName ImportDeclName(ImportedII);
+ SmallVector<NamedDecl *, 4> FoundDecls;
+ FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
+ FoundDecls);
+
+ assert(FoundDecls.size() == 1);
+
+ Decl *Imported = FromTU.import(ToAST.get(), FoundDecls.front());
+
+ assert(Imported);
+ return std::make_tuple(*FoundDecls.begin(), Imported);
+ }
+
+ // 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") {
+ assert(
+ std::find_if(FromTUs.begin(), FromTUs.end(), [FileName](const TU &E) {
+ return E.FileName == FileName;
+ }) == FromTUs.end());
+
+ ArgVector Args = getArgVectorForLanguage(Lang);
+ FromTUs.emplace_back(SrcCode, FileName, Args);
+ TU &Tu = FromTUs.back();
+
+ return Tu.TUDecl;
+ }
+
+ // Creates the To context with the given source code and returns the TU decl.
+ TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang) {
+ ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+ ToCode = ToSrcCode;
+ assert(!ToAST);
+ ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
+ ToAST->enableSourceFileDiagnostics();
+
+ return ToAST->getASTContext().getTranslationUnitDecl();
+ }
+
+ // 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) {
+ lazyInitToAST(ToLang);
+ TU *FromTU = findFromTU(From);
+ return FromTU->import(ToAST.get(), From);
+ }
+
+ QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) {
+ lazyInitToAST(ToLang);
+ TU *FromTU = findFromTU(TUDecl);
+ return FromTU->import(ToAST.get(), FromType);
+ }
+
+ ~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();
+ }
+ }
+};
+
+struct ImportExpr : TestImportBase {};
+struct ImportType : TestImportBase {};
+struct ImportDecl : TestImportBase {};
+
+struct CanonicalRedeclChain : ASTImporterTestBase {};
+
+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();
-TEST(ImportExpr, ImportFloatinglLiteralExpr) {
+ 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));
+}
+
+
+TEST_P(ImportExpr, ImportStringLiteral) {
MatchVerifier<Decl> Verifier;
- testImport("void declToImport() { 1.0; }",
- Lang_C, "", Lang_C, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- floatLiteral(
- equals(1.0),
- hasType(asString("double"))))))));
- testImport("void declToImport() { 1.0e-5f; }",
- Lang_C, "", Lang_C, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- floatLiteral(
- equals(1.0e-5f),
- hasType(asString("float"))))))));
-}
-
-TEST(ImportExpr, ImportCompoundLiteralExpr) {
- MatchVerifier<Decl> Verifier;
- testImport("void declToImport() {"
- " struct s { int x; long y; unsigned z; }; "
- " (struct s){ 42, 0L, 1U }; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- 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(ImportExpr, ImportCXXThisExpr) {
- MatchVerifier<Decl> Verifier;
- testImport("class declToImport { void f() { this; } };",
- Lang_CXX, "", Lang_CXX, Verifier,
- cxxRecordDecl(
- hasMethod(
- hasBody(
- compoundStmt(
- has(
- cxxThisExpr(
- hasType(
- asString("class declToImport *")))))))));
-}
-
-TEST(ImportExpr, ImportAtomicExpr) {
- MatchVerifier<Decl> Verifier;
- testImport("void declToImport() { int *ptr; __atomic_load_n(ptr, 1); }",
- Lang_C, "", Lang_C, Verifier,
- functionDecl(hasBody(compoundStmt(has(atomicExpr(
- has(ignoringParenImpCasts(
- declRefExpr(hasDeclaration(varDecl(hasName("ptr"))),
- hasType(asString("int *"))))),
- has(integerLiteral(equals(1), hasType(asString("int"))))))))));
+ 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(ImportExpr, ImportLabelDeclAndAddrLabelExpr) {
+TEST_P(ImportExpr, ImportGNUNullExpr) {
MatchVerifier<Decl> Verifier;
testImport(
- "void declToImport() { loop: goto loop; &&loop; }", Lang_C, "", Lang_C,
- Verifier,
- functionDecl(hasBody(compoundStmt(
- has(labelStmt(hasDeclaration(labelDecl(hasName("loop"))))),
- has(addrLabelExpr(hasDeclaration(labelDecl(hasName("loop")))))))));
+ "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, 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,
@@ -272,7 +617,7 @@ AST_MATCHER_P(TemplateDecl, hasTemplateDecl,
return Template && InnerMatcher.matches(*Template, Finder, Builder);
}
-TEST(ImportExpr, ImportParenListExpr) {
+TEST_P(ImportExpr, ImportParenListExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"template<typename T> class dummy { void f() { dummy X(*this); } };"
@@ -289,279 +634,290 @@ TEST(ImportExpr, ImportParenListExpr) {
hasUnaryOperand(cxxThisExpr())))))))))))))))))))))));
}
-TEST(ImportExpr, ImportSwitch) {
+TEST_P(ImportExpr, ImportSwitch) {
MatchVerifier<Decl> Verifier;
- testImport("void declToImport() { int b; switch (b) { case 1: break; } }",
- Lang_C, "", Lang_C, Verifier,
- functionDecl(hasBody(compoundStmt(
- has(switchStmt(has(compoundStmt(has(caseStmt())))))))));
+ testImport(
+ "void declToImport() { int b; switch (b) { case 1: break; } }",
+ Lang_C, "", Lang_C, Verifier,
+ functionDecl(hasDescendant(
+ switchStmt(has(compoundStmt(has(caseStmt())))))));
}
-TEST(ImportExpr, ImportStmtExpr) {
+TEST_P(ImportExpr, ImportStmtExpr) {
MatchVerifier<Decl> Verifier;
// NOTE: has() ignores implicit casts, using hasDescendant() to match it
testImport(
"void declToImport() { int b; int a = b ?: 1; int C = ({int X=4; X;}); }",
Lang_C, "", Lang_C, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- declStmt(
- hasSingleDecl(
- varDecl(
- hasName("C"),
- hasType(asString("int")),
- hasInitializer(
- stmtExpr(
- hasAnySubstatement(
- declStmt(
- hasSingleDecl(
- varDecl(
- hasName("X"),
- hasType(asString("int")),
- hasInitializer(
+ 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()
- )))))))))));
+ hasDescendant(
+ implicitCastExpr())))))));
}
-TEST(ImportExpr, ImportConditionalOperator) {
+TEST_P(ImportExpr, ImportConditionalOperator) {
MatchVerifier<Decl> Verifier;
testImport(
- "void declToImport() { true ? 1 : -5; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- conditionalOperator(
+ "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))))
- )))))));
+ unaryOperator(hasUnaryOperand(integerLiteral(equals(5))))))
+ )));
}
-TEST(ImportExpr, ImportBinaryConditionalOperator) {
+TEST_P(ImportExpr, ImportBinaryConditionalOperator) {
MatchVerifier<Decl> Verifier;
testImport(
- "void declToImport() { 1 ?: -5; }", Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- binaryConditionalOperator(
+ "void declToImport() { (void)(1 ?: -5); }",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ binaryConditionalOperator(
hasCondition(
- implicitCastExpr(
- hasSourceExpression(
- opaqueValueExpr(
- hasSourceExpression(integerLiteral(equals(1))))),
- hasType(booleanType()))),
+ implicitCastExpr(
+ hasSourceExpression(opaqueValueExpr(
+ hasSourceExpression(integerLiteral(equals(1))))),
+ hasType(booleanType()))),
hasTrueExpression(
- opaqueValueExpr(hasSourceExpression(
- integerLiteral(equals(1))))),
+ opaqueValueExpr(
+ hasSourceExpression(integerLiteral(equals(1))))),
hasFalseExpression(
- unaryOperator(hasOperatorName("-"),
- hasUnaryOperand(integerLiteral(equals(5)))))
- ))))));
+ unaryOperator(
+ hasOperatorName("-"),
+ hasUnaryOperand(integerLiteral(equals(5)))))))));
}
-TEST(ImportExpr, ImportDesignatedInitExpr) {
+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(
- hasBody(
- compoundStmt(
- has(
- declStmt(
- hasSingleDecl(
- varDecl(
- hasInitializer(
- initListExpr(
- hasSyntacticForm(
- initListExpr(
- has(
- designatedInitExpr(
- designatorCountIs(2),
- has(floatLiteral(
- equals(1.0))),
- has(integerLiteral(
- equals(2))))),
- has(
- designatedInitExpr(
- designatorCountIs(2),
- has(floatLiteral(
- equals(2.0))),
- has(integerLiteral(
- equals(2))))),
- has(
- designatedInitExpr(
- designatorCountIs(2),
- has(floatLiteral(
- equals(1.0))),
- has(integerLiteral(
- equals(0)))))
- ))))))))))));
-}
-
-
-TEST(ImportExpr, ImportPredefinedExpr) {
+ 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),
+ has(floatLiteral(equals(1.0))),
+ has(integerLiteral(equals(2))))),
+ has(designatedInitExpr(
+ designatorCountIs(2),
+ has(floatLiteral(equals(2.0))),
+ has(integerLiteral(equals(2))))),
+ has(designatedInitExpr(
+ designatorCountIs(2),
+ has(floatLiteral(equals(1.0))),
+ has(integerLiteral(equals(0)))))))));
+}
+
+
+TEST_P(ImportExpr, ImportPredefinedExpr) {
MatchVerifier<Decl> Verifier;
// __func__ expands as StringLiteral("declToImport")
- testImport("void declToImport() { __func__; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- predefinedExpr(
- hasType(
- asString("const char [13]")),
- has(
- stringLiteral(
- hasType(
- asString("const char [13]"))))))))));
-}
-
-TEST(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(
- hasBody(
- compoundStmt(
- has(
- declStmt(
- hasSingleDecl(
- varDecl(
- hasInitializer(
- 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)))))
- ))))))))));
+ 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(ImportExpr, ImportVAArgExpr) {
+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(
- hasBody(
- compoundStmt(
- has(
- cStyleCastExpr(
- hasSourceExpression(
- vaArgExpr())))))));
+ 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(ImportType, ImportAtomicType) {
+TEST_P(ImportType, ImportAtomicType) {
MatchVerifier<Decl> Verifier;
- testImport("void declToImport() { typedef _Atomic(int) a_int; }",
- Lang_CXX11, "", Lang_CXX11, Verifier,
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- declStmt(
- has(
- typedefDecl(
- has(atomicType())))))))));
+ testImport(
+ "void declToImport() { typedef _Atomic(int) a_int; }",
+ Lang_CXX11, "", Lang_CXX11, Verifier,
+ functionDecl(hasDescendant(typedefDecl(has(atomicType())))));
}
-TEST(ImportDecl, ImportFunctionTemplateDecl) {
+TEST_P(ImportDecl, ImportFunctionTemplateDecl) {
MatchVerifier<Decl> Verifier;
- testImport("template <typename T> void declToImport() { };", Lang_CXX, "",
- Lang_CXX, Verifier, functionTemplateDecl());
+ testImport(
+ "template <typename T> void declToImport() { };",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionTemplateDecl());
}
const internal::VariadicDynCastAllOfMatcher<Expr, CXXDependentScopeMemberExpr>
cxxDependentScopeMemberExpr;
-TEST(ImportExpr, ImportCXXDependentScopeMemberExpr) {
+TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) {
MatchVerifier<Decl> Verifier;
- testImport("template <typename T> struct C { T t; };"
- "template <typename T> void declToImport() {"
- " C<T> d;"
- " d.t;"
- "}"
- "void instantiate() { declToImport<int>(); }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionTemplateDecl(has(functionDecl(
- has(compoundStmt(has(cxxDependentScopeMemberExpr())))))));
- testImport("template <typename T> struct C { T t; };"
- "template <typename T> void declToImport() {"
- " C<T> d;"
- " (&d)->t;"
- "}"
- "void instantiate() { declToImport<int>(); }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionTemplateDecl(has(functionDecl(
- has(compoundStmt(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())))));
+ 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(ImportType, ImportTypeAliasTemplate) {
+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(
- hasBody(
- compoundStmt(
- has(
- returnStmt(
- has(
- implicitCastExpr(
- has(
- declRefExpr())))))))));
-}
-
-TEST(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(
- hasBody(
- compoundStmt(
- has(
- returnStmt(
- has(
- implicitCastExpr(
- has(
- declRefExpr())))))))));
+ 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:
@@ -573,66 +929,108 @@ TEST(ImportType, ImportPackExpansion) {
// will generate TypeTraitExpr <...> 'int'
const internal::VariadicDynCastAllOfMatcher<Stmt, TypeTraitExpr> typeTraitExpr;
-TEST(ImportExpr, ImportTypeTraitExpr) {
+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("void declToImport() { "
- " __builtin_types_compatible_p(int, int);"
+ 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(
- typeTraitExpr(hasType(asString("int"))))))));
+ functionDecl(hasBody(compoundStmt(
+ has(declStmt(hasSingleDecl(varDecl(hasName("d")))))))));
}
-TEST(ImportExpr, ImportTypeTraitExprValDep) {
- MatchVerifier<Decl> Verifier;
- testImport("template<typename T> struct declToImport {"
- " void m() { __is_pod(T); }"
- "};"
- "void f() { declToImport<int>().m(); }",
- Lang_CXX11, "", Lang_CXX11, Verifier,
- classTemplateDecl(
- has(
- cxxRecordDecl(
- has(
- functionDecl(
- hasBody(
- compoundStmt(
- has(
- typeTraitExpr(
- hasType(booleanType())
- ))))))))));
+TEST_P(ASTImporterTestBase, 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(ASTImporterTestBase, 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());
+ ASSERT_TRUE(From);
+ auto To = Import(From, Lang_C);
+ EXPECT_EQ(To, nullptr);
}
const internal::VariadicDynCastAllOfMatcher<Expr, CXXPseudoDestructorExpr>
cxxPseudoDestructorExpr;
-TEST(ImportExpr, ImportCXXPseudoDestructorExpr) {
+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(has(compoundStmt(has(
- callExpr(has(cxxPseudoDestructorExpr())))))));
+ testImport(
+ "typedef int T;"
+ "void declToImport(int *p) {"
+ " T t;"
+ " p->T::~T();"
+ "}",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ functionDecl(hasDescendant(
+ callExpr(has(cxxPseudoDestructorExpr())))));
}
-TEST(ImportDecl, ImportUsingDecl) {
+TEST_P(ImportDecl, ImportUsingDecl) {
MatchVerifier<Decl> Verifier;
- testImport("namespace foo { int bar; }"
- "int declToImport(){ using foo::bar; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- functionDecl(
- has(
- compoundStmt(
- has(
- declStmt(
- has(
- usingDecl())))))));
+ 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
@@ -648,13 +1046,1950 @@ TEST(ImportDecl, ImportUsingDecl) {
const internal::VariadicDynCastAllOfMatcher<Decl,
UsingShadowDecl> usingShadowDecl;
-TEST(ImportDecl, ImportUsingShadowDecl) {
+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;
- testImport("namespace foo { int bar; }"
- "namespace declToImport { using foo::bar; }",
- Lang_CXX, "", Lang_CXX, Verifier,
- namespaceDecl(has(usingShadowDecl())));
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase,
+ 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(ASTImporterTestBase,
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase,
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(
+ ASTImporterTestBase,
+ 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 (FieldDecl *Field : Node.fields()) {
+ if (Index == Order.size())
+ return false;
+ if (Field->getName() != Order[Index])
+ return false;
+ ++Index;
+ }
+ return Index == Order.size();
+}
+
+TEST_P(ASTImporterTestBase,
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase,
+ DISABLED_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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(
+ ASTImporterTestBase,
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase,
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase,
+ 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(ASTImporterTestBase,
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(
+ ASTImporterTestBase,
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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());
+ Import(FromD, Lang_CXX);
+ }
+ EXPECT_TRUE(Imported2->isUsed(false));
+}
+
+TEST_P(ASTImporterTestBase, 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 : ASTImporterTestBase {};
+
+TEST_P(ImportFunctions,
+ DefinitionShouldBeImportedAsDefintionWhenThereIsAPrototype) {
+ Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ FunctionDecl *FromD = // Definition
+ LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+}
+
+TEST_P(ImportFunctions, DefinitionShouldBeImportedAsADefinition) {
+ Decl *FromTU = getTuDecl("void f() {}", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ FunctionDecl *FromD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+}
+
+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, ImportPrototypes) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ ImportedD = Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, 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_FALSE(To1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportFunctions, ImportDefinitions) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD = Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl("void f(){};", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == To0);
+ EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
+}
+
+TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD = Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ToAST->getASTContext().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_TRUE(To0->doesThisDeclarationHaveABody());
+ EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportFunctions, ImportPrototypeThenDefinition) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
+ FunctionDecl *FromD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input1.cc");
+ FunctionDecl *FromD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+ FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
+ FunctionDecl *DefinitionD =
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
+}
+
+TEST_P(ImportFunctions, ImportPrototypeThenProtoAndDefinition) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl("void f(); void f(){}", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 3u);
+ FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
+
+ FunctionDecl *DefinitionD =
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
+
+ EXPECT_TRUE(DefinitionD->getPreviousDecl());
+ EXPECT_FALSE(DefinitionD->getPreviousDecl()->doesThisDeclarationHaveABody());
+ EXPECT_EQ(DefinitionD->getPreviousDecl()->getPreviousDecl(), ProtoD);
+}
+
+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()));
+}
+
+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);
+}
+
+// Disabled temporarily, because the new structural equivalence check
+// (https://reviews.llvm.org/D48628) breaks it.
+// PreviousDecl is not set because there is no structural match.
+// FIXME Enable!
+TEST_P(ImportFriendFunctions,
+ DISABLED_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());
+}
+
+// Disabled temporarily, because the new structural equivalence check
+// (https://reviews.llvm.org/D48628) breaks it.
+// PreviousDecl is not set because there is no structural match.
+// FIXME Enable!
+TEST_P(ImportFriendFunctions,
+ DISABLED_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);
+}
+
+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);
+}
+
+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())))))));
+}
+
+const internal::VariadicDynCastAllOfMatcher<Expr, UnresolvedMemberExpr>
+ unresolvedMemberExpr;
+
+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 ASTImporterTestBase {
+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);
+ }
+
+ 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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"))))));
+}
+
+struct DeclContextTest : ASTImporterTestBase {};
+
+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));
+}
+
+struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {};
+
+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(ImportFunctionTemplateSpecializations,
+ ImportPrototypes) {
+ auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
+ auto Code =
+ R"(
+ // Proto of the primary template.
+ template <class T>
+ void f();
+ // Proto of the specialization.
+ template <>
+ void f<int>();
+ )";
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
+ auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ ImportedD = Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, 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_TRUE(ImportedD != To1);
+ EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
+ EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
+ // Check that they are part of the same redecl chain.
+ EXPECT_EQ(To1->getCanonicalDecl(), To0->getCanonicalDecl());
+}
+
+TEST_P(ImportFunctionTemplateSpecializations, ImportDefinitions) {
+ auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
+ auto Code =
+ R"(
+ // Proto of the primary template.
+ template <class T>
+ void f();
+ // Specialization and definition.
+ template <>
+ void f<int>() {}
+ )";
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD = Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == To0);
+ EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
+
+ auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ ToTU, functionTemplateDecl());
+ auto *FirstSpecD = *(TemplateD->spec_begin());
+ EXPECT_EQ(FirstSpecD->getCanonicalDecl(), To0->getCanonicalDecl());
+}
+
+TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenPrototype) {
+ auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
+ auto Code =
+ R"(
+ // Proto of the primary template.
+ template <class T>
+ void f();
+ // Specialization proto.
+ template <>
+ void f<int>();
+ // Specialization proto.
+ template <>
+ void f<int>();
+ )";
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD = Import(FromD, 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_TRUE(ImportedD != To1);
+ EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
+ EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenDefinition) {
+ auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
+ auto Code =
+ R"(
+ // Proto of the primary template.
+ template <class T>
+ void f();
+ // Specialization proto.
+ template <>
+ void f<int>();
+ // Specialization definition.
+ template <>
+ void f<int>() {}
+ )";
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD = Import(FromD, 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_TRUE(ImportedD != To1);
+ EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
+ EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportFunctionTemplateSpecializations, DefinitionThenPrototype) {
+ auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
+ auto Code =
+ R"(
+ // Proto of the primary template.
+ template <class T>
+ void f();
+ // Specialization definition.
+ template <>
+ void f<int>() {}
+ // Specialization proto.
+ template <>
+ void f<int>();
+ )";
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD = Import(FromD, 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_TRUE(ImportedD != To1);
+ EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
+ EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
+ ::testing::Values(ArgVector()), );
+
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, CanonicalRedeclChain,
+ ::testing::Values(ArgVector()),);
+
+auto DefaultTestValuesForRunOptions = ::testing::Values(
+ ArgVector(),
+ ArgVector{"-fdelayed-template-parsing"},
+ ArgVector{"-fms-compatibility"},
+ ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"});
+
+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, ASTImporterTestBase,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests,
+ ImportFunctionTemplateSpecializations,
+ DefaultTestValuesForRunOptions, );
+
} // end namespace ast_matchers
} // end namespace clang