//===-- ODRDiagsEmitter.cpp - Diagnostics for ODR mismatches ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang/AST/ODRDiagsEmitter.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ODRHash.h"
#include "clang/Basic/DiagnosticAST.h"
#include "clang/Basic/Module.h"

using namespace clang;

static unsigned computeODRHash(QualType Ty) {
  ODRHash Hasher;
  Hasher.AddQualType(Ty);
  return Hasher.CalculateHash();
}

static unsigned computeODRHash(const Stmt *S) {
  ODRHash Hasher;
  Hasher.AddStmt(S);
  return Hasher.CalculateHash();
}

static unsigned computeODRHash(const Decl *D) {
  assert(D);
  ODRHash Hasher;
  Hasher.AddSubDecl(D);
  return Hasher.CalculateHash();
}

static unsigned computeODRHash(const TemplateArgument &TA) {
  ODRHash Hasher;
  Hasher.AddTemplateArgument(TA);
  return Hasher.CalculateHash();
}

std::string ODRDiagsEmitter::getOwningModuleNameForDiagnostic(const Decl *D) {
  // If we know the owning module, use it.
  if (Module *M = D->getImportedOwningModule())
    return M->getFullModuleName();

  // Not from a module.
  return {};
}

template <typename MethodT>
static bool diagnoseSubMismatchMethodParameters(DiagnosticsEngine &Diags,
                                                const NamedDecl *FirstContainer,
                                                StringRef FirstModule,
                                                StringRef SecondModule,
                                                const MethodT *FirstMethod,
                                                const MethodT *SecondMethod) {
  enum DiagMethodType {
    DiagMethod,
    DiagConstructor,
    DiagDestructor,
  };
  auto GetDiagMethodType = [](const NamedDecl *D) {
    if (isa<CXXConstructorDecl>(D))
      return DiagConstructor;
    if (isa<CXXDestructorDecl>(D))
      return DiagDestructor;
    return DiagMethod;
  };

  enum ODRMethodParametersDifference {
    NumberParameters,
    ParameterType,
    ParameterName,
  };
  auto DiagError = [&Diags, &GetDiagMethodType, FirstContainer, FirstModule,
                    FirstMethod](ODRMethodParametersDifference DiffType) {
    DeclarationName FirstName = FirstMethod->getDeclName();
    DiagMethodType FirstMethodType = GetDiagMethodType(FirstMethod);
    return Diags.Report(FirstMethod->getLocation(),
                        diag::err_module_odr_violation_method_params)
           << FirstContainer << FirstModule.empty() << FirstModule
           << FirstMethod->getSourceRange() << DiffType << FirstMethodType
           << FirstName;
  };
  auto DiagNote = [&Diags, &GetDiagMethodType, SecondModule,
                   SecondMethod](ODRMethodParametersDifference DiffType) {
    DeclarationName SecondName = SecondMethod->getDeclName();
    DiagMethodType SecondMethodType = GetDiagMethodType(SecondMethod);
    return Diags.Report(SecondMethod->getLocation(),
                        diag::note_module_odr_violation_method_params)
           << SecondModule.empty() << SecondModule
           << SecondMethod->getSourceRange() << DiffType << SecondMethodType
           << SecondName;
  };

  const unsigned FirstNumParameters = FirstMethod->param_size();
  const unsigned SecondNumParameters = SecondMethod->param_size();
  if (FirstNumParameters != SecondNumParameters) {
    DiagError(NumberParameters) << FirstNumParameters;
    DiagNote(NumberParameters) << SecondNumParameters;
    return true;
  }

  for (unsigned I = 0; I < FirstNumParameters; ++I) {
    const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I);
    const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I);

    QualType FirstParamType = FirstParam->getType();
    QualType SecondParamType = SecondParam->getType();
    if (FirstParamType != SecondParamType &&
        computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) {
      if (const DecayedType *ParamDecayedType =
              FirstParamType->getAs<DecayedType>()) {
        DiagError(ParameterType) << (I + 1) << FirstParamType << true
                                 << ParamDecayedType->getOriginalType();
      } else {
        DiagError(ParameterType) << (I + 1) << FirstParamType << false;
      }

      if (const DecayedType *ParamDecayedType =
              SecondParamType->getAs<DecayedType>()) {
        DiagNote(ParameterType) << (I + 1) << SecondParamType << true
                                << ParamDecayedType->getOriginalType();
      } else {
        DiagNote(ParameterType) << (I + 1) << SecondParamType << false;
      }
      return true;
    }

    DeclarationName FirstParamName = FirstParam->getDeclName();
    DeclarationName SecondParamName = SecondParam->getDeclName();
    if (FirstParamName != SecondParamName) {
      DiagError(ParameterName) << (I + 1) << FirstParamName;
      DiagNote(ParameterName) << (I + 1) << SecondParamName;
      return true;
    }
  }

  return false;
}

bool ODRDiagsEmitter::diagnoseSubMismatchField(
    const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule,
    const FieldDecl *FirstField, const FieldDecl *SecondField) const {
  enum ODRFieldDifference {
    FieldName,
    FieldTypeName,
    FieldSingleBitField,
    FieldDifferentWidthBitField,
    FieldSingleMutable,
    FieldSingleInitializer,
    FieldDifferentInitializers,
  };

  auto DiagError = [FirstRecord, FirstField, FirstModule,
                    this](ODRFieldDifference DiffType) {
    return Diag(FirstField->getLocation(), diag::err_module_odr_violation_field)
           << FirstRecord << FirstModule.empty() << FirstModule
           << FirstField->getSourceRange() << DiffType;
  };
  auto DiagNote = [SecondField, SecondModule,
                   this](ODRFieldDifference DiffType) {
    return Diag(SecondField->getLocation(),
                diag::note_module_odr_violation_field)
           << SecondModule.empty() << SecondModule << SecondField->getSourceRange() << DiffType;
  };

  IdentifierInfo *FirstII = FirstField->getIdentifier();
  IdentifierInfo *SecondII = SecondField->getIdentifier();
  if (FirstII->getName() != SecondII->getName()) {
    DiagError(FieldName) << FirstII;
    DiagNote(FieldName) << SecondII;
    return true;
  }

  QualType FirstType = FirstField->getType();
  QualType SecondType = SecondField->getType();
  if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
    DiagError(FieldTypeName) << FirstII << FirstType;
    DiagNote(FieldTypeName) << SecondII << SecondType;
    return true;
  }

  assert(Context.hasSameType(FirstField->getType(), SecondField->getType()));
  (void)Context;

  const bool IsFirstBitField = FirstField->isBitField();
  const bool IsSecondBitField = SecondField->isBitField();
  if (IsFirstBitField != IsSecondBitField) {
    DiagError(FieldSingleBitField) << FirstII << IsFirstBitField;
    DiagNote(FieldSingleBitField) << SecondII << IsSecondBitField;
    return true;
  }

  if (IsFirstBitField && IsSecondBitField) {
    unsigned FirstBitWidthHash = computeODRHash(FirstField->getBitWidth());
    unsigned SecondBitWidthHash = computeODRHash(SecondField->getBitWidth());
    if (FirstBitWidthHash != SecondBitWidthHash) {
      DiagError(FieldDifferentWidthBitField)
          << FirstII << FirstField->getBitWidth()->getSourceRange();
      DiagNote(FieldDifferentWidthBitField)
          << SecondII << SecondField->getBitWidth()->getSourceRange();
      return true;
    }
  }

  if (!LangOpts.CPlusPlus)
    return false;

  const bool IsFirstMutable = FirstField->isMutable();
  const bool IsSecondMutable = SecondField->isMutable();
  if (IsFirstMutable != IsSecondMutable) {
    DiagError(FieldSingleMutable) << FirstII << IsFirstMutable;
    DiagNote(FieldSingleMutable) << SecondII << IsSecondMutable;
    return true;
  }

  const Expr *FirstInitializer = FirstField->getInClassInitializer();
  const Expr *SecondInitializer = SecondField->getInClassInitializer();
  if ((!FirstInitializer && SecondInitializer) ||
      (FirstInitializer && !SecondInitializer)) {
    DiagError(FieldSingleInitializer)
        << FirstII << (FirstInitializer != nullptr);
    DiagNote(FieldSingleInitializer)
        << SecondII << (SecondInitializer != nullptr);
    return true;
  }

  if (FirstInitializer && SecondInitializer) {
    unsigned FirstInitHash = computeODRHash(FirstInitializer);
    unsigned SecondInitHash = computeODRHash(SecondInitializer);
    if (FirstInitHash != SecondInitHash) {
      DiagError(FieldDifferentInitializers)
          << FirstII << FirstInitializer->getSourceRange();
      DiagNote(FieldDifferentInitializers)
          << SecondII << SecondInitializer->getSourceRange();
      return true;
    }
  }

  return false;
}

bool ODRDiagsEmitter::diagnoseSubMismatchTypedef(
    const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule,
    const TypedefNameDecl *FirstTD, const TypedefNameDecl *SecondTD,
    bool IsTypeAlias) const {
  enum ODRTypedefDifference {
    TypedefName,
    TypedefType,
  };

  auto DiagError = [FirstRecord, FirstTD, FirstModule,
                    this](ODRTypedefDifference DiffType) {
    return Diag(FirstTD->getLocation(), diag::err_module_odr_violation_typedef)
           << FirstRecord << FirstModule.empty() << FirstModule
           << FirstTD->getSourceRange() << DiffType;
  };
  auto DiagNote = [SecondTD, SecondModule,
                   this](ODRTypedefDifference DiffType) {
    return Diag(SecondTD->getLocation(),
                diag::note_module_odr_violation_typedef)
           << SecondModule << SecondTD->getSourceRange() << DiffType;
  };

  DeclarationName FirstName = FirstTD->getDeclName();
  DeclarationName SecondName = SecondTD->getDeclName();
  if (FirstName != SecondName) {
    DiagError(TypedefName) << IsTypeAlias << FirstName;
    DiagNote(TypedefName) << IsTypeAlias << SecondName;
    return true;
  }

  QualType FirstType = FirstTD->getUnderlyingType();
  QualType SecondType = SecondTD->getUnderlyingType();
  if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
    DiagError(TypedefType) << IsTypeAlias << FirstName << FirstType;
    DiagNote(TypedefType) << IsTypeAlias << SecondName << SecondType;
    return true;
  }
  return false;
}

