r349349 - [ASTImporter] Fix redecl chain of classes and class templates

Gabor Marton via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 17 04:42:12 PST 2018


Author: martong
Date: Mon Dec 17 04:42:12 2018
New Revision: 349349

URL: http://llvm.org/viewvc/llvm-project?rev=349349&view=rev
Log:
[ASTImporter] Fix redecl chain of classes and class templates

Summary:
The crux of the issue that is being fixed is that lookup could not find
previous decls of a friend class. The solution involves making the
friend declarations visible in their decl context (i.e. adding them to
the lookup table).
Also, we simplify `VisitRecordDecl` greatly.

This fix involves two other repairs (without these the unittests fail):
(1) We could not handle the addition of injected class types properly
when a redecl chain was involved, now this is fixed.
(2) DeclContext::removeDecl failed if the lookup table in Vector form
did not contain the to be removed element. This caused troubles in
ASTImporter::ImportDeclContext. This is also fixed.

Reviewers: a_sidorin, balazske, a.sidorin

Subscribers: rnkovacs, dkrupp, Szelethus, cfe-commits

Differential Revision: https://reviews.llvm.org/D53655

Modified:
    cfe/trunk/docs/LibASTMatchersReference.html
    cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
    cfe/trunk/lib/AST/ASTImporter.cpp
    cfe/trunk/lib/AST/DeclBase.cpp
    cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
    cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
    cfe/trunk/unittests/AST/ASTImporterTest.cpp
    cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp

Modified: cfe/trunk/docs/LibASTMatchersReference.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LibASTMatchersReference.html?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/docs/LibASTMatchersReference.html (original)
+++ cfe/trunk/docs/LibASTMatchersReference.html Mon Dec 17 04:42:12 2018
@@ -300,6 +300,16 @@ Example matches f
 </pre></td></tr>
 
 
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('indirectFieldDecl0')"><a name="indirectFieldDecl0Anchor">indirectFieldDecl</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1IndirectFieldDecl.html">IndirectFieldDecl</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="indirectFieldDecl0"><pre>Matches indirect field declarations.
+
+Given
+  struct X { struct { int a; }; };
+indirectFieldDecl()
+  matches 'a'.
+</pre></td></tr>
+
+
 <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('labelDecl0')"><a name="labelDecl0Anchor">labelDecl</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1LabelDecl.html">LabelDecl</a>>...</td></tr>
 <tr><td colspan="4" class="doc" id="labelDecl0"><pre>Matches a declaration of label.
 

Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h Mon Dec 17 04:42:12 2018
@@ -1158,6 +1158,17 @@ extern const internal::VariadicDynCastAl
 ///   matches 'm'.
 extern const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> fieldDecl;
 
+/// Matches indirect field declarations.
+///
+/// Given
+/// \code
+///   struct X { struct { int a; }; };
+/// \endcode
+/// indirectFieldDecl()
+///   matches 'a'.
+extern const internal::VariadicDynCastAllOfMatcher<Decl, IndirectFieldDecl>
+    indirectFieldDecl;
+
 /// Matches function declarations.
 ///
 /// Example matches f

Modified: cfe/trunk/lib/AST/ASTImporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTImporter.cpp (original)
+++ cfe/trunk/lib/AST/ASTImporter.cpp Mon Dec 17 04:42:12 2018
@@ -122,6 +122,8 @@ namespace clang {
       return getCanonicalForwardRedeclChain<FunctionDecl>(FD);
     if (auto *VD = dyn_cast<VarDecl>(D))
       return getCanonicalForwardRedeclChain<VarDecl>(VD);
+    if (auto *TD = dyn_cast<TagDecl>(D))
+      return getCanonicalForwardRedeclChain<TagDecl>(TD);
     llvm_unreachable("Bad declaration kind");
   }
 
@@ -2607,10 +2609,9 @@ ExpectedDecl ASTNodeImporter::VisitRecor
       return std::move(Err);
     IDNS = Decl::IDNS_Ordinary;
   } else if (Importer.getToContext().getLangOpts().CPlusPlus)
-    IDNS |= Decl::IDNS_Ordinary;
+    IDNS |= Decl::IDNS_Ordinary | Decl::IDNS_TagFriend;
 
   // We may already have a record of the same name; try to find and match it.
