blob: 5b1cdc16e2ea2cfbac2d21ff32910306ee930b89 [file] [log] [blame]
//===-- 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) {
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.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;
}