bool ODRDiagsEmitter::diagnoseSubMismatchVar(const NamedDecl *FirstRecord,
                                             StringRef FirstModule,
                                             StringRef SecondModule,
                                             const VarDecl *FirstVD,
                                             const VarDecl *SecondVD) const {
  enum ODRVarDifference {
    VarName,
    VarType,
    VarSingleInitializer,
    VarDifferentInitializer,
    VarConstexpr,
  };

  auto DiagError = [FirstRecord, FirstVD, FirstModule,
                    this](ODRVarDifference DiffType) {
    return Diag(FirstVD->getLocation(), diag::err_module_odr_violation_variable)
           << FirstRecord << FirstModule.empty() << FirstModule
           << FirstVD->getSourceRange() << DiffType;
  };
  auto DiagNote = [SecondVD, SecondModule, this](ODRVarDifference DiffType) {
    return Diag(SecondVD->getLocation(),
                diag::note_module_odr_violation_variable)
           << SecondModule << SecondVD->getSourceRange() << DiffType;
  };

  DeclarationName FirstName = FirstVD->getDeclName();
  DeclarationName SecondName = SecondVD->getDeclName();
  if (FirstName != SecondName) {
    DiagError(VarName) << FirstName;
    DiagNote(VarName) << SecondName;
    return true;
  }

  QualType FirstType = FirstVD->getType();
  QualType SecondType = SecondVD->getType();
  if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
    DiagError(VarType) << FirstName << FirstType;
    DiagNote(VarType) << SecondName << SecondType;
    return true;
  }

  if (!LangOpts.CPlusPlus)
    return false;

  const Expr *FirstInit = FirstVD->getInit();
  const Expr *SecondInit = SecondVD->getInit();
  if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
    DiagError(VarSingleInitializer)
        << FirstName << (FirstInit == nullptr)
        << (FirstInit ? FirstInit->getSourceRange() : SourceRange());
    DiagNote(VarSingleInitializer)
        << SecondName << (SecondInit == nullptr)
        << (SecondInit ? SecondInit->getSourceRange() : SourceRange());
    return true;
  }

  if (FirstInit && SecondInit &&
      computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
    DiagError(VarDifferentInitializer)
        << FirstName << FirstInit->getSourceRange();
    DiagNote(VarDifferentInitializer)
        << SecondName << SecondInit->getSourceRange();
    return true;
  }

  const bool FirstIsConstexpr = FirstVD->isConstexpr();
  const bool SecondIsConstexpr = SecondVD->isConstexpr();
  if (FirstIsConstexpr != SecondIsConstexpr) {
    DiagError(VarConstexpr) << FirstName << FirstIsConstexpr;
    DiagNote(VarConstexpr) << SecondName << SecondIsConstexpr;
    return true;
  }
  return false;
}

bool ODRDiagsEmitter::diagnoseSubMismatchProtocols(
    const ObjCProtocolList &FirstProtocols,
    const ObjCContainerDecl *FirstContainer, StringRef FirstModule,
    const ObjCProtocolList &SecondProtocols,
    const ObjCContainerDecl *SecondContainer, StringRef SecondModule) const {
  // Keep in sync with err_module_odr_violation_referenced_protocols.
  enum ODRReferencedProtocolDifference {
    NumProtocols,
    ProtocolType,
  };
  auto DiagRefProtocolError = [FirstContainer, FirstModule,
                               this](SourceLocation Loc, SourceRange Range,
                                     ODRReferencedProtocolDifference DiffType) {
    return Diag(Loc, diag::err_module_odr_violation_referenced_protocols)
           << FirstContainer << FirstModule.empty() << FirstModule << Range
           << DiffType;
  };
  auto DiagRefProtocolNote = [SecondModule,
                              this](SourceLocation Loc, SourceRange Range,
                                    ODRReferencedProtocolDifference DiffType) {
    return Diag(Loc, diag::note_module_odr_violation_referenced_protocols)
           << SecondModule.empty() << SecondModule << Range << DiffType;
  };
  auto GetProtoListSourceRange = [](const ObjCProtocolList &PL) {
    if (PL.empty())
      return SourceRange();
    return SourceRange(*PL.loc_begin(), *std::prev(PL.loc_end()));
  };

  if (FirstProtocols.size() != SecondProtocols.size()) {
    DiagRefProtocolError(FirstContainer->getLocation(),
                         GetProtoListSourceRange(FirstProtocols), NumProtocols)
        << FirstProtocols.size();
    DiagRefProtocolNote(SecondContainer->getLocation(),
                        GetProtoListSourceRange(SecondProtocols), NumProtocols)
        << SecondProtocols.size();
    return true;
  }

  for (unsigned I = 0, E = FirstProtocols.size(); I != E; ++I) {
    const ObjCProtocolDecl *FirstProtocol = FirstProtocols[I];
    const ObjCProtocolDecl *SecondProtocol = SecondProtocols[I];
    DeclarationName FirstProtocolName = FirstProtocol->getDeclName();
    DeclarationName SecondProtocolName = SecondProtocol->getDeclName();
    if (FirstProtocolName != SecondProtocolName) {
      SourceLocation FirstLoc = *(FirstProtocols.loc_begin() + I);
      SourceLocation SecondLoc = *(SecondProtocols.loc_begin() + I);
      SourceRange EmptyRange;
      DiagRefProtocolError(FirstLoc, EmptyRange, ProtocolType)
          << (I + 1) << FirstProtocolName;
      DiagRefProtocolNote(SecondLoc, EmptyRange, ProtocolType)
          << (I + 1) << SecondProtocolName;
      return true;
    }
  }

  return false;
}

bool ODRDiagsEmitter::diagnoseSubMismatchObjCMethod(
    const NamedDecl *FirstObjCContainer, StringRef FirstModule,
    StringRef SecondModule, const ObjCMethodDecl *FirstMethod,
    const ObjCMethodDecl *SecondMethod) const {
  enum ODRMethodDifference {
    ReturnType,
    InstanceOrClass,
    ControlLevel, // optional/required
    DesignatedInitializer,
    Directness,
    Name,
  };

  auto DiagError = [FirstObjCContainer, FirstModule, FirstMethod,
                    this](ODRMethodDifference DiffType) {
    return Diag(FirstMethod->getLocation(),
                diag::err_module_odr_violation_objc_method)
           << FirstObjCContainer << FirstModule.empty() << FirstModule
           << FirstMethod->getSourceRange() << DiffType;
  };
  auto DiagNote = [SecondModule, SecondMethod,
                   this](ODRMethodDifference DiffType) {
    return Diag(SecondMethod->getLocation(),
                diag::note_module_odr_violation_objc_method)
           << SecondModule.empty() << SecondModule
           << SecondMethod->getSourceRange() << DiffType;
  };

  if (computeODRHash(FirstMethod->getReturnType()) !=
      computeODRHash(SecondMethod->getReturnType())) {
    DiagError(ReturnType) << FirstMethod << FirstMethod->getReturnType();
    DiagNote(ReturnType) << SecondMethod << SecondMethod->getReturnType();
    return true;
  }

  if (FirstMethod->isInstanceMethod() != SecondMethod->isInstanceMethod()) {
    DiagError(InstanceOrClass)
        << FirstMethod << FirstMethod->isInstanceMethod();
    DiagNote(InstanceOrClass)
        << SecondMethod << SecondMethod->isInstanceMethod();
    return true;
  }
  if (FirstMethod->getImplementationControl() !=
      SecondMethod->getImplementationControl()) {
    DiagError(ControlLevel) << FirstMethod->getImplementationControl();
    DiagNote(ControlLevel) << SecondMethod->getImplementationControl();
    return true;
  }
  if (FirstMethod->isThisDeclarationADesignatedInitializer() !=
      SecondMethod->isThisDeclarationADesignatedInitializer()) {
    DiagError(DesignatedInitializer)
        << FirstMethod
        << FirstMethod->isThisDeclarationADesignatedInitializer();
    DiagNote(DesignatedInitializer)
        << SecondMethod
        << SecondMethod->isThisDeclarationADesignatedInitializer();
    return true;
  }
  if (FirstMethod->isDirectMethod() != SecondMethod->isDirectMethod()) {
    DiagError(Directness) << FirstMethod << FirstMethod->isDirectMethod();
    DiagNote(Directness) << SecondMethod << SecondMethod->isDirectMethod();
    return true;
  }
  if (diagnoseSubMismatchMethodParameters(Diags, FirstObjCContainer,
                                          FirstModule, SecondModule,
                                          FirstMethod, SecondMethod))
    return true;

  // Check method name *after* looking at the parameters otherwise we get a
  // less ideal diagnostics: a ObjCMethodName mismatch given that selectors
  // for different parameters are likely to be different.
  DeclarationName FirstName = FirstMethod->getDeclName();
  DeclarationName SecondName = SecondMethod->getDeclName();
  if (FirstName != SecondName) {
    DiagError(Name) << FirstName;
    DiagNote(Name) << SecondName;
    return true;
  }

  return false;
}

