diff options
author | Gabor Marton <martongabesz@gmail.com> | 2018-06-25 14:41:58 +0000 |
---|---|---|
committer | Gabor Marton <martongabesz@gmail.com> | 2018-06-25 14:41:58 +0000 |
commit | d0ec7bd1c7ac80c3a76cf5fb1a71132fad244001 (patch) | |
tree | 77f519294c96aae9811babed4f559e23494b9893 /clang/unittests | |
parent | 871b0591294ba68f694ec7105e0479499354f99e (diff) | |
download | bcm5719-llvm-d0ec7bd1c7ac80c3a76cf5fb1a71132fad244001.tar.gz bcm5719-llvm-d0ec7bd1c7ac80c3a76cf5fb1a71132fad244001.zip |
[ASTImporter] Import the whole redecl chain of functions
Summary:
With this patch when any `FunctionDecl` of a redeclaration chain is imported
then we bring in the whole declaration chain. This involves functions and
function template specializations. Also friend functions are affected. The
chain is imported as it is in the "from" tu, the order of the redeclarations
are kept. I also changed the lookup logic in order to find friends, but first
making them visible in their declaration context. We may have long
redeclaration chains if all TU contains the same prototype, but our
measurements shows no degradation in time of CTU analysis (Tmux, Xerces,
Bitcoin, Protobuf). Also, as further work we could squash redundant
prototypes, but first ensure that functionality is working properly; then
should we optimize.
This may seem like a huge patch, sorry about that. But, most of the changes are
new tests, changes in the production code is not that much. I also tried to
create a smaller patch which does not affect specializations, but that patch
failed to pass some of the `clang-import-test`s because there we import
function specializations. Also very importantly, we can't just change the
import of `FunctionDecl`s without changing the import of function template
specializations because they are handled as `FunctionDecl`s.
Reviewers: a.sidorin, r.stahl, xazax.hun, balazske
Subscribers: rnkovacs, dkrupp, cfe-commits
Differential Revision: https://reviews.llvm.org/D47532
llvm-svn: 335480
Diffstat (limited to 'clang/unittests')
-rw-r--r-- | clang/unittests/AST/ASTImporterTest.cpp | 714 |
1 files changed, 644 insertions, 70 deletions
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 77dbd43a7d2..69611a46247 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -20,7 +20,7 @@ #include "DeclMatcher.h" #include "Language.h" -#include "gtest/gtest.h" +#include "gmock/gmock.h" #include "llvm/ADT/StringMap.h" namespace clang { @@ -428,6 +428,48 @@ 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(); + + 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\"; }", @@ -1673,34 +1715,6 @@ TEST_P( 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")); @@ -1710,7 +1724,7 @@ TEST_P(ImportFunctions, 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()); } @@ -1727,30 +1741,40 @@ TEST_P(ImportFunctions, DefinitionShouldBeImportedAsADefinition) { 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) { @@ -1759,23 +1783,48 @@ 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) { @@ -1784,23 +1833,24 @@ 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) { @@ -1823,38 +1873,40 @@ 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) { @@ -1894,6 +1946,202 @@ TEST_P(ImportFunctions, VirtualFlagShouldBePreservedWhenImportingPrototype) { 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()) @@ -2024,9 +2272,328 @@ TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) { 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"}, @@ -2048,5 +2615,12 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterTestBase, 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 |