[clang] 3e7350f - [ODRHash diagnostics] Move `ODRDiagsEmitter` to libAST in separate files. NFC.

Volodymyr Sapsai via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 7 14:41:03 PDT 2022


Author: Volodymyr Sapsai
Date: 2022-09-07T14:40:37-07:00
New Revision: 3e7350f3173b02b3c77917100b2228f1964c55c6

URL: https://github.com/llvm/llvm-project/commit/3e7350f3173b02b3c77917100b2228f1964c55c6
DIFF: https://github.com/llvm/llvm-project/commit/3e7350f3173b02b3c77917100b2228f1964c55c6.diff

LOG: [ODRHash diagnostics] Move `ODRDiagsEmitter` to libAST in separate files. NFC.

Intend to use `ODRDiagsEmitter` during parsing to diagnose a parsed
definition differing from a definition with the same name from a hidden
[sub]module.

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

Added: 
    clang/include/clang/AST/ODRDiagsEmitter.h
    clang/lib/AST/ODRDiagsEmitter.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticASTKinds.td
    clang/include/clang/Basic/DiagnosticSerializationKinds.td
    clang/lib/AST/CMakeLists.txt
    clang/lib/Serialization/ASTReader.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/ODRDiagsEmitter.h b/clang/include/clang/AST/ODRDiagsEmitter.h
new file mode 100644
index 0000000000000..37e7dd4b05c9d
--- /dev/null
+++ b/clang/include/clang/AST/ODRDiagsEmitter.h
@@ -0,0 +1,127 @@
+//===- ODRDiagsEmitter.h - Emits diagnostic 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_ODRDIAGSEMITTER_H
+#define LLVM_CLANG_AST_ODRDIAGSEMITTER_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+
+namespace clang {
+
+class ODRDiagsEmitter {
+public:
+  ODRDiagsEmitter(DiagnosticsEngine &Diags, const ASTContext &Context,
+                  const LangOptions &LangOpts)
+      : Diags(Diags), Context(Context), LangOpts(LangOpts) {}
+
+  /// Diagnose ODR mismatch between 2 FunctionDecl.
+  ///
+  /// Returns true if found a mismatch and diagnosed it.
+  bool diagnoseMismatch(const FunctionDecl *FirstFunction,
+                        const FunctionDecl *SecondFunction) const;
+
+  /// Diagnose ODR mismatch between 2 EnumDecl.
+  ///
+  /// Returns true if found a mismatch and diagnosed it.
+  bool diagnoseMismatch(const EnumDecl *FirstEnum,
+                        const EnumDecl *SecondEnum) const;
+
+  /// Diagnose ODR mismatch between 2 CXXRecordDecl.
+  ///
+  /// Returns true if found a mismatch and diagnosed it.
+  /// To compare 2 declarations with merged and identical definition data
+  /// you need to provide pre-merge definition data in \p SecondDD.
+  bool
+  diagnoseMismatch(const CXXRecordDecl *FirstRecord,
+                   const CXXRecordDecl *SecondRecord,
+                   const struct CXXRecordDecl::DefinitionData *SecondDD) const;
+
+  /// Get the best name we know for the module that owns the given
+  /// declaration, or an empty string if the declaration is not from a module.
+  static std::string getOwningModuleNameForDiagnostic(const Decl *D);
+
+private:
+  using DeclHashes = llvm::SmallVector<std::pair<const Decl *, unsigned>, 4>;
+
+  // Used with err_module_odr_violation_mismatch_decl and
+  // note_module_odr_violation_mismatch_decl
+  // This list should be the same Decl's as in ODRHash::isDeclToBeProcessed
+  enum ODRMismatchDecl {
+    EndOfClass,
+    PublicSpecifer,
+    PrivateSpecifer,
+    ProtectedSpecifer,
+    StaticAssert,
+    Field,
+    CXXMethod,
+    TypeAlias,
+    TypeDef,
+    Var,
+    Friend,
+    FunctionTemplate,
+    Other
+  };
+
+  struct DiffResult {
+    const Decl *FirstDecl = nullptr, *SecondDecl = nullptr;
+    ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other;
+  };
+
+  // If there is a diagnoseable 
diff erence, FirstDiffType and
+  // SecondDiffType will not be Other and FirstDecl and SecondDecl will be
+  // filled in if not EndOfClass.
+  static DiffResult FindTypeDiffs(DeclHashes &FirstHashes,
+                                  DeclHashes &SecondHashes);
+
+  DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) const {
+    return Diags.Report(Loc, DiagID);
+  }
+
+  // Use this to diagnose that an unexpected Decl was encountered
+  // or no 
diff erence was detected. This causes a generic error
+  // message to be emitted.
+  void diagnoseSubMismatchUnexpected(DiffResult &DR,
+                                     const NamedDecl *FirstRecord,
+                                     StringRef FirstModule,
+                                     const NamedDecl *SecondRecord,
+                                     StringRef SecondModule) const;
+
+  void diagnoseSubMismatchDifferentDeclKinds(DiffResult &DR,
+                                             const NamedDecl *FirstRecord,
+                                             StringRef FirstModule,
+                                             const NamedDecl *SecondRecord,
+                                             StringRef SecondModule) const;
+
+  bool diagnoseSubMismatchField(const NamedDecl *FirstRecord,
+                                StringRef FirstModule, StringRef SecondModule,
+                                const FieldDecl *FirstField,
+                                const FieldDecl *SecondField) const;
+
+  bool diagnoseSubMismatchTypedef(const NamedDecl *FirstRecord,
+                                  StringRef FirstModule, StringRef SecondModule,
+                                  const TypedefNameDecl *FirstTD,
+                                  const TypedefNameDecl *SecondTD,
+                                  bool IsTypeAlias) const;
+
+  bool diagnoseSubMismatchVar(const NamedDecl *FirstRecord,
+                              StringRef FirstModule, StringRef SecondModule,
+                              const VarDecl *FirstVD,
+                              const VarDecl *SecondVD) const;
+
+private:
+  DiagnosticsEngine &Diags;
+  const ASTContext &Context;
+  const LangOptions &LangOpts;
+};
+
+} // namespace clang
+
+#endif