bool ODRDiagsEmitter::diagnoseSubMismatchObjCProperty(
    const NamedDecl *FirstObjCContainer, StringRef FirstModule,
    StringRef SecondModule, const ObjCPropertyDecl *FirstProp,
    const ObjCPropertyDecl *SecondProp) const {
  enum ODRPropertyDifference {
    Name,
    Type,
    ControlLevel, // optional/required
    Attribute,
  };

  auto DiagError = [FirstObjCContainer, FirstModule, FirstProp,
                    this](SourceLocation Loc, ODRPropertyDifference DiffType) {
    return Diag(Loc, diag::err_module_odr_violation_objc_property)
           << FirstObjCContainer << FirstModule.empty() << FirstModule
           << FirstProp->getSourceRange() << DiffType;
  };
  auto DiagNote = [SecondModule, SecondProp,
                   this](SourceLocation Loc, ODRPropertyDifference DiffType) {
    return Diag(Loc, diag::note_module_odr_violation_objc_property)
           << SecondModule.empty() << SecondModule
           << SecondProp->getSourceRange() << DiffType;
  };

  IdentifierInfo *FirstII = FirstProp->getIdentifier();
  IdentifierInfo *SecondII = SecondProp->getIdentifier();
  if (FirstII->getName() != SecondII->getName()) {
    DiagError(FirstProp->getLocation(), Name) << FirstII;
    DiagNote(SecondProp->getLocation(), Name) << SecondII;
    return true;
  }
  if (computeODRHash(FirstProp->getType()) !=
      computeODRHash(SecondProp->getType())) {
    DiagError(FirstProp->getLocation(), Type)
        << FirstII << FirstProp->getType();
    DiagNote(SecondProp->getLocation(), Type)
        << SecondII << SecondProp->getType();
    return true;
  }
  if (FirstProp->getPropertyImplementation() !=
      SecondProp->getPropertyImplementation()) {
    DiagError(FirstProp->getLocation(), ControlLevel)
        << FirstProp->getPropertyImplementation();
    DiagNote(SecondProp->getLocation(), ControlLevel)
        << SecondProp->getPropertyImplementation();
    return true;
  }

  // Go over the property attributes and stop at the first mismatch.
  unsigned FirstAttrs = (unsigned)FirstProp->getPropertyAttributes();
  unsigned SecondAttrs = (unsigned)SecondProp->getPropertyAttributes();
  if (FirstAttrs != SecondAttrs) {
    for (unsigned I = 0; I < NumObjCPropertyAttrsBits; ++I) {
      unsigned CheckedAttr = (1 << I);
      if ((FirstAttrs & CheckedAttr) == (SecondAttrs & CheckedAttr))
        continue;

      bool IsFirstWritten =
          (unsigned)FirstProp->getPropertyAttributesAsWritten() & CheckedAttr;
      bool IsSecondWritten =
          (unsigned)SecondProp->getPropertyAttributesAsWritten() & CheckedAttr;
      DiagError(IsFirstWritten ? FirstProp->getLParenLoc()
                               : FirstProp->getLocation(),
                Attribute)
          << FirstII << (I + 1) << IsFirstWritten;
      DiagNote(IsSecondWritten ? SecondProp->getLParenLoc()
                               : SecondProp->getLocation(),
               Attribute)
          << SecondII << (I + 1);
      return true;
    }
  }

  return false;
}

ODRDiagsEmitter::DiffResult
ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes,
                               DeclHashes &SecondHashes) {
  auto DifferenceSelector = [](const Decl *D) {
    assert(D && "valid Decl required");
    switch (D->getKind()) {
    default:
      return Other;
    case Decl::AccessSpec:
      switch (D->getAccess()) {
      case AS_public:
        return PublicSpecifer;
      case AS_private:
        return PrivateSpecifer;
      case AS_protected:
        return ProtectedSpecifer;
      case AS_none:
        break;
      }
      llvm_unreachable("Invalid access specifier");
    case Decl::StaticAssert:
      return StaticAssert;
    case Decl::Field:
      return Field;
    case Decl::CXXMethod:
    case Decl::CXXConstructor:
    case Decl::CXXDestructor:
      return CXXMethod;
    case Decl::TypeAlias:
      return TypeAlias;
    case Decl::Typedef:
      return TypeDef;
    case Decl::Var:
      return Var;
    case Decl::Friend:
      return Friend;
    case Decl::FunctionTemplate:
      return FunctionTemplate;
    case Decl::ObjCMethod:
      return ObjCMethod;
    case Decl::ObjCIvar:
      return ObjCIvar;
    case Decl::ObjCProperty:
      return ObjCProperty;
    }
  };

  DiffResult DR;
  auto FirstIt = FirstHashes.begin();
  auto SecondIt = SecondHashes.begin();
  while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) {
    if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() &&
        FirstIt->second == SecondIt->second) {
      ++FirstIt;
      ++SecondIt;
      continue;
    }

    DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first;
    DR.SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first;

    DR.FirstDiffType =
        DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass;
    DR.SecondDiffType =
        DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass;
    return DR;
  }
  return DR;
}

void ODRDiagsEmitter::diagnoseSubMismatchUnexpected(
    DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule,
    const NamedDecl *SecondRecord, StringRef SecondModule) const {
  Diag(FirstRecord->getLocation(),
       diag::err_module_odr_violation_different_definitions)
      << FirstRecord << FirstModule.empty() << FirstModule;

  if (DR.FirstDecl) {
    Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference)
        << FirstRecord << DR.FirstDecl->getSourceRange();
  }

  Diag(SecondRecord->getLocation(),
       diag::note_module_odr_violation_different_definitions)
      << SecondModule;

  if (DR.SecondDecl) {
    Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference)
        << DR.SecondDecl->getSourceRange();
  }
}

void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds(
    DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule,
    const NamedDecl *SecondRecord, StringRef SecondModule) const {
  auto GetMismatchedDeclLoc = [](const NamedDecl *Container,
                                 ODRMismatchDecl DiffType, const Decl *D) {
    SourceLocation Loc;
    SourceRange Range;
    if (DiffType == EndOfClass) {
      if (auto *Tag = dyn_cast<TagDecl>(Container))
        Loc = Tag->getBraceRange().getEnd();
      else if (auto *IF = dyn_cast<ObjCInterfaceDecl>(Container))
        Loc = IF->getAtEndRange().getBegin();
      else
        Loc = Container->getEndLoc();
    } else {
      Loc = D->getLocation();
      Range = D->getSourceRange();
    }
    return std::make_pair(Loc, Range);
  };

  auto FirstDiagInfo =
      GetMismatchedDeclLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl);
  Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl)
      << FirstRecord << FirstModule.empty() << FirstModule
      << FirstDiagInfo.second << DR.FirstDiffType;

  auto SecondDiagInfo =
      GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl);
  Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl)
      << SecondModule.empty() << SecondModule << SecondDiagInfo.second
      << DR.SecondDiffType;
}

