Index: docs/LibASTMatchersReference.html =================================================================== --- docs/LibASTMatchersReference.html +++ docs/LibASTMatchersReference.html @@ -300,6 +300,16 @@ +Matcher<Decl>indirectFieldDeclMatcher<IndirectFieldDecl>... +
Matches indirect field declarations.
+
+Given
+  struct X { struct { int a; }; };
+indirectFieldDecl()
+  matches 'a'.
+
+ + Matcher<Decl>labelDeclMatcher<LabelDecl>...
Matches a declaration of label.
 
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -1158,6 +1158,17 @@
 ///   matches 'm'.
 extern const internal::VariadicDynCastAllOfMatcher fieldDecl;
 
+/// Matches indirect field declarations.
+///
+/// Given
+/// \code
+///   struct X { struct { int a; }; };
+/// \endcode
+/// indirectFieldDecl()
+///   matches 'a'.
+extern const internal::VariadicDynCastAllOfMatcher
+    indirectFieldDecl;
+
 /// Matches function declarations.
 ///
 /// Example matches f
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -122,6 +122,8 @@
       return getCanonicalForwardRedeclChain(FD);
     if (auto *VD = dyn_cast(D))
       return getCanonicalForwardRedeclChain(VD);
+    if (auto *TD = dyn_cast(D))
+      return getCanonicalForwardRedeclChain(TD);
     llvm_unreachable("Bad declaration kind");
   }
 
@@ -2604,10 +2606,9 @@
       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 ConflictingDecls;
@@ -2640,26 +2641,22 @@
       }
 
       if (auto *FoundRecord = dyn_cast(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);
@@ -2667,46 +2664,20 @@
               auto *FoundCXX = dyn_cast(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(Found))
-        continue;
+      }
 
       ConflictingDecls.push_back(FoundDecl);
-    }
+    } // for
 
     if (!ConflictingDecls.empty() && SearchName) {
       Name = Importer.HandleNameConflict(Name, DC, IDNS,
@@ -2722,79 +2693,90 @@
     return BeginLocOrErr.takeError();
 
   // Create the record declaration.
-  RecordDecl *D2 = AdoptDecl;
-  if (!D2) {
-    CXXRecordDecl *D2CXX = nullptr;
-    if (auto *DCXX = dyn_cast(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(DC));
-      } else {
-        if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
-                                    D->getTagKind(), DC, *BeginLocOrErr, Loc,
-                                    Name.getAsIdentifierInfo(),
-                                    cast_or_null(PrevDecl)))
-          return D2CXX;
-      }
+  RecordDecl *D2 = nullptr;
+  CXXRecordDecl *D2CXX = nullptr;
+  if (auto *DCXX = dyn_cast(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(PrevDecl), DelayTypeCreation))
+        return D2CXX;
+      Importer.getToContext().getTypeDeclType(
+          D2CXX, dyn_cast(DC));
+    } else {
+      if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
+                                  D->getTagKind(), DC, *BeginLocOrErr, Loc,
+                                  Name.getAsIdentifierInfo(),
+                                  cast_or_null(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(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(Found);
+          if (Record && Record->isInjectedClassName()) {
+            Injected = Record;
+            break;
           }
         }
-      } else if (MemberSpecializationInfo *MemberInfo =
+        // Create an injected type for the whole redecl chain.
+        SmallVector Redecls =
+            getCanonicalForwardRedeclChain(D2CXX);
+        for (auto *R : Redecls) {
+          auto *RI = cast(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();
@@ -2811,27 +2793,24 @@
             *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))
@@ -4987,14 +4966,12 @@
 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(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();
@@ -5010,38 +4987,29 @@
   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 ConflictingDecls;
     SmallVector 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(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(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;
         }
       }
 
@@ -5078,18 +5046,39 @@
 
   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(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!
Index: lib/AST/DeclBase.cpp
===================================================================
--- lib/AST/DeclBase.cpp
+++ lib/AST/DeclBase.cpp
@@ -1466,7 +1466,9 @@
       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()));
Index: lib/ASTMatchers/ASTMatchersInternal.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchersInternal.cpp
+++ lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -606,6 +606,8 @@
     cxxConversionDecl;
 const internal::VariadicDynCastAllOfMatcher varDecl;
 const internal::VariadicDynCastAllOfMatcher fieldDecl;
+const internal::VariadicDynCastAllOfMatcher
+    indirectFieldDecl;
 const internal::VariadicDynCastAllOfMatcher functionDecl;
 const internal::VariadicDynCastAllOfMatcher
     functionTemplateDecl;
Index: lib/ASTMatchers/Dynamic/Registry.cpp
===================================================================
--- lib/ASTMatchers/Dynamic/Registry.cpp
+++ lib/ASTMatchers/Dynamic/Registry.cpp
@@ -211,6 +211,7 @@
   REGISTER_MATCHER(expr);
   REGISTER_MATCHER(exprWithCleanups);
   REGISTER_MATCHER(fieldDecl);
+  REGISTER_MATCHER(indirectFieldDecl);
   REGISTER_MATCHER(floatLiteral);
   REGISTER_MATCHER(forEach);
   REGISTER_MATCHER(forEachArgumentWithParam);
Index: unittests/AST/ASTImporterTest.cpp
===================================================================
--- unittests/AST/ASTImporterTest.cpp
+++ unittests/AST/ASTImporterTest.cpp
@@ -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 @@
   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().match(FromTU0, recordDecl(hasName("X")));
+  auto *X1 =
+      FirstDeclMatcher().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().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().match(FromTU0, recordDecl(hasName("X")));
+  auto *X1 =
+      FirstDeclMatcher().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().match(ToTU, recordDecl(hasName("X"))));
+}
+
 TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) {
   auto Pattern = varDecl(hasName("x"));
   VarDecl *Imported1;
@@ -2930,93 +2990,6 @@
   EXPECT_EQ(FromIndex, 3u);
 }
 