-  RecordDecl *AdoptDecl = nullptr;
   RecordDecl *PrevDecl = nullptr;
   if (!DC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
@@ -2643,26 +2644,22 @@ ExpectedDecl ASTNodeImporter::VisitRecor
       }
 
       if (auto *FoundRecord = dyn_cast<RecordDecl>(Found)) {
-        if (!SearchName) {
+        // Do not emit false positive diagnostic in case of unnamed
+        // struct/union and in case of anonymous structs.  Would be false
+        // because there may be several anonymous/unnamed structs in a class.
+        // E.g. these are both valid:
+        //  struct A { // unnamed structs
+        //    struct { struct A *next; } entry0;
+        //    struct { struct A *next; } entry1;
+        //  };
+        //  struct X { struct { int a; }; struct { int b; }; }; // anon structs
+        if (!SearchName)
           if (!IsStructuralMatch(D, FoundRecord, false))
             continue;
-        } else {
-          if (!IsStructuralMatch(D, FoundRecord)) {
-            ConflictingDecls.push_back(FoundDecl);
-            continue;
-          }
-        }
 
-        PrevDecl = FoundRecord;
-
-        if (RecordDecl *FoundDef = FoundRecord->getDefinition()) {
-          if ((SearchName && !D->isCompleteDefinition() && !IsFriendTemplate)
-              || (D->isCompleteDefinition() &&
-                  D->isAnonymousStructOrUnion()
-                    == FoundDef->isAnonymousStructOrUnion())) {
-            // The record types structurally match, or the "from" translation
-            // unit only had a forward declaration anyway; call it the same
-            // function.
+        if (IsStructuralMatch(D, FoundRecord)) {
+          RecordDecl *FoundDef = FoundRecord->getDefinition();
+          if (D->isThisDeclarationADefinition() && FoundDef) {
             // FIXME: Structural equivalence check should check for same
             // user-defined methods.
             Importer.MapImported(D, FoundDef);
@@ -2670,46 +2667,20 @@ ExpectedDecl ASTNodeImporter::VisitRecor
               auto *FoundCXX = dyn_cast<CXXRecordDecl>(FoundDef);
               assert(FoundCXX && "Record type mismatch");
 
-              if (D->isCompleteDefinition() && !Importer.isMinimalImport())
+              if (!Importer.isMinimalImport())
                 // FoundDef may not have every implicit method that D has
                 // because implicit methods are created only if they are used.
                 if (Error Err = ImportImplicitMethods(DCXX, FoundCXX))
                   return std::move(Err);
             }
-            return FoundDef;
-          }
-          if (IsFriendTemplate)
-            continue;
-        } else if (!D->isCompleteDefinition()) {
-          // We have a forward declaration of this type, so adopt that forward
-          // declaration rather than building a new one.
-
-          // If one or both can be completed from external storage then try one
-          // last time to complete and compare them before doing this.
-
-          if (FoundRecord->hasExternalLexicalStorage() &&
-              !FoundRecord->isCompleteDefinition())
-            FoundRecord->getASTContext().getExternalSource()->CompleteType(FoundRecord);
-          if (D->hasExternalLexicalStorage())
-            D->getASTContext().getExternalSource()->CompleteType(D);
-
-          if (FoundRecord->isCompleteDefinition() &&
-              D->isCompleteDefinition() &&
-              !IsStructuralMatch(D, FoundRecord)) {
-            ConflictingDecls.push_back(FoundDecl);
-            continue;
           }
-
-          AdoptDecl = FoundRecord;
-          continue;
+          PrevDecl = FoundRecord->getMostRecentDecl();
+          break;
         }
-
-        continue;
-      } else if (isa<ValueDecl>(Found))
-        continue;
+      }
 
       ConflictingDecls.push_back(FoundDecl);
-    }
+    } // for
 
     if (!ConflictingDecls.empty() && SearchName) {
       Name = Importer.HandleNameConflict(Name, DC, IDNS,
@@ -2725,79 +2696,90 @@ ExpectedDecl ASTNodeImporter::VisitRecor
     return BeginLocOrErr.takeError();
 
   // Create the record declaration.
-  RecordDecl *D2 = AdoptDecl;
-  if (!D2) {
-    CXXRecordDecl *D2CXX = nullptr;
-    if (auto *DCXX = dyn_cast<CXXRecordDecl>(D)) {
-      if (DCXX->isLambda()) {
-        auto TInfoOrErr = import(DCXX->getLambdaTypeInfo());
-        if (!TInfoOrErr)
-          return TInfoOrErr.takeError();
-        if (GetImportedOrCreateSpecialDecl(
-                D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(),
-                DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(),
-                DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
-          return D2CXX;
-        ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
-        if (!CDeclOrErr)
-          return CDeclOrErr.takeError();
-        D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr);
-      } else if (DCXX->isInjectedClassName()) {
-        // We have to be careful to do a similar dance to the one in
-        // Sema::ActOnStartCXXMemberDeclarations
-        CXXRecordDecl *const PrevDecl = nullptr;
-        const bool DelayTypeCreation = true;
-        if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
-                                    D->getTagKind(), DC, *BeginLocOrErr, Loc,
-                                    Name.getAsIdentifierInfo(), PrevDecl,
-                                    DelayTypeCreation))
-          return D2CXX;
-        Importer.getToContext().getTypeDeclType(
-            D2CXX, dyn_cast<CXXRecordDecl>(DC));
-      } else {
-        if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
-                                    D->getTagKind(), DC, *BeginLocOrErr, Loc,
-                                    Name.getAsIdentifierInfo(),
-                                    cast_or_null<CXXRecordDecl>(PrevDecl)))
-          return D2CXX;
-      }
+  RecordDecl *D2 = nullptr;
+  CXXRecordDecl *D2CXX = nullptr;
+  if (auto *DCXX = dyn_cast<CXXRecordDecl>(D)) {
+    if (DCXX->isLambda()) {
+      auto TInfoOrErr = import(DCXX->getLambdaTypeInfo());
+      if (!TInfoOrErr)
+        return TInfoOrErr.takeError();
+      if (GetImportedOrCreateSpecialDecl(
+              D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(),
+              DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(),
+              DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
+        return D2CXX;
+      ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
+      if (!CDeclOrErr)
+        return CDeclOrErr.takeError();
+      D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr);
+    } else if (DCXX->isInjectedClassName()) {
+      // We have to be careful to do a similar dance to the one in
+      // Sema::ActOnStartCXXMemberDeclarations
+      const bool DelayTypeCreation = true;
+      if (GetImportedOrCreateDecl(
+              D2CXX, D, Importer.getToContext(), D->getTagKind(), DC,
+              *BeginLocOrErr, Loc, Name.getAsIdentifierInfo(),
+              cast_or_null<CXXRecordDecl>(PrevDecl), DelayTypeCreation))
+        return D2CXX;
+      Importer.getToContext().getTypeDeclType(
+          D2CXX, dyn_cast<CXXRecordDecl>(DC));
+    } else {
+      if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
+                                  D->getTagKind(), DC, *BeginLocOrErr, Loc,
+                                  Name.getAsIdentifierInfo(),
+                                  cast_or_null<CXXRecordDecl>(PrevDecl)))
+        return D2CXX;
+    }
 
-      D2 = D2CXX;
-      D2->setAccess(D->getAccess());
-      D2->setLexicalDeclContext(LexicalDC);
-      if (!DCXX->getDescribedClassTemplate() || DCXX->isImplicit())
-        LexicalDC->addDeclInternal(D2);
-
-      if (ClassTemplateDecl *FromDescribed =
-          DCXX->getDescribedClassTemplate()) {
-        ClassTemplateDecl *ToDescribed;
-        if (Error Err = importInto(ToDescribed, FromDescribed))
-          return std::move(Err);
-        D2CXX->setDescribedClassTemplate(ToDescribed);
-        if (!DCXX->isInjectedClassName() && !IsFriendTemplate) {
-          // In a record describing a template the type should be an
-          // InjectedClassNameType (see Sema::CheckClassTemplate). Update the
-          // previously set type to the correct value here (ToDescribed is not
-          // available at record create).
-          // FIXME: The previous type is cleared but not removed from
-          // ASTContext's internal storage.
-          CXXRecordDecl *Injected = nullptr;
-          for (NamedDecl *Found : D2CXX->noload_lookup(Name)) {
-            auto *Record = dyn_cast<CXXRecordDecl>(Found);
-            if (Record && Record->isInjectedClassName()) {
-              Injected = Record;
-              break;
-            }
-          }
-          D2CXX->setTypeForDecl(nullptr);
-          Importer.getToContext().getInjectedClassNameType(D2CXX,
-              ToDescribed->getInjectedClassNameSpecialization());
-          if (Injected) {
-            Injected->setTypeForDecl(nullptr);
-            Importer.getToContext().getTypeDeclType(Injected, D2CXX);
+    D2 = D2CXX;
+    D2->setAccess(D->getAccess());
+    D2->setLexicalDeclContext(LexicalDC);
+    if (!DCXX->getDescribedClassTemplate() || DCXX->isImplicit())
+      LexicalDC->addDeclInternal(D2);
+
+    if (LexicalDC != DC && D->isInIdentifierNamespace(Decl::IDNS_TagFriend))
+      DC->makeDeclVisibleInContext(D2);
+
+    if (ClassTemplateDecl *FromDescribed =
+        DCXX->getDescribedClassTemplate()) {
+      ClassTemplateDecl *ToDescribed;
+      if (Error Err = importInto(ToDescribed, FromDescribed))
+        return std::move(Err);
+      D2CXX->setDescribedClassTemplate(ToDescribed);
+      if (!DCXX->isInjectedClassName() && !IsFriendTemplate) {
+        // In a record describing a template the type should be an
+        // InjectedClassNameType (see Sema::CheckClassTemplate). Update the
+        // previously set type to the correct value here (ToDescribed is not
+        // available at record create).
+        // FIXME: The previous type is cleared but not removed from
+        // ASTContext's internal storage.
+        CXXRecordDecl *Injected = nullptr;
+        for (NamedDecl *Found : D2CXX->noload_lookup(Name)) {
+          auto *Record = dyn_cast<CXXRecordDecl>(Found);
+          if (Record && Record->isInjectedClassName()) {
+            Injected = Record;
+            break;
           }
         }
-      } else if (MemberSpecializationInfo *MemberInfo =
+        // Create an injected type for the whole redecl chain.
+        SmallVector<Decl *, 2> Redecls =
+            getCanonicalForwardRedeclChain(D2CXX);
+        for (auto *R : Redecls) {
+          auto *RI = cast<CXXRecordDecl>(R);
+          RI->setTypeForDecl(nullptr);
+          // Below we create a new injected type and assign that to the
+          // canonical decl, subsequent declarations in the chain will reuse
+          // that type.
+          Importer.getToContext().getInjectedClassNameType(
+              RI, ToDescribed->getInjectedClassNameSpecialization());
+        }
+        // Set the new type for the previous injected decl too.
+        if (Injected) {
+          Injected->setTypeForDecl(nullptr);
+          Importer.getToContext().getTypeDeclType(Injected, D2CXX);
+        }
+      }
+    } else if (MemberSpecializationInfo *MemberInfo =
                    DCXX->getMemberSpecializationInfo()) {
         TemplateSpecializationKind SK =
             MemberInfo->getTemplateSpecializationKind();
@@ -2814,27 +2796,24 @@ ExpectedDecl ASTNodeImporter::VisitRecor
             *POIOrErr);
         else
           return POIOrErr.takeError();
-      }
-
-    } else {
-      if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(),
-                                  D->getTagKind(), DC, *BeginLocOrErr, Loc,
-                                  Name.getAsIdentifierInfo(), PrevDecl))
-        return D2;
-      D2->setLexicalDeclContext(LexicalDC);
-      LexicalDC->addDeclInternal(D2);
     }
 
-    if (auto QualifierLocOrErr = import(D->getQualifierLoc()))
-      D2->setQualifierInfo(*QualifierLocOrErr);
-    else
-      return QualifierLocOrErr.takeError();
-
-    if (D->isAnonymousStructOrUnion())
-      D2->setAnonymousStructOrUnion(true);
+  } else {
+    if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(),
+                                D->getTagKind(), DC, *BeginLocOrErr, Loc,
+                                Name.getAsIdentifierInfo(), PrevDecl))
+      return D2;
+    D2->setLexicalDeclContext(LexicalDC);
+    LexicalDC->addDeclInternal(D2);
   }
 
-  Importer.MapImported(D, D2);
+  if (auto QualifierLocOrErr = import(D->getQualifierLoc()))
+    D2->setQualifierInfo(*QualifierLocOrErr);
+  else
+    return QualifierLocOrErr.takeError();
+
+  if (D->isAnonymousStructOrUnion())
+    D2->setAnonymousStructOrUnion(true);
 
   if (D->isCompleteDefinition())
     if (Error Err = ImportDefinition(D, D2, IDK_Default))