bool ODRDiagsEmitter::diagnoseMismatch(
    const CXXRecordDecl *FirstRecord, const CXXRecordDecl *SecondRecord,
    const struct CXXRecordDecl::DefinitionData *SecondDD) const {
  // Multiple different declarations got merged together; tell the user
  // where they came from.
  if (FirstRecord == SecondRecord)
    return false;

  std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord);
  std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord);

  const struct CXXRecordDecl::DefinitionData *FirstDD =
      FirstRecord->DefinitionData;
  assert(FirstDD && SecondDD && "Definitions without DefinitionData");

  // Diagnostics from DefinitionData are emitted here.
  if (FirstDD != SecondDD) {
    // Keep in sync with err_module_odr_violation_definition_data.
    enum ODRDefinitionDataDifference {
      NumBases,
      NumVBases,
      BaseType,
      BaseVirtual,
      BaseAccess,
    };
    auto DiagBaseError = [FirstRecord, &FirstModule,
                          this](SourceLocation Loc, SourceRange Range,
                                ODRDefinitionDataDifference DiffType) {
      return Diag(Loc, diag::err_module_odr_violation_definition_data)
             << FirstRecord << FirstModule.empty() << FirstModule << Range
             << DiffType;
    };
    auto DiagBaseNote = [&SecondModule,
                         this](SourceLocation Loc, SourceRange Range,
                               ODRDefinitionDataDifference DiffType) {
      return Diag(Loc, diag::note_module_odr_violation_definition_data)
             << SecondModule << Range << DiffType;
    };
    auto GetSourceRange = [](const struct CXXRecordDecl::DefinitionData *DD) {
      unsigned NumBases = DD->NumBases;
      if (NumBases == 0)
        return SourceRange();
      ArrayRef<CXXBaseSpecifier> bases = DD->bases();
      return SourceRange(bases[0].getBeginLoc(),
                         bases[NumBases - 1].getEndLoc());
    };

    unsigned FirstNumBases = FirstDD->NumBases;
    unsigned FirstNumVBases = FirstDD->NumVBases;
    unsigned SecondNumBases = SecondDD->NumBases;
    unsigned SecondNumVBases = SecondDD->NumVBases;
    if (FirstNumBases != SecondNumBases) {
      DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD),
                    NumBases)
          << FirstNumBases;
      DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD),
                   NumBases)
          << SecondNumBases;
      return true;
    }

    if (FirstNumVBases != SecondNumVBases) {
      DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD),
                    NumVBases)
          << FirstNumVBases;
      DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD),
                   NumVBases)
          << SecondNumVBases;
      return true;
    }

    ArrayRef<CXXBaseSpecifier> FirstBases = FirstDD->bases();
    ArrayRef<CXXBaseSpecifier> SecondBases = SecondDD->bases();
    for (unsigned I = 0; I < FirstNumBases; ++I) {
      const CXXBaseSpecifier FirstBase = FirstBases[I];
      const CXXBaseSpecifier SecondBase = SecondBases[I];
      if (computeODRHash(FirstBase.getType()) !=
          computeODRHash(SecondBase.getType())) {
        DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
                      BaseType)
            << (I + 1) << FirstBase.getType();
        DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(),
                     BaseType)
            << (I + 1) << SecondBase.getType();
        return true;
      }

      if (FirstBase.isVirtual() != SecondBase.isVirtual()) {
        DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
                      BaseVirtual)
            << (I + 1) << FirstBase.isVirtual() << FirstBase.getType();
        DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(),
                     BaseVirtual)
            << (I + 1) << SecondBase.isVirtual() << SecondBase.getType();
        return true;
      }

      if (FirstBase.getAccessSpecifierAsWritten() !=
          SecondBase.getAccessSpecifierAsWritten()) {
        DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
                      BaseAccess)
            << (I + 1) << FirstBase.getType()
            << (int)FirstBase.getAccessSpecifierAsWritten();
        DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(),
                     BaseAccess)
            << (I + 1) << SecondBase.getType()
            << (int)SecondBase.getAccessSpecifierAsWritten();
        return true;
      }
    }
  }

  const ClassTemplateDecl *FirstTemplate =
      FirstRecord->getDescribedClassTemplate();
  const ClassTemplateDecl *SecondTemplate =
      SecondRecord->getDescribedClassTemplate();

  assert(!FirstTemplate == !SecondTemplate &&
         "Both pointers should be null or non-null");

  if (FirstTemplate && SecondTemplate) {
    ArrayRef<const NamedDecl *> FirstTemplateParams =
        FirstTemplate->getTemplateParameters()->asArray();
    ArrayRef<const NamedDecl *> SecondTemplateParams =
        SecondTemplate->getTemplateParameters()->asArray();
    assert(FirstTemplateParams.size() == SecondTemplateParams.size() &&
           "Number of template parameters should be equal.");
    for (auto Pair : llvm::zip(FirstTemplateParams, SecondTemplateParams)) {
      const NamedDecl *FirstDecl = std::get<0>(Pair);
      const NamedDecl *SecondDecl = std::get<1>(Pair);
      if (computeODRHash(FirstDecl) == computeODRHash(SecondDecl))
        continue;

      assert(FirstDecl->getKind() == SecondDecl->getKind() &&
             "Parameter Decl's should be the same kind.");

      enum ODRTemplateDifference {
        ParamEmptyName,
        ParamName,
        ParamSingleDefaultArgument,
        ParamDifferentDefaultArgument,
      };

      auto hasDefaultArg = [](const NamedDecl *D) {
        if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(D))
          return TTP->hasDefaultArgument() &&
                 !TTP->defaultArgumentWasInherited();
        if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D))
          return NTTP->hasDefaultArgument() &&
                 !NTTP->defaultArgumentWasInherited();
        auto *TTP = cast<TemplateTemplateParmDecl>(D);
        return TTP->hasDefaultArgument() && !TTP->defaultArgumentWasInherited();
      };
      bool hasFirstArg = hasDefaultArg(FirstDecl);
      bool hasSecondArg = hasDefaultArg(SecondDecl);

      ODRTemplateDifference ErrDiffType;
      ODRTemplateDifference NoteDiffType;

      DeclarationName FirstName = FirstDecl->getDeclName();
      DeclarationName SecondName = SecondDecl->getDeclName();

      if (FirstName != SecondName) {
        bool FirstNameEmpty =
            FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo();
        bool SecondNameEmpty =
            SecondName.isIdentifier() && !SecondName.getAsIdentifierInfo();
        ErrDiffType = FirstNameEmpty ? ParamEmptyName : ParamName;
        NoteDiffType = SecondNameEmpty ? ParamEmptyName : ParamName;
      } else if (hasFirstArg == hasSecondArg)
        ErrDiffType = NoteDiffType = ParamDifferentDefaultArgument;
      else
        ErrDiffType = NoteDiffType = ParamSingleDefaultArgument;

      Diag(FirstDecl->getLocation(),
           diag::err_module_odr_violation_template_parameter)
          << FirstRecord << FirstModule.empty() << FirstModule
          << FirstDecl->getSourceRange() << ErrDiffType << hasFirstArg
          << FirstName;
      Diag(SecondDecl->getLocation(),
           diag::note_module_odr_violation_template_parameter)
          << SecondModule << SecondDecl->getSourceRange() << NoteDiffType
          << hasSecondArg << SecondName;
      return true;
    }
  }

  auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record,
                           const DeclContext *DC) {
    for (const Decl *D : Record->decls()) {
      if (!ODRHash::isSubDeclToBeProcessed(D, DC))
        continue;
      Hashes.emplace_back(D, computeODRHash(D));
    }
  };

  DeclHashes FirstHashes;
  DeclHashes SecondHashes;
  const DeclContext *DC = FirstRecord;
  PopulateHashes(FirstHashes, FirstRecord, DC);
  PopulateHashes(SecondHashes, SecondRecord, DC);

  DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
  ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
  ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
  const Decl *FirstDecl = DR.FirstDecl;
  const Decl *SecondDecl = DR.SecondDecl;

  if (FirstDiffType == Other || SecondDiffType == Other) {
    diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord,
                                  SecondModule);
    return true;
  }

  if (FirstDiffType != SecondDiffType) {
    diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule,
                                          SecondRecord, SecondModule);
    return true;
  }

  // Used with err_module_odr_violation_record and
  // note_module_odr_violation_record
  enum ODRCXXRecordDifference {
    StaticAssertCondition,
    StaticAssertMessage,
    StaticAssertOnlyMessage,
    MethodName,
    MethodDeleted,
    MethodDefaulted,
    MethodVirtual,
    MethodStatic,
    MethodVolatile,
    MethodConst,
    MethodInline,
    MethodParameterSingleDefaultArgument,
    MethodParameterDifferentDefaultArgument,
    MethodNoTemplateArguments,
    MethodDifferentNumberTemplateArguments,
    MethodDifferentTemplateArgument,
    MethodSingleBody,
    MethodDifferentBody,
    FriendTypeFunction,
    FriendType,
    FriendFunction,
    FunctionTemplateDifferentNumberParameters,
    FunctionTemplateParameterDifferentKind,
    FunctionTemplateParameterName,
    FunctionTemplateParameterSingleDefaultArgument,
    FunctionTemplateParameterDifferentDefaultArgument,
    FunctionTemplateParameterDifferentType,
    FunctionTemplatePackParameter,
  };
  auto DiagError = [FirstRecord, &FirstModule,
                    this](SourceLocation Loc, SourceRange Range,
                          ODRCXXRecordDifference DiffType) {
    return Diag(Loc, diag::err_module_odr_violation_record)
           << FirstRecord << FirstModule.empty() << FirstModule << Range
           << DiffType;
  };
  auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
                                        ODRCXXRecordDifference DiffType) {
    return Diag(Loc, diag::note_module_odr_violation_record)
           << SecondModule << Range << DiffType;
  };

  assert(FirstDiffType == SecondDiffType);
  switch (FirstDiffType) {
  case Other:
  case EndOfClass:
  case PublicSpecifer:
  case PrivateSpecifer:
  case ProtectedSpecifer:
  case ObjCMethod:
  case ObjCIvar:
  case ObjCProperty:
    llvm_unreachable("Invalid diff type");

  case StaticAssert: {
    const StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl);
    const StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl);

    const Expr *FirstExpr = FirstSA->getAssertExpr();
    const Expr *SecondExpr = SecondSA->getAssertExpr();
    unsigned FirstODRHash = computeODRHash(FirstExpr);
    unsigned SecondODRHash = computeODRHash(SecondExpr);
    if (FirstODRHash != SecondODRHash) {
      DiagError(FirstExpr->getBeginLoc(), FirstExpr->getSourceRange(),
                StaticAssertCondition);
      DiagNote(SecondExpr->getBeginLoc(), SecondExpr->getSourceRange(),
               StaticAssertCondition);
      return true;
    }

    const Expr *FirstMessage = FirstSA->getMessage();
    const Expr *SecondMessage = SecondSA->getMessage();
    assert((FirstMessage || SecondMessage) && "Both messages cannot be empty");
    if ((FirstMessage && !SecondMessage) || (!FirstMessage && SecondMessage)) {
      SourceLocation FirstLoc, SecondLoc;
      SourceRange FirstRange, SecondRange;
      if (FirstMessage) {
        FirstLoc = FirstMessage->getBeginLoc();
        FirstRange = FirstMessage->getSourceRange();
      } else {
        FirstLoc = FirstSA->getBeginLoc();
        FirstRange = FirstSA->getSourceRange();
      }
      if (SecondMessage) {
        SecondLoc = SecondMessage->getBeginLoc();
        SecondRange = SecondMessage->getSourceRange();
      } else {
        SecondLoc = SecondSA->getBeginLoc();
        SecondRange = SecondSA->getSourceRange();
      }
      DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
          << (FirstMessage == nullptr);
      DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
          << (SecondMessage == nullptr);
      return true;
    }

    if (FirstMessage && SecondMessage) {
      unsigned FirstMessageODRHash = computeODRHash(FirstMessage);
      unsigned SecondMessageODRHash = computeODRHash(SecondMessage);
      if (FirstMessageODRHash != SecondMessageODRHash) {
        DiagError(FirstMessage->getBeginLoc(), FirstMessage->getSourceRange(),
                  StaticAssertMessage);
        DiagNote(SecondMessage->getBeginLoc(), SecondMessage->getSourceRange(),
                 StaticAssertMessage);
        return true;
      }
    }
    break;
  }

  case Field: {
    if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule,
                                 cast<FieldDecl>(FirstDecl),
                                 cast<FieldDecl>(SecondDecl)))
      return true;
    break;
  }

  case CXXMethod: {
    enum {
      DiagMethod,
      DiagConstructor,
      DiagDestructor,
    } FirstMethodType,
        SecondMethodType;
    auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl *D) {
      if (isa<CXXConstructorDecl>(D))
        return DiagConstructor;
      if (isa<CXXDestructorDecl>(D))
        return DiagDestructor;
      return DiagMethod;
    };
    const CXXMethodDecl *FirstMethod = cast<CXXMethodDecl>(FirstDecl);
    const CXXMethodDecl *SecondMethod = cast<CXXMethodDecl>(SecondDecl);
    FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod);
    SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod);
    DeclarationName FirstName = FirstMethod->getDeclName();
    DeclarationName SecondName = SecondMethod->getDeclName();
    auto DiagMethodError = [&DiagError, FirstMethod, FirstMethodType,
                            FirstName](ODRCXXRecordDifference DiffType) {
      return DiagError(FirstMethod->getLocation(),
                       FirstMethod->getSourceRange(), DiffType)
             << FirstMethodType << FirstName;
    };
    auto DiagMethodNote = [&DiagNote, SecondMethod, SecondMethodType,
                           SecondName](ODRCXXRecordDifference DiffType) {
      return DiagNote(SecondMethod->getLocation(),
                      SecondMethod->getSourceRange(), DiffType)
             << SecondMethodType << SecondName;
    };

    if (FirstMethodType != SecondMethodType || FirstName != SecondName) {
      DiagMethodError(MethodName);
      DiagMethodNote(MethodName);
      return true;
    }

    const bool FirstDeleted = FirstMethod->isDeletedAsWritten();
    const bool SecondDeleted = SecondMethod->isDeletedAsWritten();
    if (FirstDeleted != SecondDeleted) {
      DiagMethodError(MethodDeleted) << FirstDeleted;
      DiagMethodNote(MethodDeleted) << SecondDeleted;
      return true;
    }

    const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted();
    const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted();
    if (FirstDefaulted != SecondDefaulted) {
      DiagMethodError(MethodDefaulted) << FirstDefaulted;
      DiagMethodNote(MethodDefaulted) << SecondDefaulted;
      return true;
    }

    const bool FirstVirtual = FirstMethod->isVirtualAsWritten();
    const bool SecondVirtual = SecondMethod->isVirtualAsWritten();
    const bool FirstPure = FirstMethod->isPureVirtual();
    const bool SecondPure = SecondMethod->isPureVirtual();
    if ((FirstVirtual || SecondVirtual) &&
        (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) {
      DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual;
      DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual;
      return true;
    }

    // CXXMethodDecl::isStatic uses the canonical Decl.  With Decl merging,
    // FirstDecl is the canonical Decl of SecondDecl, so the storage
    // class needs to be checked instead.
    StorageClass FirstStorage = FirstMethod->getStorageClass();
    StorageClass SecondStorage = SecondMethod->getStorageClass();
    const bool FirstStatic = FirstStorage == SC_Static;
    const bool SecondStatic = SecondStorage == SC_Static;
    if (FirstStatic != SecondStatic) {
      DiagMethodError(MethodStatic) << FirstStatic;
      DiagMethodNote(MethodStatic) << SecondStatic;
      return true;
    }

    const bool FirstVolatile = FirstMethod->isVolatile();
    const bool SecondVolatile = SecondMethod->isVolatile();
    if (FirstVolatile != SecondVolatile) {
      DiagMethodError(MethodVolatile) << FirstVolatile;
      DiagMethodNote(MethodVolatile) << SecondVolatile;
      return true;
    }

    const bool FirstConst = FirstMethod->isConst();
    const bool SecondConst = SecondMethod->isConst();
    if (FirstConst != SecondConst) {
      DiagMethodError(MethodConst) << FirstConst;
      DiagMethodNote(MethodConst) << SecondConst;
      return true;
    }

    const bool FirstInline = FirstMethod->isInlineSpecified();
    const bool SecondInline = SecondMethod->isInlineSpecified();
    if (FirstInline != SecondInline) {
      DiagMethodError(MethodInline) << FirstInline;
      DiagMethodNote(MethodInline) << SecondInline;
      return true;
    }

    if (diagnoseSubMismatchMethodParameters(Diags, FirstRecord,
                                            FirstModule, SecondModule,
                                            FirstMethod, SecondMethod))
      return true;

    for (unsigned I = 0, N = FirstMethod->param_size(); I < N; ++I) {
      const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I);
      const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I);

      const Expr *FirstInit = FirstParam->getInit();
      const Expr *SecondInit = SecondParam->getInit();
      if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
        DiagMethodError(MethodParameterSingleDefaultArgument)
            << (I + 1) << (FirstInit == nullptr)
            << (FirstInit ? FirstInit->getSourceRange() : SourceRange());
        DiagMethodNote(MethodParameterSingleDefaultArgument)
            << (I + 1) << (SecondInit == nullptr)
            << (SecondInit ? SecondInit->getSourceRange() : SourceRange());
        return true;
      }

      if (FirstInit && SecondInit &&
          computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
        DiagMethodError(MethodParameterDifferentDefaultArgument)
            << (I + 1) << FirstInit->getSourceRange();
        DiagMethodNote(MethodParameterDifferentDefaultArgument)
            << (I + 1) << SecondInit->getSourceRange();
        return true;
      }
    }

    const TemplateArgumentList *FirstTemplateArgs =
        FirstMethod->getTemplateSpecializationArgs();
    const TemplateArgumentList *SecondTemplateArgs =
        SecondMethod->getTemplateSpecializationArgs();

    if ((FirstTemplateArgs && !SecondTemplateArgs) ||
        (!FirstTemplateArgs && SecondTemplateArgs)) {
      DiagMethodError(MethodNoTemplateArguments)
          << (FirstTemplateArgs != nullptr);
      DiagMethodNote(MethodNoTemplateArguments)
          << (SecondTemplateArgs != nullptr);
      return true;
    }

    if (FirstTemplateArgs && SecondTemplateArgs) {
      // Remove pack expansions from argument list.
      auto ExpandTemplateArgumentList = [](const TemplateArgumentList *TAL) {
        llvm::SmallVector<const TemplateArgument *, 8> ExpandedList;
        for (const TemplateArgument &TA : TAL->asArray()) {
          if (TA.getKind() != TemplateArgument::Pack) {
            ExpandedList.push_back(&TA);
            continue;
          }
          llvm::append_range(ExpandedList,
                             llvm::make_pointer_range(TA.getPackAsArray()));
        }
        return ExpandedList;
      };
      llvm::SmallVector<const TemplateArgument *, 8> FirstExpandedList =
          ExpandTemplateArgumentList(FirstTemplateArgs);
      llvm::SmallVector<const TemplateArgument *, 8> SecondExpandedList =
          ExpandTemplateArgumentList(SecondTemplateArgs);

      if (FirstExpandedList.size() != SecondExpandedList.size()) {
        DiagMethodError(MethodDifferentNumberTemplateArguments)
            << (unsigned)FirstExpandedList.size();
        DiagMethodNote(MethodDifferentNumberTemplateArguments)
            << (unsigned)SecondExpandedList.size();
        return true;
      }

      for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) {
        const TemplateArgument &FirstTA = *FirstExpandedList[i],
                               &SecondTA = *SecondExpandedList[i];
        if (computeODRHash(FirstTA) == computeODRHash(SecondTA))
          continue;

        DiagMethodError(MethodDifferentTemplateArgument) << FirstTA << i + 1;
        DiagMethodNote(MethodDifferentTemplateArgument) << SecondTA << i + 1;
        return true;
      }
    }

    // Compute the hash of the method as if it has no body.
    auto ComputeCXXMethodODRHash = [](const CXXMethodDecl *D) {
      ODRHash Hasher;
      Hasher.AddFunctionDecl(D, true /*SkipBody*/);
      return Hasher.CalculateHash();
    };

    // Compare the hash generated to the hash stored.  A difference means
    // that a body was present in the original source.  Due to merging,
    // the standard way of detecting a body will not work.
    const bool HasFirstBody =
        ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash();
    const bool HasSecondBody =
        ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash();

    if (HasFirstBody != HasSecondBody) {
      DiagMethodError(MethodSingleBody) << HasFirstBody;
      DiagMethodNote(MethodSingleBody) << HasSecondBody;
      return true;
    }

    if (HasFirstBody && HasSecondBody) {
      DiagMethodError(MethodDifferentBody);
      DiagMethodNote(MethodDifferentBody);
      return true;
    }

    break;
  }

  case TypeAlias:
  case TypeDef: {
    if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule,
                                   cast<TypedefNameDecl>(FirstDecl),
                                   cast<TypedefNameDecl>(SecondDecl),
                                   FirstDiffType == TypeAlias))
      return true;
    break;
  }
  case Var: {
    if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule,
                               cast<VarDecl>(FirstDecl),
                               cast<VarDecl>(SecondDecl)))
      return true;
    break;
  }
  case Friend: {
    const FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl);
    const FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl);

    const NamedDecl *FirstND = FirstFriend->getFriendDecl();
    const NamedDecl *SecondND = SecondFriend->getFriendDecl();

    TypeSourceInfo *FirstTSI = FirstFriend->getFriendType();
    TypeSourceInfo *SecondTSI = SecondFriend->getFriendType();

    if (FirstND && SecondND) {
      DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(),
                FriendFunction)
          << FirstND;
      DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(),
               FriendFunction)
          << SecondND;
      return true;
    }

    if (FirstTSI && SecondTSI) {
      QualType FirstFriendType = FirstTSI->getType();
      QualType SecondFriendType = SecondTSI->getType();
      assert(computeODRHash(FirstFriendType) !=
             computeODRHash(SecondFriendType));
      DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(),
                FriendType)
          << FirstFriendType;
      DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(),
               FriendType)
          << SecondFriendType;
      return true;
    }

    DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(),
              FriendTypeFunction)
        << (FirstTSI == nullptr);
    DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(),
             FriendTypeFunction)
        << (SecondTSI == nullptr);
    return true;
  }
  case FunctionTemplate: {
    const FunctionTemplateDecl *FirstTemplate =
        cast<FunctionTemplateDecl>(FirstDecl);
    const FunctionTemplateDecl *SecondTemplate =
        cast<FunctionTemplateDecl>(SecondDecl);

    TemplateParameterList *FirstTPL = FirstTemplate->getTemplateParameters();
    TemplateParameterList *SecondTPL = SecondTemplate->getTemplateParameters();

    auto DiagTemplateError = [&DiagError,
                              FirstTemplate](ODRCXXRecordDifference DiffType) {
      return DiagError(FirstTemplate->getLocation(),
                       FirstTemplate->getSourceRange(), DiffType)
             << FirstTemplate;
    };
    auto DiagTemplateNote = [&DiagNote,
                             SecondTemplate](ODRCXXRecordDifference DiffType) {
      return DiagNote(SecondTemplate->getLocation(),
                      SecondTemplate->getSourceRange(), DiffType)
             << SecondTemplate;
    };

    if (FirstTPL->size() != SecondTPL->size()) {
      DiagTemplateError(FunctionTemplateDifferentNumberParameters)
          << FirstTPL->size();
      DiagTemplateNote(FunctionTemplateDifferentNumberParameters)
          << SecondTPL->size();
      return true;
    }

    for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) {
      NamedDecl *FirstParam = FirstTPL->getParam(i);
      NamedDecl *SecondParam = SecondTPL->getParam(i);

      if (FirstParam->getKind() != SecondParam->getKind()) {
        enum {
          TemplateTypeParameter,
          NonTypeTemplateParameter,
          TemplateTemplateParameter,
        };
        auto GetParamType = [](NamedDecl *D) {
          switch (D->getKind()) {
          default:
            llvm_unreachable("Unexpected template parameter type");
          case Decl::TemplateTypeParm:
            return TemplateTypeParameter;
          case Decl::NonTypeTemplateParm:
            return NonTypeTemplateParameter;
          case Decl::TemplateTemplateParm:
            return TemplateTemplateParameter;
          }
        };

        DiagTemplateError(FunctionTemplateParameterDifferentKind)
            << (i + 1) << GetParamType(FirstParam);
        DiagTemplateNote(FunctionTemplateParameterDifferentKind)
            << (i + 1) << GetParamType(SecondParam);
        return true;
      }

      if (FirstParam->getName() != SecondParam->getName()) {
        DiagTemplateError(FunctionTemplateParameterName)
            << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam;
        DiagTemplateNote(FunctionTemplateParameterName)
            << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam;
        return true;
      }

      if (isa<TemplateTypeParmDecl>(FirstParam) &&
          isa<TemplateTypeParmDecl>(SecondParam)) {
        TemplateTypeParmDecl *FirstTTPD =
            cast<TemplateTypeParmDecl>(FirstParam);
        TemplateTypeParmDecl *SecondTTPD =
            cast<TemplateTypeParmDecl>(SecondParam);
        bool HasFirstDefaultArgument =
            FirstTTPD->hasDefaultArgument() &&
            !FirstTTPD->defaultArgumentWasInherited();
        bool HasSecondDefaultArgument =
            SecondTTPD->hasDefaultArgument() &&
            !SecondTTPD->defaultArgumentWasInherited();
        if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
          DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument)
              << (i + 1) << HasFirstDefaultArgument;
          DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument)
              << (i + 1) << HasSecondDefaultArgument;
          return true;
        }

        if (HasFirstDefaultArgument && HasSecondDefaultArgument) {
          TemplateArgument FirstTA =
              FirstTTPD->getDefaultArgument().getArgument();
          TemplateArgument SecondTA =
              SecondTTPD->getDefaultArgument().getArgument();
          if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) {
            DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument)
                << (i + 1) << FirstTA;
            DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument)
                << (i + 1) << SecondTA;
            return true;
          }
        }

        if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) {
          DiagTemplateError(FunctionTemplatePackParameter)
              << (i + 1) << FirstTTPD->isParameterPack();
          DiagTemplateNote(FunctionTemplatePackParameter)
              << (i + 1) << SecondTTPD->isParameterPack();
          return true;
        }
      }

      if (isa<TemplateTemplateParmDecl>(FirstParam) &&
          isa<TemplateTemplateParmDecl>(SecondParam)) {
        TemplateTemplateParmDecl *FirstTTPD =
            cast<TemplateTemplateParmDecl>(FirstParam);
        TemplateTemplateParmDecl *SecondTTPD =
            cast<TemplateTemplateParmDecl>(SecondParam);

        TemplateParameterList *FirstTPL = FirstTTPD->getTemplateParameters();
        TemplateParameterList *SecondTPL = SecondTTPD->getTemplateParameters();

        auto ComputeTemplateParameterListODRHash =
            [](const TemplateParameterList *TPL) {
              assert(TPL);
              ODRHash Hasher;
              Hasher.AddTemplateParameterList(TPL);
              return Hasher.CalculateHash();
            };

        if (ComputeTemplateParameterListODRHash(FirstTPL) !=
            ComputeTemplateParameterListODRHash(SecondTPL)) {
          DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1);
          DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1);
          return true;
        }

        bool HasFirstDefaultArgument =
            FirstTTPD->hasDefaultArgument() &&
            !FirstTTPD->defaultArgumentWasInherited();
        bool HasSecondDefaultArgument =
            SecondTTPD->hasDefaultArgument() &&
            !SecondTTPD->defaultArgumentWasInherited();
        if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
          DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument)
              << (i + 1) << HasFirstDefaultArgument;
          DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument)
              << (i + 1) << HasSecondDefaultArgument;
          return true;
        }

        if (HasFirstDefaultArgument && HasSecondDefaultArgument) {
          TemplateArgument FirstTA =
              FirstTTPD->getDefaultArgument().getArgument();
          TemplateArgument SecondTA =
              SecondTTPD->getDefaultArgument().getArgument();
          if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) {
            DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument)
                << (i + 1) << FirstTA;
            DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument)
                << (i + 1) << SecondTA;
            return true;
          }
        }

        if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) {
          DiagTemplateError(FunctionTemplatePackParameter)
              << (i + 1) << FirstTTPD->isParameterPack();
          DiagTemplateNote(FunctionTemplatePackParameter)
              << (i + 1) << SecondTTPD->isParameterPack();
          return true;
        }
      }

      if (isa<NonTypeTemplateParmDecl>(FirstParam) &&
          isa<NonTypeTemplateParmDecl>(SecondParam)) {
        NonTypeTemplateParmDecl *FirstNTTPD =
            cast<NonTypeTemplateParmDecl>(FirstParam);
        NonTypeTemplateParmDecl *SecondNTTPD =
            cast<NonTypeTemplateParmDecl>(SecondParam);

        QualType FirstType = FirstNTTPD->getType();
        QualType SecondType = SecondNTTPD->getType();
        if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
          DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1);
          DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1);
          return true;
        }

        bool HasFirstDefaultArgument =
            FirstNTTPD->hasDefaultArgument() &&
            !FirstNTTPD->defaultArgumentWasInherited();
        bool HasSecondDefaultArgument =
            SecondNTTPD->hasDefaultArgument() &&
            !SecondNTTPD->defaultArgumentWasInherited();
        if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
          DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument)
              << (i + 1) << HasFirstDefaultArgument;
          DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument)
              << (i + 1) << HasSecondDefaultArgument;
          return true;
        }

        if (HasFirstDefaultArgument && HasSecondDefaultArgument) {
          TemplateArgument FirstDefaultArgument =
              FirstNTTPD->getDefaultArgument().getArgument();
          TemplateArgument SecondDefaultArgument =
              SecondNTTPD->getDefaultArgument().getArgument();

          if (computeODRHash(FirstDefaultArgument) !=
              computeODRHash(SecondDefaultArgument)) {
            DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument)
                << (i + 1) << FirstDefaultArgument;
            DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument)
                << (i + 1) << SecondDefaultArgument;
            return true;
          }
        }

        if (FirstNTTPD->isParameterPack() != SecondNTTPD->isParameterPack()) {
          DiagTemplateError(FunctionTemplatePackParameter)
              << (i + 1) << FirstNTTPD->isParameterPack();
          DiagTemplateNote(FunctionTemplatePackParameter)
              << (i + 1) << SecondNTTPD->isParameterPack();
          return true;
        }
      }
    }
    break;
  }
  }

  Diag(FirstDecl->getLocation(),
       diag::err_module_odr_violation_mismatch_decl_unknown)
      << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType
      << FirstDecl->getSourceRange();
  Diag(SecondDecl->getLocation(),
       diag::note_module_odr_violation_mismatch_decl_unknown)
      << SecondModule.empty() << SecondModule << FirstDiffType
      << SecondDecl->getSourceRange();
  return true;
}