-TEST_P(
-    ASTImporterTestBase,
-    ImportOfFriendRecordDoesNotMergeDefinition) {
-  Decl *FromTU = getTuDecl(
-      R"(
-      class A {
-        template  class F {};
-        class X {
-          template  friend class F;
-        };
-      };
-      )",
-      Lang_CXX, "input0.cc");
-
-  auto *FromClass = FirstDeclMatcher().match(
-      FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
-  auto *FromFriendClass = LastDeclMatcher().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(Import(FromClass, Lang_CXX));
-  auto *ToFriendClass = cast(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().match(
-      FromTu, cxxRecordDecl(hasName("declToImport")));
-  auto *ToD = Import(FromD, Lang_CXX);
-  auto Pattern = cxxRecordDecl(hasName("declToImport"), has(friendDecl()));
-  ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern));
-  EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern));
-}
-
-TEST_P(
-    ASTImporterTestBase,
-    ImportOfRecursiveFriendClassTemplate) {
-  Decl *FromTu = getTuDecl(
-      R"(
-      template  class declToImport {
-        template  friend class declToImport;
-      };
-      )",
-      Lang_CXX, "input.cc");
-
-  auto *FromD = FirstDeclMatcher().match(
-      FromTu, classTemplateDecl(hasName("declToImport")));
-  auto *ToD = Import(FromD, Lang_CXX);
-
-  auto Pattern = classTemplateDecl(
-      has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
-  ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern));
-  EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern));
-
-  auto *Class =
-      FirstDeclMatcher().match(ToD, classTemplateDecl());
-  auto *Friend = FirstDeclMatcher().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 @@
   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().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u);
+  auto ToD = LastDeclMatcher().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().match(FromTU, Pattern);
+  auto From1 = LastDeclMatcher().match(FromTU, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher().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().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u);
+  EXPECT_TRUE(cast(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().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher().match(FromTU1, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher().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().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher().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().match(ToTU, Pattern), 1u);
+  auto To0 = FirstDeclMatcher().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().match(FromTU0, Pattern);
+  auto FromProto = FirstDeclMatcher().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().match(ToTU, Pattern), 2u);
+  auto ToDef = FirstDeclMatcher().match(ToTU, Pattern);
+  auto ToProto = LastDeclMatcher().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().match(FromTU0, Pattern);
+  auto FromDef = FirstDeclMatcher().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().match(ToTU, Pattern), 2u);
+  auto ToProto = FirstDeclMatcher().match(ToTU, Pattern);
+  auto ToDef = LastDeclMatcher().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 X;", Lang_CXX);
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto FromD = FirstDeclMatcher().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u);
+  auto ToD = LastDeclMatcher().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedD == ToD);
+  ASSERT_TRUE(ToD->getTemplatedDecl());
+  EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportPrototypeAfterImportedPrototype) {
+  Decl *FromTU = getTuDecl(
+      "template  class X; template  class X;", Lang_CXX);
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher().match(FromTU, Pattern);
+  auto From1 = LastDeclMatcher().match(FromTU, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher().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 X {};", Lang_CXX);
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto *FromD = FirstDeclMatcher().match(FromTU, Pattern);
+
+  Decl *ImportedD = Import(FromD, Lang_CXX);
+  Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u);
+  auto ToD = LastDeclMatcher().match(ToTU, Pattern);
+  ASSERT_TRUE(ToD->getTemplatedDecl());
+  EXPECT_TRUE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates,
+       ImportPrototypeFromDifferentTUAfterImportedPrototype) {
+  Decl *FromTU0 =
+      getTuDecl("template  class X;", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template  class X;", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher().match(FromTU1, Pattern);
+
+  Decl *Imported0 = Import(From0, Lang_CXX);
+  Decl *Imported1 = Import(From1, Lang_CXX);
+  Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+  EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u);
+  auto To0 = FirstDeclMatcher().match(ToTU, Pattern);
+  auto To1 = LastDeclMatcher().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 X {};", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template  class X {};", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto From0 = FirstDeclMatcher().match(FromTU0, Pattern);
+  auto From1 = FirstDeclMatcher().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().match(ToTU, Pattern), 1u);
+  auto To0 = FirstDeclMatcher().match(ToTU, Pattern);
+  EXPECT_TRUE(Imported0 == To0);
+  ASSERT_TRUE(To0->getTemplatedDecl());
+  EXPECT_TRUE(To0->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportDefinitionThenPrototype) {
+  Decl *FromTU0 =
+      getTuDecl("template  class X {};", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template  class X;", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto FromDef = FirstDeclMatcher().match(FromTU0, Pattern);
+  auto FromProto =
+      FirstDeclMatcher().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().match(ToTU, Pattern), 2u);
+  auto ToDef = FirstDeclMatcher().match(ToTU, Pattern);
+  auto ToProto = LastDeclMatcher().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 X;", Lang_CXX, "input0.cc");
+  Decl *FromTU1 =
+      getTuDecl("template  class X {};", Lang_CXX, "input1.cc");
+  auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+  auto FromProto =
+      FirstDeclMatcher().match(FromTU0, Pattern);
+  auto FromDef = FirstDeclMatcher().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().match(ToTU, Pattern), 2u);
+  auto ToProto = FirstDeclMatcher().match(ToTU, Pattern);
+  auto ToDef = LastDeclMatcher().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  class F {};
+        class X {
+          template  friend class F;
+        };
+      };
+      )",
+      Lang_CXX, "input0.cc");
+
+  auto *FromClass = FirstDeclMatcher().match(
+      FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
+  auto *FromFriendClass = LastDeclMatcher().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(Import(FromClass, Lang_CXX));
+  auto *ToFriendClass = cast(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().match(
+      FromTu, cxxRecordDecl(hasName("declToImport")));
+  auto *ToD = Import(FromD, Lang_CXX);
+  auto Pattern = cxxRecordDecl(has(friendDecl()));
+  ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern));
+  EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern));
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
+  Decl *FromTu = getTuDecl(
+      R"(
+      template class declToImport {
+        template friend class declToImport;
+      };
+      )",
+      Lang_CXX, "input.cc");
+
+  auto *FromD =
+      FirstDeclMatcher().match(FromTu, classTemplateDecl());
+  auto *ToD = Import(FromD, Lang_CXX);
+
+  auto Pattern = classTemplateDecl(
+      has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
+  ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern));
+  EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern));
+
+  auto *Class =
+      FirstDeclMatcher().match(ToD, classTemplateDecl());
+  auto *Friend = FirstDeclMatcher().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 X;"
+                             "struct Y { friend class X; };",
+                             Lang_CXX, "input0.cc");
+    auto *FromD = FirstDeclMatcher().match(
+        FromTU, Pattern);
+
+    Imported1 = cast(Import(FromD, Lang_CXX));
+  }
+  ClassTemplateSpecializationDecl *Imported2;
+  {
+    Decl *FromTU = getTuDecl("template class X;"
+                             "template<> class X{};"
+                             "struct Z { friend class X; };",
+                             Lang_CXX, "input1.cc");
+    auto *FromD = FirstDeclMatcher().match(
+        FromTU, Pattern);
+
+    Imported2 = cast(Import(FromD, Lang_CXX));
+  }
+
+  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+  EXPECT_EQ(DeclCounter().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 
+        friend class F; // The decl context of F is the global namespace.
+      };
+      )",
+      Lang_CXX, "input0.cc");
+  auto *Fwd = FirstDeclMatcher().match(
+      FromTU0, classTemplateDecl(hasName("F")));
+  auto *Imported0 = cast(Import(Fwd, Lang_CXX));
+  Decl *FromTU1 = getTuDecl(
+      R"(
+      template 
+      class F {};
+      )",
+      Lang_CXX, "input1.cc");
+  auto *Definition = FirstDeclMatcher().match(
+      FromTU1, classTemplateDecl(hasName("F")));
+  auto *Imported1 = cast(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(To);
+
+  EXPECT_TRUE(Imported->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+       ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
+  Decl *ToTU = getToTuDecl(
+      R"(
+      class X {
+        class Y;
+      };
+      class X::Y {
+        template 
+        friend class F; // The decl context of F is the global namespace.
+      };
+      )",
+      Lang_CXX);
+  auto *ToDecl = FirstDeclMatcher().match(
+      ToTU, classTemplateDecl(hasName("F")));
+  Decl *FromTU = getTuDecl(
+      R"(
+      template 
+      class F {};
+      )",
+      Lang_CXX, "input0.cc");
+  auto *Definition = FirstDeclMatcher().match(
+      FromTU, classTemplateDecl(hasName("F")));
+  auto *ImportedDef = cast(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 
+        friend class F; // The decl context of F is the global namespace.
+      };
+      )",
+      Lang_CXX, "input0.cc");
+  auto *Fwd = FirstDeclMatcher().match(
+      FromTU0, classTemplateDecl(hasName("F")));
+  auto *ImportedFwd = cast(Import(Fwd, Lang_CXX));
+  Decl *FromTU1 = getTuDecl(
+      R"(
+      template 
+      class F {};
+      )",
+      Lang_CXX, "input1.cc");
+  auto *Definition = FirstDeclMatcher().match(
+      FromTU1, classTemplateDecl(hasName("F")));
+  auto *ImportedDef = cast(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().match(FromTU0, friendDecl());
+  QualType FT = Friend->getFriendType()->getType();
+  FT = FromTU0->getASTContext().getCanonicalType(FT);
+  auto *Fwd = cast(FT)->getDecl();
+  auto *ImportedFwd = Import(Fwd, Lang_CXX);
+  Decl *FromTU1 = getTuDecl(
+      R"(
+      class F {};
+      )",
+      Lang_CXX, "input1.cc");
+  auto *Definition = FirstDeclMatcher().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 @@
   EXPECT_FALSE(NS->containsDecl(Spec));
 }
 
+TEST_P(DeclContextTest,
+       removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
+  Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
+  auto *A0 = FirstDeclMatcher().match(TU, varDecl(hasName("A")));
+  auto *A1 = LastDeclMatcher().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(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, );
Index: unittests/AST/StructuralEquivalenceTest.cpp
===================================================================
--- unittests/AST/StructuralEquivalenceTest.cpp
+++ unittests/AST/StructuralEquivalenceTest.cpp
@@ -597,6 +597,77 @@
   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().match(
+      TU, indirectFieldDecl(hasName("a")));
+  auto *FA = cast(A->chain().front());
+  RecordDecl *RA = cast(FA->getType().getTypePtr())->getDecl();
+  auto *B = FirstDeclMatcher().match(
+      TU, indirectFieldDecl(hasName("b")));
+  auto *FB = cast(B->chain().front());
+  RecordDecl *RB = cast(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().match(
+      TU, indirectFieldDecl(hasName("a")));
+  auto *FA = cast(A->chain().front());
+  RecordDecl *RA = cast(FA->getType().getTypePtr())->getDecl();
+
+  auto *TU1 = get<1>(t);
+  auto *A1 = FirstDeclMatcher().match(
+      TU1, indirectFieldDecl(hasName("a")));
+  auto *FA1 = cast(A1->chain().front());
+  RecordDecl *RA1 = cast(FA1->getType().getTypePtr())->getDecl();
+
+  RecordDecl *X =
+      FirstDeclMatcher().match(TU, recordDecl(hasName("X")));
+  RecordDecl *X1 =
+      FirstDeclMatcher().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 =