diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 5fc5937b80d35..7965da593f218 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -139,6 +139,11 @@ def warn_decls_in_multiple_modules : Warning< InGroup>, DefaultIgnore; +def warn_module_file_mapping_mismatch + : Warning<"loaded module file '%0' conflicts with imported file '%1'">, + InGroup>, + DefaultIgnore; + def err_failed_to_find_module_file : Error< "failed to find module file for module '%0'">; } // let CategoryName diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 70b54b7296882..352558de79249 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3286,6 +3286,8 @@ ASTReader::ReadControlBlock(ModuleFile &F, time_t StoredModTime = 0; ASTFileSignature StoredSignature; std::string ImportedFile; + std::string StoredFile; + bool IgnoreImportedByNote = false; // For prebuilt and explicit modules first consult the file map for // an override. Note that here we don't search prebuilt module @@ -3311,11 +3313,26 @@ ASTReader::ReadControlBlock(ModuleFile &F, SignatureBytes.end()); Blob = Blob.substr(ASTFileSignature::size); + // Use BaseDirectoryAsWritten to ensure we use the same path in the + // ModuleCache as when writing. + StoredFile = ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob); if (ImportedFile.empty()) { - // Use BaseDirectoryAsWritten to ensure we use the same path in the - // ModuleCache as when writing. - ImportedFile = - ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob); + ImportedFile = StoredFile; + } else if (!getDiags().isIgnored( + diag::warn_module_file_mapping_mismatch, + CurrentImportLoc)) { + auto ImportedFileRef = + PP.getFileManager().getOptionalFileRef(ImportedFile); + auto StoredFileRef = + PP.getFileManager().getOptionalFileRef(StoredFile); + if ((ImportedFileRef && StoredFileRef) && + (*ImportedFileRef != *StoredFileRef)) { + Diag(diag::warn_module_file_mapping_mismatch) + << ImportedFile << StoredFile; + Diag(diag::note_module_file_imported_by) + << F.FileName << !F.ModuleName.empty() << F.ModuleName; + IgnoreImportedByNote = true; + } } } @@ -3349,7 +3366,8 @@ ASTReader::ReadControlBlock(ModuleFile &F, .getModuleCache() .getInMemoryModuleCache() .isPCMFinal(F.FileName); - if (isDiagnosedResult(Result, Capabilities) || recompilingFinalized) + if (!IgnoreImportedByNote && + (isDiagnosedResult(Result, Capabilities) || recompilingFinalized)) Diag(diag::note_module_file_imported_by) << F.FileName << !F.ModuleName.empty() << F.ModuleName; diff --git a/clang/test/Modules/invalid-module-dep.c b/clang/test/Modules/invalid-module-dep.c new file mode 100644 index 0000000000000..2bcda8be5f1b6 --- /dev/null +++ b/clang/test/Modules/invalid-module-dep.c @@ -0,0 +1,64 @@ +/// This tests the expected error case when there is a mismatch between the pcm dependencies passed in +/// the command line with `fmodule-file` and whats encoded in the pcm. + +/// The steps are: +/// 1. Build the module A with no dependencies. The first variant to build is A-1.pcm. +/// 2. Build the same module with files that resolve from different search paths. +/// This variant is named A-2.pcm. +/// 3. Build module B that depends on the earlier module A-1.pcm. +/// 4. Build client that directly depends on both modules (A & B), +/// but depends on a incompatible variant of A (A-2.pcm) for B to use. + +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \ +// RUN: -I%t/Sysroot/usr/include \ +// RUN: -fmodule-name=A %t/Sysroot/usr/include/A/module.modulemap -o %t/A-1.pcm + +// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \ +// RUN: -I%t/BuildDir \ +// RUN: -fmodule-name=A %t/BuildDir/A/module.modulemap -o %t/A-2.pcm + +// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \ +// RUN: -I%t/Sysroot/usr/include \ +// RUN: -fmodule-map-file=%t/Sysroot/usr/include/A/module.modulemap \ +// RUN: -fmodule-file=A=%t/A-1.pcm \ +// RUN: -fmodule-name=B %t/Sysroot/usr/include/B/module.modulemap -o %t/B-1.pcm + +// RUN: %clang_cc1 -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \ +// RUN: -I%t/BuildDir -I%t/Sysroot/usr/include \ +// RUN: -fmodule-map-file=%t/BuildDir/A/module.modulemap \ +// RUN: -fmodule-map-file=%t/Sysroot/usr/include/B/module.modulemap \ +// RUN: -fmodule-file=A=%t/A-2.pcm -fmodule-file=B=%t/B-1.pcm \ +// RUN: -Wmodule-file-mapping-mismatch -verify %s + + +#include +#include // expected-warning {{conflicts with imported file}} \ + // expected-note {{imported by module 'B'}} \ + // expected-error {{out of date and needs to be rebuilt}} + +//--- Sysroot/usr/include/A/module.modulemap +module A [system] { + umbrella header "A.h" +} +//--- Sysroot/usr/include/A/A.h +typedef int A_t; + +//--- Sysroot/usr/include/B/module.modulemap +module B [system] { + umbrella header "B.h" +} + +//--- Sysroot/usr/include/B/B.h +#include +typedef int B_t; + +//--- BuildDir/A/module.modulemap +module A [system] { + umbrella header "A.h" +} + +//--- BuildDir/A/A.h +typedef int A_t;