bool ODRDiagsEmitter::diagnoseMismatch(const RecordDecl *FirstRecord,
                                       const RecordDecl *SecondRecord) const {
  if (FirstRecord == SecondRecord)
    return false;

  std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord);
  std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord);

  auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record,
                           const DeclContext *DC) {
    for (const Decl *D : Record->decls()) {
      if (!ODRHash::isSubDeclToBeProcessed(D, DC))
        continue;
      Hashes.emplace_back(D, computeODRHash(D));
    }
  };

  DeclHashes FirstHashes;
  DeclHashes SecondHashes;
  const DeclContext *DC = FirstRecord;
  PopulateHashes(FirstHashes, FirstRecord, DC);
  PopulateHashes(SecondHashes, SecondRecord, DC);

  DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
  ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
  ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
  const Decl *FirstDecl = DR.FirstDecl;
  const Decl *SecondDecl = DR.SecondDecl;

  if (FirstDiffType == Other || SecondDiffType == Other) {
    diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord,
                                  SecondModule);
    return true;
  }

  if (FirstDiffType != SecondDiffType) {
    diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule,
                                          SecondRecord, SecondModule);
    return true;
  }

  assert(FirstDiffType == SecondDiffType);
  switch (FirstDiffType) {
  // Already handled.
  case EndOfClass:
  case Other:
  // C++ only, invalid in this context.
  case PublicSpecifer:
  case PrivateSpecifer:
  case ProtectedSpecifer:
  case StaticAssert:
  case CXXMethod:
  case TypeAlias:
  case Friend:
  case FunctionTemplate:
  // Cannot be contained by RecordDecl, invalid in this context.
  case ObjCMethod:
  case ObjCIvar:
  case ObjCProperty:
    llvm_unreachable("Invalid diff type");

  case Field: {
    if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule,
                                 cast<FieldDecl>(FirstDecl),
                                 cast<FieldDecl>(SecondDecl)))
      return true;
    break;
  }
  case TypeDef: {
    if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule,
                                   cast<TypedefNameDecl>(FirstDecl),
                                   cast<TypedefNameDecl>(SecondDecl),
                                   /*IsTypeAlias=*/false))
      return true;
    break;
  }
  case Var: {
    if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule,
                               cast<VarDecl>(FirstDecl),
                               cast<VarDecl>(SecondDecl)))
      return true;
    break;
  }
  }

  Diag(FirstDecl->getLocation(),
       diag::err_module_odr_violation_mismatch_decl_unknown)
      << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType
      << FirstDecl->getSourceRange();
  Diag(SecondDecl->getLocation(),
       diag::note_module_odr_violation_mismatch_decl_unknown)
      << SecondModule.empty() << SecondModule << FirstDiffType
      << SecondDecl->getSourceRange();
  return true;
}