diff  --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 02b0cf01aa057..7f47dc8ce0d88 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -566,6 +566,301 @@ def warn_odr_non_type_parameter_type_inconsistent : Warning<
   InGroup<ODR>;
 def err_unsupported_ast_node: Error<"cannot import unsupported AST node %0">;
 
+// Compare ODR hashes
+def err_module_odr_violation_
diff erent_definitions : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; "
+  "%select{definition in module '%2' is here|defined here}1">;
+def note_first_module_
diff erence : Note<
+  "in first definition, possible 
diff erence is here">;
+def note_module_odr_violation_
diff erent_definitions : Note<
+  "definition in module '%0' is here">;
+def note_second_module_
diff erence : Note<
+  "in second definition, possible 
diff erence is here">;
+
+def err_module_odr_violation_definition_data : Error <
+  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "%4 base %plural{1:class|:classes}4|"
+  "%4 virtual base %plural{1:class|:classes}4|"
+  "%ordinal4 base class with type %5|"
+  "%ordinal4 %select{non-virtual|virtual}5 base class %6|"
+  "%ordinal4 base class %5 with "
+  "%select{public|protected|private|no}6 access specifier}3">;
+
+def note_module_odr_violation_definition_data : Note <
+  "but in '%0' found "
+  "%select{"
+  "%2 base %plural{1:class|:classes}2|"
+  "%2 virtual base %plural{1:class|:classes}2|"
+  "%ordinal2 base class with 
diff erent type %3|"
+  "%ordinal2 %select{non-virtual|virtual}3 base class %4|"
+  "%ordinal2 base class %3 with "
+  "%select{public|protected|private|no}4 access specifier}1">;
+
+def err_module_odr_violation_template_parameter : Error <
+  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "unnamed template parameter|"
+  "template parameter %5|"
+  "template parameter with %select{no |}4default argument|"
+  "template parameter with default argument}3">;
+
+def note_module_odr_violation_template_parameter : Note <
+  "but in '%0' found "
+  "%select{"
+  "unnamed template parameter %2|"
+  "template parameter %3|"
+  "template parameter with %select{no |}2default argument|"
+  "template parameter with 
diff erent default argument}1">;
+
+def err_module_odr_violation_mismatch_decl : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier|static assert|field|method|type alias|typedef|"
+  "data member|friend declaration|function template}3">;
+def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier|static assert|field|method|type alias|typedef|"
+  "data member|friend declaration|function template}1">;
+
+def err_module_odr_violation_record : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "static assert with condition|"
+  "static assert with message|"
+  "static assert with %select{|no }4message|"
+  "%select{method %5|constructor|destructor}4|"
+  "%select{method %5|constructor|destructor}4 "
+    "is %select{not deleted|deleted}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "is %select{not defaulted|defaulted}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "is %select{|pure }6%select{not virtual|virtual}7|"
+  "%select{method %5|constructor|destructor}4 "
+    "is %select{not static|static}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "is %select{not volatile|volatile}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "is %select{not const|const}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "is %select{not inline|inline}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "that has %6 parameter%s6|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %ordinal6 parameter of type %7%select{| decayed from %9}8|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %ordinal6 parameter named %7|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %ordinal6 parameter with%select{out|}7 a default argument|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %ordinal6 parameter with a default argument|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %select{no |}6template arguments|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %6 template argument%s6|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %6 for %ordinal7 template argument|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %select{no body|body}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "with body|"
+  "friend %select{class|function}4|"
+  "friend %4|"
+  "friend function %4|"
+  "function template %4 with %5 template parameter%s5|"
+  "function template %4 with %ordinal5 template parameter being a "
+    "%select{type|non-type|template}6 template parameter|"
+  "function template %4 with %ordinal5 template parameter "
+    "%select{with no name|named %7}6|"
+  "function template %4 with %ordinal5 template parameter with "
+    "%select{no |}6default argument|"
+  "function template %4 with %ordinal5 template parameter with "
+    "default argument %6|"
+  "function template %4 with %ordinal5 template parameter with one type|"
+  "function template %4 with %ordinal5 template parameter %select{not |}6"
+    "being a template parameter pack|"
+  "}3">;
+
+def note_module_odr_violation_record : Note<"but in '%0' found "
+  "%select{"
+  "static assert with 
diff erent condition|"
+  "static assert with 
diff erent message|"
+  "static assert with %select{|no }2message|"
+  "%select{method %3|constructor|destructor}2|"
+  "%select{method %3|constructor|destructor}2 "
+    "is %select{not deleted|deleted}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "is %select{not defaulted|defaulted}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "is %select{|pure }4%select{not virtual|virtual}5|"
+  "%select{method %3|constructor|destructor}2 "
+    "is %select{not static|static}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "is %select{not volatile|volatile}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "is %select{not const|const}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "is %select{not inline|inline}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "that has %4 parameter%s4|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %ordinal4 parameter of type %5%select{| decayed from %7}6|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %ordinal4 parameter named %5|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %ordinal4 parameter with%select{out|}5 a default argument|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %ordinal4 parameter with a 
diff erent default argument|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %select{no |}4template arguments|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %4 template argument%s4|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %4 for %ordinal5 template argument|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %select{no body|body}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "with 
diff erent body|"
+  "friend %select{class|function}2|"
+  "friend %2|"
+  "friend function %2|"
+  "function template %2 with %3 template parameter%s3|"
+  "function template %2 with %ordinal3 template paramter being a "
+    "%select{type|non-type|template}4 template parameter|"
+  "function template %2 with %ordinal3 template parameter "
+    "%select{with no name|named %5}4|"
+  "function template %2 with %ordinal3 template parameter with "
+    "%select{no |}4default argument|"
+  "function template %2 with %ordinal3 template parameter with "
+    "default argument %4|"
+  "function template %2 with %ordinal3 template parameter with 
diff erent type|"
+  "function template %2 with %ordinal3 template parameter %select{not |}4"
+    "being a template parameter pack|"
+  "}1">;
+
+def err_module_odr_violation_field : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "field %4|"
+  "field %4 with type %5|"
+  "%select{non-|}5bitfield %4|"
+  "bitfield %4 with one width expression|"
+  "%select{non-|}5mutable field %4|"
+  "field %4 with %select{no|an}5 initalizer|"
+  "field %4 with an initializer"
+  "}3">;
+def note_module_odr_violation_field : Note<"but in '%0' found "
+  "%select{"
+  "field %2|"
+  "field %2 with type %3|"
+  "%select{non-|}3bitfield %2|"
+  "bitfield %2 with 
diff erent width expression|"
+  "%select{non-|}3mutable field %2|"
+  "field %2 with %select{no|an}3 initializer|"
+  "field %2 with a 
diff erent initializer"
+  "}1">;
+
+def err_module_odr_violation_typedef : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "%select{typedef|type alias}4 name %5|"
+  "%select{typedef|type alias}4 %5 with underlying type %6"
+  "}3">;
+def note_module_odr_violation_typedef : Note<"but in '%0' found "
+  "%select{"
+  "%select{typedef|type alias}2 name %3|"
+  "%select{typedef|type alias}2 %3 with 
diff erent underlying type %4"
+  "}1">;
+
+def err_module_odr_violation_variable : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "data member with name %4|"
+  "data member %4 with type %5|"
+  "data member %4 with%select{out|}5 an initializer|"
+  "data member %4 with an initializer|"
+  "data member %4 %select{is constexpr|is not constexpr}5"
+  "}3">;
+def note_module_odr_violation_variable : Note<"but in '%0' found "
+  "%select{"
+  "data member with name %2|"
+  "data member %2 with 
diff erent type %3|"
+  "data member %2 with%select{out|}3 an initializer|"
+  "data member %2 with a 
diff erent initializer|"
+  "data member %2 %select{is constexpr|is not constexpr}3"
+  "}1">;
+
+def err_module_odr_violation_function : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; "
+  "%select{definition in module '%2'|defined here}1 "
+  "first 
diff erence is "
+  "%select{"
+  "return type is %4|"
+  "%ordinal4 parameter with name %5|"
+  "%ordinal4 parameter with type %5%select{| decayed from %7}6|"
+  "%ordinal4 parameter with%select{out|}5 a default argument|"
+  "%ordinal4 parameter with a default argument|"
+  "function body"
+  "}3">;
+
+def note_module_odr_violation_function : Note<"but in '%0' found "
+  "%select{"
+  "
diff erent return type %2|"
+  "%ordinal2 parameter with name %3|"
+  "%ordinal2 parameter with type %3%select{| decayed from %5}4|"
+  "%ordinal2 parameter with%select{out|}3 a default argument|"
+  "%ordinal2 parameter with a 
diff erent default argument|"
+  "a 
diff erent body"
+  "}1">;
+
+def err_module_odr_violation_enum : Error<
+  "%q0 has 
diff erent definitions in 
diff erent modules; "
+  "%select{definition in module '%2'|defined here}1 "
+  "first 
diff erence is "
+  "%select{"
+  "enum that is %select{not scoped|scoped}4|"
+  "enum scoped with keyword %select{struct|class}4|"
+  "enum %select{without|with}4 specified type|"
+  "enum with specified type %4|"
+  "enum with %4 element%s4|"
+  "%ordinal4 element has name %5|"
+  "%ordinal4 element %5 %select{has|does not have}6 an initializer|"
+  "%ordinal4 element %5 has an initializer|"
+  "}3">;
+
+def note_module_odr_violation_enum : Note<"but in '%0' found "
+  "%select{"
+  "enum that is %select{not scoped|scoped}2|"
+  "enum scoped with keyword %select{struct|class}2|"
+  "enum %select{without|with}2 specified type|"
+  "enum with specified type %2|"
+  "enum with %2 element%s2|"
+  "%ordinal2 element has name %3|"
+  "%ordinal2 element %3 %select{has|does not have}4 an initializer|"
+  "%ordinal2 element %3 has 
diff erent initializer|"
+  "}1">;
+
+def err_module_odr_violation_mismatch_decl_unknown : Error<
+  "%q0 %select{with definition in module '%2'|defined here}1 has 
diff erent "
+  "definitions in 
diff erent modules; first 
diff erence is this "
+  "%select{||||static assert|field|method|type alias|typedef|data member|"
+  "friend declaration|function template|"
+  "unexpected decl}3">;
+def note_module_odr_violation_mismatch_decl_unknown : Note<
+  "but in '%0' found "
+  "%select{||||
diff erent static assert|
diff erent field|
diff erent method|"
+  "
diff erent type alias|
diff erent typedef|
diff erent data member|"
+  "
diff erent friend declaration|
diff erent function template|"
+  "another unexpected decl}1">;
+
+
 def remark_sanitize_address_insert_extra_padding_accepted : Remark<
     "-fsanitize-address-field-padding applied to %0">, ShowInSystemHeader,
     InGroup<SanitizeAddressRemarks>;