@@ -4990,14 +4969,12 @@ static ClassTemplateDecl *getDefinition(
 ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
   bool IsFriend = D->getFriendObjectKind() != Decl::FOK_None;
 
-  // If this record has a definition in the translation unit we're coming from,
-  // but this particular declaration is not that definition, import the
+  // If this template has a definition in the translation unit we're coming
+  // from, but this particular declaration is not that definition, import the
   // definition and map to that.
-  auto *Definition =
-      cast_or_null<CXXRecordDecl>(D->getTemplatedDecl()->getDefinition());
-  if (Definition && Definition != D->getTemplatedDecl() && !IsFriend) {
-    if (ExpectedDecl ImportedDefOrErr = import(
-        Definition->getDescribedClassTemplate()))
+  ClassTemplateDecl *Definition = getDefinition(D);
+  if (Definition && Definition != D && !IsFriend) {
+    if (ExpectedDecl ImportedDefOrErr = import(Definition))
       return Importer.MapImported(D, *ImportedDefOrErr);
     else
       return ImportedDefOrErr.takeError();
@@ -5013,38 +4990,29 @@ ExpectedDecl ASTNodeImporter::VisitClass
   if (ToD)
     return ToD;
 
+  ClassTemplateDecl *FoundByLookup = nullptr;
+
   // We may already have a template of the same name; try to find and match it.
   if (!DC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
     SmallVector<NamedDecl *, 2> FoundDecls;
     DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
     for (auto *FoundDecl : FoundDecls) {
-      if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
+      if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary |
+                                              Decl::IDNS_TagFriend))
         continue;
 
       Decl *Found = FoundDecl;
-      if (auto *FoundTemplate = dyn_cast<ClassTemplateDecl>(Found)) {
-
-        // The class to be imported is a definition.
-        if (D->isThisDeclarationADefinition()) {
-          // Lookup will find the fwd decl only if that is more recent than the
-          // definition. So, try to get the definition if that is available in
-          // the redecl chain.
-          ClassTemplateDecl *TemplateWithDef = getDefinition(FoundTemplate);
-          if (TemplateWithDef)
-            FoundTemplate = TemplateWithDef;
-          else
-            continue;
-        }
+      auto *FoundTemplate = dyn_cast<ClassTemplateDecl>(Found);
+      if (FoundTemplate) {
 
         if (IsStructuralMatch(D, FoundTemplate)) {
-          if (!IsFriend) {
-            Importer.MapImported(D->getTemplatedDecl(),
-                                 FoundTemplate->getTemplatedDecl());
-            return Importer.MapImported(D, FoundTemplate);
+          ClassTemplateDecl *TemplateWithDef = getDefinition(FoundTemplate);
+          if (D->isThisDeclarationADefinition() && TemplateWithDef) {
+            return Importer.MapImported(D, TemplateWithDef);
           }
-
-          continue;
+          FoundByLookup = FoundTemplate;
+          break;
         }
       }
 
@@ -5081,18 +5049,39 @@ ExpectedDecl ASTNodeImporter::VisitClass
 
   ToTemplated->setDescribedClassTemplate(D2);
 
-  if (ToTemplated->getPreviousDecl()) {
-    assert(
-        ToTemplated->getPreviousDecl()->getDescribedClassTemplate() &&
-        "Missing described template");
-    D2->setPreviousDecl(
-        ToTemplated->getPreviousDecl()->getDescribedClassTemplate());
-  }
   D2->setAccess(D->getAccess());
   D2->setLexicalDeclContext(LexicalDC);
-  if (!IsFriend)
+
+  if (D->getDeclContext()->containsDeclAndLoad(D))
+    DC->addDeclInternal(D2);
+  if (DC != LexicalDC && D->getLexicalDeclContext()->containsDeclAndLoad(D))
     LexicalDC->addDeclInternal(D2);
 
+  if (FoundByLookup) {
+    auto *Recent =
+        const_cast<ClassTemplateDecl *>(FoundByLookup->getMostRecentDecl());
+
+    // It is possible that during the import of the class template definition
+    // we start the import of a fwd friend decl of the very same class template
+    // and we add the fwd friend decl to the lookup table. But the ToTemplated
+    // had been created earlier and by that time the lookup could not find
+    // anything existing, so it has no previous decl. Later, (still during the
+    // import of the fwd friend decl) we start to import the definition again
+    // and this time the lookup finds the previous fwd friend class template.
+    // In this case we must set up the previous decl for the templated decl.
+    if (!ToTemplated->getPreviousDecl()) {
+      CXXRecordDecl *PrevTemplated =
+          FoundByLookup->getTemplatedDecl()->getMostRecentDecl();
+      if (ToTemplated != PrevTemplated)
+        ToTemplated->setPreviousDecl(PrevTemplated);
+    }
+
+    D2->setPreviousDecl(Recent);
+  }
+
+  if (LexicalDC != DC && IsFriend)
+    DC->makeDeclVisibleInContext(D2);
+
   if (FromTemplated->isCompleteDefinition() &&
       !ToTemplated->isCompleteDefinition()) {
     // FIXME: Import definition!

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Mon Dec 17 04:42:12 2018
@@ -1463,7 +1463,9 @@ void DeclContext::removeDecl(Decl *D) {
       if (Map) {
         StoredDeclsMap::iterator Pos = Map->find(ND->getDeclName());
         assert(Pos != Map->end() && "no lookup entry for decl");
-        if (Pos->second.getAsVector() || Pos->second.getAsDecl() == ND)
+        // Remove the decl only if it is contained.
+        StoredDeclsList::DeclsTy *Vec = Pos->second.getAsVector();
+        if ((Vec && is_contained(*Vec, ND)) || Pos->second.getAsDecl() == ND)
           Pos->second.remove(ND);
       }
     } while (DC->isTransparentContext() && (DC = DC->getParent()));

Modified: cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp Mon Dec 17 04:42:12 2018
@@ -606,6 +606,8 @@ const internal::VariadicDynCastAllOfMatc
     cxxConversionDecl;
 const internal::VariadicDynCastAllOfMatcher<Decl, VarDecl> varDecl;
 const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> fieldDecl;
+const internal::VariadicDynCastAllOfMatcher<Decl, IndirectFieldDecl>
+    indirectFieldDecl;
 const internal::VariadicDynCastAllOfMatcher<Decl, FunctionDecl> functionDecl;
 const internal::VariadicDynCastAllOfMatcher<Decl, FunctionTemplateDecl>
     functionTemplateDecl;

Modified: cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp Mon Dec 17 04:42:12 2018
@@ -212,6 +212,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(expr);
   REGISTER_MATCHER(exprWithCleanups);
   REGISTER_MATCHER(fieldDecl);
+  REGISTER_MATCHER(indirectFieldDecl);
   REGISTER_MATCHER(floatLiteral);
   REGISTER_MATCHER(forEach);
   REGISTER_MATCHER(forEachArgumentWithParam);

Modified: cfe/trunk/unittests/AST/ASTImporterTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/ASTImporterTest.cpp?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/ASTImporterTest.cpp (original)
+++ cfe/trunk/unittests/AST/ASTImporterTest.cpp Mon Dec 17 04:42:12 2018
@@ -11,9 +11,10 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/ASTImporter.h"
 #include "MatchVerifier.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTImporter.h"
+#include "clang/AST/DeclContextInternals.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/Tooling.h"
@@ -1808,6 +1809,65 @@ TEST_P(ASTImporterTestBase, ObjectsWithU
   EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
 }
 
+TEST_P(ASTImporterTestBase, AnonymousRecords) {
+  auto *Code =
+      R"(
+      struct X {
+        struct { int a; };
+        struct { int b; };
+      };
+      )";
+  Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c");
+
+  Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c");
+
+  auto *X0 =
+      FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+  auto *X1 =
+      FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+  Import(X0, Lang_C);
+  Import(X1, Lang_C);
+
+  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+  // We expect no (ODR) warning during the import.
+  EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+  EXPECT_EQ(1u,
+            DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
+TEST_P(ASTImporterTestBase, AnonymousRecordsReversed) {
+  Decl *FromTU0 = getTuDecl(
+      R"(
+      struct X {
+        struct { int a; };
+        struct { int b; };
+      };
+      )",
+      Lang_C, "input0.c");
+
+  Decl *FromTU1 = getTuDecl(
+      R"(
+      struct X { // reversed order
+        struct { int b; };
+        struct { int a; };
+      };
+      )",
+      Lang_C, "input1.c");
+
+  auto *X0 =
+      FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+  auto *X1 =
+      FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+  Import(X0, Lang_C);
+  Import(X1, Lang_C);
+
+  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+  // We expect one (ODR) warning during the import.
+  EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+  EXPECT_EQ(2u,
+            DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
 TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) {
   auto Pattern = varDecl(hasName("x"));
   VarDecl *Imported1;
@@ -2930,93 +2990,6 @@ TEST_P(ASTImporterTestBase, ImportUnname
   EXPECT_EQ(FromIndex, 3u);
 }
 
-TEST_P(
-    ASTImporterTestBase,
-    ImportOfFriendRecordDoesNotMergeDefinition) {
-  Decl *FromTU = getTuDecl(
-      R"(
-      class A {
-        template <int I> class F {};
-        class X {
-          template <int I> friend class F;
-        };
-      };
-      )",
-      Lang_CXX, "input0.cc");
-
-  auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
-      FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
-  auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
-      FromTU, cxxRecordDecl(hasName("F")));
-
-  ASSERT_TRUE(FromClass);
-  ASSERT_TRUE(FromFriendClass);
-  ASSERT_NE(FromClass, FromFriendClass);
-  ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
-  ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
-  ASSERT_EQ(
-      FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
-      FromClass->getDescribedClassTemplate());
-
-  auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
-  auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
-
-  EXPECT_TRUE(ToClass);
-  EXPECT_TRUE(ToFriendClass);
-  EXPECT_NE(ToClass, ToFriendClass);
-  EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
-  EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
-  EXPECT_EQ(
-      ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
-      ToClass->getDescribedClassTemplate());
-}
-
-TEST_P(
-    ASTImporterTestBase,
-    ImportOfRecursiveFriendClass) {
-  Decl *FromTu = getTuDecl(
-      R"(
-      class declToImport {
-        friend class declToImport;
-      };
-      )",
-      Lang_CXX, "input.cc");
-
-  auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
-      FromTu, cxxRecordDecl(hasName("declToImport")));
-  auto *ToD = Import(FromD, Lang_CXX);
-  auto Pattern = cxxRecordDecl(hasName("declToImport"), has(friendDecl()));
-  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
-  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
-}
-
-TEST_P(
-    ASTImporterTestBase,
-    ImportOfRecursiveFriendClassTemplate) {
-  Decl *FromTu = getTuDecl(
-      R"(
-      template <class A> class declToImport {
-        template <class A1> friend class declToImport;
-      };
-      )",
-      Lang_CXX, "input.cc");
-
-  auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
-      FromTu, classTemplateDecl(hasName("declToImport")));
-  auto *ToD = Import(FromD, Lang_CXX);
-
-  auto Pattern = classTemplateDecl(
-      has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
-  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
-  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
-
-  auto *Class =
-      FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
-  auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
-  EXPECT_NE(Friend->getFriendDecl(), Class);
-  EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
-}
-
 TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) {
   std::string ClassTemplate =
       R"(
@@ -3381,6 +3354,542 @@ TEST_P(ImportVariables, InitAndDefinitio
   EXPECT_TRUE(ImportedD->getDefinition());
 }
 
+struct ImportClasses : ASTImporterTestBase {};
+
+TEST_P(ImportClasses,
+       PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
+  Decl *FromTU = getTuDecl("class X;", Lang_CXX);
+  auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+  auto FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+  auto ToD = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedD == ToD);
+  EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportPrototypeAfterImportedPrototype) {
+  Decl *FromTU = getTuDecl("class X; class X;", Lang_CXX);
+  auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+  auto From1 = LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(Imported0 == To0);
+  EXPECT_TRUE(Imported1 == To1);
+  EXPECT_FALSE(To0->isThisDeclarationADefinition());
+  EXPECT_FALSE(To1->isThisDeclarationADefinition());
+  EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportClasses, DefinitionShouldBeImportedAsADefinition) {
+  Decl *FromTU = getTuDecl("class X {};", Lang_CXX);
+  auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+  auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+  EXPECT_TRUE(cast<CXXRecordDecl>(ImportedD)->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportPrototypeFromDifferentTUAfterImportedPrototype) {
+  Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
+  Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
+  auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(Imported0 == To0);
+  EXPECT_TRUE(Imported1 == To1);
+  EXPECT_FALSE(To0->isThisDeclarationADefinition());
+  EXPECT_FALSE(To1->isThisDeclarationADefinition());
+  EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportClasses, ImportDefinitions) {
+  Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
+  Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
+  auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(Imported0, Imported1);
+  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+  auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(Imported0 == To0);
+  EXPECT_TRUE(To0->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportDefinitionThenPrototype) {
+  Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
+  Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
+  auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+  auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+  auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedDef = Import(FromDef, Lang_CXX);
+  Decl *ImportedProto = Import(FromProto, Lang_CXX);
+  Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+  EXPECT_NE(ImportedDef, ImportedProto);
+  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+  auto ToDef = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  auto ToProto = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ImportedProto == ToProto);
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
+}
+
+TEST_P(ImportClasses, ImportPrototypeThenDefinition) {
+  Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
+  Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
+  auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+  auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+  auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedProto = Import(FromProto, Lang_CXX);
+  Decl *ImportedDef = Import(FromDef, Lang_CXX);
+  Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+  EXPECT_NE(ImportedDef, ImportedProto);
+  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+  auto ToProto = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  auto ToDef = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ImportedProto == ToProto);
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+struct ImportClassTemplates : ASTImporterTestBase {};
+
+TEST_P(ImportClassTemplates,
+       PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
+  Decl *FromTU = getTuDecl("template <class T> class X;", Lang_CXX);
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+  auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedD == ToD);
+  ASSERT_TRUE(ToD->getTemplatedDecl());
+  EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportPrototypeAfterImportedPrototype) {
+  Decl *FromTU = getTuDecl(
+      "template <class T> class X; template <class T> class X;", Lang_CXX);
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+  auto From1 = LastDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(Imported0 == To0);
+  EXPECT_TRUE(Imported1 == To1);
+  ASSERT_TRUE(To0->getTemplatedDecl());
+  ASSERT_TRUE(To1->getTemplatedDecl());
+  EXPECT_FALSE(To0->isThisDeclarationADefinition());
+  EXPECT_FALSE(To1->isThisDeclarationADefinition());
+  EXPECT_EQ(To1->getPreviousDecl(), To0);
+  EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
+            To0->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, DefinitionShouldBeImportedAsADefinition) {
+  Decl *FromTU = getTuDecl("template <class T> class X {};", Lang_CXX);
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+  auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  ASSERT_TRUE(ToD->getTemplatedDecl());
+  EXPECT_TRUE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates,
+       ImportPrototypeFromDifferentTUAfterImportedPrototype) {
+  Decl *FromTU0 =
+      getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(Imported0 == To0);
+  EXPECT_TRUE(Imported1 == To1);
+  ASSERT_TRUE(To0->getTemplatedDecl());
+  ASSERT_TRUE(To1->getTemplatedDecl());
+  EXPECT_FALSE(To0->isThisDeclarationADefinition());
+  EXPECT_FALSE(To1->isThisDeclarationADefinition());
+  EXPECT_EQ(To1->getPreviousDecl(), To0);
+  EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
+            To0->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, ImportDefinitions) {
+  Decl *FromTU0 =
+      getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(Imported0, Imported1);
+  EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+  auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(Imported0 == To0);
+  ASSERT_TRUE(To0->getTemplatedDecl());
+  EXPECT_TRUE(To0->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportDefinitionThenPrototype) {
+  Decl *FromTU0 =
+      getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+  auto FromProto =
+      FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedDef = Import(FromDef, Lang_CXX);
+  Decl *ImportedProto = Import(FromProto, Lang_CXX);
+  Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+  EXPECT_NE(ImportedDef, ImportedProto);
+  EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+  auto ToDef = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  auto ToProto = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ImportedProto == ToProto);
+  ASSERT_TRUE(ToDef->getTemplatedDecl());
+  ASSERT_TRUE(ToProto->getTemplatedDecl());
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
+  EXPECT_EQ(ToProto->getTemplatedDecl()->getPreviousDecl(),
+            ToDef->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, ImportPrototypeThenDefinition) {
+  Decl *FromTU0 =
+      getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto FromProto =
+      FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+  auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedProto = Import(FromProto, Lang_CXX);
+  Decl *ImportedDef = Import(FromDef, Lang_CXX);
+  Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+  EXPECT_NE(ImportedDef, ImportedProto);
+  EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+  auto ToProto = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  auto ToDef = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ImportedProto == ToProto);
+  ASSERT_TRUE(ToProto->getTemplatedDecl());
+  ASSERT_TRUE(ToDef->getTemplatedDecl());
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+  EXPECT_EQ(ToDef->getTemplatedDecl()->getPreviousDecl(),
+            ToProto->getTemplatedDecl());
+}
+
+struct ImportFriendClasses : ASTImporterTestBase {};
+
+TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
+  Decl *FromTU = getTuDecl(
+      R"(
+      class A {
+        template <int I> class F {};
+        class X {
+          template <int I> friend class F;
+        };
+      };
+      )",
+      Lang_CXX, "input0.cc");
+
+  auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
+      FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
+  auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
+      FromTU, cxxRecordDecl(hasName("F")));
+
+  ASSERT_TRUE(FromClass);
+  ASSERT_TRUE(FromFriendClass);
+  ASSERT_NE(FromClass, FromFriendClass);
+  ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
+  ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
+  ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+            FromClass->getDescribedClassTemplate());
+
+  auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
+  auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
+
+  EXPECT_TRUE(ToClass);
+  EXPECT_TRUE(ToFriendClass);
+  EXPECT_NE(ToClass, ToFriendClass);
+  EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
+  EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
+  EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+            ToClass->getDescribedClassTemplate());
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) {
+  Decl *FromTu = getTuDecl(
+      R"(
+      class declToImport {
+        friend class declToImport;
+      };
+      )",
+      Lang_CXX, "input.cc");
+
+  auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
+      FromTu, cxxRecordDecl(hasName("declToImport")));
+  auto *ToD = Import(FromD, Lang_CXX);
+  auto Pattern = cxxRecordDecl(has(friendDecl()));
+  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
+  Decl *FromTu = getTuDecl(
+      R"(
+      template<class A> class declToImport {
+        template<class A1> friend class declToImport;
+      };
+      )",
+      Lang_CXX, "input.cc");
+
+  auto *FromD =
+      FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
+  auto *ToD = Import(FromD, Lang_CXX);
+
+  auto Pattern = classTemplateDecl(
+      has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
+  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+
+  auto *Class =
+      FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
+  auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
+  EXPECT_NE(Friend->getFriendDecl(), Class);
+  EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
+}
+
+TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {
+  auto Pattern = classTemplateSpecializationDecl(hasName("X"));
+
+  ClassTemplateSpecializationDecl *Imported1;
+  {
+    Decl *FromTU = getTuDecl("template<class T> class X;"
+                             "struct Y { friend class X<int>; };",
+                             Lang_CXX, "input0.cc");
+    auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+        FromTU, Pattern);
+
+    Imported1 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+  }
+  ClassTemplateSpecializationDecl *Imported2;
+  {
+    Decl *FromTU = getTuDecl("template<class T> class X;"
+                             "template<> class X<int>{};"
+                             "struct Z { friend class X<int>; };",
+                             Lang_CXX, "input1.cc");
+    auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+        FromTU, Pattern);
+
+    Imported2 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+  }
+
+  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+  EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern),
+            2u);
+  ASSERT_TRUE(Imported2->getPreviousDecl());
+  EXPECT_EQ(Imported2->getPreviousDecl(), Imported1);
+}
+
+TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) {
+  Decl *FromTU0 = getTuDecl(
+      R"(
+      class X {
+        class Y;
+      };
+      class X::Y {
+        template <typename T>
+        friend class F; // The decl context of F is the global namespace.
+      };
+      )",
+      Lang_CXX, "input0.cc");
+  auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+      FromTU0, classTemplateDecl(hasName("F")));
+  auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+  Decl *FromTU1 = getTuDecl(
+      R"(
+      template <typename T>
+      class F {};
+      )",
+      Lang_CXX, "input1.cc");
+  auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+      FromTU1, classTemplateDecl(hasName("F")));
+  auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+  EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
+            Imported1->getTemplatedDecl()->getTypeForDecl());
+}
+
+TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
+  Decl *From, *To;
+  std::tie(From, To) =
+      getImportedDecl("class declToImport {};", Lang_CXX,
+                      "class Y { friend class declToImport; };", Lang_CXX);
+  auto *Imported = cast<CXXRecordDecl>(To);
+
+  EXPECT_TRUE(Imported->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+       ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
+  Decl *ToTU = getToTuDecl(
+      R"(
+      class X {
+        class Y;
+      };
+      class X::Y {
+        template <typename T>
+        friend class F; // The decl context of F is the global namespace.
+      };
+      )",
+      Lang_CXX);
+  auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
+      ToTU, classTemplateDecl(hasName("F")));
+  Decl *FromTU = getTuDecl(
+      R"(
+      template <typename T>
+      class F {};
+      )",
+      Lang_CXX, "input0.cc");
+  auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+      FromTU, classTemplateDecl(hasName("F")));
+  auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+  EXPECT_TRUE(ImportedDef->getPreviousDecl());
+  EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl());
+  EXPECT_EQ(ToDecl->getTemplatedDecl(),
+            ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+       ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) {
+  Decl *FromTU0 = getTuDecl(
+      R"(
+      class X {
+        class Y;
+      };
+      class X::Y {
+        template <typename T>
+        friend class F; // The decl context of F is the global namespace.
+      };
+      )",
+      Lang_CXX, "input0.cc");
+  auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+      FromTU0, classTemplateDecl(hasName("F")));
+  auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+  Decl *FromTU1 = getTuDecl(
+      R"(
+      template <typename T>
+      class F {};
+      )",
+      Lang_CXX, "input1.cc");
+  auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+      FromTU1, classTemplateDecl(hasName("F")));
+  auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+  EXPECT_TRUE(ImportedDef->getPreviousDecl());
+  EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+  EXPECT_EQ(ImportedFwd->getTemplatedDecl(),
+            ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
+  Decl *FromTU0 = getTuDecl(
+      R"(
+      class X {
+        class Y;
+      };
+      class X::Y {
+        friend class F; // The decl context of F is the global namespace.
+      };
+      )",
+      Lang_CXX, "input0.cc");
+  auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl());
+  QualType FT = Friend->getFriendType()->getType();
+  FT = FromTU0->getASTContext().getCanonicalType(FT);
+  auto *Fwd = cast<TagType>(FT)->getDecl();
+  auto *ImportedFwd = Import(Fwd, Lang_CXX);
+  Decl *FromTU1 = getTuDecl(
+      R"(
+      class F {};
+      )",
+      Lang_CXX, "input1.cc");
+  auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
+      FromTU1, cxxRecordDecl(hasName("F")));
+  auto *ImportedDef = Import(Definition, Lang_CXX);
+  EXPECT_TRUE(ImportedDef->getPreviousDecl());
+  EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+}
+
 struct DeclContextTest : ASTImporterTestBase {};
 
 TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
