| //===-- 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) << FirstMethod->getImplementationControl(); |
| DiagNote(ControlLevel) << 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 StringLiteral *FirstStr = FirstSA->getMessage(); |
| const StringLiteral *SecondStr = SecondSA->getMessage(); |
| assert((FirstStr || SecondStr) && "Both messages cannot be empty"); |
| if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { |
| SourceLocation FirstLoc, SecondLoc; |
| SourceRange FirstRange, SecondRange; |
| if (FirstStr) { |
| FirstLoc = FirstStr->getBeginLoc(); |
| FirstRange = FirstStr->getSourceRange(); |
| } else { |
| FirstLoc = FirstSA->getBeginLoc(); |
| FirstRange = FirstSA->getSourceRange(); |
| } |
| if (SecondStr) { |
| SecondLoc = SecondStr->getBeginLoc(); |
| SecondRange = SecondStr->getSourceRange(); |
| } else { |
| SecondLoc = SecondSA->getBeginLoc(); |
| SecondRange = SecondSA->getSourceRange(); |
| } |
| DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage) |
| << (FirstStr == nullptr); |
| DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) |
| << (SecondStr == nullptr); |
| return true; |
| } |
| |
| if (FirstStr && SecondStr && |
| FirstStr->getString() != SecondStr->getString()) { |
| DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), |
| StaticAssertMessage); |
| DiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), |
| StaticAssertMessage); |
| return true; |
| } |
| break; |
| } |
| |
| case Field: { |
| if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule, |
| cast<FieldDecl>(FirstDecl), |
| cast<FieldDecl>(SecondDecl))) |
| return true; |
| break; |
| } |
| |
| case CXXMethod: { |
| enum { |
| DiagMethod, |
| DiagConstructor, |
| DiagDestructor, |
| } FirstMethodType, |
| SecondMethodType; |
| auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl *D) { |
| if (isa<CXXConstructorDecl>(D)) |
| return DiagConstructor; |
| if (isa<CXXDestructorDecl>(D)) |
| return DiagDestructor; |
| return DiagMethod; |
| }; |
| const CXXMethodDecl *FirstMethod = cast<CXXMethodDecl>(FirstDecl); |
| const CXXMethodDecl *SecondMethod = cast<CXXMethodDecl>(SecondDecl); |
| FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod); |
| SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod); |
| DeclarationName FirstName = FirstMethod->getDeclName(); |
| DeclarationName SecondName = SecondMethod->getDeclName(); |
| auto DiagMethodError = [&DiagError, FirstMethod, FirstMethodType, |
| FirstName](ODRCXXRecordDifference DiffType) { |
| return DiagError(FirstMethod->getLocation(), |
| FirstMethod->getSourceRange(), DiffType) |
| << FirstMethodType << FirstName; |
| }; |
| auto DiagMethodNote = [&DiagNote, SecondMethod, SecondMethodType, |
| SecondName](ODRCXXRecordDifference DiffType) { |
| return DiagNote(SecondMethod->getLocation(), |
| SecondMethod->getSourceRange(), DiffType) |
| << SecondMethodType << SecondName; |
| }; |
| |
| if (FirstMethodType != SecondMethodType || FirstName != SecondName) { |
| DiagMethodError(MethodName); |
| DiagMethodNote(MethodName); |
| return true; |
| } |
| |
| const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); |
| const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); |
| if (FirstDeleted != SecondDeleted) { |
| DiagMethodError(MethodDeleted) << FirstDeleted; |
| DiagMethodNote(MethodDeleted) << SecondDeleted; |
| return true; |
| } |
| |
| const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); |
| const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); |
| if (FirstDefaulted != SecondDefaulted) { |
| DiagMethodError(MethodDefaulted) << FirstDefaulted; |
| DiagMethodNote(MethodDefaulted) << SecondDefaulted; |
| return true; |
| } |
| |
| const bool FirstVirtual = FirstMethod->isVirtualAsWritten(); |
| const bool SecondVirtual = SecondMethod->isVirtualAsWritten(); |
| const bool FirstPure = FirstMethod->isPure(); |
| const bool SecondPure = SecondMethod->isPure(); |
| if ((FirstVirtual || SecondVirtual) && |
| (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { |
| DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual; |
| DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual; |
| return true; |
| } |
| |
| // CXXMethodDecl::isStatic uses the canonical Decl. With Decl merging, |
| // FirstDecl is the canonical Decl of SecondDecl, so the storage |
| // class needs to be checked instead. |
| StorageClass FirstStorage = FirstMethod->getStorageClass(); |
| StorageClass SecondStorage = SecondMethod->getStorageClass(); |
| const bool FirstStatic = FirstStorage == SC_Static; |
| const bool SecondStatic = SecondStorage == SC_Static; |
| if (FirstStatic != SecondStatic) { |
| DiagMethodError(MethodStatic) << FirstStatic; |
| DiagMethodNote(MethodStatic) << SecondStatic; |
| return true; |
| } |
| |
| const bool FirstVolatile = FirstMethod->isVolatile(); |
| const bool SecondVolatile = SecondMethod->isVolatile(); |
| if (FirstVolatile != SecondVolatile) { |
| DiagMethodError(MethodVolatile) << FirstVolatile; |
| DiagMethodNote(MethodVolatile) << SecondVolatile; |
| return true; |
| } |
| |
| const bool FirstConst = FirstMethod->isConst(); |
| const bool SecondConst = SecondMethod->isConst(); |
| if (FirstConst != SecondConst) { |
| DiagMethodError(MethodConst) << FirstConst; |
| DiagMethodNote(MethodConst) << SecondConst; |
| return true; |
| } |
| |
| const bool FirstInline = FirstMethod->isInlineSpecified(); |
| const bool SecondInline = SecondMethod->isInlineSpecified(); |
| if (FirstInline != SecondInline) { |
| DiagMethodError(MethodInline) << FirstInline; |
| DiagMethodNote(MethodInline) << SecondInline; |
| return true; |
| } |
| |
| 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; |
| } |
| |
| 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 << 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; |
| } |