diff  --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td
index 0bd3734a4a049..f515ea0d9f6dd 100644
--- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td
@@ -115,302 +115,10 @@ def note_module_odr_violation_no_possible_decls : Note<
   "definition has no member %0">;
 def note_module_odr_violation_possible_decl : Note<
   "declaration of %0 does not match">;
-def err_module_odr_violation_
diff erent_definitions : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; "
-  "%select{definition in module '%2' is here|defined here}1">;
-def note_first_module_
diff erence : Note<
-  "in first definition, possible 
diff erence is here">;
-def note_module_odr_violation_
diff erent_definitions : Note<
-  "definition in module '%0' is here">;
-def note_second_module_
diff erence : Note<
-  "in second definition, possible 
diff erence is here">;
 
 def err_module_odr_violation_
diff erent_instantiations : Error<
   "instantiation of %q0 is 
diff erent in 
diff erent modules">;
 
-def err_module_odr_violation_definition_data : Error <
-  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
-  "%select{definition in module '%2'|defined here}1 found "
-  "%select{"
-  "%4 base %plural{1:class|:classes}4|"
-  "%4 virtual base %plural{1:class|:classes}4|"
-  "%ordinal4 base class with type %5|"
-  "%ordinal4 %select{non-virtual|virtual}5 base class %6|"
-  "%ordinal4 base class %5 with "
-  "%select{public|protected|private|no}6 access specifier}3">;
-
-def note_module_odr_violation_definition_data : Note <
-  "but in '%0' found "
-  "%select{"
-  "%2 base %plural{1:class|:classes}2|"
-  "%2 virtual base %plural{1:class|:classes}2|"
-  "%ordinal2 base class with 
diff erent type %3|"
-  "%ordinal2 %select{non-virtual|virtual}3 base class %4|"
-  "%ordinal2 base class %3 with "
-  "%select{public|protected|private|no}4 access specifier}1">;
-
-def err_module_odr_violation_template_parameter : Error <
-  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
-  "%select{definition in module '%2'|defined here}1 found "
-  "%select{"
-  "unnamed template parameter|"
-  "template parameter %5|"
-  "template parameter with %select{no |}4default argument|"
-  "template parameter with default argument}3">;
-
-def note_module_odr_violation_template_parameter : Note <
-  "but in '%0' found "
-  "%select{"
-  "unnamed template parameter %2|"
-  "template parameter %3|"
-  "template parameter with %select{no |}2default argument|"
-  "template parameter with 
diff erent default argument}1">;
-
-def err_module_odr_violation_mismatch_decl : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
-  "%select{definition in module '%2'|defined here}1 found "
-  "%select{end of class|public access specifier|private access specifier|"
-  "protected access specifier|static assert|field|method|type alias|typedef|"
-  "data member|friend declaration|function template}3">;
-def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
-  "%select{end of class|public access specifier|private access specifier|"
-  "protected access specifier|static assert|field|method|type alias|typedef|"
-  "data member|friend declaration|function template}1">;
-
-def err_module_odr_violation_record : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
-  "%select{definition in module '%2'|defined here}1 found "
-  "%select{"
-  "static assert with condition|"
-  "static assert with message|"
-  "static assert with %select{|no }4message|"
-  "%select{method %5|constructor|destructor}4|"
-  "%select{method %5|constructor|destructor}4 "
-    "is %select{not deleted|deleted}6|"
-  "%select{method %5|constructor|destructor}4 "
-    "is %select{not defaulted|defaulted}6|"
-  "%select{method %5|constructor|destructor}4 "
-    "is %select{|pure }6%select{not virtual|virtual}7|"
-  "%select{method %5|constructor|destructor}4 "
-    "is %select{not static|static}6|"
-  "%select{method %5|constructor|destructor}4 "
-    "is %select{not volatile|volatile}6|"
-  "%select{method %5|constructor|destructor}4 "
-    "is %select{not const|const}6|"
-  "%select{method %5|constructor|destructor}4 "
-    "is %select{not inline|inline}6|"
-  "%select{method %5|constructor|destructor}4 "
-    "that has %6 parameter%s6|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %ordinal6 parameter of type %7%select{| decayed from %9}8|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %ordinal6 parameter named %7|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %ordinal6 parameter with%select{out|}7 a default argument|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %ordinal6 parameter with a default argument|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %select{no |}6template arguments|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %6 template argument%s6|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %6 for %ordinal7 template argument|"
-  "%select{method %5|constructor|destructor}4 "
-    "with %select{no body|body}6|"
-  "%select{method %5|constructor|destructor}4 "
-    "with body|"
-  "friend %select{class|function}4|"
-  "friend %4|"
-  "friend function %4|"
-  "function template %4 with %5 template parameter%s5|"
-  "function template %4 with %ordinal5 template parameter being a "
-    "%select{type|non-type|template}6 template parameter|"
-  "function template %4 with %ordinal5 template parameter "
-    "%select{with no name|named %7}6|"
-  "function template %4 with %ordinal5 template parameter with "
-    "%select{no |}6default argument|"
-  "function template %4 with %ordinal5 template parameter with "
-    "default argument %6|"
-  "function template %4 with %ordinal5 template parameter with one type|"
-  "function template %4 with %ordinal5 template parameter %select{not |}6"
-    "being a template parameter pack|"
-  "}3">;
-
-def note_module_odr_violation_record : Note<"but in '%0' found "
-  "%select{"
-  "static assert with 
diff erent condition|"
-  "static assert with 
diff erent message|"
-  "static assert with %select{|no }2message|"
-  "%select{method %3|constructor|destructor}2|"
-  "%select{method %3|constructor|destructor}2 "
-    "is %select{not deleted|deleted}4|"
-  "%select{method %3|constructor|destructor}2 "
-    "is %select{not defaulted|defaulted}4|"
-  "%select{method %3|constructor|destructor}2 "
-    "is %select{|pure }4%select{not virtual|virtual}5|"
-  "%select{method %3|constructor|destructor}2 "
-    "is %select{not static|static}4|"
-  "%select{method %3|constructor|destructor}2 "
-    "is %select{not volatile|volatile}4|"
-  "%select{method %3|constructor|destructor}2 "
-    "is %select{not const|const}4|"
-  "%select{method %3|constructor|destructor}2 "
-    "is %select{not inline|inline}4|"
-  "%select{method %3|constructor|destructor}2 "
-    "that has %4 parameter%s4|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %ordinal4 parameter of type %5%select{| decayed from %7}6|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %ordinal4 parameter named %5|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %ordinal4 parameter with%select{out|}5 a default argument|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %ordinal4 parameter with a 
diff erent default argument|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %select{no |}4template arguments|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %4 template argument%s4|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %4 for %ordinal5 template argument|"
-  "%select{method %3|constructor|destructor}2 "
-    "with %select{no body|body}4|"
-  "%select{method %3|constructor|destructor}2 "
-    "with 
diff erent body|"
-  "friend %select{class|function}2|"
-  "friend %2|"
-  "friend function %2|"
-  "function template %2 with %3 template parameter%s3|"
-  "function template %2 with %ordinal3 template paramter being a "
-    "%select{type|non-type|template}4 template parameter|"
-  "function template %2 with %ordinal3 template parameter "
-    "%select{with no name|named %5}4|"
-  "function template %2 with %ordinal3 template parameter with "
-    "%select{no |}4default argument|"
-  "function template %2 with %ordinal3 template parameter with "
-    "default argument %4|"
-  "function template %2 with %ordinal3 template parameter with 
diff erent type|"
-  "function template %2 with %ordinal3 template parameter %select{not |}4"
-    "being a template parameter pack|"
-  "}1">;
-
-def err_module_odr_violation_field : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
-  "%select{definition in module '%2'|defined here}1 found "
-  "%select{"
-  "field %4|"
-  "field %4 with type %5|"
-  "%select{non-|}5bitfield %4|"
-  "bitfield %4 with one width expression|"
-  "%select{non-|}5mutable field %4|"
-  "field %4 with %select{no|an}5 initalizer|"
-  "field %4 with an initializer"
-  "}3">;
-def note_module_odr_violation_field : Note<"but in '%0' found "
-  "%select{"
-  "field %2|"
-  "field %2 with type %3|"
-  "%select{non-|}3bitfield %2|"
-  "bitfield %2 with 
diff erent width expression|"
-  "%select{non-|}3mutable field %2|"
-  "field %2 with %select{no|an}3 initializer|"
-  "field %2 with a 
diff erent initializer"
-  "}1">;
-
-def err_module_odr_violation_typedef : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
-  "%select{definition in module '%2'|defined here}1 found "
-  "%select{"
-  "%select{typedef|type alias}4 name %5|"
-  "%select{typedef|type alias}4 %5 with underlying type %6"
-  "}3">;
-def note_module_odr_violation_typedef : Note<"but in '%0' found "
-  "%select{"
-  "%select{typedef|type alias}2 name %3|"
-  "%select{typedef|type alias}2 %3 with 
diff erent underlying type %4"
-  "}1">;
-
-def err_module_odr_violation_variable : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
-  "%select{definition in module '%2'|defined here}1 found "
-  "%select{"
-  "data member with name %4|"
-  "data member %4 with type %5|"
-  "data member %4 with%select{out|}5 an initializer|"
-  "data member %4 with an initializer|"
-  "data member %4 %select{is constexpr|is not constexpr}5"
-  "}3">;
-def note_module_odr_violation_variable : Note<"but in '%0' found "
-  "%select{"
-  "data member with name %2|"
-  "data member %2 with 
diff erent type %3|"
-  "data member %2 with%select{out|}3 an initializer|"
-  "data member %2 with a 
diff erent initializer|"
-  "data member %2 %select{is constexpr|is not constexpr}3"
-  "}1">;
-
-def err_module_odr_violation_function : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; "
-  "%select{definition in module '%2'|defined here}1 "
-  "first 
diff erence is "
-  "%select{"
-  "return type is %4|"
-  "%ordinal4 parameter with name %5|"
-  "%ordinal4 parameter with type %5%select{| decayed from %7}6|"
-  "%ordinal4 parameter with%select{out|}5 a default argument|"
-  "%ordinal4 parameter with a default argument|"
-  "function body"
-  "}3">;
-
-def note_module_odr_violation_function : Note<"but in '%0' found "
-  "%select{"
-  "
diff erent return type %2|"
-  "%ordinal2 parameter with name %3|"
-  "%ordinal2 parameter with type %3%select{| decayed from %5}4|"
-  "%ordinal2 parameter with%select{out|}3 a default argument|"
-  "%ordinal2 parameter with a 
diff erent default argument|"
-  "a 
diff erent body"
-  "}1">;
-
-def err_module_odr_violation_enum : Error<
-  "%q0 has 
diff erent definitions in 
diff erent modules; "
-  "%select{definition in module '%2'|defined here}1 "
-  "first 
diff erence is "
-  "%select{"
-  "enum that is %select{not scoped|scoped}4|"
-  "enum scoped with keyword %select{struct|class}4|"
-  "enum %select{without|with}4 specified type|"
-  "enum with specified type %4|"
-  "enum with %4 element%s4|"
-  "%ordinal4 element has name %5|"
-  "%ordinal4 element %5 %select{has|does not have}6 an initializer|"
-  "%ordinal4 element %5 has an initializer|"
-  "}3">;
-
-def note_module_odr_violation_enum : Note<"but in '%0' found "
-  "%select{"
-  "enum that is %select{not scoped|scoped}2|"
-  "enum scoped with keyword %select{struct|class}2|"
-  "enum %select{without|with}2 specified type|"
-  "enum with specified type %2|"
-  "enum with %2 element%s2|"
-  "%ordinal2 element has name %3|"
-  "%ordinal2 element %3 %select{has|does not have}4 an initializer|"
-  "%ordinal2 element %3 has 
diff erent initializer|"
-  "}1">;
-
-def err_module_odr_violation_mismatch_decl_unknown : Error<
-  "%q0 %select{with definition in module '%2'|defined here}1 has 
diff erent "
-  "definitions in 
diff erent modules; first 
diff erence is this "
-  "%select{||||static assert|field|method|type alias|typedef|data member|"
-  "friend declaration|function template|"
-  "unexpected decl}3">;
-def note_module_odr_violation_mismatch_decl_unknown : Note<
-  "but in '%0' found "
-  "%select{||||
diff erent static assert|
diff erent field|
diff erent method|"
-  "
diff erent type alias|
diff erent typedef|
diff erent data member|"
-  "
diff erent friend declaration|
diff erent function template|"
-  "another unexpected decl}1">;
-
 def warn_duplicate_module_file_extension : Warning<
   "duplicate module file extension block name '%0'">,
   InGroup<ModuleFileExtension>;