@@ -3410,6 +3919,40 @@ TEST_P(DeclContextTest, removeDeclOfClas
   EXPECT_FALSE(NS->containsDecl(Spec));
 }
 
+TEST_P(DeclContextTest,
+       removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
+  Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
+  auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+  auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+
+  // Investigate the list.
+  auto *DC = A0->getDeclContext();
+  ASSERT_TRUE(DC->containsDecl(A0));
+  ASSERT_TRUE(DC->containsDecl(A1));
+
+  // Investigate the lookup table.
+  auto *Map = DC->getLookupPtr();
+  ASSERT_TRUE(Map);
+  auto I = Map->find(A0->getDeclName());
+  ASSERT_NE(I, Map->end());
+  StoredDeclsList &L = I->second;
+  // The lookup table contains the most recent decl of A.
+  ASSERT_NE(L.getAsDecl(), A0);
+  ASSERT_EQ(L.getAsDecl(), A1);
+
+  ASSERT_TRUE(L.getAsDecl());
+  // Simulate the private function DeclContext::reconcileExternalVisibleStorage.
+  // The point here is to have a Vec with only one element, which is not the
+  // one we are going to delete from the DC later.
+  L.setHasExternalDecls();
+  ASSERT_TRUE(L.getAsVector());
+  ASSERT_EQ(1u, L.getAsVector()->size());
+
+  // This asserts in the old implementation.
+  DC->removeDecl(A0);
+  EXPECT_FALSE(DC->containsDecl(A0));
+}
+
 struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {};
 
 TEST_P(ImportFunctionTemplateSpecializations,
@@ -3827,6 +4370,15 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTes
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
                         DefaultTestValuesForRunOptions, );
 
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
+                        DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,
+                        DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses,
+                        DefaultTestValuesForRunOptions, );
+
 INSTANTIATE_TEST_CASE_P(ParameterizedTests,
                         ImportFunctionTemplateSpecializations,
                         DefaultTestValuesForRunOptions, );

