namespace clang {
+ template <class T>
+ SmallVector<Decl*, 2>
+ getCanonicalForwardRedeclChain(Redeclarable<T>* D) {
+ SmallVector<Decl*, 2> Redecls;
+ for (auto *R : D->getFirstDecl()->redecls()) {
+ if (R != D->getFirstDecl())
+ Redecls.push_back(R);
+ }
+ Redecls.push_back(D->getFirstDecl());
+ std::reverse(Redecls.begin(), Redecls.end());
+ return Redecls;
+ }
+
+ SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D) {
+ // Currently only FunctionDecl is supported
+ auto FD = cast<FunctionDecl>(D);
+ return getCanonicalForwardRedeclChain<FunctionDecl>(FD);
+ }
+
class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, QualType>,
public DeclVisitor<ASTNodeImporter, Decl *>,
public StmtVisitor<ASTNodeImporter, Stmt *> {
const InContainerTy &Container,
TemplateArgumentListInfo &Result);
+ using TemplateArgsTy = SmallVector<TemplateArgument, 8>;
+ using OptionalTemplateArgsTy = Optional<TemplateArgsTy>;
+ std::tuple<FunctionTemplateDecl *, OptionalTemplateArgsTy>
+ ImportFunctionTemplateWithTemplateArgsFromSpecialization(
+ FunctionDecl *FromFD);
+
bool ImportTemplateInformation(FunctionDecl *FromFD, FunctionDecl *ToFD);
bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord,
// Importing overrides.
void ImportOverrides(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod);
+
+ FunctionDecl *FindFunctionTemplateSpecialization(FunctionDecl *FromFD);
};
template <typename InContainerTy>
From.arguments(), Result);
}
+std::tuple<FunctionTemplateDecl *, ASTNodeImporter::OptionalTemplateArgsTy>
+ASTNodeImporter::ImportFunctionTemplateWithTemplateArgsFromSpecialization(
+ FunctionDecl *FromFD) {
+ assert(FromFD->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization);
+ auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
+ auto *Template = cast_or_null<FunctionTemplateDecl>(
+ Importer.Import(FTSInfo->getTemplate()));
+
+ // Import template arguments.
+ auto TemplArgs = FTSInfo->TemplateArguments->asArray();
+ TemplateArgsTy ToTemplArgs;
+ if (ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(),
+ ToTemplArgs)) // Error during import.
+ return std::make_tuple(Template, OptionalTemplateArgsTy());
+
+ return std::make_tuple(Template, ToTemplArgs);
+}
+
} // namespace clang
//----------------------------------------------------------------------------
}
case FunctionDecl::TK_FunctionTemplateSpecialization: {
- auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
- auto *Template = cast_or_null<FunctionTemplateDecl>(
- Importer.Import(FTSInfo->getTemplate()));
- if (!Template)
- return true;
- TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();
-
- // Import template arguments.
- auto TemplArgs = FTSInfo->TemplateArguments->asArray();
- SmallVector<TemplateArgument, 8> ToTemplArgs;
- if (ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(),
- ToTemplArgs))
+ FunctionTemplateDecl* Template;
+ OptionalTemplateArgsTy ToTemplArgs;
+ std::tie(Template, ToTemplArgs) =
+ ImportFunctionTemplateWithTemplateArgsFromSpecialization(FromFD);
+ if (!Template || !ToTemplArgs)
return true;
TemplateArgumentList *ToTAList = TemplateArgumentList::CreateCopy(
- Importer.getToContext(), ToTemplArgs);
+ Importer.getToContext(), *ToTemplArgs);
+ auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
TemplateArgumentListInfo ToTAInfo;
const auto *FromTAArgsAsWritten = FTSInfo->TemplateArgumentsAsWritten;
if (FromTAArgsAsWritten)
SourceLocation POI = Importer.Import(FTSInfo->getPointOfInstantiation());
+ TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();
ToFD->setFunctionTemplateSpecialization(
Template, ToTAList, /* InsertPos= */ nullptr,
TSK, FromTAArgsAsWritten ? &ToTAInfo : nullptr, POI);
llvm_unreachable("All cases should be covered!");
}
+FunctionDecl *
+ASTNodeImporter::FindFunctionTemplateSpecialization(FunctionDecl *FromFD) {
+ FunctionTemplateDecl* Template;
+ OptionalTemplateArgsTy ToTemplArgs;
+ std::tie(Template, ToTemplArgs) =
+ ImportFunctionTemplateWithTemplateArgsFromSpecialization(FromFD);
+ if (!Template || !ToTemplArgs)
+ return nullptr;
+
+ void *InsertPos = nullptr;
+ auto *FoundSpec = Template->findSpecialization(*ToTemplArgs, InsertPos);
+ return FoundSpec;
+}
+
Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
+
+ SmallVector<Decl*, 2> Redecls = getCanonicalForwardRedeclChain(D);
+ auto RedeclIt = Redecls.begin();
+ // Import the first part of the decl chain. I.e. import all previous
+ // declarations starting from the canonical decl.
+ for (; RedeclIt != Redecls.end() && *RedeclIt != D; ++RedeclIt)
+ if (!Importer.Import(*RedeclIt))
+ return nullptr;
+ assert(*RedeclIt == D);
+
// Import the major distinguishing characteristics of this function.
DeclContext *DC, *LexicalDC;
DeclarationName Name;
if (ToD)
return ToD;
- const FunctionDecl *FoundWithoutBody = nullptr;
-
+ const FunctionDecl *FoundByLookup = nullptr;
+
+ // If this is a function template specialization, then try to find the same
+ // existing specialization in the "to" context. The localUncachedLookup
+ // below will not find any specialization, but would find the primary
+ // template; thus, we have to skip normal lookup in case of specializations.
+ // FIXME handle member function templates (TK_MemberSpecialization) similarly?
+ if (D->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization) {
+ if (FunctionDecl *FoundFunction = FindFunctionTemplateSpecialization(D)) {
+ if (D->doesThisDeclarationHaveABody() &&
+ FoundFunction->hasBody())
+ return Importer.Imported(D, FoundFunction);
+ FoundByLookup = FoundFunction;
+ }
+ }
// Try to find a function in our own ("to") context with the same name, same
// type, and in the same context as the function we're importing.
- if (!LexicalDC->isFunctionOrMethod()) {
+ else if (!LexicalDC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
- unsigned IDNS = Decl::IDNS_Ordinary;
+ unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_OrdinaryFriend;
SmallVector<NamedDecl *, 2> FoundDecls;
DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
for (auto *FoundDecl : FoundDecls) {
D->hasExternalFormalLinkage()) {
if (Importer.IsStructurallyEquivalent(D->getType(),
FoundFunction->getType())) {
- // FIXME: Actually try to merge the body and other attributes.
- const FunctionDecl *FromBodyDecl = nullptr;
- D->hasBody(FromBodyDecl);
- if (D == FromBodyDecl && !FoundFunction->hasBody()) {
- // This function is needed to merge completely.
- FoundWithoutBody = FoundFunction;
+ if (D->doesThisDeclarationHaveABody() &&
+ FoundFunction->hasBody())
+ return Importer.Imported(D, FoundFunction);
+ FoundByLookup = FoundFunction;
break;
- }
- return Importer.Imported(D, FoundFunction);
}
// FIXME: Check for overloading more carefully, e.g., by boosting
}
ToFunction->setParams(Parameters);
- if (FoundWithoutBody) {
+ if (FoundByLookup) {
auto *Recent = const_cast<FunctionDecl *>(
- FoundWithoutBody->getMostRecentDecl());
+ FoundByLookup->getMostRecentDecl());
ToFunction->setPreviousDecl(Recent);
}
ToFunction->setType(T);
}
- // Import the body, if any.
- if (Stmt *FromBody = D->getBody()) {
- if (Stmt *ToBody = Importer.Import(FromBody)) {
- ToFunction->setBody(ToBody);
+ if (D->doesThisDeclarationHaveABody()) {
+ if (Stmt *FromBody = D->getBody()) {
+ if (Stmt *ToBody = Importer.Import(FromBody)) {
+ ToFunction->setBody(ToBody);
+ }
}
}
if (ImportTemplateInformation(D, ToFunction))
return nullptr;
- // Add this function to the lexical context.
- // NOTE: If the function is templated declaration, it should be not added into
- // LexicalDC. But described template is imported during import of
- // FunctionTemplateDecl (it happens later). So, we use source declaration
- // to determine if we should add the result function.
- if (!D->getDescribedFunctionTemplate())
+ bool IsFriend = D->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend);
+
+ // TODO Can we generalize this approach to other AST nodes as well?
+ if (D->getDeclContext()->containsDecl(D))
+ DC->addDeclInternal(ToFunction);
+ if (DC != LexicalDC && D->getLexicalDeclContext()->containsDecl(D))
LexicalDC->addDeclInternal(ToFunction);
+ // Friend declaration's lexical context is the befriending class, but the
+ // semantic context is the enclosing scope of the befriending class.
+ // We want the friend functions to be found in the semantic context by lookup.
+ // FIXME should we handle this generically in VisitFriendDecl?
+ // In Other cases when LexicalDC != DC we don't want it to be added,
+ // e.g out-of-class definitions like void B::f() {} .
+ if (LexicalDC != DC && IsFriend) {
+ DC->makeDeclVisibleInContext(ToFunction);
+ }
+
+ // Import the rest of the chain. I.e. import all subsequent declarations.
+ for (++RedeclIt; RedeclIt != Redecls.end(); ++RedeclIt)
+ if (!Importer.Import(*RedeclIt))
+ return nullptr;
+
if (auto *FromCXXMethod = dyn_cast<CXXMethodDecl>(D))
ImportOverrides(cast<CXXMethodDecl>(ToFunction), FromCXXMethod);
#include "DeclMatcher.h"
#include "Language.h"
-#include "gtest/gtest.h"
+#include "gmock/gmock.h"
#include "llvm/ADT/StringMap.h"
namespace clang {
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();
+
+ 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() { \"foo\"; }",
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);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
- EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
-TEST_P(ImportFunctions, DISABLED_ImportPrototypeOfRecursiveFunction) {
+TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) {
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
auto Pattern = functionDecl(hasName("f"));
- FunctionDecl *PrototypeFD =
- FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ auto *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Proto
- Decl *ImportedD = Import(PrototypeFD, Lang_CXX);
+ Decl *ImportedD = Import(From, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
- EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
- EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+ 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"));
- FunctionDecl *DefinitionFD =
- LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ auto *From =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Def
- Decl *ImportedD = Import(DefinitionFD, Lang_CXX);
+ Decl *ImportedD = Import(From, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
- EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
- EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+ 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) {
Decl *ImportedD;
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
- FunctionDecl *FromD =
- FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ auto *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);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ Import(FromD, Lang_CXX);
}
- Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ 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);
- EXPECT_EQ(ImportedD, ImportedD1);
- EXPECT_TRUE(!cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
+ auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == To0);
+ EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
}
TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
Decl *ImportedD;
{
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
- FunctionDecl *FromD =
- FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
-
+ auto *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);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
+ 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());
+
+ 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) {
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
- EXPECT_TRUE(!ProtoD->doesThisDeclarationHaveABody());
+ EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
FunctionDecl *DefinitionD =
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
}
-TEST_P(ImportFunctions, DISABLED_ImportPrototypeThenProtoAndDefinition) {
+TEST_P(ImportFunctions, ImportPrototypeThenProtoAndDefinition) {
auto Pattern = functionDecl(hasName("f"));
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
- FunctionDecl *FromD =
- FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
-
+ auto *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);
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
- ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 3u);
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
- EXPECT_TRUE(!ProtoD->doesThisDeclarationHaveABody());
+ EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
+
FunctionDecl *DefinitionD =
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
- EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
+
+ EXPECT_TRUE(DefinitionD->getPreviousDecl());
+ EXPECT_FALSE(DefinitionD->getPreviousDecl()->doesThisDeclarationHaveABody());
+ EXPECT_EQ(DefinitionD->getPreviousDecl()->getPreviousDecl(), ProtoD);
}
TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) {
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);
+}
+
+// This test is disabled, because ATM we create a redundant FunctionDecl. We
+// start the import with the definition of `f` then we continue with the import
+// of the type of `f` which involves `X`. During the import of `X` we start
+// again the import of the definition of `f` and then finally we create the
+// node. But then in the first frame of `VisitFunctionDecl` we create a node
+// again since we do not check if such a node exists yet or not. This is being
+// fixed in a separate patch: https://reviews.llvm.org/D47632
+// FIXME enable this test once the above patch is approved.
+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());
+}
+
+// This test is disabled, because ATM we create a redundant FunctionDecl. We
+// start the import with the definition of `f` then we continue with the import
+// of the type of `f` which involves `X`. During the import of `X` we start
+// again the import of the definition of `f` and then finally we create the
+// node. But then in the first frame of `VisitFunctionDecl` we create a node
+// again since we do not check if such a node exists yet or not. This is being
+// fixed in a separate patch: https://reviews.llvm.org/D47632
+// FIXME enable this test once the above patch is approved.
+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())
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"},
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