mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 04:36:06 +00:00

To avoid any possible confusion with the notion of pure function and the gnu::pure attribute.
2214 lines
83 KiB
C++
2214 lines
83 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) {
|
|
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;
|
|
}
|