Modified: cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp?rev=349349&r1=349348&r2=349349&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp (original)
+++ cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp Mon Dec 17 04:42:12 2018
@@ -597,6 +597,77 @@ TEST_F(StructuralEquivalenceRecordTest,
   EXPECT_FALSE(testStructuralMatch(R0, R1));
 }
 
+TEST_F(StructuralEquivalenceRecordTest, AnonymousRecordsShouldBeInequivalent) {
+  auto t = makeTuDecls(
+      R"(
+      struct X {
+        struct {
+          int a;
+        };
+        struct {
+          int b;
+        };
+      };
+      )",
+      "", Lang_C);
+  auto *TU = get<0>(t);
+  auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
+      TU, indirectFieldDecl(hasName("a")));
+  auto *FA = cast<FieldDecl>(A->chain().front());
+  RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
+  auto *B = FirstDeclMatcher<IndirectFieldDecl>().match(
+      TU, indirectFieldDecl(hasName("b")));
+  auto *FB = cast<FieldDecl>(B->chain().front());
+  RecordDecl *RB = cast<RecordType>(FB->getType().getTypePtr())->getDecl();
+
+  ASSERT_NE(RA, RB);
+  EXPECT_TRUE(testStructuralMatch(RA, RA));
+  EXPECT_TRUE(testStructuralMatch(RB, RB));
+  EXPECT_FALSE(testStructuralMatch(RA, RB));
+}
+
+TEST_F(StructuralEquivalenceRecordTest,
+       RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) {
+  auto t = makeTuDecls(
+      R"(
+      struct X {
+        struct { int a; };
+        struct { int b; };
+      };
+      )",
+      R"(
+      struct X { // The order is reversed.
+        struct { int b; };
+        struct { int a; };
+      };
+      )",
+      Lang_C);
+
+  auto *TU = get<0>(t);
+  auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
+      TU, indirectFieldDecl(hasName("a")));
+  auto *FA = cast<FieldDecl>(A->chain().front());
+  RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
+
+  auto *TU1 = get<1>(t);
+  auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match(
+      TU1, indirectFieldDecl(hasName("a")));
+  auto *FA1 = cast<FieldDecl>(A1->chain().front());
+  RecordDecl *RA1 = cast<RecordType>(FA1->getType().getTypePtr())->getDecl();
+
+  RecordDecl *X =
+      FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X")));
+  RecordDecl *X1 =
+      FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X")));
+  ASSERT_NE(X, X1);
+  EXPECT_FALSE(testStructuralMatch(X, X1));
+
+  ASSERT_NE(RA, RA1);
+  EXPECT_TRUE(testStructuralMatch(RA, RA));
+  EXPECT_TRUE(testStructuralMatch(RA1, RA1));
+  EXPECT_FALSE(testStructuralMatch(RA1, RA));
+}
+
 TEST_F(StructuralEquivalenceRecordTest,
        UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) {
   auto Code =




More information about the cfe-commits mailing list