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 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 {
--- /dev/null
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// Build first header file
+// RUN: echo "#define FIRST" >> %t/include/first.h
+// RUN: cat %t/test.c >> %t/include/first.h
+// RUN: echo "#undef FIRST" >> %t/include/first.h
+
+// Build second header file
+// RUN: echo "#define SECOND" >> %t/include/second.h
+// RUN: cat %t/test.c >> %t/include/second.h
+// RUN: echo "#undef SECOND" >> %t/include/second.h
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/first.h -fblocks -fobjc-arc
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/second.h -fblocks -fobjc-arc
+
+// Run test
+// RUN: %clang_cc1 -I%t/include -verify %t/test.c -fblocks -fobjc-arc \
+// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
+
+// Run tests for nested structs
+// DEFINE: %{filename} = test-nested-struct.c
+// DEFINE: %{macro_flag} = -DCASE1=1
+// DEFINE: %{command} = %clang_cc1 -I%t/include -verify %t/%{filename} -fblocks -fobjc-arc \
+// DEFINE: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache \
+// DEFINE: %{macro_flag} -emit-llvm -o %t/%{filename}.bc
+// RUN: %{command}
+// REDEFINE: %{macro_flag} = -DCASE2=1
+// RUN: %{command}
+// REDEFINE: %{macro_flag} = -DCASE3=1
+// RUN: %{command}
+
+// Test that we don't accept different structs and unions with the same name
+// from multiple modules but detect mismatches and provide actionable
+// diagnostic.
+
+//--- include/first-empty.h
+//--- include/module.modulemap
+module First {
+ module Empty {
+ header "first-empty.h"
+ }
+ module Hidden {
+ header "first.h"
+ header "first-nested-struct.h"
+ export *
+ }
+}
+module Second {
+ header "second.h"
+ header "second-nested-struct.h"
+ export *
+}
+
+//--- test.c
+#if !defined(FIRST) && !defined(SECOND)
+# include "first-empty.h"
+# include "second.h"
+#endif
+
+#if defined(FIRST)
+struct CompareForwardDeclaration1;
+struct CompareForwardDeclaration2 {};
+#elif defined(SECOND)
+struct CompareForwardDeclaration1 {};
+struct CompareForwardDeclaration2;
+#else
+struct CompareForwardDeclaration1 *compareForwardDeclaration1;
+struct CompareForwardDeclaration2 *compareForwardDeclaration2;
+#endif
+
+#if defined(FIRST)
+struct CompareMatchingFields {
+ int matchingFieldName;
+};
+
+struct CompareFieldPresence1 {
+ int fieldPresence1;
+};
+struct CompareFieldPresence2 {};
+
+struct CompareFieldName {
+ int fieldNameA;
+};
+
+struct CompareFieldOrder {
+ int fieldOrderX;
+ int fieldOrderY;
+};
+#elif defined(SECOND)
+struct CompareMatchingFields {
+ int matchingFieldName;
+};
+
+struct CompareFieldPresence1 {
+};
+struct CompareFieldPresence2 {
+ int fieldPresence2;
+};
+
+struct CompareFieldName {
+ int fieldNameB;
+};
+
+struct CompareFieldOrder {
+ int fieldOrderY;
+ int fieldOrderX;
+};
+#else
+struct CompareMatchingFields compareMatchingFields;
+struct CompareFieldPresence1 compareFieldPresence1;
+// expected-error@first.h:* {{'CompareFieldPresence1' has different definitions in different modules; first difference is definition in module 'First.Hidden' found field}}
+// expected-note@second.h:* {{but in 'Second' found end of class}}
+struct CompareFieldPresence2 compareFieldPresence2;
+// expected-error@second.h:* {{'CompareFieldPresence2::fieldPresence2' from module 'Second' is not present in definition of 'struct CompareFieldPresence2' in module 'First.Hidden'}}
+// expected-note@first.h:* {{definition has no member 'fieldPresence2'}}
+struct CompareFieldName compareFieldName;
+// expected-error@second.h:* {{'CompareFieldName::fieldNameB' from module 'Second' is not present in definition of 'struct CompareFieldName' in module 'First.Hidden'}}
+// expected-note@first.h:* {{definition has no member 'fieldNameB'}}
+struct CompareFieldOrder compareFieldOrder;
+// expected-error@first.h:* {{'CompareFieldOrder' has different definitions in different modules; first difference is definition in module 'First.Hidden' found field 'fieldOrderX'}}
+// expected-note@second.h:* {{but in 'Second' found field 'fieldOrderY'}}
+#endif
+
+#if defined(FIRST)
+struct CompareFieldType {
+ int fieldType;
+};
+
+typedef int FieldTypedefNameA;
+struct CompareFieldTypedefName {
+ FieldTypedefNameA fieldTypedefName;
+};
+
+typedef int TypedefUnderlyingType;
+struct CompareFieldTypeUnderlyingTypedef {
+ TypedefUnderlyingType fieldTypeUnderlyingTypedef;
+};
+
+typedef int TypedefFinal;
+struct CompareFieldTypedefChain {
+ TypedefFinal fieldTypeTypedefChain;
+};
+#elif defined(SECOND)
+struct CompareFieldType {
+ float fieldType;
+};
+
+typedef int FieldTypedefNameB;
+struct CompareFieldTypedefName {
+ FieldTypedefNameB fieldTypedefName;
+};
+
+struct CompareFieldTypeUnderlyingTypedef {
+ int fieldTypeUnderlyingTypedef;
+};
+
+typedef int TypedefIntermediate;
+typedef TypedefIntermediate TypedefFinal;
+struct CompareFieldTypedefChain {
+ TypedefFinal fieldTypeTypedefChain;
+};
+#else
+struct CompareFieldType compareFieldType;
+// expected-error@second.h:* {{'CompareFieldType::fieldType' from module 'Second' is not present in definition of 'struct CompareFieldType' in module 'First.Hidden'}}
+// expected-note@first.h:* {{declaration of 'fieldType' does not match}}
+struct CompareFieldTypedefName compareFieldTypedefName;
+// expected-error@first.h:* {{'CompareFieldTypedefName' has different definitions in different modules; first difference is definition in module 'First.Hidden' found field 'fieldTypedefName' with type 'FieldTypedefNameA' (aka 'int')}}
+// expected-note@second.h:* {{but in 'Second' found field 'fieldTypedefName' with type 'FieldTypedefNameB' (aka 'int')}}
+struct CompareFieldTypeUnderlyingTypedef compareFieldTypeUnderlyingTypedef;
+// expected-error@first.h:* {{'CompareFieldTypeUnderlyingTypedef' has different definitions in different modules; first difference is definition in module 'First.Hidden' found field 'fieldTypeUnderlyingTypedef' with type 'TypedefUnderlyingType' (aka 'int')}}
+// expected-note@second.h:* {{but in 'Second' found field 'fieldTypeUnderlyingTypedef' with type 'int'}}
+struct CompareFieldTypedefChain compareFieldTypedefChain;
+#endif
+
+#if defined(FIRST)
+struct CompareMatchingBitfields {
+ unsigned matchingBitfields : 3;
+};
+
+struct CompareBitfieldPresence1 {
+ unsigned bitfieldPresence1 : 1;
+};
+struct CompareBitfieldPresence2 {
+ unsigned bitfieldPresence2;
+};
+
+struct CompareBitfieldWidth {
+ unsigned bitfieldWidth : 2;
+};
+
+struct CompareBitfieldWidthExpression {
+ unsigned bitfieldWidthExpression : 1 + 1;
+};
+#elif defined(SECOND)
+struct CompareMatchingBitfields {
+ unsigned matchingBitfields : 3;
+};
+
+struct CompareBitfieldPresence1 {
+ unsigned bitfieldPresence1;
+};
+struct CompareBitfieldPresence2 {
+ unsigned bitfieldPresence2 : 1;
+};
+
+struct CompareBitfieldWidth {
+ unsigned bitfieldWidth : 1;
+};
+
+struct CompareBitfieldWidthExpression {
+ unsigned bitfieldWidthExpression : 2;
+};
+#else
+struct CompareMatchingBitfields compareMatchingBitfields;
+struct CompareBitfieldPresence1 compareBitfieldPresence1;
+// expected-error@first.h:* {{'CompareBitfieldPresence1' has different definitions in different modules; first difference is definition in module 'First.Hidden' found bitfield 'bitfieldPresence1'}}
+// expected-note@second.h:* {{but in 'Second' found non-bitfield 'bitfieldPresence1'}}
+struct CompareBitfieldPresence2 compareBitfieldPresence2;
+// expected-error@first.h:* {{'CompareBitfieldPresence2' has different definitions in different modules; first difference is definition in module 'First.Hidden' found non-bitfield 'bitfieldPresence2'}}
+// expected-note@second.h:* {{but in 'Second' found bitfield 'bitfieldPresence2'}}
+struct CompareBitfieldWidth compareBitfieldWidth;
+// expected-error@first.h:* {{'CompareBitfieldWidth' has different definitions in different modules; first difference is definition in module 'First.Hidden' found bitfield 'bitfieldWidth' with one width expression}}
+// expected-note@second.h:* {{but in 'Second' found bitfield 'bitfieldWidth' with different width expression}}
+struct CompareBitfieldWidthExpression compareBitfieldWidthExpression;
+// expected-error@first.h:* {{'CompareBitfieldWidthExpression' has different definitions in different modules; first difference is definition in module 'First.Hidden' found bitfield 'bitfieldWidthExpression' with one width expression}}
+// expected-note@second.h:* {{but in 'Second' found bitfield 'bitfieldWidthExpression' with different width expressio}}
+#endif
+
+#if defined(FIRST)
+struct CompareMatchingArrayFields {
+ int matchingArrayField[7];
+};
+
+struct CompareArrayLength {
+ int arrayLengthField[5];
+};
+
+struct CompareArrayType {
+ int arrayTypeField[5];
+};
+#elif defined(SECOND)
+struct CompareMatchingArrayFields {
+ int matchingArrayField[7];
+};
+
+struct CompareArrayLength {
+ int arrayLengthField[7];
+};
+
+struct CompareArrayType {
+ float arrayTypeField[5];
+};
+#else
+struct CompareMatchingArrayFields compareMatchingArrayFields;
+struct CompareArrayLength compareArrayLength;
+// expected-error@second.h:* {{'CompareArrayLength::arrayLengthField' from module 'Second' is not present in definition of 'struct CompareArrayLength' in module 'First.Hidden'}}
+// expected-note@first.h:* {{declaration of 'arrayLengthField' does not match}}
+struct CompareArrayType compareArrayType;
+// expected-error@second.h:* {{'CompareArrayType::arrayTypeField' from module 'Second' is not present in definition of 'struct CompareArrayType' in module 'First.Hidden'}}
+// expected-note@first.h:* {{declaration of 'arrayTypeField' does not match}}
+#endif
+
+#if defined(FIRST)
+struct CompareFieldAsForwardDeclaration {
+ struct FieldForwardDeclaration *fieldForwardDeclaration;
+};
+
+enum FieldEnumA { kFieldEnumValue };
+struct CompareFieldAsEnum {
+ enum FieldEnumA fieldEnum;
+};
+
+struct FieldStructA {};
+struct CompareFieldAsStruct {
+ struct FieldStructA fieldStruct;
+};
+#elif defined(SECOND)
+struct FieldForwardDeclaration {};
+struct CompareFieldAsForwardDeclaration {
+ struct FieldForwardDeclaration *fieldForwardDeclaration;
+};
+
+enum FieldEnumB { kFieldEnumValue };
+struct CompareFieldAsEnum {
+ enum FieldEnumB fieldEnum;
+};
+
+struct FieldStructB {};
+struct CompareFieldAsStruct {
+ struct FieldStructB fieldStruct;
+};
+#else
+struct CompareFieldAsForwardDeclaration compareFieldAsForwardDeclaration;
+struct CompareFieldAsEnum compareFieldAsEnum;
+// expected-error@second.h:* {{'CompareFieldAsEnum::fieldEnum' from module 'Second' is not present in definition of 'struct CompareFieldAsEnum' in module 'First.Hidden'}}
+// expected-note@first.h:* {{declaration of 'fieldEnum' does not match}}
+struct CompareFieldAsStruct compareFieldAsStruct;
+// expected-error@second.h:* {{'CompareFieldAsStruct::fieldStruct' from module 'Second' is not present in definition of 'struct CompareFieldAsStruct' in module 'First.Hidden'}}
+// expected-note@first.h:* {{declaration of 'fieldStruct' does not match}}
+#endif
+
+#if defined(FIRST)
+union CompareMatchingUnionFields {
+ int matchingFieldA;
+ float matchingFieldB;
+};
+
+union CompareUnionFieldOrder {
+ int unionFieldOrderA;
+ float unionFieldOrderB;
+};
+
+union CompareUnionFieldType {
+ int unionFieldType;
+};
+#elif defined(SECOND)
+union CompareMatchingUnionFields {
+ int matchingFieldA;
+ float matchingFieldB;
+};
+
+union CompareUnionFieldOrder {
+ float unionFieldOrderB;
+ int unionFieldOrderA;
+};
+
+union CompareUnionFieldType {
+ unsigned int unionFieldType;
+};
+#else
+union CompareMatchingUnionFields compareMatchingUnionFields;
+union CompareUnionFieldOrder compareUnionFieldOrder;
+// expected-error@first.h:* {{'CompareUnionFieldOrder' has different definitions in different modules; first difference is definition in module 'First.Hidden' found field 'unionFieldOrderA'}}
+// expected-note@second.h:* {{but in 'Second' found field 'unionFieldOrderB'}}
+union CompareUnionFieldType compareUnionFieldType;
+// expected-error@second.h:* {{'CompareUnionFieldType::unionFieldType' from module 'Second' is not present in definition of 'union CompareUnionFieldType' in module 'First.Hidden'}}
+// expected-note@first.h:* {{declaration of 'unionFieldType' does not match}}
+#endif
+
+// Test that we find and compare definitions even if they are not the first encountered declaration in a module.
+#if defined(FIRST)
+struct CompareDefinitionsRegardlessForwardDeclarations {
+ int definitionField;
+};
+#elif defined(SECOND)
+struct CompareDefinitionsRegardlessForwardDeclarations;
+struct CompareDefinitionsRegardlessForwardDeclarations {
+ float definitionField;
+};
+#else
+struct CompareDefinitionsRegardlessForwardDeclarations compareDefinitions;
+// expected-error@second.h:* {{'CompareDefinitionsRegardlessForwardDeclarations::definitionField' from module 'Second' is not present in definition of 'struct CompareDefinitionsRegardlessForwardDeclarations' in module 'First.Hidden'}}
+// expected-note@first.h:* {{declaration of 'definitionField' does not match}}
+#endif
+
+//--- include/first-nested-struct.h
+struct CompareNestedStruct {
+ struct NestedLevel1 {
+ struct NestedLevel2 {
+ int a;
+ } y;
+ } x;
+};
+
+struct IndirectStruct {
+ int mismatchingField;
+};
+struct DirectStruct {
+ struct IndirectStruct indirectField;
+};
+struct CompareDifferentFieldInIndirectStruct {
+ struct DirectStruct directField;
+};
+struct CompareIndirectStructPointer {
+ struct DirectStruct *directFieldPointer;
+};
+
+//--- include/second-nested-struct.h
+struct CompareNestedStruct {
+ struct NestedLevel1 {
+ struct NestedLevel2 {
+ float b;
+ } y;
+ } x;
+};
+
+struct IndirectStruct {
+ float mismatchingField;
+};
+struct DirectStruct {
+ struct IndirectStruct indirectField;
+};
+struct CompareDifferentFieldInIndirectStruct {
+ struct DirectStruct directField;
+};
+struct CompareIndirectStructPointer {
+ struct DirectStruct *directFieldPointer;
+};
+
+//--- test-nested-struct.c
+#include "first-empty.h"
+#include "second-nested-struct.h"
+
+#if defined(CASE1)
+struct CompareNestedStruct compareNestedStruct;
+// expected-error@second-nested-struct.h:* {{'NestedLevel2::b' from module 'Second' is not present in definition of 'struct NestedLevel2' in module 'First.Hidden'}}
+// expected-note@first-nested-struct.h:* {{definition has no member 'b'}}
+#elif defined(CASE2)
+struct CompareDifferentFieldInIndirectStruct compareIndirectStruct;
+// expected-error@second-nested-struct.h:* {{'IndirectStruct::mismatchingField' from module 'Second' is not present in definition of 'struct IndirectStruct' in module 'First.Hidden'}}
+// expected-note@first-nested-struct.h:* {{declaration of 'mismatchingField' does not match}}
+#elif defined(CASE3)
+struct CompareIndirectStructPointer compareIndirectStructPointer;
+// expected-error@second-nested-struct.h:* {{'IndirectStruct::mismatchingField' from module 'Second' is not present in definition of 'struct IndirectStruct' in module 'First.Hidden'}}
+// expected-note@first-nested-struct.h:* {{declaration of 'mismatchingField' does not match}}
+#endif