diff  --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index 24c8cb89c7ff6..fc7921788943d 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -91,6 +91,7 @@ add_clang_library(clangAST
   MicrosoftMangle.cpp
   NestedNameSpecifier.cpp
   NSAPI.cpp
+  ODRDiagsEmitter.cpp
   ODRHash.cpp
   OpenMPClause.cpp
   OSLog.cpp

diff  --git a/clang/lib/AST/ODRDiagsEmitter.cpp b/clang/lib/AST/ODRDiagsEmitter.cpp
new file mode 100644
index 0000000000000..ac66f50a558fd
--- /dev/null
+++ b/clang/lib/AST/ODRDiagsEmitter.cpp
@@ -0,0 +1,1535 @@
+//===-- 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 {};
+}
+
+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 << SecondField->getSourceRange() << DiffType;
+  };
+
+  IdentifierInfo *FirstII = FirstField->getIdentifier();
+  IdentifierInfo *SecondII = SecondField->getIdentifier();
+  if (FirstII->getName() != SecondII->getName()) {
+    DiagError(FieldName) << FirstII;
+    DiagNote(FieldName) << SecondII;
+    return true;
+  }
+
+  assert(Context.hasSameType(FirstField->getType(), SecondField->getType()));
+  (void)Context;
+
+  QualType FirstType = FirstField->getType();
+  QualType SecondType = SecondField->getType();
+  if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
+    DiagError(FieldTypeName) << FirstII << FirstType;
+    DiagNote(FieldTypeName) << SecondII << SecondType;
+    return true;
+  }
+
+  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;
+}
+
+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;
+    }
+  };
+
+  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_
diff erent_definitions)
+      << FirstRecord << FirstModule.empty() << FirstModule;
+
+  if (DR.FirstDecl) {
+    Diag(DR.FirstDecl->getLocation(), diag::note_first_module_
diff erence)
+        << FirstRecord << DR.FirstDecl->getSourceRange();
+  }
+
+  Diag(SecondRecord->getLocation(),
+       diag::note_module_odr_violation_
diff erent_definitions)
+      << SecondModule;
+
+  if (DR.SecondDecl) {
+    Diag(DR.SecondDecl->getLocation(), diag::note_second_module_
diff erence)
+        << 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;
+    auto *Tag = dyn_cast<TagDecl>(Container);
+    if (DiffType == EndOfClass && Tag) {
+      Loc = Tag->getBraceRange().getEnd();
+    } 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 << SecondDiagInfo.second << DR.SecondDiffType;
+}
+
+bool ODRDiagsEmitter::diagnoseMismatch(
+    const CXXRecordDecl *FirstRecord, const CXXRecordDecl *SecondRecord,
+    const struct CXXRecordDecl::DefinitionData *SecondDD) const {
+  // Multiple 
diff erent 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::isDeclToBeProcessed(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,
+    MethodNumberParameters,
+    MethodParameterType,
+    MethodParameterName,
+    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:
+    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 StringLiteral *FirstStr = FirstSA->getMessage();
+    const StringLiteral *SecondStr = SecondSA->getMessage();
+    assert((FirstStr || SecondStr) && "Both messages cannot be empty");
+    if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {
+      SourceLocation FirstLoc, SecondLoc;
+      SourceRange FirstRange, SecondRange;
+      if (FirstStr) {
+        FirstLoc = FirstStr->getBeginLoc();
+        FirstRange = FirstStr->getSourceRange();
+      } else {
+        FirstLoc = FirstSA->getBeginLoc();
+        FirstRange = FirstSA->getSourceRange();
+      }
+      if (SecondStr) {
+        SecondLoc = SecondStr->getBeginLoc();
+        SecondRange = SecondStr->getSourceRange();
+      } else {
+        SecondLoc = SecondSA->getBeginLoc();
+        SecondRange = SecondSA->getSourceRange();
+      }
+      DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
+          << (FirstStr == nullptr);
+      DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
+          << (SecondStr == nullptr);
+      return true;
+    }
+
+    if (FirstStr && SecondStr &&
+        FirstStr->getString() != SecondStr->getString()) {
+      DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(),
+                StaticAssertMessage);
+      DiagNote(SecondStr->getBeginLoc(), SecondStr->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->isPure();
+    const bool SecondPure = SecondMethod->isPure();
+    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;
+    }
+
+    const unsigned FirstNumParameters = FirstMethod->param_size();
+    const unsigned SecondNumParameters = SecondMethod->param_size();
+    if (FirstNumParameters != SecondNumParameters) {
+      DiagMethodError(MethodNumberParameters) << FirstNumParameters;
+      DiagMethodNote(MethodNumberParameters) << 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>()) {
+          DiagMethodError(MethodParameterType)
+              << (I + 1) << FirstParamType << true
+              << ParamDecayedType->getOriginalType();
+        } else {
+          DiagMethodError(MethodParameterType)
+              << (I + 1) << FirstParamType << false;
+        }
+
+        if (const DecayedType *ParamDecayedType =
+                SecondParamType->getAs<DecayedType>()) {
+          DiagMethodNote(MethodParameterType)
+              << (I + 1) << SecondParamType << true
+              << ParamDecayedType->getOriginalType();
+        } else {
+          DiagMethodNote(MethodParameterType)
+              << (I + 1) << SecondParamType << false;
+        }
+        return true;
+      }
+
+      DeclarationName FirstParamName = FirstParam->getDeclName();
+      DeclarationName SecondParamName = SecondParam->getDeclName();
+      if (FirstParamName != SecondParamName) {
+        DiagMethodError(MethodParameterName) << (I + 1) << FirstParamName;
+        DiagMethodNote(MethodParameterName) << (I + 1) << SecondParamName;
+        return true;
+      }
+
+      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 
diff erence 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) {
+          QualType FirstType = FirstTTPD->getDefaultArgument();
+          QualType SecondType = SecondTTPD->getDefaultArgument();
+          if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
+            DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument)
+                << (i + 1) << FirstType;
+            DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument)
+                << (i + 1) << SecondType;
+            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) {
+          Expr *FirstDefaultArgument = FirstNTTPD->getDefaultArgument();
+          Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument();
+          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 << 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 
diff erent 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 
diff erent 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;
+    }
+
+    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 
diff erence.");
+  }
+
+  // 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::isDeclToBeProcessed(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;
+}

