diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -371,6 +371,10 @@ /// The set of use declarations that have yet to be resolved. SmallVector UnresolvedDirectUses; + /// When \c NoUndeclaredIncludes is true, the set of modules this module tried + /// to import but didn't because they are not direct uses. + llvm::SmallSetVector UndeclaredUses; + /// A library or framework to link against when an entity from this /// module is used. struct LinkLibrary { @@ -601,7 +605,7 @@ /// Determine whether this module has declared its intention to /// directly use another module. - bool directlyUses(const Module *Requested) const; + bool directlyUses(const Module *Requested); /// Add the given feature requirement to the list of features /// required by this module. diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -267,7 +267,7 @@ return llvm::makeArrayRef(TopHeaders.begin(), TopHeaders.end()); } -bool Module::directlyUses(const Module *Requested) const { +bool Module::directlyUses(const Module *Requested) { auto *Top = getTopLevelModule(); // A top-level module implicitly uses itself. @@ -282,6 +282,9 @@ if (!Requested->Parent && Requested->Name == "_Builtin_stddef_max_align_t") return true; + if (NoUndeclaredIncludes) + UndeclaredUses.insert(Requested); + return false; } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -164,8 +164,8 @@ std::set GetAllModuleMaps(const HeaderSearch &HS, Module *RootModule) { std::set ModuleMaps{}; - std::set ProcessedModules; - SmallVector ModulesToProcess{RootModule}; + std::set ProcessedModules; + SmallVector ModulesToProcess{RootModule}; SmallVector FilesByUID; HS.getFileMgr().GetUniqueIDMapping(FilesByUID); @@ -209,6 +209,11 @@ } ModulesToProcess.push_back(ImportedModule); } + + for (const Module *UndeclaredModule : CurrentModule->UndeclaredUses) + if (UndeclaredModule && + ProcessedModules.find(UndeclaredModule) == ProcessedModules.end()) + ModulesToProcess.push_back(UndeclaredModule); } return ModuleMaps; @@ -2861,6 +2866,8 @@ // Might be unnecessary as use declarations are only used to build the // module itself. + // TODO: Consider serializing undeclared uses of modules. + // Emit the link libraries. for (const auto &LL : Mod->LinkLibraries) { RecordData::value_type Record[] = {SUBMODULE_LINK_LIBRARY, diff --git a/clang/test/Modules/add-remove-irrelevant-module-map.m b/clang/test/Modules/add-remove-irrelevant-module-map.m --- a/clang/test/Modules/add-remove-irrelevant-module-map.m +++ b/clang/test/Modules/add-remove-irrelevant-module-map.m @@ -25,6 +25,34 @@ // Neither PCM file considers 'b.modulemap' an input: // -// RUN: %clang_cc1 -module-file-info %t/cache-without-b/a.pcm | FileCheck %s -// RUN: %clang_cc1 -module-file-info %t/cache-with-b/a.pcm | FileCheck %s -// CHECK-NOT: Input file: {{.*}}/b.modulemap +// RUN: %clang_cc1 -module-file-info %t/cache-without-b/a.pcm | FileCheck %s --check-prefix=CHECK-B +// RUN: %clang_cc1 -module-file-info %t/cache-with-b/a.pcm | FileCheck %s --check-prefix=CHECK-B +// CHECK-B-NOT: Input file: {{.*}}/b.modulemap + +//--- c.modulemap +module c [no_undeclared_includes] { header "c.h" } + +//--- c.h +#if __has_include("d.h") // This should use 'd.modulemap' in order to determine that 'd.h' + // doesn't exist for 'c' because of its '[no_undeclared_includes]'. +#endif + +//--- d.modulemap +module d { header "d.h" } + +//--- d.h +// empty + +//--- test-no-undeclared-includes.m +// expected-no-diagnostics +@import c; + +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache -fdisable-module-hash \ +// RUN: -fmodule-map-file=%t/c.modulemap -fmodule-map-file=%t/d.modulemap \ +// RUN: %t/test-no-undeclared-includes.m -verify + +// The PCM file considers 'd.modulemap' an input because it affects the compilation, +// although it doesn't describe the built module or its imports. +// +// RUN: %clang_cc1 -module-file-info %t/cache/c.pcm | FileCheck %s --check-prefix=CHECK-D -DDIR=%t +// CHECK-D: Input file: [[DIR]]/d.modulemap