diff options
| -rw-r--r-- | clang/include/clang/AST/ASTImporter.h | 9 | ||||
| -rw-r--r-- | clang/lib/AST/ASTImporter.cpp | 165 | ||||
| -rw-r--r-- | clang/lib/AST/DeclBase.cpp | 2 | ||||
| -rw-r--r-- | clang/test/ASTMerge/class/test.cpp | 8 | ||||
| -rw-r--r-- | clang/unittests/AST/ASTImporterTest.cpp | 714 | 
5 files changed, 787 insertions, 111 deletions
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 42f99f3f3f5..de3bfbdf041 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -43,6 +43,15 @@ class TagDecl;  class TypeSourceInfo;  class Attr; +  // \brief Returns with a list of declarations started from the canonical decl +  // then followed by subsequent decls in the translation unit. +  // This gives a canonical list for each entry in the redecl chain. +  // `Decl::redecls()` gives a list of decls which always start from the +  // previous decl and the next item is actually the previous item in the order +  // of source locations.  Thus, `Decl::redecls()` gives different lists for +  // the different entries in a given redecl chain. +  llvm::SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D); +    /// Imports selected nodes from one AST context into another context,    /// merging AST nodes where appropriate.    class ASTImporter { diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index f812244ed3c..7dfcdeb8c4f 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -71,6 +71,25 @@  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 *> { @@ -195,6 +214,12 @@ namespace clang {                                          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, @@ -408,6 +433,8 @@ namespace clang {      // Importing overrides.      void ImportOverrides(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod); + +    FunctionDecl *FindFunctionTemplateSpecialization(FunctionDecl *FromFD);    };  template <typename InContainerTy> @@ -437,6 +464,25 @@ bool ASTNodeImporter::ImportTemplateArgumentListInfo<                                          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  //---------------------------------------------------------------------------- @@ -2252,23 +2298,17 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,    }    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) @@ -2277,6 +2317,7 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,      SourceLocation POI = Importer.Import(FTSInfo->getPointOfInstantiation()); +    TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();      ToFD->setFunctionTemplateSpecialization(          Template, ToTAList, /* InsertPos= */ nullptr,          TSK, FromTAArgsAsWritten ? &ToTAInfo : nullptr, POI); @@ -2312,7 +2353,31 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,    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; @@ -2323,13 +2388,27 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {    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) { @@ -2341,15 +2420,11 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {              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 @@ -2499,9 +2574,9 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {    }    ToFunction->setParams(Parameters); -  if (FoundWithoutBody) { +  if (FoundByLookup) {      auto *Recent = const_cast<FunctionDecl *>( -          FoundWithoutBody->getMostRecentDecl()); +          FoundByLookup->getMostRecentDecl());      ToFunction->setPreviousDecl(Recent);    } @@ -2523,10 +2598,11 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {      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); +      }      }    } @@ -2536,14 +2612,29 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {    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); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 7b7febdc410..64fac22bace 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1343,6 +1343,8 @@ bool DeclContext::decls_empty() const {  }  bool DeclContext::containsDecl(Decl *D) const { +  if (hasExternalLexicalStorage()) +    LoadLexicalDeclsFromExternalStorage();    return (D->getLexicalDeclContext() == this &&            (D->NextInContextAndBits.getPointer() || D == LastDecl));  } diff --git a/clang/test/ASTMerge/class/test.cpp b/clang/test/ASTMerge/class/test.cpp index 99926b649bc..ba553af4072 100644 --- a/clang/test/ASTMerge/class/test.cpp +++ b/clang/test/ASTMerge/class/test.cpp @@ -13,12 +13,12 @@  // CHECK: class1.cpp:19:3: note: enumerator 'b' with value 1 here  // CHECK: class2.cpp:12:3: note: enumerator 'a' with value 0 here -// CHECK: class1.cpp:36:8: warning: type 'F2' has incompatible definitions in different translation units -// CHECK: class1.cpp:39:3: note: friend declared here -// CHECK: class2.cpp:30:8: note: no corresponding friend here -  // CHECK: class1.cpp:43:8: warning: type 'F3' has incompatible definitions in different translation units  // CHECK: class1.cpp:46:3: note: friend declared here  // CHECK: class2.cpp:36:8: note: no corresponding friend here +// CHECK: class1.cpp:36:8: warning: type 'F2' has incompatible definitions in different translation units +// CHECK: class1.cpp:39:3: note: friend declared here +// CHECK: class2.cpp:30:8: note: no corresponding friend here +  // CHECK: 4 warnings generated. 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  | 