diff  --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 568d948b541e5..2196a82604e9c 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -30,6 +30,7 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExternalASTSource.h"
 #include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/ODRDiagsEmitter.h"
 #include "clang/AST/ODRHash.h"
 #include "clang/AST/OpenMPClause.h"
 #include "clang/AST/RawCommentList.h"
@@ -9433,148 +9434,6 @@ void ASTReader::finishPendingActions() {
   PendingMergedDefinitionsToDeduplicate.clear();
 }
 
-namespace clang {
-class ODRDiagsEmitter {
-public:
-  ODRDiagsEmitter(DiagnosticsEngine &Diags, const ASTContext &Context,
-                  const LangOptions &LangOpts)
-      : Diags(Diags), Context(Context), LangOpts(LangOpts) {}
-
-  /// Diagnose ODR mismatch between 2 FunctionDecl.
-  ///
-  /// Returns true if found a mismatch and diagnosed it.
-  bool diagnoseMismatch(const FunctionDecl *FirstFunction,
-                        const FunctionDecl *SecondFunction) const;
-
-  /// Diagnose ODR mismatch between 2 EnumDecl.
-  ///
-  /// Returns true if found a mismatch and diagnosed it.
-  bool diagnoseMismatch(const EnumDecl *FirstEnum,
-                        const EnumDecl *SecondEnum) const;
-
-  /// Diagnose ODR mismatch between 2 CXXRecordDecl.
-  ///
-  /// Returns true if found a mismatch and diagnosed it.
-  /// To compare 2 declarations with merged and identical definition data
-  /// you need to provide pre-merge definition data in \p SecondDD.
-  bool
-  diagnoseMismatch(const CXXRecordDecl *FirstRecord,
-                   const CXXRecordDecl *SecondRecord,
-                   const struct CXXRecordDecl::DefinitionData *SecondDD) const;
-
-  /// Get the best name we know for the module that owns the given
-  /// declaration, or an empty string if the declaration is not from a module.
-  static std::string getOwningModuleNameForDiagnostic(const Decl *D);
-
-private:
-  using DeclHashes = llvm::SmallVector<std::pair<const Decl *, unsigned>, 4>;
-
-  // Used with err_module_odr_violation_mismatch_decl and
-  // note_module_odr_violation_mismatch_decl
-  // This list should be the same Decl's as in ODRHash::isDeclToBeProcessed
-  enum ODRMismatchDecl {
-    EndOfClass,
-    PublicSpecifer,
-    PrivateSpecifer,
-    ProtectedSpecifer,
-    StaticAssert,
-    Field,
-    CXXMethod,
-    TypeAlias,
-    TypeDef,
-    Var,
-    Friend,
-    FunctionTemplate,
-    Other
-  };
-
-  struct DiffResult {
-    const Decl *FirstDecl = nullptr, *SecondDecl = nullptr;
-    ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other;
-  };
-
-  // If there is a diagnoseable 
diff erence, FirstDiffType and
-  // SecondDiffType will not be Other and FirstDecl and SecondDecl will be
-  // filled in if not EndOfClass.
-  static DiffResult FindTypeDiffs(DeclHashes &FirstHashes,
-                                  DeclHashes &SecondHashes);
-
-  DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) const {
-    return Diags.Report(Loc, DiagID);
-  }
-
-  // Use this to diagnose that an unexpected Decl was encountered
-  // or no 
diff erence was detected. This causes a generic error
-  // message to be emitted.
-  void diagnoseSubMismatchUnexpected(DiffResult &DR,
-                                     const NamedDecl *FirstRecord,
-                                     StringRef FirstModule,
-                                     const NamedDecl *SecondRecord,
-                                     StringRef SecondModule) const;
-
-  void diagnoseSubMismatchDifferentDeclKinds(DiffResult &DR,
-                                             const NamedDecl *FirstRecord,
-                                             StringRef FirstModule,
-                                             const NamedDecl *SecondRecord,
-                                             StringRef SecondModule) const;
-
-  bool diagnoseSubMismatchField(const NamedDecl *FirstRecord,
-                                StringRef FirstModule, StringRef SecondModule,
-                                const FieldDecl *FirstField,
-                                const FieldDecl *SecondField) const;
-
-  bool diagnoseSubMismatchTypedef(const NamedDecl *FirstRecord,
-                                  StringRef FirstModule, StringRef SecondModule,
-                                  const TypedefNameDecl *FirstTD,
-                                  const TypedefNameDecl *SecondTD,
-                                  bool IsTypeAlias) const;
-
-  bool diagnoseSubMismatchVar(const NamedDecl *FirstRecord,
-                              StringRef FirstModule, StringRef SecondModule,
-                              const VarDecl *FirstVD,
-                              const VarDecl *SecondVD) const;
-
-private:
-  DiagnosticsEngine &Diags;
-  const ASTContext &Context;
-  const LangOptions &LangOpts;
-};
-} // 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 {};
-}
-
 void ASTReader::diagnoseOdrViolations() {
   if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() &&
       PendingFunctionOdrMergeFailures.empty() &&
@@ -9781,1527 +9640,6 @@ void ASTReader::diagnoseOdrViolations() {
   }
 }
 