bool ODRDiagsEmitter::diagnoseMismatch(
    const FunctionDecl *FirstFunction,
    const FunctionDecl *SecondFunction) const {
  if (FirstFunction == SecondFunction)
    return false;

  // Keep in sync with select options in err_module_odr_violation_function.
  enum ODRFunctionDifference {
    ReturnType,
    ParameterName,
    ParameterType,
    ParameterSingleDefaultArgument,
    ParameterDifferentDefaultArgument,
    FunctionBody,
  };

  std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction);
  std::string SecondModule = getOwningModuleNameForDiagnostic(SecondFunction);

  auto DiagError = [FirstFunction, &FirstModule,
                    this](SourceLocation Loc, SourceRange Range,
                          ODRFunctionDifference DiffType) {
    return Diag(Loc, diag::err_module_odr_violation_function)
           << FirstFunction << FirstModule.empty() << FirstModule << Range
           << DiffType;
  };
  auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
                                        ODRFunctionDifference DiffType) {
    return Diag(Loc, diag::note_module_odr_violation_function)
           << SecondModule << Range << DiffType;
  };

  if (computeODRHash(FirstFunction->getReturnType()) !=
      computeODRHash(SecondFunction->getReturnType())) {
    DiagError(FirstFunction->getReturnTypeSourceRange().getBegin(),
              FirstFunction->getReturnTypeSourceRange(), ReturnType)
        << FirstFunction->getReturnType();
    DiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(),
             SecondFunction->getReturnTypeSourceRange(), ReturnType)
        << SecondFunction->getReturnType();
    return true;
  }

  assert(FirstFunction->param_size() == SecondFunction->param_size() &&
         "Merged functions with different number of parameters");

  size_t ParamSize = FirstFunction->param_size();
  for (unsigned I = 0; I < ParamSize; ++I) {
    const ParmVarDecl *FirstParam = FirstFunction->getParamDecl(I);
    const ParmVarDecl *SecondParam = SecondFunction->getParamDecl(I);

    assert(Context.hasSameType(FirstParam->getType(), SecondParam->getType()) &&
           "Merged function has different parameter types.");

    if (FirstParam->getDeclName() != SecondParam->getDeclName()) {
      DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
                ParameterName)
          << I + 1 << FirstParam->getDeclName();
      DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
               ParameterName)
          << I + 1 << SecondParam->getDeclName();
      return true;
    };

    QualType FirstParamType = FirstParam->getType();
    QualType SecondParamType = SecondParam->getType();
    if (FirstParamType != SecondParamType &&
        computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) {
      if (const DecayedType *ParamDecayedType =
              FirstParamType->getAs<DecayedType>()) {
        DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
                  ParameterType)
            << (I + 1) << FirstParamType << true
            << ParamDecayedType->getOriginalType();
      } else {
        DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
                  ParameterType)
            << (I + 1) << FirstParamType << false;
      }

      if (const DecayedType *ParamDecayedType =
              SecondParamType->getAs<DecayedType>()) {
        DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
                 ParameterType)
            << (I + 1) << SecondParamType << true
            << ParamDecayedType->getOriginalType();
      } else {
        DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
                 ParameterType)
            << (I + 1) << SecondParamType << false;
      }
      return true;
    }

    // Note, these calls can trigger deserialization.
    const Expr *FirstInit = FirstParam->getInit();
    const Expr *SecondInit = SecondParam->getInit();
    if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
      DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
                ParameterSingleDefaultArgument)
          << (I + 1) << (FirstInit == nullptr)
          << (FirstInit ? FirstInit->getSourceRange() : SourceRange());
      DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
               ParameterSingleDefaultArgument)
          << (I + 1) << (SecondInit == nullptr)
          << (SecondInit ? SecondInit->getSourceRange() : SourceRange());
      return true;
    }

    if (FirstInit && SecondInit &&
        computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
      DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
                ParameterDifferentDefaultArgument)
          << (I + 1) << FirstInit->getSourceRange();
      DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
               ParameterDifferentDefaultArgument)
          << (I + 1) << SecondInit->getSourceRange();
      return true;
    }

    assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) &&
           "Undiagnosed parameter difference.");
  }

  // If no error has been generated before now, assume the problem is in
  // the body and generate a message.
  DiagError(FirstFunction->getLocation(), FirstFunction->getSourceRange(),
            FunctionBody);
  DiagNote(SecondFunction->getLocation(), SecondFunction->getSourceRange(),
           FunctionBody);
  return true;
}

bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum,
                                       const EnumDecl *SecondEnum) const {
  if (FirstEnum == SecondEnum)
    return false;

  // Keep in sync with select options in err_module_odr_violation_enum.
  enum ODREnumDifference {
    SingleScopedEnum,
    EnumTagKeywordMismatch,
    SingleSpecifiedType,
    DifferentSpecifiedTypes,
    DifferentNumberEnumConstants,
    EnumConstantName,
    EnumConstantSingleInitializer,
    EnumConstantDifferentInitializer,
  };

  std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum);
  std::string SecondModule = getOwningModuleNameForDiagnostic(SecondEnum);

  auto DiagError = [FirstEnum, &FirstModule, this](const auto *DiagAnchor,
                                                   ODREnumDifference DiffType) {
    return Diag(DiagAnchor->getLocation(), diag::err_module_odr_violation_enum)
           << FirstEnum << FirstModule.empty() << FirstModule
           << DiagAnchor->getSourceRange() << DiffType;
  };
  auto DiagNote = [&SecondModule, this](const auto *DiagAnchor,
                                        ODREnumDifference DiffType) {
    return Diag(DiagAnchor->getLocation(), diag::note_module_odr_violation_enum)
           << SecondModule << DiagAnchor->getSourceRange() << DiffType;
  };

  if (FirstEnum->isScoped() != SecondEnum->isScoped()) {
    DiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped();
    DiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped();
    return true;
  }

  if (FirstEnum->isScoped() && SecondEnum->isScoped()) {
    if (FirstEnum->isScopedUsingClassTag() !=
        SecondEnum->isScopedUsingClassTag()) {
      DiagError(FirstEnum, EnumTagKeywordMismatch)
          << FirstEnum->isScopedUsingClassTag();
      DiagNote(SecondEnum, EnumTagKeywordMismatch)
          << SecondEnum->isScopedUsingClassTag();
      return true;
    }
  }

  QualType FirstUnderlyingType =
      FirstEnum->getIntegerTypeSourceInfo()
          ? FirstEnum->getIntegerTypeSourceInfo()->getType()
          : QualType();
  QualType SecondUnderlyingType =
      SecondEnum->getIntegerTypeSourceInfo()
          ? SecondEnum->getIntegerTypeSourceInfo()->getType()
          : QualType();
  if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) {
    DiagError(FirstEnum, SingleSpecifiedType) << !FirstUnderlyingType.isNull();
    DiagNote(SecondEnum, SingleSpecifiedType) << !SecondUnderlyingType.isNull();
    return true;
  }

  if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) {
    if (computeODRHash(FirstUnderlyingType) !=
        computeODRHash(SecondUnderlyingType)) {
      DiagError(FirstEnum, DifferentSpecifiedTypes) << FirstUnderlyingType;
      DiagNote(SecondEnum, DifferentSpecifiedTypes) << SecondUnderlyingType;
      return true;
    }
  }

  // Compare enum constants.
  using DeclHashes =
      llvm::SmallVector<std::pair<const EnumConstantDecl *, unsigned>, 4>;
  auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, const EnumDecl *Enum) {
    for (const Decl *D : Enum->decls()) {
      // Due to decl merging, the first EnumDecl is the parent of
      // Decls in both records.
      if (!ODRHash::isSubDeclToBeProcessed(D, FirstEnum))
        continue;
      assert(isa<EnumConstantDecl>(D) && "Unexpected Decl kind");
      Hashes.emplace_back(cast<EnumConstantDecl>(D), computeODRHash(D));
    }
  };
  DeclHashes FirstHashes;
  PopulateHashes(FirstHashes, FirstEnum);
  DeclHashes SecondHashes;
  PopulateHashes(SecondHashes, SecondEnum);

  if (FirstHashes.size() != SecondHashes.size()) {
    DiagError(FirstEnum, DifferentNumberEnumConstants)
        << (int)FirstHashes.size();
    DiagNote(SecondEnum, DifferentNumberEnumConstants)
        << (int)SecondHashes.size();
    return true;
  }

  for (unsigned I = 0, N = FirstHashes.size(); I < N; ++I) {
    if (FirstHashes[I].second == SecondHashes[I].second)
      continue;
    const EnumConstantDecl *FirstConstant = FirstHashes[I].first;
    const EnumConstantDecl *SecondConstant = SecondHashes[I].first;

    if (FirstConstant->getDeclName() != SecondConstant->getDeclName()) {
      DiagError(FirstConstant, EnumConstantName) << I + 1 << FirstConstant;
      DiagNote(SecondConstant, EnumConstantName) << I + 1 << SecondConstant;
      return true;
    }

    const Expr *FirstInit = FirstConstant->getInitExpr();
    const Expr *SecondInit = SecondConstant->getInitExpr();
    if (!FirstInit && !SecondInit)
      continue;

    if (!FirstInit || !SecondInit) {
      DiagError(FirstConstant, EnumConstantSingleInitializer)
          << I + 1 << FirstConstant << (FirstInit != nullptr);
      DiagNote(SecondConstant, EnumConstantSingleInitializer)
          << I + 1 << SecondConstant << (SecondInit != nullptr);
      return true;
    }

    if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
      DiagError(FirstConstant, EnumConstantDifferentInitializer)
          << I + 1 << FirstConstant;
      DiagNote(SecondConstant, EnumConstantDifferentInitializer)
          << I + 1 << SecondConstant;
      return true;
    }
  }
  return false;
}

bool ODRDiagsEmitter::diagnoseMismatch(
    const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID,
    const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const {
  // Multiple different declarations got merged together; tell the user
  // where they came from.
  if (FirstID == SecondID)
    return false;

  std::string FirstModule = getOwningModuleNameForDiagnostic(FirstID);
  std::string SecondModule = getOwningModuleNameForDiagnostic(SecondID);

  // Keep in sync with err_module_odr_violation_objc_interface.
  enum ODRInterfaceDifference {
    SuperClassType,
    IVarAccess,
  };

  auto DiagError = [FirstID, &FirstModule,
                    this](SourceLocation Loc, SourceRange Range,
                          ODRInterfaceDifference DiffType) {
    return Diag(Loc, diag::err_module_odr_violation_objc_interface)
           << FirstID << FirstModule.empty() << FirstModule << Range
           << DiffType;
  };
  auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
                                        ODRInterfaceDifference DiffType) {
    return Diag(Loc, diag::note_module_odr_violation_objc_interface)
           << SecondModule.empty() << SecondModule << Range << DiffType;
  };

  const struct ObjCInterfaceDecl::DefinitionData *FirstDD = &FirstID->data();
  assert(FirstDD && SecondDD && "Definitions without DefinitionData");
  if (FirstDD != SecondDD) {
    // Check for matching super class.
    auto GetSuperClassSourceRange = [](const TypeSourceInfo *SuperInfo,
                                       const ObjCInterfaceDecl *ID) {
      if (!SuperInfo)
        return ID->getSourceRange();
      TypeLoc Loc = SuperInfo->getTypeLoc();
      return SourceRange(Loc.getBeginLoc(), Loc.getEndLoc());
    };

    ObjCInterfaceDecl *FirstSuperClass = FirstID->getSuperClass();
    ObjCInterfaceDecl *SecondSuperClass = nullptr;
    const TypeSourceInfo *FirstSuperInfo = FirstID->getSuperClassTInfo();
    const TypeSourceInfo *SecondSuperInfo = SecondDD->SuperClassTInfo;
    if (SecondSuperInfo)
      SecondSuperClass =
          SecondSuperInfo->getType()->castAs<ObjCObjectType>()->getInterface();

    if ((FirstSuperClass && SecondSuperClass &&
         FirstSuperClass->getODRHash() != SecondSuperClass->getODRHash()) ||
        (FirstSuperClass && !SecondSuperClass) ||
        (!FirstSuperClass && SecondSuperClass)) {
      QualType FirstType;
      if (FirstSuperInfo)
        FirstType = FirstSuperInfo->getType();

      DiagError(FirstID->getLocation(),
                GetSuperClassSourceRange(FirstSuperInfo, FirstID),
                SuperClassType)
          << (bool)FirstSuperInfo << FirstType;

      QualType SecondType;
      if (SecondSuperInfo)
        SecondType = SecondSuperInfo->getType();

      DiagNote(SecondID->getLocation(),
               GetSuperClassSourceRange(SecondSuperInfo, SecondID),
               SuperClassType)
          << (bool)SecondSuperInfo << SecondType;
      return true;
    }

    // Check both interfaces reference the same protocols.
    auto &FirstProtos = FirstID->getReferencedProtocols();
    auto &SecondProtos = SecondDD->ReferencedProtocols;
    if (diagnoseSubMismatchProtocols(FirstProtos, FirstID, FirstModule,
                                     SecondProtos, SecondID, SecondModule))
      return true;
  }

  auto PopulateHashes = [](DeclHashes &Hashes, const ObjCInterfaceDecl *ID,
                           const DeclContext *DC) {
    for (auto *D : ID->decls()) {
      if (!ODRHash::isSubDeclToBeProcessed(D, DC))
        continue;
      Hashes.emplace_back(D, computeODRHash(D));
    }
  };

  DeclHashes FirstHashes;
  DeclHashes SecondHashes;
  // Use definition as DeclContext because definitions are merged when
  // DeclContexts are merged and separate when DeclContexts are separate.
  PopulateHashes(FirstHashes, FirstID, FirstID->getDefinition());
  PopulateHashes(SecondHashes, SecondID, SecondID->getDefinition());

  DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
  ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
  ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
  const Decl *FirstDecl = DR.FirstDecl;
  const Decl *SecondDecl = DR.SecondDecl;

  if (FirstDiffType == Other || SecondDiffType == Other) {
    diagnoseSubMismatchUnexpected(DR, FirstID, FirstModule, SecondID,
                                  SecondModule);
    return true;
  }

  if (FirstDiffType != SecondDiffType) {
    diagnoseSubMismatchDifferentDeclKinds(DR, FirstID, FirstModule, SecondID,
                                          SecondModule);
    return true;
  }

  assert(FirstDiffType == SecondDiffType);
  switch (FirstDiffType) {
  // Already handled.
  case EndOfClass:
  case Other:
  // Cannot be contained by ObjCInterfaceDecl, invalid in this context.
  case Field:
  case TypeDef:
  case Var:
  // C++ only, invalid in this context.
  case PublicSpecifer:
  case PrivateSpecifer:
  case ProtectedSpecifer:
  case StaticAssert:
  case CXXMethod:
  case TypeAlias:
  case Friend:
  case FunctionTemplate:
    llvm_unreachable("Invalid diff type");

  case ObjCMethod: {
    if (diagnoseSubMismatchObjCMethod(FirstID, FirstModule, SecondModule,
                                      cast<ObjCMethodDecl>(FirstDecl),
                                      cast<ObjCMethodDecl>(SecondDecl)))
      return true;
    break;
  }
  case ObjCIvar: {
    if (diagnoseSubMismatchField(FirstID, FirstModule, SecondModule,
                                 cast<FieldDecl>(FirstDecl),
                                 cast<FieldDecl>(SecondDecl)))
      return true;

    // Check if the access match.
    const ObjCIvarDecl *FirstIvar = cast<ObjCIvarDecl>(FirstDecl);
    const ObjCIvarDecl *SecondIvar = cast<ObjCIvarDecl>(SecondDecl);
    if (FirstIvar->getCanonicalAccessControl() !=
        SecondIvar->getCanonicalAccessControl()) {
      DiagError(FirstIvar->getLocation(), FirstIvar->getSourceRange(),
                IVarAccess)
          << FirstIvar->getName()
          << (int)FirstIvar->getCanonicalAccessControl();
      DiagNote(SecondIvar->getLocation(), SecondIvar->getSourceRange(),
               IVarAccess)
          << SecondIvar->getName()
          << (int)SecondIvar->getCanonicalAccessControl();
      return true;
    }
    break;
  }
  case ObjCProperty: {
    if (diagnoseSubMismatchObjCProperty(FirstID, FirstModule, SecondModule,
                                        cast<ObjCPropertyDecl>(FirstDecl),
                                        cast<ObjCPropertyDecl>(SecondDecl)))
      return true;
    break;
  }
  }

  Diag(FirstDecl->getLocation(),
       diag::err_module_odr_violation_mismatch_decl_unknown)
      << FirstID << FirstModule.empty() << FirstModule << FirstDiffType
      << FirstDecl->getSourceRange();
  Diag(SecondDecl->getLocation(),
       diag::note_module_odr_violation_mismatch_decl_unknown)
      << SecondModule.empty() << SecondModule << FirstDiffType
      << SecondDecl->getSourceRange();
  return true;
}

bool ODRDiagsEmitter::diagnoseMismatch(
    const ObjCProtocolDecl *FirstProtocol,
    const ObjCProtocolDecl *SecondProtocol,
    const struct ObjCProtocolDecl::DefinitionData *SecondDD) const {
  if (FirstProtocol == SecondProtocol)
    return false;

  std::string FirstModule = getOwningModuleNameForDiagnostic(FirstProtocol);
  std::string SecondModule = getOwningModuleNameForDiagnostic(SecondProtocol);

  const ObjCProtocolDecl::DefinitionData *FirstDD = &FirstProtocol->data();
  assert(FirstDD && SecondDD && "Definitions without DefinitionData");
  // Diagnostics from ObjCProtocol DefinitionData are emitted here.
  if (FirstDD != SecondDD) {
    // Check both protocols reference the same protocols.
    const ObjCProtocolList &FirstProtocols =
        FirstProtocol->getReferencedProtocols();
    const ObjCProtocolList &SecondProtocols = SecondDD->ReferencedProtocols;
    if (diagnoseSubMismatchProtocols(FirstProtocols, FirstProtocol, FirstModule,
                                     SecondProtocols, SecondProtocol,
                                     SecondModule))
      return true;
  }

  auto PopulateHashes = [](DeclHashes &Hashes, const ObjCProtocolDecl *ID,
                           const DeclContext *DC) {
    for (const Decl *D : ID->decls()) {
      if (!ODRHash::isSubDeclToBeProcessed(D, DC))
        continue;
      Hashes.emplace_back(D, computeODRHash(D));
    }
  };

  DeclHashes FirstHashes;
  DeclHashes SecondHashes;
  // Use definition as DeclContext because definitions are merged when
  // DeclContexts are merged and separate when DeclContexts are separate.
  PopulateHashes(FirstHashes, FirstProtocol, FirstProtocol->getDefinition());
  PopulateHashes(SecondHashes, SecondProtocol, SecondProtocol->getDefinition());

  DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
  ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
  ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
  const Decl *FirstDecl = DR.FirstDecl;
  const Decl *SecondDecl = DR.SecondDecl;

  if (FirstDiffType == Other || SecondDiffType == Other) {
    diagnoseSubMismatchUnexpected(DR, FirstProtocol, FirstModule,
                                  SecondProtocol, SecondModule);
    return true;
  }

  if (FirstDiffType != SecondDiffType) {
    diagnoseSubMismatchDifferentDeclKinds(DR, FirstProtocol, FirstModule,
                                          SecondProtocol, SecondModule);
    return true;
  }

  assert(FirstDiffType == SecondDiffType);
  switch (FirstDiffType) {
  // Already handled.
  case EndOfClass:
  case Other:
  // Cannot be contained by ObjCProtocolDecl, invalid in this context.
  case Field:
  case TypeDef:
  case Var:
  case ObjCIvar:
  // C++ only, invalid in this context.
  case PublicSpecifer:
  case PrivateSpecifer:
  case ProtectedSpecifer:
  case StaticAssert:
  case CXXMethod:
  case TypeAlias:
  case Friend:
  case FunctionTemplate:
    llvm_unreachable("Invalid diff type");
  case ObjCMethod: {
    if (diagnoseSubMismatchObjCMethod(FirstProtocol, FirstModule, SecondModule,
                                      cast<ObjCMethodDecl>(FirstDecl),
                                      cast<ObjCMethodDecl>(SecondDecl)))
      return true;
    break;
  }
  case ObjCProperty: {
    if (diagnoseSubMismatchObjCProperty(FirstProtocol, FirstModule,
                                        SecondModule,
                                        cast<ObjCPropertyDecl>(FirstDecl),
                                        cast<ObjCPropertyDecl>(SecondDecl)))
      return true;
    break;
  }
  }

  Diag(FirstDecl->getLocation(),
       diag::err_module_odr_violation_mismatch_decl_unknown)
      << FirstProtocol << FirstModule.empty() << FirstModule << FirstDiffType
      << FirstDecl->getSourceRange();
  Diag(SecondDecl->getLocation(),
       diag::note_module_odr_violation_mismatch_decl_unknown)
      << SecondModule.empty() << SecondModule << FirstDiffType
      << SecondDecl->getSourceRange();
  return true;
}
