llvm-project/clang/lib/AST/ODRDiagsEmitter.cpp
Matheus Izvekov 2bde13cda1
[clang] NFCI: use TemplateArgumentLoc for NTTP DefaultArgument (#92852)
This is an enabler for https://github.com/llvm/llvm-project/pull/92855

This allows an NTTP default argument to be set as an arbitrary
TemplateArgument, not just an expression.
This allows template parameter packs to have default arguments in the
AST, even though the language proper doesn't support the syntax for it.

This allows NTTP default arguments to be other kinds of arguments, like
packs, integral constants, and such.
2024-05-22 12:18:44 -03:00

2219 lines
84 KiB
C++

//===-- ODRDiagsEmitter.cpp - Diagnostics for ODR mismatches ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ODRDiagsEmitter.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ODRHash.h"
#include "clang/Basic/DiagnosticAST.h"
#include "clang/Basic/Module.h"
using namespace clang;
static unsigned computeODRHash(QualType Ty) {
ODRHash Hasher;
Hasher.AddQualType(Ty);
return Hasher.CalculateHash();
}
static unsigned computeODRHash(const Stmt *S) {
ODRHash Hasher;
Hasher.AddStmt(S);
return Hasher.CalculateHash();
}
static unsigned computeODRHash(const Decl *D) {
assert(D);
ODRHash Hasher;
Hasher.AddSubDecl(D);
return Hasher.CalculateHash();
}
static unsigned computeODRHash(const TemplateArgument &TA) {
ODRHash Hasher;
Hasher.AddTemplateArgument(TA);
return Hasher.CalculateHash();
}
std::string ODRDiagsEmitter::getOwningModuleNameForDiagnostic(const Decl *D) {
// If we know the owning module, use it.
if (Module *M = D->getImportedOwningModule())
return M->getFullModuleName();
// Not from a module.
return {};
}
template <typename MethodT>
static bool diagnoseSubMismatchMethodParameters(DiagnosticsEngine &Diags,
const NamedDecl *FirstContainer,
StringRef FirstModule,
StringRef SecondModule,
const MethodT *FirstMethod,
const MethodT *SecondMethod) {
enum DiagMethodType {
DiagMethod,
DiagConstructor,
DiagDestructor,
};
auto GetDiagMethodType = [](const NamedDecl *D) {
if (isa<CXXConstructorDecl>(D))
return DiagConstructor;
if (isa<CXXDestructorDecl>(D))
return DiagDestructor;
return DiagMethod;
};
enum ODRMethodParametersDifference {
NumberParameters,
ParameterType,
ParameterName,
};
auto DiagError = [&Diags, &GetDiagMethodType, FirstContainer, FirstModule,
FirstMethod](ODRMethodParametersDifference DiffType) {
DeclarationName FirstName = FirstMethod->getDeclName();
DiagMethodType FirstMethodType = GetDiagMethodType(FirstMethod);
return Diags.Report(FirstMethod->getLocation(),
diag::err_module_odr_violation_method_params)
<< FirstContainer << FirstModule.empty() << FirstModule
<< FirstMethod->getSourceRange() << DiffType << FirstMethodType
<< FirstName;
};
auto DiagNote = [&Diags, &GetDiagMethodType, SecondModule,
SecondMethod](ODRMethodParametersDifference DiffType) {
DeclarationName SecondName = SecondMethod->getDeclName();
DiagMethodType SecondMethodType = GetDiagMethodType(SecondMethod);
return Diags.Report(SecondMethod->getLocation(),
diag::note_module_odr_violation_method_params)
<< SecondModule.empty() << SecondModule
<< SecondMethod->getSourceRange() << DiffType << SecondMethodType
<< SecondName;
};
const unsigned FirstNumParameters = FirstMethod->param_size();
const unsigned SecondNumParameters = SecondMethod->param_size();
if (FirstNumParameters != SecondNumParameters) {
DiagError(NumberParameters) << FirstNumParameters;
DiagNote(NumberParameters) << SecondNumParameters;
return true;
}
for (unsigned I = 0; I < FirstNumParameters; ++I) {
const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I);
const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I);
QualType FirstParamType = FirstParam->getType();
QualType SecondParamType = SecondParam->getType();
if (FirstParamType != SecondParamType &&
computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) {
if (const DecayedType *ParamDecayedType =
FirstParamType->getAs<DecayedType>()) {
DiagError(ParameterType) << (I + 1) << FirstParamType << true
<< ParamDecayedType->getOriginalType();
} else {
DiagError(ParameterType) << (I + 1) << FirstParamType << false;
}
if (const DecayedType *ParamDecayedType =
SecondParamType->getAs<DecayedType>()) {
DiagNote(ParameterType) << (I + 1) << SecondParamType << true
<< ParamDecayedType->getOriginalType();
} else {
DiagNote(ParameterType) << (I + 1) << SecondParamType << false;
}
return true;
}
DeclarationName FirstParamName = FirstParam->getDeclName();
DeclarationName SecondParamName = SecondParam->getDeclName();
if (FirstParamName != SecondParamName) {
DiagError(ParameterName) << (I + 1) << FirstParamName;
DiagNote(ParameterName) << (I + 1) << SecondParamName;
return true;
}
}
return false;
}
bool ODRDiagsEmitter::diagnoseSubMismatchField(
const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule,
const FieldDecl *FirstField, const FieldDecl *SecondField) const {
enum ODRFieldDifference {
FieldName,
FieldTypeName,
FieldSingleBitField,
FieldDifferentWidthBitField,
FieldSingleMutable,
FieldSingleInitializer,
FieldDifferentInitializers,
};
auto DiagError = [FirstRecord, FirstField, FirstModule,
this](ODRFieldDifference DiffType) {
return Diag(FirstField->getLocation(), diag::err_module_odr_violation_field)
<< FirstRecord << FirstModule.empty() << FirstModule
<< FirstField->getSourceRange() << DiffType;
};
auto DiagNote = [SecondField, SecondModule,
this](ODRFieldDifference DiffType) {
return Diag(SecondField->getLocation(),
diag::note_module_odr_violation_field)
<< SecondModule.empty() << SecondModule << SecondField->getSourceRange() << DiffType;
};
IdentifierInfo *FirstII = FirstField->getIdentifier();
IdentifierInfo *SecondII = SecondField->getIdentifier();
if (FirstII->getName() != SecondII->getName()) {
DiagError(FieldName) << FirstII;
DiagNote(FieldName) << SecondII;
return true;
}
QualType FirstType = FirstField->getType();
QualType SecondType = SecondField->getType();
if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
DiagError(FieldTypeName) << FirstII << FirstType;
DiagNote(FieldTypeName) << SecondII << SecondType;
return true;
}
assert(Context.hasSameType(FirstField->getType(), SecondField->getType()));
(void)Context;
const bool IsFirstBitField = FirstField->isBitField();
const bool IsSecondBitField = SecondField->isBitField();
if (IsFirstBitField != IsSecondBitField) {
DiagError(FieldSingleBitField) << FirstII << IsFirstBitField;
DiagNote(FieldSingleBitField) << SecondII << IsSecondBitField;
return true;
}
if (IsFirstBitField && IsSecondBitField) {
unsigned FirstBitWidthHash = computeODRHash(FirstField->getBitWidth());
unsigned SecondBitWidthHash = computeODRHash(SecondField->getBitWidth());
if (FirstBitWidthHash != SecondBitWidthHash) {
DiagError(FieldDifferentWidthBitField)
<< FirstII << FirstField->getBitWidth()->getSourceRange();
DiagNote(FieldDifferentWidthBitField)
<< SecondII << SecondField->getBitWidth()->getSourceRange();
return true;
}
}
if (!LangOpts.CPlusPlus)
return false;
const bool IsFirstMutable = FirstField->isMutable();
const bool IsSecondMutable = SecondField->isMutable();
if (IsFirstMutable != IsSecondMutable) {
DiagError(FieldSingleMutable) << FirstII << IsFirstMutable;
DiagNote(FieldSingleMutable) << SecondII << IsSecondMutable;
return true;
}
const Expr *FirstInitializer = FirstField->getInClassInitializer();
const Expr *SecondInitializer = SecondField->getInClassInitializer();
if ((!FirstInitializer && SecondInitializer) ||
(FirstInitializer && !SecondInitializer)) {
DiagError(FieldSingleInitializer)
<< FirstII << (FirstInitializer != nullptr);
DiagNote(FieldSingleInitializer)
<< SecondII << (SecondInitializer != nullptr);
return true;
}
if (FirstInitializer && SecondInitializer) {
unsigned FirstInitHash = computeODRHash(FirstInitializer);
unsigned SecondInitHash = computeODRHash(SecondInitializer);
if (FirstInitHash != SecondInitHash) {
DiagError(FieldDifferentInitializers)
<< FirstII << FirstInitializer->getSourceRange();
DiagNote(FieldDifferentInitializers)
<< SecondII << SecondInitializer->getSourceRange();
return true;
}
}
return false;
}
bool ODRDiagsEmitter::diagnoseSubMismatchTypedef(
const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule,
const TypedefNameDecl *FirstTD, const TypedefNameDecl *SecondTD,
bool IsTypeAlias) const {
enum ODRTypedefDifference {
TypedefName,
TypedefType,
};
auto DiagError = [FirstRecord, FirstTD, FirstModule,
this](ODRTypedefDifference DiffType) {
return Diag(FirstTD->getLocation(), diag::err_module_odr_violation_typedef)
<< FirstRecord << FirstModule.empty() << FirstModule
<< FirstTD->getSourceRange() << DiffType;
};
auto DiagNote = [SecondTD, SecondModule,
this](ODRTypedefDifference DiffType) {
return Diag(SecondTD->getLocation(),
diag::note_module_odr_violation_typedef)
<< SecondModule << SecondTD->getSourceRange() << DiffType;
};
DeclarationName FirstName = FirstTD->getDeclName();
DeclarationName SecondName = SecondTD->getDeclName();
if (FirstName != SecondName) {
DiagError(TypedefName) << IsTypeAlias << FirstName;
DiagNote(TypedefName) << IsTypeAlias << SecondName;
return true;
}
QualType FirstType = FirstTD->getUnderlyingType();
QualType SecondType = SecondTD->getUnderlyingType();
if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
DiagError(TypedefType) << IsTypeAlias << FirstName << FirstType;
DiagNote(TypedefType) << IsTypeAlias << SecondName << SecondType;
return true;
}
return false;
}
bool ODRDiagsEmitter::diagnoseSubMismatchVar(const NamedDecl *FirstRecord,
StringRef FirstModule,
StringRef SecondModule,
const VarDecl *FirstVD,
const VarDecl *SecondVD) const {
enum ODRVarDifference {
VarName,
VarType,
VarSingleInitializer,
VarDifferentInitializer,
VarConstexpr,
};
auto DiagError = [FirstRecord, FirstVD, FirstModule,
this](ODRVarDifference DiffType) {
return Diag(FirstVD->getLocation(), diag::err_module_odr_violation_variable)
<< FirstRecord << FirstModule.empty() << FirstModule
<< FirstVD->getSourceRange() << DiffType;
};
auto DiagNote = [SecondVD, SecondModule, this](ODRVarDifference DiffType) {
return Diag(SecondVD->getLocation(),
diag::note_module_odr_violation_variable)
<< SecondModule << SecondVD->getSourceRange() << DiffType;
};
DeclarationName FirstName = FirstVD->getDeclName();
DeclarationName SecondName = SecondVD->getDeclName();
if (FirstName != SecondName) {
DiagError(VarName) << FirstName;
DiagNote(VarName) << SecondName;
return true;
}
QualType FirstType = FirstVD->getType();
QualType SecondType = SecondVD->getType();
if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
DiagError(VarType) << FirstName << FirstType;
DiagNote(VarType) << SecondName << SecondType;
return true;
}
if (!LangOpts.CPlusPlus)
return false;
const Expr *FirstInit = FirstVD->getInit();
const Expr *SecondInit = SecondVD->getInit();
if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
DiagError(VarSingleInitializer)
<< FirstName << (FirstInit == nullptr)
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange());
DiagNote(VarSingleInitializer)
<< SecondName << (SecondInit == nullptr)
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange());
return true;
}
if (FirstInit && SecondInit &&
computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
DiagError(VarDifferentInitializer)
<< FirstName << FirstInit->getSourceRange();
DiagNote(VarDifferentInitializer)
<< SecondName << SecondInit->getSourceRange();
return true;
}
const bool FirstIsConstexpr = FirstVD->isConstexpr();
const bool SecondIsConstexpr = SecondVD->isConstexpr();
if (FirstIsConstexpr != SecondIsConstexpr) {
DiagError(VarConstexpr) << FirstName << FirstIsConstexpr;
DiagNote(VarConstexpr) << SecondName << SecondIsConstexpr;
return true;
}
return false;
}
bool ODRDiagsEmitter::diagnoseSubMismatchProtocols(
const ObjCProtocolList &FirstProtocols,
const ObjCContainerDecl *FirstContainer, StringRef FirstModule,
const ObjCProtocolList &SecondProtocols,
const ObjCContainerDecl *SecondContainer, StringRef SecondModule) const {
// Keep in sync with err_module_odr_violation_referenced_protocols.
enum ODRReferencedProtocolDifference {
NumProtocols,
ProtocolType,
};
auto DiagRefProtocolError = [FirstContainer, FirstModule,
this](SourceLocation Loc, SourceRange Range,
ODRReferencedProtocolDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_referenced_protocols)
<< FirstContainer << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto DiagRefProtocolNote = [SecondModule,
this](SourceLocation Loc, SourceRange Range,
ODRReferencedProtocolDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_referenced_protocols)
<< SecondModule.empty() << SecondModule << Range << DiffType;
};
auto GetProtoListSourceRange = [](const ObjCProtocolList &PL) {
if (PL.empty())
return SourceRange();
return SourceRange(*PL.loc_begin(), *std::prev(PL.loc_end()));
};
if (FirstProtocols.size() != SecondProtocols.size()) {
DiagRefProtocolError(FirstContainer->getLocation(),
GetProtoListSourceRange(FirstProtocols), NumProtocols)
<< FirstProtocols.size();
DiagRefProtocolNote(SecondContainer->getLocation(),
GetProtoListSourceRange(SecondProtocols), NumProtocols)
<< SecondProtocols.size();
return true;
}
for (unsigned I = 0, E = FirstProtocols.size(); I != E; ++I) {
const ObjCProtocolDecl *FirstProtocol = FirstProtocols[I];
const ObjCProtocolDecl *SecondProtocol = SecondProtocols[I];
DeclarationName FirstProtocolName = FirstProtocol->getDeclName();
DeclarationName SecondProtocolName = SecondProtocol->getDeclName();
if (FirstProtocolName != SecondProtocolName) {
SourceLocation FirstLoc = *(FirstProtocols.loc_begin() + I);
SourceLocation SecondLoc = *(SecondProtocols.loc_begin() + I);
SourceRange EmptyRange;
DiagRefProtocolError(FirstLoc, EmptyRange, ProtocolType)
<< (I + 1) << FirstProtocolName;
DiagRefProtocolNote(SecondLoc, EmptyRange, ProtocolType)
<< (I + 1) << SecondProtocolName;
return true;
}
}
return false;
}
bool ODRDiagsEmitter::diagnoseSubMismatchObjCMethod(
const NamedDecl *FirstObjCContainer, StringRef FirstModule,
StringRef SecondModule, const ObjCMethodDecl *FirstMethod,
const ObjCMethodDecl *SecondMethod) const {
enum ODRMethodDifference {
ReturnType,
InstanceOrClass,
ControlLevel, // optional/required
DesignatedInitializer,
Directness,
Name,
};
auto DiagError = [FirstObjCContainer, FirstModule, FirstMethod,
this](ODRMethodDifference DiffType) {
return Diag(FirstMethod->getLocation(),
diag::err_module_odr_violation_objc_method)
<< FirstObjCContainer << FirstModule.empty() << FirstModule
<< FirstMethod->getSourceRange() << DiffType;
};
auto DiagNote = [SecondModule, SecondMethod,
this](ODRMethodDifference DiffType) {
return Diag(SecondMethod->getLocation(),
diag::note_module_odr_violation_objc_method)
<< SecondModule.empty() << SecondModule
<< SecondMethod->getSourceRange() << DiffType;
};
if (computeODRHash(FirstMethod->getReturnType()) !=
computeODRHash(SecondMethod->getReturnType())) {
DiagError(ReturnType) << FirstMethod << FirstMethod->getReturnType();
DiagNote(ReturnType) << SecondMethod << SecondMethod->getReturnType();
return true;
}
if (FirstMethod->isInstanceMethod() != SecondMethod->isInstanceMethod()) {
DiagError(InstanceOrClass)
<< FirstMethod << FirstMethod->isInstanceMethod();
DiagNote(InstanceOrClass)
<< SecondMethod << SecondMethod->isInstanceMethod();
return true;
}
if (FirstMethod->getImplementationControl() !=
SecondMethod->getImplementationControl()) {
DiagError(ControlLevel)
<< llvm::to_underlying(FirstMethod->getImplementationControl());
DiagNote(ControlLevel) << llvm::to_underlying(
SecondMethod->getImplementationControl());
return true;
}
if (FirstMethod->isThisDeclarationADesignatedInitializer() !=
SecondMethod->isThisDeclarationADesignatedInitializer()) {
DiagError(DesignatedInitializer)
<< FirstMethod
<< FirstMethod->isThisDeclarationADesignatedInitializer();
DiagNote(DesignatedInitializer)
<< SecondMethod
<< SecondMethod->isThisDeclarationADesignatedInitializer();
return true;
}
if (FirstMethod->isDirectMethod() != SecondMethod->isDirectMethod()) {
DiagError(Directness) << FirstMethod << FirstMethod->isDirectMethod();
DiagNote(Directness) << SecondMethod << SecondMethod->isDirectMethod();
return true;
}
if (diagnoseSubMismatchMethodParameters(Diags, FirstObjCContainer,
FirstModule, SecondModule,
FirstMethod, SecondMethod))
return true;
// Check method name *after* looking at the parameters otherwise we get a
// less ideal diagnostics: a ObjCMethodName mismatch given that selectors
// for different parameters are likely to be different.
DeclarationName FirstName = FirstMethod->getDeclName();
DeclarationName SecondName = SecondMethod->getDeclName();
if (FirstName != SecondName) {
DiagError(Name) << FirstName;
DiagNote(Name) << SecondName;
return true;
}
return false;
}
bool ODRDiagsEmitter::diagnoseSubMismatchObjCProperty(
const NamedDecl *FirstObjCContainer, StringRef FirstModule,
StringRef SecondModule, const ObjCPropertyDecl *FirstProp,
const ObjCPropertyDecl *SecondProp) const {
enum ODRPropertyDifference {
Name,
Type,
ControlLevel, // optional/required
Attribute,
};
auto DiagError = [FirstObjCContainer, FirstModule, FirstProp,
this](SourceLocation Loc, ODRPropertyDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_objc_property)
<< FirstObjCContainer << FirstModule.empty() << FirstModule
<< FirstProp->getSourceRange() << DiffType;
};
auto DiagNote = [SecondModule, SecondProp,
this](SourceLocation Loc, ODRPropertyDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_objc_property)
<< SecondModule.empty() << SecondModule
<< SecondProp->getSourceRange() << DiffType;
};
IdentifierInfo *FirstII = FirstProp->getIdentifier();
IdentifierInfo *SecondII = SecondProp->getIdentifier();
if (FirstII->getName() != SecondII->getName()) {
DiagError(FirstProp->getLocation(), Name) << FirstII;
DiagNote(SecondProp->getLocation(), Name) << SecondII;
return true;
}
if (computeODRHash(FirstProp->getType()) !=
computeODRHash(SecondProp->getType())) {
DiagError(FirstProp->getLocation(), Type)
<< FirstII << FirstProp->getType();
DiagNote(SecondProp->getLocation(), Type)
<< SecondII << SecondProp->getType();
return true;
}
if (FirstProp->getPropertyImplementation() !=
SecondProp->getPropertyImplementation()) {
DiagError(FirstProp->getLocation(), ControlLevel)
<< FirstProp->getPropertyImplementation();
DiagNote(SecondProp->getLocation(), ControlLevel)
<< SecondProp->getPropertyImplementation();
return true;
}
// Go over the property attributes and stop at the first mismatch.
unsigned FirstAttrs = (unsigned)FirstProp->getPropertyAttributes();
unsigned SecondAttrs = (unsigned)SecondProp->getPropertyAttributes();
if (FirstAttrs != SecondAttrs) {
for (unsigned I = 0; I < NumObjCPropertyAttrsBits; ++I) {
unsigned CheckedAttr = (1 << I);
if ((FirstAttrs & CheckedAttr) == (SecondAttrs & CheckedAttr))
continue;
bool IsFirstWritten =
(unsigned)FirstProp->getPropertyAttributesAsWritten() & CheckedAttr;
bool IsSecondWritten =
(unsigned)SecondProp->getPropertyAttributesAsWritten() & CheckedAttr;
DiagError(IsFirstWritten ? FirstProp->getLParenLoc()
: FirstProp->getLocation(),
Attribute)
<< FirstII << (I + 1) << IsFirstWritten;
DiagNote(IsSecondWritten ? SecondProp->getLParenLoc()
: SecondProp->getLocation(),
Attribute)
<< SecondII << (I + 1);
return true;
}
}
return false;
}
ODRDiagsEmitter::DiffResult
ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes,
DeclHashes &SecondHashes) {
auto DifferenceSelector = [](const Decl *D) {
assert(D && "valid Decl required");
switch (D->getKind()) {
default:
return Other;
case Decl::AccessSpec:
switch (D->getAccess()) {
case AS_public:
return PublicSpecifer;
case AS_private:
return PrivateSpecifer;
case AS_protected:
return ProtectedSpecifer;
case AS_none:
break;
}
llvm_unreachable("Invalid access specifier");
case Decl::StaticAssert:
return StaticAssert;
case Decl::Field:
return Field;
case Decl::CXXMethod:
case Decl::CXXConstructor:
case Decl::CXXDestructor:
return CXXMethod;
case Decl::TypeAlias:
return TypeAlias;
case Decl::Typedef:
return TypeDef;
case Decl::Var:
return Var;
case Decl::Friend:
return Friend;
case Decl::FunctionTemplate:
return FunctionTemplate;
case Decl::ObjCMethod:
return ObjCMethod;
case Decl::ObjCIvar:
return ObjCIvar;
case Decl::ObjCProperty:
return ObjCProperty;
}
};
DiffResult DR;
auto FirstIt = FirstHashes.begin();
auto SecondIt = SecondHashes.begin();
while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) {
if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() &&
FirstIt->second == SecondIt->second) {
++FirstIt;
++SecondIt;
continue;
}
DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first;
DR.SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first;
DR.FirstDiffType =
DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass;
DR.SecondDiffType =
DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass;
return DR;
}
return DR;
}
void ODRDiagsEmitter::diagnoseSubMismatchUnexpected(
DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule,
const NamedDecl *SecondRecord, StringRef SecondModule) const {
Diag(FirstRecord->getLocation(),
diag::err_module_odr_violation_different_definitions)
<< FirstRecord << FirstModule.empty() << FirstModule;
if (DR.FirstDecl) {
Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference)
<< FirstRecord << DR.FirstDecl->getSourceRange();
}
Diag(SecondRecord->getLocation(),
diag::note_module_odr_violation_different_definitions)
<< SecondModule;
if (DR.SecondDecl) {
Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference)
<< DR.SecondDecl->getSourceRange();
}
}
void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds(
DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule,
const NamedDecl *SecondRecord, StringRef SecondModule) const {
auto GetMismatchedDeclLoc = [](const NamedDecl *Container,
ODRMismatchDecl DiffType, const Decl *D) {
SourceLocation Loc;
SourceRange Range;
if (DiffType == EndOfClass) {
if (auto *Tag = dyn_cast<TagDecl>(Container))
Loc = Tag->getBraceRange().getEnd();
else if (auto *IF = dyn_cast<ObjCInterfaceDecl>(Container))
Loc = IF->getAtEndRange().getBegin();
else
Loc = Container->getEndLoc();
} else {
Loc = D->getLocation();
Range = D->getSourceRange();
}
return std::make_pair(Loc, Range);
};
auto FirstDiagInfo =
GetMismatchedDeclLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl);
Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl)
<< FirstRecord << FirstModule.empty() << FirstModule
<< FirstDiagInfo.second << DR.FirstDiffType;
auto SecondDiagInfo =
GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl);
Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl)
<< SecondModule.empty() << SecondModule << SecondDiagInfo.second
<< DR.SecondDiffType;
}
bool ODRDiagsEmitter::diagnoseMismatch(
const CXXRecordDecl *FirstRecord, const CXXRecordDecl *SecondRecord,
const struct CXXRecordDecl::DefinitionData *SecondDD) const {
// Multiple different declarations got merged together; tell the user
// where they came from.
if (FirstRecord == SecondRecord)
return false;
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord);
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord);
const struct CXXRecordDecl::DefinitionData *FirstDD =
FirstRecord->DefinitionData;
assert(FirstDD && SecondDD && "Definitions without DefinitionData");
// Diagnostics from DefinitionData are emitted here.
if (FirstDD != SecondDD) {
// Keep in sync with err_module_odr_violation_definition_data.
enum ODRDefinitionDataDifference {
NumBases,
NumVBases,
BaseType,
BaseVirtual,
BaseAccess,
};
auto DiagBaseError = [FirstRecord, &FirstModule,
this](SourceLocation Loc, SourceRange Range,
ODRDefinitionDataDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_definition_data)
<< FirstRecord << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto DiagBaseNote = [&SecondModule,
this](SourceLocation Loc, SourceRange Range,
ODRDefinitionDataDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_definition_data)
<< SecondModule << Range << DiffType;
};
auto GetSourceRange = [](const struct CXXRecordDecl::DefinitionData *DD) {
unsigned NumBases = DD->NumBases;
if (NumBases == 0)
return SourceRange();
ArrayRef<CXXBaseSpecifier> bases = DD->bases();
return SourceRange(bases[0].getBeginLoc(),
bases[NumBases - 1].getEndLoc());
};
unsigned FirstNumBases = FirstDD->NumBases;
unsigned FirstNumVBases = FirstDD->NumVBases;
unsigned SecondNumBases = SecondDD->NumBases;
unsigned SecondNumVBases = SecondDD->NumVBases;
if (FirstNumBases != SecondNumBases) {
DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD),
NumBases)
<< FirstNumBases;
DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD),
NumBases)
<< SecondNumBases;
return true;
}
if (FirstNumVBases != SecondNumVBases) {
DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD),
NumVBases)
<< FirstNumVBases;
DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD),
NumVBases)
<< SecondNumVBases;
return true;
}
ArrayRef<CXXBaseSpecifier> FirstBases = FirstDD->bases();
ArrayRef<CXXBaseSpecifier> SecondBases = SecondDD->bases();
for (unsigned I = 0; I < FirstNumBases; ++I) {
const CXXBaseSpecifier FirstBase = FirstBases[I];
const CXXBaseSpecifier SecondBase = SecondBases[I];
if (computeODRHash(FirstBase.getType()) !=
computeODRHash(SecondBase.getType())) {
DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
BaseType)
<< (I + 1) << FirstBase.getType();
DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(),
BaseType)
<< (I + 1) << SecondBase.getType();
return true;
}
if (FirstBase.isVirtual() != SecondBase.isVirtual()) {
DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
BaseVirtual)
<< (I + 1) << FirstBase.isVirtual() << FirstBase.getType();
DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(),
BaseVirtual)
<< (I + 1) << SecondBase.isVirtual() << SecondBase.getType();
return true;
}
if (FirstBase.getAccessSpecifierAsWritten() !=
SecondBase.getAccessSpecifierAsWritten()) {
DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
BaseAccess)
<< (I + 1) << FirstBase.getType()
<< (int)FirstBase.getAccessSpecifierAsWritten();
DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(),
BaseAccess)
<< (I + 1) << SecondBase.getType()
<< (int)SecondBase.getAccessSpecifierAsWritten();
return true;
}
}
}
const ClassTemplateDecl *FirstTemplate =
FirstRecord->getDescribedClassTemplate();
const ClassTemplateDecl *SecondTemplate =
SecondRecord->getDescribedClassTemplate();
assert(!FirstTemplate == !SecondTemplate &&
"Both pointers should be null or non-null");
if (FirstTemplate && SecondTemplate) {
ArrayRef<const NamedDecl *> FirstTemplateParams =
FirstTemplate->getTemplateParameters()->asArray();
ArrayRef<const NamedDecl *> SecondTemplateParams =
SecondTemplate->getTemplateParameters()->asArray();
assert(FirstTemplateParams.size() == SecondTemplateParams.size() &&
"Number of template parameters should be equal.");
for (auto Pair : llvm::zip(FirstTemplateParams, SecondTemplateParams)) {
const NamedDecl *FirstDecl = std::get<0>(Pair);
const NamedDecl *SecondDecl = std::get<1>(Pair);
if (computeODRHash(FirstDecl) == computeODRHash(SecondDecl))
continue;
assert(FirstDecl->getKind() == SecondDecl->getKind() &&
"Parameter Decl's should be the same kind.");
enum ODRTemplateDifference {
ParamEmptyName,
ParamName,
ParamSingleDefaultArgument,
ParamDifferentDefaultArgument,
};
auto hasDefaultArg = [](const NamedDecl *D) {
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(D))
return TTP->hasDefaultArgument() &&
!TTP->defaultArgumentWasInherited();
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D))
return NTTP->hasDefaultArgument() &&
!NTTP->defaultArgumentWasInherited();
auto *TTP = cast<TemplateTemplateParmDecl>(D);
return TTP->hasDefaultArgument() && !TTP->defaultArgumentWasInherited();
};
bool hasFirstArg = hasDefaultArg(FirstDecl);
bool hasSecondArg = hasDefaultArg(SecondDecl);
ODRTemplateDifference ErrDiffType;
ODRTemplateDifference NoteDiffType;
DeclarationName FirstName = FirstDecl->getDeclName();
DeclarationName SecondName = SecondDecl->getDeclName();
if (FirstName != SecondName) {
bool FirstNameEmpty =
FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo();
bool SecondNameEmpty =
SecondName.isIdentifier() && !SecondName.getAsIdentifierInfo();
ErrDiffType = FirstNameEmpty ? ParamEmptyName : ParamName;
NoteDiffType = SecondNameEmpty ? ParamEmptyName : ParamName;
} else if (hasFirstArg == hasSecondArg)
ErrDiffType = NoteDiffType = ParamDifferentDefaultArgument;
else
ErrDiffType = NoteDiffType = ParamSingleDefaultArgument;
Diag(FirstDecl->getLocation(),
diag::err_module_odr_violation_template_parameter)
<< FirstRecord << FirstModule.empty() << FirstModule
<< FirstDecl->getSourceRange() << ErrDiffType << hasFirstArg
<< FirstName;
Diag(SecondDecl->getLocation(),
diag::note_module_odr_violation_template_parameter)
<< SecondModule << SecondDecl->getSourceRange() << NoteDiffType
<< hasSecondArg << SecondName;
return true;
}
}
auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record,
const DeclContext *DC) {
for (const Decl *D : Record->decls()) {
if (!ODRHash::isSubDeclToBeProcessed(D, DC))
continue;
Hashes.emplace_back(D, computeODRHash(D));
}
};
DeclHashes FirstHashes;
DeclHashes SecondHashes;
const DeclContext *DC = FirstRecord;
PopulateHashes(FirstHashes, FirstRecord, DC);
PopulateHashes(SecondHashes, SecondRecord, DC);
DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
const Decl *FirstDecl = DR.FirstDecl;
const Decl *SecondDecl = DR.SecondDecl;
if (FirstDiffType == Other || SecondDiffType == Other) {
diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord,
SecondModule);
return true;
}
if (FirstDiffType != SecondDiffType) {
diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule,
SecondRecord, SecondModule);
return true;
}
// Used with err_module_odr_violation_record and
// note_module_odr_violation_record
enum ODRCXXRecordDifference {
StaticAssertCondition,
StaticAssertMessage,
StaticAssertOnlyMessage,
MethodName,
MethodDeleted,
MethodDefaulted,
MethodVirtual,
MethodStatic,
MethodVolatile,
MethodConst,
MethodInline,
MethodParameterSingleDefaultArgument,
MethodParameterDifferentDefaultArgument,
MethodNoTemplateArguments,
MethodDifferentNumberTemplateArguments,
MethodDifferentTemplateArgument,
MethodSingleBody,
MethodDifferentBody,
FriendTypeFunction,
FriendType,
FriendFunction,
FunctionTemplateDifferentNumberParameters,
FunctionTemplateParameterDifferentKind,
FunctionTemplateParameterName,
FunctionTemplateParameterSingleDefaultArgument,
FunctionTemplateParameterDifferentDefaultArgument,
FunctionTemplateParameterDifferentType,
FunctionTemplatePackParameter,
};
auto DiagError = [FirstRecord, &FirstModule,
this](SourceLocation Loc, SourceRange Range,
ODRCXXRecordDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_record)
<< FirstRecord << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
ODRCXXRecordDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_record)
<< SecondModule << Range << DiffType;
};
assert(FirstDiffType == SecondDiffType);
switch (FirstDiffType) {
case Other:
case EndOfClass:
case PublicSpecifer:
case PrivateSpecifer:
case ProtectedSpecifer:
case ObjCMethod:
case ObjCIvar:
case ObjCProperty:
llvm_unreachable("Invalid diff type");
case StaticAssert: {
const StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl);
const StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl);
const Expr *FirstExpr = FirstSA->getAssertExpr();
const Expr *SecondExpr = SecondSA->getAssertExpr();
unsigned FirstODRHash = computeODRHash(FirstExpr);
unsigned SecondODRHash = computeODRHash(SecondExpr);
if (FirstODRHash != SecondODRHash) {
DiagError(FirstExpr->getBeginLoc(), FirstExpr->getSourceRange(),
StaticAssertCondition);
DiagNote(SecondExpr->getBeginLoc(), SecondExpr->getSourceRange(),
StaticAssertCondition);
return true;
}
const Expr *FirstMessage = FirstSA->getMessage();
const Expr *SecondMessage = SecondSA->getMessage();
assert((FirstMessage || SecondMessage) && "Both messages cannot be empty");
if ((FirstMessage && !SecondMessage) || (!FirstMessage && SecondMessage)) {
SourceLocation FirstLoc, SecondLoc;
SourceRange FirstRange, SecondRange;
if (FirstMessage) {
FirstLoc = FirstMessage->getBeginLoc();
FirstRange = FirstMessage->getSourceRange();
} else {
FirstLoc = FirstSA->getBeginLoc();
FirstRange = FirstSA->getSourceRange();
}
if (SecondMessage) {
SecondLoc = SecondMessage->getBeginLoc();
SecondRange = SecondMessage->getSourceRange();
} else {
SecondLoc = SecondSA->getBeginLoc();
SecondRange = SecondSA->getSourceRange();
}
DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
<< (FirstMessage == nullptr);
DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
<< (SecondMessage == nullptr);
return true;
}
if (FirstMessage && SecondMessage) {
unsigned FirstMessageODRHash = computeODRHash(FirstMessage);
unsigned SecondMessageODRHash = computeODRHash(SecondMessage);
if (FirstMessageODRHash != SecondMessageODRHash) {
DiagError(FirstMessage->getBeginLoc(), FirstMessage->getSourceRange(),
StaticAssertMessage);
DiagNote(SecondMessage->getBeginLoc(), SecondMessage->getSourceRange(),
StaticAssertMessage);
return true;
}
}
break;
}
case Field: {
if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule,
cast<FieldDecl>(FirstDecl),
cast<FieldDecl>(SecondDecl)))
return true;
break;
}
case CXXMethod: {
enum {
DiagMethod,
DiagConstructor,
DiagDestructor,
} FirstMethodType,
SecondMethodType;
auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl *D) {
if (isa<CXXConstructorDecl>(D))
return DiagConstructor;
if (isa<CXXDestructorDecl>(D))
return DiagDestructor;
return DiagMethod;
};
const CXXMethodDecl *FirstMethod = cast<CXXMethodDecl>(FirstDecl);
const CXXMethodDecl *SecondMethod = cast<CXXMethodDecl>(SecondDecl);
FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod);
SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod);
DeclarationName FirstName = FirstMethod->getDeclName();
DeclarationName SecondName = SecondMethod->getDeclName();
auto DiagMethodError = [&DiagError, FirstMethod, FirstMethodType,
FirstName](ODRCXXRecordDifference DiffType) {
return DiagError(FirstMethod->getLocation(),
FirstMethod->getSourceRange(), DiffType)
<< FirstMethodType << FirstName;
};
auto DiagMethodNote = [&DiagNote, SecondMethod, SecondMethodType,
SecondName](ODRCXXRecordDifference DiffType) {
return DiagNote(SecondMethod->getLocation(),
SecondMethod->getSourceRange(), DiffType)
<< SecondMethodType << SecondName;
};
if (FirstMethodType != SecondMethodType || FirstName != SecondName) {
DiagMethodError(MethodName);
DiagMethodNote(MethodName);
return true;
}
const bool FirstDeleted = FirstMethod->isDeletedAsWritten();
const bool SecondDeleted = SecondMethod->isDeletedAsWritten();
if (FirstDeleted != SecondDeleted) {
DiagMethodError(MethodDeleted) << FirstDeleted;
DiagMethodNote(MethodDeleted) << SecondDeleted;
return true;
}
const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted();
const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted();
if (FirstDefaulted != SecondDefaulted) {
DiagMethodError(MethodDefaulted) << FirstDefaulted;
DiagMethodNote(MethodDefaulted) << SecondDefaulted;
return true;
}
const bool FirstVirtual = FirstMethod->isVirtualAsWritten();
const bool SecondVirtual = SecondMethod->isVirtualAsWritten();
const bool FirstPure = FirstMethod->isPureVirtual();
const bool SecondPure = SecondMethod->isPureVirtual();
if ((FirstVirtual || SecondVirtual) &&
(FirstVirtual != SecondVirtual || FirstPure != SecondPure)) {
DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual;
DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual;
return true;
}
// CXXMethodDecl::isStatic uses the canonical Decl. With Decl merging,
// FirstDecl is the canonical Decl of SecondDecl, so the storage
// class needs to be checked instead.
StorageClass FirstStorage = FirstMethod->getStorageClass();
StorageClass SecondStorage = SecondMethod->getStorageClass();
const bool FirstStatic = FirstStorage == SC_Static;
const bool SecondStatic = SecondStorage == SC_Static;
if (FirstStatic != SecondStatic) {
DiagMethodError(MethodStatic) << FirstStatic;
DiagMethodNote(MethodStatic) << SecondStatic;
return true;
}
const bool FirstVolatile = FirstMethod->isVolatile();
const bool SecondVolatile = SecondMethod->isVolatile();
if (FirstVolatile != SecondVolatile) {
DiagMethodError(MethodVolatile) << FirstVolatile;
DiagMethodNote(MethodVolatile) << SecondVolatile;
return true;
}
const bool FirstConst = FirstMethod->isConst();
const bool SecondConst = SecondMethod->isConst();
if (FirstConst != SecondConst) {
DiagMethodError(MethodConst) << FirstConst;
DiagMethodNote(MethodConst) << SecondConst;
return true;
}
const bool FirstInline = FirstMethod->isInlineSpecified();
const bool SecondInline = SecondMethod->isInlineSpecified();
if (FirstInline != SecondInline) {
DiagMethodError(MethodInline) << FirstInline;
DiagMethodNote(MethodInline) << SecondInline;
return true;
}
if (diagnoseSubMismatchMethodParameters(Diags, FirstRecord,
FirstModule, SecondModule,
FirstMethod, SecondMethod))
return true;
for (unsigned I = 0, N = FirstMethod->param_size(); I < N; ++I) {
const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I);
const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I);
const Expr *FirstInit = FirstParam->getInit();
const Expr *SecondInit = SecondParam->getInit();
if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
DiagMethodError(MethodParameterSingleDefaultArgument)
<< (I + 1) << (FirstInit == nullptr)
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange());
DiagMethodNote(MethodParameterSingleDefaultArgument)
<< (I + 1) << (SecondInit == nullptr)
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange());
return true;
}
if (FirstInit && SecondInit &&
computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
DiagMethodError(MethodParameterDifferentDefaultArgument)
<< (I + 1) << FirstInit->getSourceRange();
DiagMethodNote(MethodParameterDifferentDefaultArgument)
<< (I + 1) << SecondInit->getSourceRange();
return true;
}
}
const TemplateArgumentList *FirstTemplateArgs =
FirstMethod->getTemplateSpecializationArgs();
const TemplateArgumentList *SecondTemplateArgs =
SecondMethod->getTemplateSpecializationArgs();
if ((FirstTemplateArgs && !SecondTemplateArgs) ||
(!FirstTemplateArgs && SecondTemplateArgs)) {
DiagMethodError(MethodNoTemplateArguments)
<< (FirstTemplateArgs != nullptr);
DiagMethodNote(MethodNoTemplateArguments)
<< (SecondTemplateArgs != nullptr);
return true;
}
if (FirstTemplateArgs && SecondTemplateArgs) {
// Remove pack expansions from argument list.
auto ExpandTemplateArgumentList = [](const TemplateArgumentList *TAL) {
llvm::SmallVector<const TemplateArgument *, 8> ExpandedList;
for (const TemplateArgument &TA : TAL->asArray()) {
if (TA.getKind() != TemplateArgument::Pack) {
ExpandedList.push_back(&TA);
continue;
}
llvm::append_range(ExpandedList,
llvm::make_pointer_range(TA.getPackAsArray()));
}
return ExpandedList;
};
llvm::SmallVector<const TemplateArgument *, 8> FirstExpandedList =
ExpandTemplateArgumentList(FirstTemplateArgs);
llvm::SmallVector<const TemplateArgument *, 8> SecondExpandedList =
ExpandTemplateArgumentList(SecondTemplateArgs);
if (FirstExpandedList.size() != SecondExpandedList.size()) {
DiagMethodError(MethodDifferentNumberTemplateArguments)
<< (unsigned)FirstExpandedList.size();
DiagMethodNote(MethodDifferentNumberTemplateArguments)
<< (unsigned)SecondExpandedList.size();
return true;
}
for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) {
const TemplateArgument &FirstTA = *FirstExpandedList[i],
&SecondTA = *SecondExpandedList[i];
if (computeODRHash(FirstTA) == computeODRHash(SecondTA))
continue;
DiagMethodError(MethodDifferentTemplateArgument) << FirstTA << i + 1;
DiagMethodNote(MethodDifferentTemplateArgument) << SecondTA << i + 1;
return true;
}
}
// Compute the hash of the method as if it has no body.
auto ComputeCXXMethodODRHash = [](const CXXMethodDecl *D) {
ODRHash Hasher;
Hasher.AddFunctionDecl(D, true /*SkipBody*/);
return Hasher.CalculateHash();
};
// Compare the hash generated to the hash stored. A difference means
// that a body was present in the original source. Due to merging,
// the standard way of detecting a body will not work.
const bool HasFirstBody =
ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash();
const bool HasSecondBody =
ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash();
if (HasFirstBody != HasSecondBody) {
DiagMethodError(MethodSingleBody) << HasFirstBody;
DiagMethodNote(MethodSingleBody) << HasSecondBody;
return true;
}
if (HasFirstBody && HasSecondBody) {
DiagMethodError(MethodDifferentBody);
DiagMethodNote(MethodDifferentBody);
return true;
}
break;
}
case TypeAlias:
case TypeDef: {
if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule,
cast<TypedefNameDecl>(FirstDecl),
cast<TypedefNameDecl>(SecondDecl),
FirstDiffType == TypeAlias))
return true;
break;
}
case Var: {
if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule,
cast<VarDecl>(FirstDecl),
cast<VarDecl>(SecondDecl)))
return true;
break;
}
case Friend: {
const FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl);
const FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl);
const NamedDecl *FirstND = FirstFriend->getFriendDecl();
const NamedDecl *SecondND = SecondFriend->getFriendDecl();
TypeSourceInfo *FirstTSI = FirstFriend->getFriendType();
TypeSourceInfo *SecondTSI = SecondFriend->getFriendType();
if (FirstND && SecondND) {
DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(),
FriendFunction)
<< FirstND;
DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(),
FriendFunction)
<< SecondND;
return true;
}
if (FirstTSI && SecondTSI) {
QualType FirstFriendType = FirstTSI->getType();
QualType SecondFriendType = SecondTSI->getType();
assert(computeODRHash(FirstFriendType) !=
computeODRHash(SecondFriendType));
DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(),
FriendType)
<< FirstFriendType;
DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(),
FriendType)
<< SecondFriendType;
return true;
}
DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(),
FriendTypeFunction)
<< (FirstTSI == nullptr);
DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(),
FriendTypeFunction)
<< (SecondTSI == nullptr);
return true;
}
case FunctionTemplate: {
const FunctionTemplateDecl *FirstTemplate =
cast<FunctionTemplateDecl>(FirstDecl);
const FunctionTemplateDecl *SecondTemplate =
cast<FunctionTemplateDecl>(SecondDecl);
TemplateParameterList *FirstTPL = FirstTemplate->getTemplateParameters();
TemplateParameterList *SecondTPL = SecondTemplate->getTemplateParameters();
auto DiagTemplateError = [&DiagError,
FirstTemplate](ODRCXXRecordDifference DiffType) {
return DiagError(FirstTemplate->getLocation(),
FirstTemplate->getSourceRange(), DiffType)
<< FirstTemplate;
};
auto DiagTemplateNote = [&DiagNote,
SecondTemplate](ODRCXXRecordDifference DiffType) {
return DiagNote(SecondTemplate->getLocation(),
SecondTemplate->getSourceRange(), DiffType)
<< SecondTemplate;
};
if (FirstTPL->size() != SecondTPL->size()) {
DiagTemplateError(FunctionTemplateDifferentNumberParameters)
<< FirstTPL->size();
DiagTemplateNote(FunctionTemplateDifferentNumberParameters)
<< SecondTPL->size();
return true;
}
for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) {
NamedDecl *FirstParam = FirstTPL->getParam(i);
NamedDecl *SecondParam = SecondTPL->getParam(i);
if (FirstParam->getKind() != SecondParam->getKind()) {
enum {
TemplateTypeParameter,
NonTypeTemplateParameter,
TemplateTemplateParameter,
};
auto GetParamType = [](NamedDecl *D) {
switch (D->getKind()) {
default:
llvm_unreachable("Unexpected template parameter type");
case Decl::TemplateTypeParm:
return TemplateTypeParameter;
case Decl::NonTypeTemplateParm:
return NonTypeTemplateParameter;
case Decl::TemplateTemplateParm:
return TemplateTemplateParameter;
}
};
DiagTemplateError(FunctionTemplateParameterDifferentKind)
<< (i + 1) << GetParamType(FirstParam);
DiagTemplateNote(FunctionTemplateParameterDifferentKind)
<< (i + 1) << GetParamType(SecondParam);
return true;
}
if (FirstParam->getName() != SecondParam->getName()) {
DiagTemplateError(FunctionTemplateParameterName)
<< (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam;
DiagTemplateNote(FunctionTemplateParameterName)
<< (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam;
return true;
}
if (isa<TemplateTypeParmDecl>(FirstParam) &&
isa<TemplateTypeParmDecl>(SecondParam)) {
TemplateTypeParmDecl *FirstTTPD =
cast<TemplateTypeParmDecl>(FirstParam);
TemplateTypeParmDecl *SecondTTPD =
cast<TemplateTypeParmDecl>(SecondParam);
bool HasFirstDefaultArgument =
FirstTTPD->hasDefaultArgument() &&
!FirstTTPD->defaultArgumentWasInherited();
bool HasSecondDefaultArgument =
SecondTTPD->hasDefaultArgument() &&
!SecondTTPD->defaultArgumentWasInherited();
if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument)
<< (i + 1) << HasFirstDefaultArgument;
DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument)
<< (i + 1) << HasSecondDefaultArgument;
return true;
}
if (HasFirstDefaultArgument && HasSecondDefaultArgument) {
TemplateArgument FirstTA =
FirstTTPD->getDefaultArgument().getArgument();
TemplateArgument SecondTA =
SecondTTPD->getDefaultArgument().getArgument();
if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) {
DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument)
<< (i + 1) << FirstTA;
DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument)
<< (i + 1) << SecondTA;
return true;
}
}
if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) {
DiagTemplateError(FunctionTemplatePackParameter)
<< (i + 1) << FirstTTPD->isParameterPack();
DiagTemplateNote(FunctionTemplatePackParameter)
<< (i + 1) << SecondTTPD->isParameterPack();
return true;
}
}
if (isa<TemplateTemplateParmDecl>(FirstParam) &&
isa<TemplateTemplateParmDecl>(SecondParam)) {
TemplateTemplateParmDecl *FirstTTPD =
cast<TemplateTemplateParmDecl>(FirstParam);
TemplateTemplateParmDecl *SecondTTPD =
cast<TemplateTemplateParmDecl>(SecondParam);
TemplateParameterList *FirstTPL = FirstTTPD->getTemplateParameters();
TemplateParameterList *SecondTPL = SecondTTPD->getTemplateParameters();
auto ComputeTemplateParameterListODRHash =
[](const TemplateParameterList *TPL) {
assert(TPL);
ODRHash Hasher;
Hasher.AddTemplateParameterList(TPL);
return Hasher.CalculateHash();
};
if (ComputeTemplateParameterListODRHash(FirstTPL) !=
ComputeTemplateParameterListODRHash(SecondTPL)) {
DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1);
DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1);
return true;
}
bool HasFirstDefaultArgument =
FirstTTPD->hasDefaultArgument() &&
!FirstTTPD->defaultArgumentWasInherited();
bool HasSecondDefaultArgument =
SecondTTPD->hasDefaultArgument() &&
!SecondTTPD->defaultArgumentWasInherited();
if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument)
<< (i + 1) << HasFirstDefaultArgument;
DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument)
<< (i + 1) << HasSecondDefaultArgument;
return true;
}
if (HasFirstDefaultArgument && HasSecondDefaultArgument) {
TemplateArgument FirstTA =
FirstTTPD->getDefaultArgument().getArgument();
TemplateArgument SecondTA =
SecondTTPD->getDefaultArgument().getArgument();
if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) {
DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument)
<< (i + 1) << FirstTA;
DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument)
<< (i + 1) << SecondTA;
return true;
}
}
if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) {
DiagTemplateError(FunctionTemplatePackParameter)
<< (i + 1) << FirstTTPD->isParameterPack();
DiagTemplateNote(FunctionTemplatePackParameter)
<< (i + 1) << SecondTTPD->isParameterPack();
return true;
}
}
if (isa<NonTypeTemplateParmDecl>(FirstParam) &&
isa<NonTypeTemplateParmDecl>(SecondParam)) {
NonTypeTemplateParmDecl *FirstNTTPD =
cast<NonTypeTemplateParmDecl>(FirstParam);
NonTypeTemplateParmDecl *SecondNTTPD =
cast<NonTypeTemplateParmDecl>(SecondParam);
QualType FirstType = FirstNTTPD->getType();
QualType SecondType = SecondNTTPD->getType();
if (computeODRHash(FirstType) != computeODRHash(SecondType)) {
DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1);
DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1);
return true;
}
bool HasFirstDefaultArgument =
FirstNTTPD->hasDefaultArgument() &&
!FirstNTTPD->defaultArgumentWasInherited();
bool HasSecondDefaultArgument =
SecondNTTPD->hasDefaultArgument() &&
!SecondNTTPD->defaultArgumentWasInherited();
if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument)
<< (i + 1) << HasFirstDefaultArgument;
DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument)
<< (i + 1) << HasSecondDefaultArgument;
return true;
}
if (HasFirstDefaultArgument && HasSecondDefaultArgument) {
TemplateArgument FirstDefaultArgument =
FirstNTTPD->getDefaultArgument().getArgument();
TemplateArgument SecondDefaultArgument =
SecondNTTPD->getDefaultArgument().getArgument();
if (computeODRHash(FirstDefaultArgument) !=
computeODRHash(SecondDefaultArgument)) {
DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument)
<< (i + 1) << FirstDefaultArgument;
DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument)
<< (i + 1) << SecondDefaultArgument;
return true;
}
}
if (FirstNTTPD->isParameterPack() != SecondNTTPD->isParameterPack()) {
DiagTemplateError(FunctionTemplatePackParameter)
<< (i + 1) << FirstNTTPD->isParameterPack();
DiagTemplateNote(FunctionTemplatePackParameter)
<< (i + 1) << SecondNTTPD->isParameterPack();
return true;
}
}
}
break;
}
}
Diag(FirstDecl->getLocation(),
diag::err_module_odr_violation_mismatch_decl_unknown)
<< FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType
<< FirstDecl->getSourceRange();
Diag(SecondDecl->getLocation(),
diag::note_module_odr_violation_mismatch_decl_unknown)
<< SecondModule.empty() << SecondModule << FirstDiffType
<< SecondDecl->getSourceRange();
return true;
}
bool ODRDiagsEmitter::diagnoseMismatch(const RecordDecl *FirstRecord,
const RecordDecl *SecondRecord) const {
if (FirstRecord == SecondRecord)
return false;
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord);
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord);
auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record,
const DeclContext *DC) {
for (const Decl *D : Record->decls()) {
if (!ODRHash::isSubDeclToBeProcessed(D, DC))
continue;
Hashes.emplace_back(D, computeODRHash(D));
}
};
DeclHashes FirstHashes;
DeclHashes SecondHashes;
const DeclContext *DC = FirstRecord;
PopulateHashes(FirstHashes, FirstRecord, DC);
PopulateHashes(SecondHashes, SecondRecord, DC);
DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
const Decl *FirstDecl = DR.FirstDecl;
const Decl *SecondDecl = DR.SecondDecl;
if (FirstDiffType == Other || SecondDiffType == Other) {
diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord,
SecondModule);
return true;
}
if (FirstDiffType != SecondDiffType) {
diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule,
SecondRecord, SecondModule);
return true;
}
assert(FirstDiffType == SecondDiffType);
switch (FirstDiffType) {
// Already handled.
case EndOfClass:
case Other:
// C++ only, invalid in this context.
case PublicSpecifer:
case PrivateSpecifer:
case ProtectedSpecifer:
case StaticAssert:
case CXXMethod:
case TypeAlias:
case Friend:
case FunctionTemplate:
// Cannot be contained by RecordDecl, invalid in this context.
case ObjCMethod:
case ObjCIvar:
case ObjCProperty:
llvm_unreachable("Invalid diff type");
case Field: {
if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule,
cast<FieldDecl>(FirstDecl),
cast<FieldDecl>(SecondDecl)))
return true;
break;
}
case TypeDef: {
if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule,
cast<TypedefNameDecl>(FirstDecl),
cast<TypedefNameDecl>(SecondDecl),
/*IsTypeAlias=*/false))
return true;
break;
}
case Var: {
if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule,
cast<VarDecl>(FirstDecl),
cast<VarDecl>(SecondDecl)))
return true;
break;
}
}
Diag(FirstDecl->getLocation(),
diag::err_module_odr_violation_mismatch_decl_unknown)
<< FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType
<< FirstDecl->getSourceRange();
Diag(SecondDecl->getLocation(),
diag::note_module_odr_violation_mismatch_decl_unknown)
<< SecondModule.empty() << SecondModule << FirstDiffType
<< SecondDecl->getSourceRange();
return true;
}
bool ODRDiagsEmitter::diagnoseMismatch(
const FunctionDecl *FirstFunction,
const FunctionDecl *SecondFunction) const {
if (FirstFunction == SecondFunction)
return false;
// Keep in sync with select options in err_module_odr_violation_function.
enum ODRFunctionDifference {
ReturnType,
ParameterName,
ParameterType,
ParameterSingleDefaultArgument,
ParameterDifferentDefaultArgument,
FunctionBody,
};
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction);
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondFunction);
auto DiagError = [FirstFunction, &FirstModule,
this](SourceLocation Loc, SourceRange Range,
ODRFunctionDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_function)
<< FirstFunction << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
ODRFunctionDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_function)
<< SecondModule << Range << DiffType;
};
if (computeODRHash(FirstFunction->getReturnType()) !=
computeODRHash(SecondFunction->getReturnType())) {
DiagError(FirstFunction->getReturnTypeSourceRange().getBegin(),
FirstFunction->getReturnTypeSourceRange(), ReturnType)
<< FirstFunction->getReturnType();
DiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(),
SecondFunction->getReturnTypeSourceRange(), ReturnType)
<< SecondFunction->getReturnType();
return true;
}
assert(FirstFunction->param_size() == SecondFunction->param_size() &&
"Merged functions with different number of parameters");
size_t ParamSize = FirstFunction->param_size();
for (unsigned I = 0; I < ParamSize; ++I) {
const ParmVarDecl *FirstParam = FirstFunction->getParamDecl(I);
const ParmVarDecl *SecondParam = SecondFunction->getParamDecl(I);
assert(Context.hasSameType(FirstParam->getType(), SecondParam->getType()) &&
"Merged function has different parameter types.");
if (FirstParam->getDeclName() != SecondParam->getDeclName()) {
DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
ParameterName)
<< I + 1 << FirstParam->getDeclName();
DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
ParameterName)
<< I + 1 << SecondParam->getDeclName();
return true;
};
QualType FirstParamType = FirstParam->getType();
QualType SecondParamType = SecondParam->getType();
if (FirstParamType != SecondParamType &&
computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) {
if (const DecayedType *ParamDecayedType =
FirstParamType->getAs<DecayedType>()) {
DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
ParameterType)
<< (I + 1) << FirstParamType << true
<< ParamDecayedType->getOriginalType();
} else {
DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
ParameterType)
<< (I + 1) << FirstParamType << false;
}
if (const DecayedType *ParamDecayedType =
SecondParamType->getAs<DecayedType>()) {
DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
ParameterType)
<< (I + 1) << SecondParamType << true
<< ParamDecayedType->getOriginalType();
} else {
DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
ParameterType)
<< (I + 1) << SecondParamType << false;
}
return true;
}
// Note, these calls can trigger deserialization.
const Expr *FirstInit = FirstParam->getInit();
const Expr *SecondInit = SecondParam->getInit();
if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
ParameterSingleDefaultArgument)
<< (I + 1) << (FirstInit == nullptr)
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange());
DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
ParameterSingleDefaultArgument)
<< (I + 1) << (SecondInit == nullptr)
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange());
return true;
}
if (FirstInit && SecondInit &&
computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(),
ParameterDifferentDefaultArgument)
<< (I + 1) << FirstInit->getSourceRange();
DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(),
ParameterDifferentDefaultArgument)
<< (I + 1) << SecondInit->getSourceRange();
return true;
}
assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) &&
"Undiagnosed parameter difference.");
}
// If no error has been generated before now, assume the problem is in
// the body and generate a message.
DiagError(FirstFunction->getLocation(), FirstFunction->getSourceRange(),
FunctionBody);
DiagNote(SecondFunction->getLocation(), SecondFunction->getSourceRange(),
FunctionBody);
return true;
}
bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum,
const EnumDecl *SecondEnum) const {
if (FirstEnum == SecondEnum)
return false;
// Keep in sync with select options in err_module_odr_violation_enum.
enum ODREnumDifference {
SingleScopedEnum,
EnumTagKeywordMismatch,
SingleSpecifiedType,
DifferentSpecifiedTypes,
DifferentNumberEnumConstants,
EnumConstantName,
EnumConstantSingleInitializer,
EnumConstantDifferentInitializer,
};
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum);
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondEnum);
auto DiagError = [FirstEnum, &FirstModule, this](const auto *DiagAnchor,
ODREnumDifference DiffType) {
return Diag(DiagAnchor->getLocation(), diag::err_module_odr_violation_enum)
<< FirstEnum << FirstModule.empty() << FirstModule
<< DiagAnchor->getSourceRange() << DiffType;
};
auto DiagNote = [&SecondModule, this](const auto *DiagAnchor,
ODREnumDifference DiffType) {
return Diag(DiagAnchor->getLocation(), diag::note_module_odr_violation_enum)
<< SecondModule << DiagAnchor->getSourceRange() << DiffType;
};
if (FirstEnum->isScoped() != SecondEnum->isScoped()) {
DiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped();
DiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped();
return true;
}
if (FirstEnum->isScoped() && SecondEnum->isScoped()) {
if (FirstEnum->isScopedUsingClassTag() !=
SecondEnum->isScopedUsingClassTag()) {
DiagError(FirstEnum, EnumTagKeywordMismatch)
<< FirstEnum->isScopedUsingClassTag();
DiagNote(SecondEnum, EnumTagKeywordMismatch)
<< SecondEnum->isScopedUsingClassTag();
return true;
}
}
QualType FirstUnderlyingType =
FirstEnum->getIntegerTypeSourceInfo()
? FirstEnum->getIntegerTypeSourceInfo()->getType()
: QualType();
QualType SecondUnderlyingType =
SecondEnum->getIntegerTypeSourceInfo()
? SecondEnum->getIntegerTypeSourceInfo()->getType()
: QualType();
if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) {
DiagError(FirstEnum, SingleSpecifiedType) << !FirstUnderlyingType.isNull();
DiagNote(SecondEnum, SingleSpecifiedType) << !SecondUnderlyingType.isNull();
return true;
}
if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) {
if (computeODRHash(FirstUnderlyingType) !=
computeODRHash(SecondUnderlyingType)) {
DiagError(FirstEnum, DifferentSpecifiedTypes) << FirstUnderlyingType;
DiagNote(SecondEnum, DifferentSpecifiedTypes) << SecondUnderlyingType;
return true;
}
}
// Compare enum constants.
using DeclHashes =
llvm::SmallVector<std::pair<const EnumConstantDecl *, unsigned>, 4>;
auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, const EnumDecl *Enum) {
for (const Decl *D : Enum->decls()) {
// Due to decl merging, the first EnumDecl is the parent of
// Decls in both records.
if (!ODRHash::isSubDeclToBeProcessed(D, FirstEnum))
continue;
assert(isa<EnumConstantDecl>(D) && "Unexpected Decl kind");
Hashes.emplace_back(cast<EnumConstantDecl>(D), computeODRHash(D));
}
};
DeclHashes FirstHashes;
PopulateHashes(FirstHashes, FirstEnum);
DeclHashes SecondHashes;
PopulateHashes(SecondHashes, SecondEnum);
if (FirstHashes.size() != SecondHashes.size()) {
DiagError(FirstEnum, DifferentNumberEnumConstants)
<< (int)FirstHashes.size();
DiagNote(SecondEnum, DifferentNumberEnumConstants)
<< (int)SecondHashes.size();
return true;
}
for (unsigned I = 0, N = FirstHashes.size(); I < N; ++I) {
if (FirstHashes[I].second == SecondHashes[I].second)
continue;
const EnumConstantDecl *FirstConstant = FirstHashes[I].first;
const EnumConstantDecl *SecondConstant = SecondHashes[I].first;
if (FirstConstant->getDeclName() != SecondConstant->getDeclName()) {
DiagError(FirstConstant, EnumConstantName) << I + 1 << FirstConstant;
DiagNote(SecondConstant, EnumConstantName) << I + 1 << SecondConstant;
return true;
}
const Expr *FirstInit = FirstConstant->getInitExpr();
const Expr *SecondInit = SecondConstant->getInitExpr();
if (!FirstInit && !SecondInit)
continue;
if (!FirstInit || !SecondInit) {
DiagError(FirstConstant, EnumConstantSingleInitializer)
<< I + 1 << FirstConstant << (FirstInit != nullptr);
DiagNote(SecondConstant, EnumConstantSingleInitializer)
<< I + 1 << SecondConstant << (SecondInit != nullptr);
return true;
}
if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) {
DiagError(FirstConstant, EnumConstantDifferentInitializer)
<< I + 1 << FirstConstant;
DiagNote(SecondConstant, EnumConstantDifferentInitializer)
<< I + 1 << SecondConstant;
return true;
}
}
return false;
}
bool ODRDiagsEmitter::diagnoseMismatch(
const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID,
const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const {
// Multiple different declarations got merged together; tell the user
// where they came from.
if (FirstID == SecondID)
return false;
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstID);
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondID);
// Keep in sync with err_module_odr_violation_objc_interface.
enum ODRInterfaceDifference {
SuperClassType,
IVarAccess,
};
auto DiagError = [FirstID, &FirstModule,
this](SourceLocation Loc, SourceRange Range,
ODRInterfaceDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_objc_interface)
<< FirstID << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
ODRInterfaceDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_objc_interface)
<< SecondModule.empty() << SecondModule << Range << DiffType;
};
const struct ObjCInterfaceDecl::DefinitionData *FirstDD = &FirstID->data();
assert(FirstDD && SecondDD && "Definitions without DefinitionData");
if (FirstDD != SecondDD) {
// Check for matching super class.
auto GetSuperClassSourceRange = [](const TypeSourceInfo *SuperInfo,
const ObjCInterfaceDecl *ID) {
if (!SuperInfo)
return ID->getSourceRange();
TypeLoc Loc = SuperInfo->getTypeLoc();
return SourceRange(Loc.getBeginLoc(), Loc.getEndLoc());
};
ObjCInterfaceDecl *FirstSuperClass = FirstID->getSuperClass();
ObjCInterfaceDecl *SecondSuperClass = nullptr;
const TypeSourceInfo *FirstSuperInfo = FirstID->getSuperClassTInfo();
const TypeSourceInfo *SecondSuperInfo = SecondDD->SuperClassTInfo;
if (SecondSuperInfo)
SecondSuperClass =
SecondSuperInfo->getType()->castAs<ObjCObjectType>()->getInterface();
if ((FirstSuperClass && SecondSuperClass &&
FirstSuperClass->getODRHash() != SecondSuperClass->getODRHash()) ||
(FirstSuperClass && !SecondSuperClass) ||
(!FirstSuperClass && SecondSuperClass)) {
QualType FirstType;
if (FirstSuperInfo)
FirstType = FirstSuperInfo->getType();
DiagError(FirstID->getLocation(),
GetSuperClassSourceRange(FirstSuperInfo, FirstID),
SuperClassType)
<< (bool)FirstSuperInfo << FirstType;
QualType SecondType;
if (SecondSuperInfo)
SecondType = SecondSuperInfo->getType();
DiagNote(SecondID->getLocation(),
GetSuperClassSourceRange(SecondSuperInfo, SecondID),
SuperClassType)
<< (bool)SecondSuperInfo << SecondType;
return true;
}
// Check both interfaces reference the same protocols.
auto &FirstProtos = FirstID->getReferencedProtocols();
auto &SecondProtos = SecondDD->ReferencedProtocols;
if (diagnoseSubMismatchProtocols(FirstProtos, FirstID, FirstModule,
SecondProtos, SecondID, SecondModule))
return true;
}
auto PopulateHashes = [](DeclHashes &Hashes, const ObjCInterfaceDecl *ID,
const DeclContext *DC) {
for (auto *D : ID->decls()) {
if (!ODRHash::isSubDeclToBeProcessed(D, DC))
continue;
Hashes.emplace_back(D, computeODRHash(D));
}
};
DeclHashes FirstHashes;
DeclHashes SecondHashes;
// Use definition as DeclContext because definitions are merged when
// DeclContexts are merged and separate when DeclContexts are separate.
PopulateHashes(FirstHashes, FirstID, FirstID->getDefinition());
PopulateHashes(SecondHashes, SecondID, SecondID->getDefinition());
DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
const Decl *FirstDecl = DR.FirstDecl;
const Decl *SecondDecl = DR.SecondDecl;
if (FirstDiffType == Other || SecondDiffType == Other) {
diagnoseSubMismatchUnexpected(DR, FirstID, FirstModule, SecondID,
SecondModule);
return true;
}
if (FirstDiffType != SecondDiffType) {
diagnoseSubMismatchDifferentDeclKinds(DR, FirstID, FirstModule, SecondID,
SecondModule);
return true;
}
assert(FirstDiffType == SecondDiffType);
switch (FirstDiffType) {
// Already handled.
case EndOfClass:
case Other:
// Cannot be contained by ObjCInterfaceDecl, invalid in this context.
case Field:
case TypeDef:
case Var:
// C++ only, invalid in this context.
case PublicSpecifer:
case PrivateSpecifer:
case ProtectedSpecifer:
case StaticAssert:
case CXXMethod:
case TypeAlias:
case Friend:
case FunctionTemplate:
llvm_unreachable("Invalid diff type");
case ObjCMethod: {
if (diagnoseSubMismatchObjCMethod(FirstID, FirstModule, SecondModule,
cast<ObjCMethodDecl>(FirstDecl),
cast<ObjCMethodDecl>(SecondDecl)))
return true;
break;
}
case ObjCIvar: {
if (diagnoseSubMismatchField(FirstID, FirstModule, SecondModule,
cast<FieldDecl>(FirstDecl),
cast<FieldDecl>(SecondDecl)))
return true;
// Check if the access match.
const ObjCIvarDecl *FirstIvar = cast<ObjCIvarDecl>(FirstDecl);
const ObjCIvarDecl *SecondIvar = cast<ObjCIvarDecl>(SecondDecl);
if (FirstIvar->getCanonicalAccessControl() !=
SecondIvar->getCanonicalAccessControl()) {
DiagError(FirstIvar->getLocation(), FirstIvar->getSourceRange(),
IVarAccess)
<< FirstIvar->getName()
<< (int)FirstIvar->getCanonicalAccessControl();
DiagNote(SecondIvar->getLocation(), SecondIvar->getSourceRange(),
IVarAccess)
<< SecondIvar->getName()
<< (int)SecondIvar->getCanonicalAccessControl();
return true;
}
break;
}
case ObjCProperty: {
if (diagnoseSubMismatchObjCProperty(FirstID, FirstModule, SecondModule,
cast<ObjCPropertyDecl>(FirstDecl),
cast<ObjCPropertyDecl>(SecondDecl)))
return true;
break;
}
}
Diag(FirstDecl->getLocation(),
diag::err_module_odr_violation_mismatch_decl_unknown)
<< FirstID << FirstModule.empty() << FirstModule << FirstDiffType
<< FirstDecl->getSourceRange();
Diag(SecondDecl->getLocation(),
diag::note_module_odr_violation_mismatch_decl_unknown)
<< SecondModule.empty() << SecondModule << FirstDiffType
<< SecondDecl->getSourceRange();
return true;
}
bool ODRDiagsEmitter::diagnoseMismatch(
const ObjCProtocolDecl *FirstProtocol,
const ObjCProtocolDecl *SecondProtocol,
const struct ObjCProtocolDecl::DefinitionData *SecondDD) const {
if (FirstProtocol == SecondProtocol)
return false;
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstProtocol);
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondProtocol);
const ObjCProtocolDecl::DefinitionData *FirstDD = &FirstProtocol->data();
assert(FirstDD && SecondDD && "Definitions without DefinitionData");
// Diagnostics from ObjCProtocol DefinitionData are emitted here.
if (FirstDD != SecondDD) {
// Check both protocols reference the same protocols.
const ObjCProtocolList &FirstProtocols =
FirstProtocol->getReferencedProtocols();
const ObjCProtocolList &SecondProtocols = SecondDD->ReferencedProtocols;
if (diagnoseSubMismatchProtocols(FirstProtocols, FirstProtocol, FirstModule,
SecondProtocols, SecondProtocol,
SecondModule))
return true;
}
auto PopulateHashes = [](DeclHashes &Hashes, const ObjCProtocolDecl *ID,
const DeclContext *DC) {
for (const Decl *D : ID->decls()) {
if (!ODRHash::isSubDeclToBeProcessed(D, DC))
continue;
Hashes.emplace_back(D, computeODRHash(D));
}
};
DeclHashes FirstHashes;
DeclHashes SecondHashes;
// Use definition as DeclContext because definitions are merged when
// DeclContexts are merged and separate when DeclContexts are separate.
PopulateHashes(FirstHashes, FirstProtocol, FirstProtocol->getDefinition());
PopulateHashes(SecondHashes, SecondProtocol, SecondProtocol->getDefinition());
DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
const Decl *FirstDecl = DR.FirstDecl;
const Decl *SecondDecl = DR.SecondDecl;
if (FirstDiffType == Other || SecondDiffType == Other) {
diagnoseSubMismatchUnexpected(DR, FirstProtocol, FirstModule,
SecondProtocol, SecondModule);
return true;
}
if (FirstDiffType != SecondDiffType) {
diagnoseSubMismatchDifferentDeclKinds(DR, FirstProtocol, FirstModule,
SecondProtocol, SecondModule);
return true;
}
assert(FirstDiffType == SecondDiffType);
switch (FirstDiffType) {
// Already handled.
case EndOfClass:
case Other:
// Cannot be contained by ObjCProtocolDecl, invalid in this context.
case Field:
case TypeDef:
case Var:
case ObjCIvar:
// C++ only, invalid in this context.
case PublicSpecifer:
case PrivateSpecifer:
case ProtectedSpecifer:
case StaticAssert:
case CXXMethod:
case TypeAlias:
case Friend:
case FunctionTemplate:
llvm_unreachable("Invalid diff type");
case ObjCMethod: {
if (diagnoseSubMismatchObjCMethod(FirstProtocol, FirstModule, SecondModule,
cast<ObjCMethodDecl>(FirstDecl),
cast<ObjCMethodDecl>(SecondDecl)))
return true;
break;
}
case ObjCProperty: {
if (diagnoseSubMismatchObjCProperty(FirstProtocol, FirstModule,
SecondModule,
cast<ObjCPropertyDecl>(FirstDecl),
cast<ObjCPropertyDecl>(SecondDecl)))
return true;
break;
}
}
Diag(FirstDecl->getLocation(),
diag::err_module_odr_violation_mismatch_decl_unknown)
<< FirstProtocol << FirstModule.empty() << FirstModule << FirstDiffType
<< FirstDecl->getSourceRange();
Diag(SecondDecl->getLocation(),
diag::note_module_odr_violation_mismatch_decl_unknown)
<< SecondModule.empty() << SecondModule << FirstDiffType
<< SecondDecl->getSourceRange();
return true;
}