diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -181,6 +181,11 @@ llvm::DenseSet UsedSearchDirs; /// Mapping from SearchDir to HeaderSearchOptions::UserEntries indices. llvm::DenseMap SearchDirToHSEntry; + /// Mapping from module to index of the search directory that discovered its + /// module map file. + llvm::DenseMap ModuleToSearchDir; + /// While iterating through `SearchDirs`, the current one. + const DirectoryLookup *CurrentSearchDir = nullptr; unsigned AngledDirIdx = 0; unsigned SystemDirIdx = 0; @@ -711,6 +716,7 @@ /// using the search path at index `HitIdx`. void cacheLookupSuccess(LookupFileCacheInfo &CacheLookup, unsigned HitIdx, SourceLocation IncludeLoc); + void noteModuleLookupUsage(Module *M, SourceLocation Loc); /// Note that a lookup at the given include location was successful using the /// given search path. void noteLookupUsage(const DirectoryLookup *SearchDir, diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -70,6 +70,11 @@ /// \param Header The umbrella header to collect. virtual void moduleMapAddUmbrellaHeader(FileManager *FileMgr, const FileEntry *Header) {} + + /// Called when new module is created. + /// + /// \param M The newly created module. + virtual void moduleMapModuleCreated(Module *M) {} }; class ModuleMap { diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/VirtualFileSystem.h" #include #include @@ -85,7 +86,18 @@ const TargetInfo *Target) : HSOpts(std::move(HSOpts)), Diags(Diags), FileMgr(SourceMgr.getFileManager()), FrameworkMap(64), - ModMap(SourceMgr, Diags, LangOpts, Target, *this) {} + ModMap(SourceMgr, Diags, LangOpts, Target, *this) { + struct MMCallback : ModuleMapCallbacks { + HeaderSearch &HS; + MMCallback(HeaderSearch &HS) : HS(HS) {} + void moduleMapModuleCreated(Module *M) override { + // Module map parsing initiated by header search. + if (HS.CurrentSearchDir) + HS.ModuleToSearchDir[M] = HS.CurrentSearchDir; + } + }; + ModMap.addModuleMapCallbacks(std::make_unique(*this)); +} void HeaderSearch::PrintStats() { llvm::errs() << "\n*** HeaderSearch Stats:\n" @@ -123,6 +135,7 @@ SearchDirToHSEntry.clear(); for (const auto &Entry : searchDirToHSEntry) SearchDirToHSEntry.insert({SearchDirs[Entry.first], Entry.second}); + ModuleToSearchDir.clear(); AngledDirIdx = angledDirIdx; SystemDirIdx = systemDirIdx; @@ -285,8 +298,10 @@ bool AllowExtraModuleMapSearch) { // Look in the module map to determine if there is a module by this name. Module *Module = ModMap.findModule(ModuleName); - if (Module || !AllowSearch || !HSOpts->ImplicitModuleMaps) + if (Module || !AllowSearch || !HSOpts->ImplicitModuleMaps) { + noteModuleLookupUsage(Module, ImportLoc); return Module; + } StringRef SearchName = ModuleName; Module = lookupModule(ModuleName, SearchName, ImportLoc, @@ -306,6 +321,7 @@ if (!Module && SearchName.consume_back("Private")) Module = lookupModule(ModuleName, SearchName, ImportLoc, AllowExtraModuleMapSearch); + noteModuleLookupUsage(Module, ImportLoc); return Module; } @@ -313,12 +329,12 @@ SourceLocation ImportLoc, bool AllowExtraModuleMapSearch) { Module *Module = nullptr; - DirectoryLookup *SearchDir = nullptr; // Look through the various header search paths to load any available module // maps, searching for a module map that describes this module. for (unsigned Idx = 0; Idx != SearchDirs.size(); ++Idx) { - SearchDir = SearchDirs[Idx]; + llvm::SaveAndRestore X(CurrentSearchDir, + SearchDirs[Idx]); if (SearchDirs[Idx]->isFramework()) { // Search for or infer a module map for a framework. Here we use @@ -382,9 +398,6 @@ break; } - if (Module) - noteLookupUsage(SearchDir, ImportLoc); - return Module; } @@ -720,6 +733,12 @@ noteLookupUsage(SearchDirs[HitIdx], Loc); } +void HeaderSearch::noteModuleLookupUsage(Module *M, SourceLocation Loc) { + auto It = ModuleToSearchDir.find(M); + if (It != ModuleToSearchDir.end()) + noteLookupUsage(It->second, Loc); +} + void HeaderSearch::noteLookupUsage(const DirectoryLookup *SearchDir, SourceLocation Loc) { UsedSearchDirs.insert(SearchDir); @@ -1770,6 +1789,9 @@ if (HSOpts->ImplicitModuleMaps) { // Load module maps for each of the header search directories. for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) { + llvm::SaveAndRestore X(CurrentSearchDir, + SearchDirs[Idx]); + bool IsSystem = SearchDirs[Idx]->isSystemHeaderDirectory(); if (SearchDirs[Idx]->isFramework()) { std::error_code EC; @@ -1822,6 +1844,9 @@ // Load module maps for each of the header search directories. for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) { + llvm::SaveAndRestore X(CurrentSearchDir, + SearchDirs[Idx]); + // We only care about normal header directories. if (!SearchDirs[Idx]->isNormalDir()) continue; diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -107,8 +107,13 @@ Module *ModuleMap::makeModule(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, bool IsFramework, bool IsExplicit, unsigned VisibilityID) { - return new Module(Name, DefinitionLoc, Parent, IsFramework, IsExplicit, - VisibilityID); + Module *Result = new Module(Name, DefinitionLoc, Parent, IsFramework, + IsExplicit, VisibilityID); + + for (const auto &Callback : Callbacks) + Callback->moduleMapModuleCreated(Result); + + return Result; } Module::ExportDecl diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3907,7 +3907,7 @@ // An implicitly-loaded module file should have its module listed in some // module map file that we've already loaded. Module *M = - PP.getHeaderSearchInfo().lookupModule(F.ModuleName, F.ImportLoc); + PP.getHeaderSearchInfo().lookupModule(F.ModuleName, SourceLocation()); auto &Map = PP.getHeaderSearchInfo().getModuleMap(); const FileEntry *ModMap = M ? Map.getModuleMapFileForUniquing(M) : nullptr; // Don't emit module relocation error if we have -fno-validate-pch diff --git a/clang/test/Preprocessor/search-path-usage-modules.m b/clang/test/Preprocessor/search-path-usage-modules.m new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/search-path-usage-modules.m @@ -0,0 +1,40 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -Eonly %t/test-both.m -I %t/sp1 -I %t/sp2 -Rsearch-path-usage \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache 2>&1 | FileCheck %t/test-both.m +// RUN: %clang_cc1 -Eonly %t/test-both.m -I %t/sp2 -I %t/sp1 -Rsearch-path-usage \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache 2>&1 | FileCheck %t/test-both.m + +// RUN: %clang_cc1 -Eonly %t/test-first.m -I %t/sp2 -I %t/sp1 -Rsearch-path-usage \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache 2>&1 | FileCheck %t/test-first.m +// RUN: %clang_cc1 -Eonly %t/test-second.m -I %t/sp1 -I %t/sp2 -Rsearch-path-usage \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache 2>&1 | FileCheck %t/test-second.m + +//--- sp1/module.modulemap +module mod1 { header "mod1.h" } +//--- sp1/mod1.h +int module1(); + +//--- sp2/module.modulemap +module mod2 { header "mod2.h" } +//--- sp2/mod2.h +int module2(); + +//--- test-both.m +@import mod1; +@import mod2; +// CHECK: search path used: '{{.*}}/sp1' +// CHECK: search path used: '{{.*}}/sp2' + +//--- test-first.m +@import mod1; +// CHECK-NOT: search path used: '{{.*}}/sp2' +// CHECK: search path used: '{{.*}}/sp1' +// CHECK-NOT: search path used: '{{.*}}/sp2' + +//--- test-second.m +@import mod2; +// CHECK-NOT: search path used: '{{.*}}/sp1' +// CHECK: search path used: '{{.*}}/sp2' +// CHECK-NOT: search path used: '{{.*}}/sp1' diff --git a/clang/test/Preprocessor/search-path-usage.m b/clang/test/Preprocessor/search-path-usage.m --- a/clang/test/Preprocessor/search-path-usage.m +++ b/clang/test/Preprocessor/search-path-usage.m @@ -128,19 +128,3 @@ // expected-remark-re {{search path used: '{{.*}}/b-missing.hmap'}} #endif #endif - -// Check that search paths with module maps are reported. -// -// RUN: mkdir %t/modulemap_abs -// RUN: sed "s|DIR|%/S/Inputs/search-path-usage|g" \ -// RUN: %S/Inputs/search-path-usage/modulemap_abs/module.modulemap.template \ -// RUN: > %t/modulemap_abs/module.modulemap -// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \ -// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules \ -// RUN: -I %t/modulemap_abs \ -// RUN: -I %S/Inputs/search-path-usage/a \ -// RUN: -DMODMAP_ABS -verify -#ifdef MODMAP_ABS -@import b; // \ -// expected-remark-re {{search path used: '{{.*}}/modulemap_abs'}} -#endif