-// clang-format off
-  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 << SecondField->getSourceRange() << DiffType;
-    };
-
-    IdentifierInfo *FirstII = FirstField->getIdentifier();
-    IdentifierInfo *SecondII = SecondField->getIdentifier();
-    if (FirstII->getName() != SecondII->getName()) {
-      DiagError(FieldName) << FirstII;
-      DiagNote(FieldName) << SecondII;
-      return true;
-    }
-
-    assert(Context.hasSameType(FirstField->getType(), SecondField->getType()));
-    (void)Context;
-
-    QualType FirstType = FirstField->getType();
-    QualType SecondType = SecondField->getType();
-    if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
-      DiagError(FieldTypeName) << FirstII << FirstType;
-      DiagNote(FieldTypeName) << SecondII << SecondType;
-      return true;
-    }
-
-    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;
-  }
-
-  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;
-      }
-    };
-
-    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_
diff erent_definitions)
-        << FirstRecord << FirstModule.empty() << FirstModule;
-
-    if (DR.FirstDecl) {
-      Diag(DR.FirstDecl->getLocation(), diag::note_first_module_
diff erence)
-          << FirstRecord << DR.FirstDecl->getSourceRange();
-    }
-
-    Diag(SecondRecord->getLocation(),
-         diag::note_module_odr_violation_
diff erent_definitions)
-        << SecondModule;
-
-    if (DR.SecondDecl) {
-      Diag(DR.SecondDecl->getLocation(), diag::note_second_module_
diff erence)
-          << 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;
-      auto *Tag = dyn_cast<TagDecl>(Container);
-      if (DiffType == EndOfClass && Tag) {
-        Loc = Tag->getBraceRange().getEnd();
-      } 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 << SecondDiagInfo.second << DR.SecondDiffType;
-  }
-
-  bool ODRDiagsEmitter::diagnoseMismatch(
-      const CXXRecordDecl *FirstRecord, const CXXRecordDecl *SecondRecord,
-      const struct CXXRecordDecl::DefinitionData *SecondDD) const {
-      // Multiple 
diff erent 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::isDeclToBeProcessed(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,
-        MethodNumberParameters,
-        MethodParameterType,
-        MethodParameterName,
-        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:
-        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 StringLiteral *FirstStr = FirstSA->getMessage();
-        const StringLiteral *SecondStr = SecondSA->getMessage();
-        assert((FirstStr || SecondStr) && "Both messages cannot be empty");
-        if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {
-          SourceLocation FirstLoc, SecondLoc;
-          SourceRange FirstRange, SecondRange;
-          if (FirstStr) {
-            FirstLoc = FirstStr->getBeginLoc();
-            FirstRange = FirstStr->getSourceRange();
-          } else {
-            FirstLoc = FirstSA->getBeginLoc();
-            FirstRange = FirstSA->getSourceRange();
-          }
-          if (SecondStr) {
-            SecondLoc = SecondStr->getBeginLoc();
-            SecondRange = SecondStr->getSourceRange();
-          } else {
-            SecondLoc = SecondSA->getBeginLoc();
-            SecondRange = SecondSA->getSourceRange();
-          }
-          DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
-              << (FirstStr == nullptr);
-          DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
-              << (SecondStr == nullptr);
-          return true;
-        }
-
-        if (FirstStr && SecondStr &&
-            FirstStr->getString() != SecondStr->getString()) {
-          DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(),
-                           StaticAssertMessage);
-          DiagNote(SecondStr->getBeginLoc(), SecondStr->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->isPure();
-        const bool SecondPure = SecondMethod->isPure();
-        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;
-        }
-
-        const unsigned FirstNumParameters = FirstMethod->param_size();
-        const unsigned SecondNumParameters = SecondMethod->param_size();
-        if (FirstNumParameters != SecondNumParameters) {
-          DiagMethodError(MethodNumberParameters) << FirstNumParameters;
-          DiagMethodNote(MethodNumberParameters) << 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>()) {
-              DiagMethodError(MethodParameterType)
-                  << (I + 1) << FirstParamType << true
-                  << ParamDecayedType->getOriginalType();
-            } else {
-              DiagMethodError(MethodParameterType)
-                  << (I + 1) << FirstParamType << false;
-            }
-
-            if (const DecayedType *ParamDecayedType =
-                    SecondParamType->getAs<DecayedType>()) {
-              DiagMethodNote(MethodParameterType)
-                  << (I + 1) << SecondParamType << true
-                  << ParamDecayedType->getOriginalType();
-            } else {
-              DiagMethodNote(MethodParameterType)
-                  << (I + 1) << SecondParamType << false;
-            }
-            return true;
-          }
-
-          DeclarationName FirstParamName = FirstParam->getDeclName();
-          DeclarationName SecondParamName = SecondParam->getDeclName();
-          if (FirstParamName != SecondParamName) {
-            DiagMethodError(MethodParameterName) << (I + 1) << FirstParamName;
-            DiagMethodNote(MethodParameterName) << (I + 1) << SecondParamName;
-            return true;
-          }
-
-          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 
diff erence 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) {
-              QualType FirstType = FirstTTPD->getDefaultArgument();
-              QualType SecondType = SecondTTPD->getDefaultArgument();
-              if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
-                DiagTemplateError(
-                    FunctionTemplateParameterDifferentDefaultArgument)
-                    << (i + 1) << FirstType;
-                DiagTemplateNote(
-                    FunctionTemplateParameterDifferentDefaultArgument)
-                    << (i + 1) << SecondType;
-                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) {
-              Expr *FirstDefaultArgument = FirstNTTPD->getDefaultArgument();
-              Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument();
-              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 << 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 
diff erent 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 
diff erent 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;
-        }
-
-        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 
diff erence.");
-      }
-
-      // 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::isDeclToBeProcessed(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;
-}
-// clang-format on
-
 void ASTReader::StartedDeserializing() {
   if (++NumCurrentElementsDeserializing == 1 && ReadTimer.get())
     ReadTimer->startTimer();


        


More information about the cfe-commits mailing list