martong created this revision.
martong added reviewers: a.sidorin, xazax.hun, szepet.
Herald added subscribers: cfe-commits, dkrupp, rnkovacs.
[ASTImporter] Add a helper test Fixture, so we can add tests which may
check internal attributes of AST nodes. Also it makes possible to import from
several "From" contexts.
Repository:
rC Clang
https://reviews.llvm.org/D43967
Files:
unittests/AST/ASTImporterTest.cpp
unittests/AST/DeclMatcher.h
Index: unittests/AST/DeclMatcher.h
===================================================================
--- /dev/null
+++ unittests/AST/DeclMatcher.h
@@ -0,0 +1,68 @@
+//===- unittest/AST/DeclMatcher.h - AST unit test support ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_AST_DECLMATCHER_H
+#define LLVM_CLANG_UNITTESTS_AST_DECLMATCHER_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace ast_matchers {
+
+enum class DeclMatcherKind { First, Last };
+
+// Matcher class to retrieve the first/last matched node under a given AST.
+template <typename NodeType, DeclMatcherKind MatcherKind>
+class DeclMatcher : public MatchFinder::MatchCallback {
+ NodeType *Node = nullptr;
+ void run(const MatchFinder::MatchResult &Result) override {
+ if ((MatcherKind == DeclMatcherKind::First && Node == nullptr) ||
+ MatcherKind == DeclMatcherKind::Last) {
+ Node = const_cast<NodeType *>(Result.Nodes.getNodeAs<NodeType>(""));
+ }
+ }
+public:
+ // Returns the first/last matched node under the tree rooted in `D`.
+ template <typename MatcherType>
+ NodeType *match(const Decl *D, const MatcherType &AMatcher) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher.bind(""), this);
+ Finder.matchAST(D->getASTContext());
+ assert(Node);
+ return Node;
+ }
+};
+template <typename NodeType>
+using LastDeclMatcher = DeclMatcher<NodeType, DeclMatcherKind::Last>;
+template <typename NodeType>
+using FirstDeclMatcher = DeclMatcher<NodeType, DeclMatcherKind::First>;
+
+template <typename NodeType>
+class DeclCounter : public MatchFinder::MatchCallback {
+ unsigned count = 0;
+ void run(const MatchFinder::MatchResult &Result) override {
+ if(Result.Nodes.getNodeAs<NodeType>("")) {
+ ++count;
+ }
+ }
+public:
+ // Returns the number of matched nodes under the tree rooted in `D`.
+ template <typename MatcherType>
+ unsigned match(const Decl *D, const MatcherType &AMatcher) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher.bind(""), this);
+ Finder.matchAST(D->getASTContext());
+ return count;
+ }
+};
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif
Index: unittests/AST/ASTImporterTest.cpp
===================================================================
--- unittests/AST/ASTImporterTest.cpp
+++ unittests/AST/ASTImporterTest.cpp
@@ -17,6 +17,8 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
+
+#include "DeclMatcher.h"
#include "gtest/gtest.h"
namespace clang {
@@ -29,7 +31,7 @@
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) {
@@ -49,6 +51,11 @@
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.
@@ -133,6 +140,146 @@
Verifier, AMatcher));
}
+class Fixture : public ::testing::Test {
+
+ const char *const InputFileName = "input.cc";
+ const char *const OutputFileName = "output.cc";
+
+ //Buffers for the contexts, must live in test scope
+ std::string ToCode;
+
+ struct TU {
+ 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 context and related translation units.
+ std::list<TU> FromTUs;
+
+ // Creates a virtual file and assigns that to the To context. If the file
+ // already exists then the file will not be created again as a duplicate.
+ void createVirtualFile(StringRef FileName, const std::string &Code) {
+ assert(ToAST);
+ ASTContext &ToCtx = ToAST->getASTContext();
+ 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(FileName, 0, llvm::MemoryBuffer::getMemBuffer(Code.c_str()));
+ }
+
+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 call more than once within the same test.
+ std::tuple<Decl *, Decl *>
+ getImportedDecl(StringRef FromSrcCode, Language FromLang,
+ StringRef ToSrcCode, Language ToLang,
+ const char *const Identifier = "declToImport") {
+ ArgVector FromArgs, ToArgs;
+ FromArgs = getBasicRunOptionsForLanguage(FromLang);
+ ToArgs = getBasicRunOptionsForLanguage(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();
+
+ createVirtualFile(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.begin());
+ 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= getBasicRunOptionsForLanguage(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 = getBasicRunOptionsForLanguage(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());
+ // This will not create the file more than once.
+ createVirtualFile(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);
+ }
+
+ ~Fixture() {
+ if (!::testing::Test::HasFailure()) return;
+
+ for (auto &Tu : FromTUs) {
+ if (Tu.Unit) {
+ llvm::errs() << "FromAST:\n";
+ Tu.Unit->getASTContext().getTranslationUnitDecl()->dump();
+ llvm::errs() << "\n";
+ }
+ }
+ if (ToAST) {
+ llvm::errs() << "ToAST:\n";
+ ToAST->getASTContext().getTranslationUnitDecl()->dump();
+ }
+ }
+};
TEST(ImportExpr, ImportStringLiteral) {
MatchVerifier<Decl> Verifier;
@@ -816,5 +963,602 @@
has(fieldDecl(hasType(dependentSizedArrayType())))))));
}
+
+TEST_F(Fixture, 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_F(Fixture, 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 (FunctionDecl *FD = dyn_cast<FunctionDecl>(Child)) {
+ if (FD->getNameAsString() == "declToImport") {
+ GTEST_NONFATAL_FAILURE_(
+ "TU should not contain any FunctionDecl with name declToImport");
+ TU->dump();
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ assert(Check(From));
+ Check(To);
+}
+
+TEST_F(Fixture, 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 (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Child)) {
+ if (RD->getNameAsString() == "declToImport") {
+ GTEST_NONFATAL_FAILURE_(
+ "TU should not contain any CXXRecordDecl with name declToImport");
+ TU->dump();
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ assert(Check(From));
+ Check(To);
+}
+
+TEST_F(Fixture, 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 (TypeAliasDecl *AD = dyn_cast<TypeAliasDecl>(Child)) {
+ if (AD->getNameAsString() == "declToImport") {
+ GTEST_NONFATAL_FAILURE_(
+ "TU should not contain any TypeAliasDecl with name declToImport");
+ TU->dump();
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ assert(Check(From));
+ Check(To);
+}
+
+TEST_F(
+ Fixture,
+ 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_F(
+ Fixture,
+ 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_F(Fixture, 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(has(fieldDecl()))));
+ ASSERT_TRUE(Verifier.match(To, cxxRecordDecl(has(fieldDecl()))));
+
+ auto Check = [](Decl *D) -> bool {
+ std::array<const char*, 2> FieldNamesInOrder{{"a", "b"}};
+ int i = 0;
+ for (auto Child : cast<DeclContext>(D)->decls()) {
+ if (FieldDecl *FD = dyn_cast<FieldDecl>(Child)) {
+ if (FD->getNameAsString() != FieldNamesInOrder[i++]) {
+ GTEST_NONFATAL_FAILURE_(
+ "Fields are in wrong order");
+ cast<DeclContext>(D)->dumpDeclContext();
+ D->dump();
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ assert(Check(From));
+ Check(To);
+}
+
+TEST_F(
+ Fixture,
+ DISABLED_CXXRecordDeclFieldsShouldBeInCorrectOrderEvenWhenWeImportFirstTheLastDecl) {
+
+ 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(has(fieldDecl()))));
+ ASSERT_TRUE(Verifier.match(To, cxxRecordDecl(has(fieldDecl()))));
+
+ auto Check = [](Decl *D) -> bool {
+ std::array<const char*, 3> FieldNamesInOrder{{"a", "b", "c"}};
+ int i = 0;
+ for (auto Child : cast<DeclContext>(D)->decls()) {
+ if (FieldDecl *FD = dyn_cast<FieldDecl>(Child)) {
+ if (FD->getNameAsString() != FieldNamesInOrder[i++]) {
+ GTEST_NONFATAL_FAILURE_(
+ "Fields are in wrong order");
+ cast<DeclContext>(D)->dumpDeclContext();
+ D->dump();
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ assert(Check(From));
+ Check(To);
+}
+
+TEST_F(Fixture, DISABLED_ShouldImportImplicitCXXRecordDecl) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ template <typename U>
+ struct declToImport {
+ };
+ )",Lang_CXX, "", Lang_CXX);
+
+ MatchVerifier<Decl> Verifier;
+ // matches the implicit decl
+ auto Matcher = classTemplateDecl(has(cxxRecordDecl(has(cxxRecordDecl()))));
+ ASSERT_TRUE(Verifier.match(From, Matcher));
+ EXPECT_TRUE(Verifier.match(To, Matcher));
+}
+
+TEST_F(
+ Fixture,
+ 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_F(Fixture, IDNSOrdinary) {
+ Decl *From, *To;
+ std::tie(From, To) = getImportedDecl(
+ R"(
+ 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_F(Fixture, 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_F(
+ Fixture,
+ 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));
+}
+
+struct ImportFunctions : Fixture {};
+
+TEST_F(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();
+
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. without a body
+ EXPECT_TRUE(!ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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();
+
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. with a body
+ EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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();
+
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. with a body
+ EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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();
+
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. with a body
+ EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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();
+
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. with a body
+ EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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();
+
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. with a body
+ EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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();
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_EQ(ImportedD, ImportedD1);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. without a body
+ EXPECT_TRUE(!ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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();
+ // There must be only one imported FunctionDecl ...
+ EXPECT_TRUE(FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern) ==
+ LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern));
+ FunctionDecl *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_EQ(ImportedD, ImportedD1);
+ EXPECT_TRUE(ImportedD == ToFD);
+ // .. with a body
+ EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
+}
+
+TEST_F(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_F(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_F(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_F(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());
+}
+
} // end namespace ast_matchers
} // end namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits