#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
+
+#include "DeclMatcher.h"
#include "gtest/gtest.h"
namespace clang {
return Lang == Lang_CXX || Lang == Lang_CXX11;
}
-static RunOptions getRunOptionsForLanguage(Language Lang) {
+static ArgVector getBasicRunOptionsForLanguage(Language Lang) {
ArgVector BasicArgs;
// Test with basic arguments.
switch (Lang) {
case Lang_OBJCXX:
llvm_unreachable("Not implemented yet!");
}
+ return BasicArgs;
+}
+
+static RunOptions getRunOptionsForLanguage(Language Lang) {
+ ArgVector BasicArgs = getBasicRunOptionsForLanguage(Lang);
// For C++, test with "-fdelayed-template-parsing" enabled to handle MSVC
// default behaviour.
return {BasicArgs};
}
+// 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,
+ const std::string &Code) {
+ 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, llvm::MemoryBuffer::getMemBuffer(Code.c_str()));
+}
+
template<typename NodeType, typename MatcherType>
testing::AssertionResult
testImport(const std::string &FromCode, const ArgVector &FromArgs,
// 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));
+ createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromCode);
ASTImporter Importer(ToCtx, ToAST->getFileManager(),
FromCtx, FromAST->getFileManager(), false);
Verifier, AMatcher));
}
+const StringRef DeclToImportID = "declToImport";
+
+// 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 ::testing::TestWithParam<ArgVector> {
+
+ const char *const InputFileName = "input.cc";
+ const char *const OutputFileName = "output.cc";
+
+ // Buffer for the To context, must live in the test scope.
+ std::string ToCode;
+
+ 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;
+ TU(StringRef Code, StringRef FileName, ArgVector Args)
+ : Code(Code), FileName(FileName),
+ Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args,
+ this->FileName)),
+ TUDecl(Unit->getASTContext().getTranslationUnitDecl()) {}
+ };
+
+ // 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;
+
+public:
+ // We may have several From context but only one To context.
+ std::unique_ptr<ASTUnit> ToAST;
+
+ // Returns the argument vector used for a specific language, this set
+ // can be tweaked by the test parameters.
+ ArgVector getArgVectorForLanguage(Language Lang) {
+ ArgVector Args = getBasicRunOptionsForLanguage(Lang);
+ ArgVector ExtraArgs = GetParam();
+ for (const auto& Arg : ExtraArgs) {
+ Args.push_back(Arg);
+ }
+ return Args;
+ }
+
+ // 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);
+
+ ASTContext &FromCtx = FromTU.Unit->getASTContext(),
+ &ToCtx = ToAST->getASTContext();
+
+ createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code);
+
+ ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx,
+ FromTU.Unit->getFileManager(), false);
+
+ 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 = Importer.Import(FoundDecls.front());
+ assert(Imported);
+ return std::make_tuple(*FoundDecls.begin(), Imported);
+ }
+
+ // Creates a TU decl for the given source code.
+ // May be called several times in a given test.
+ 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;
+ }
+
+ // 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) {
+ if (!ToAST) {
+ ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+ // Build the AST from an empty file.
+ ToAST =
+ tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc");
+ }
+
+ // 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());
+ createVirtualFileIfNeeded(ToAST.get(), It->FileName, It->Code);
+
+ ASTContext &FromCtx = From->getASTContext(),
+ &ToCtx = ToAST->getASTContext();
+ ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx,
+ FromCtx.getSourceManager().getFileManager(), false);
+ return Importer.Import(From);
+ }
+
+ ~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();
+ }
+ }
+};
+
+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(ImportExpr, ImportStringLiteral) {
MatchVerifier<Decl> Verifier;
has(fieldDecl(hasType(dependentSizedArrayType())))))));
}
+TEST_P(ASTImporterTestBase, DISABLED_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,
+ DISABLED_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));
+}
+
+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, DISABLED_ShouldImportImplicitCXXRecordDecl) {
+ 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,
+ DISABLED_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, DISABLED_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));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ASTImporterTestBase,
+ ::testing::Values(ArgVector(), ArgVector{"-fdelayed-template-parsing"}),);
+
+struct ImportFunctions : ASTImporterTestBase {};
+
+TEST_P(ImportFunctions,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
+ 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,
+ PrototypeShouldBeImportedAsDefintionWhenThereIsADefinition) {
+ Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ FunctionDecl *FromD = // Prototype
+ 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,
+ 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), 1u);
+ 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, DISABLED_ImportPrototypeOfRecursiveFunction) {
+ Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ FunctionDecl *PrototypeFD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(PrototypeFD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+}
+
+TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) {
+ Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
+ auto Pattern = functionDecl(hasName("f"));
+ FunctionDecl *DefinitionFD =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(DefinitionFD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+}
+
+TEST_P(ImportFunctions, ImportPrototypes) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
+ FunctionDecl *FromD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ ImportedD = Import(FromD, Lang_CXX);
+ }
+ Decl *ImportedD1;
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
+ FunctionDecl *FromD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD1 = Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_EQ(ImportedD, ImportedD1);
+ EXPECT_TRUE(!cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+}
+
+TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ Decl *ImportedD;
+ {
+ Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
+ FunctionDecl *FromD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+
+ ImportedD = Import(FromD, Lang_CXX);
+ }
+ Decl *ImportedD1;
+ {
+ Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
+ FunctionDecl *FromD =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ ImportedD1 = Import(FromD, Lang_CXX);
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_EQ(ImportedD, ImportedD1);
+ EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+}
+
+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_TRUE(!ProtoD->doesThisDeclarationHaveABody());
+ FunctionDecl *DefinitionD =
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
+}
+
+TEST_P(ImportFunctions, DISABLED_ImportPrototypeThenProtoAndDefinition) {
+ 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(); 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_TRUE(!ProtoD->doesThisDeclarationHaveABody());
+ FunctionDecl *DefinitionD =
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
+ EXPECT_EQ(DefinitionD->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());
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ParameterizedTests, ImportFunctions,
+ ::testing::Values(ArgVector(), ArgVector{"-fdelayed-template-parsing"}),);
+
} // end namespace ast_matchers
} // end namespace clang