//===-- 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 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(D)) return DiagConstructor; if (isa(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()) { DiagError(ParameterType) << (I + 1) << FirstParamType << true << ParamDecayedType->getOriginalType(); } else { DiagError(ParameterType) << (I + 1) << FirstParamType << false; } if (const DecayedType *ParamDecayedType = SecondParamType->getAs()) { 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(Container)) Loc = Tag->getBraceRange().getEnd(); else if (auto *IF = dyn_cast(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 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 FirstBases = FirstDD->bases(); ArrayRef 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 FirstTemplateParams = FirstTemplate->getTemplateParameters()->asArray(); ArrayRef 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(D)) return TTP->hasDefaultArgument() && !TTP->defaultArgumentWasInherited(); if (auto *NTTP = dyn_cast(D)) return NTTP->hasDefaultArgument() && !NTTP->defaultArgumentWasInherited(); auto *TTP = cast(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(FirstDecl); const StaticAssertDecl *SecondSA = cast(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(FirstDecl), cast(SecondDecl))) return true; break; } case CXXMethod: { enum { DiagMethod, DiagConstructor, DiagDestructor, } FirstMethodType, SecondMethodType; auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl *D) { if (isa(D)) return DiagConstructor; if (isa(D)) return DiagDestructor; return DiagMethod; }; const CXXMethodDecl *FirstMethod = cast(FirstDecl); const CXXMethodDecl *SecondMethod = cast(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 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 FirstExpandedList = ExpandTemplateArgumentList(FirstTemplateArgs); llvm::SmallVector 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(FirstDecl), cast(SecondDecl), FirstDiffType == TypeAlias)) return true; break; } case Var: { if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule, cast(FirstDecl), cast(SecondDecl))) return true; break; } case Friend: { const FriendDecl *FirstFriend = cast(FirstDecl); const FriendDecl *SecondFriend = cast(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(FirstDecl); const FunctionTemplateDecl *SecondTemplate = cast(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(FirstParam) && isa(SecondParam)) { TemplateTypeParmDecl *FirstTTPD = cast(FirstParam); TemplateTypeParmDecl *SecondTTPD = cast(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(FirstParam) && isa(SecondParam)) { TemplateTemplateParmDecl *FirstTTPD = cast(FirstParam); TemplateTemplateParmDecl *SecondTTPD = cast(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(FirstParam) && isa(SecondParam)) { NonTypeTemplateParmDecl *FirstNTTPD = cast(FirstParam); NonTypeTemplateParmDecl *SecondNTTPD = cast(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(FirstDecl), cast(SecondDecl))) return true; break; } case TypeDef: { if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule, cast(FirstDecl), cast(SecondDecl), /*IsTypeAlias=*/false)) return true; break; } case Var: { if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule, cast(FirstDecl), cast(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()) { 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()) { 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, 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(D) && "Unexpected Decl kind"); Hashes.emplace_back(cast(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()->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(FirstDecl), cast(SecondDecl))) return true; break; } case ObjCIvar: { if (diagnoseSubMismatchField(FirstID, FirstModule, SecondModule, cast(FirstDecl), cast(SecondDecl))) return true; // Check if the access match. const ObjCIvarDecl *FirstIvar = cast(FirstDecl); const ObjCIvarDecl *SecondIvar = cast(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(FirstDecl), cast(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(FirstDecl), cast(SecondDecl))) return true; break; } case ObjCProperty: { if (diagnoseSubMismatchObjCProperty(FirstProtocol, FirstModule, SecondModule, cast(FirstDecl), cast(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; }