diff --git a/clang-tools-extra/modularize/ModularizeUtilities.cpp b/clang-tools-extra/modularize/ModularizeUtilities.cpp index ef57b0f5bbea5..576e863c8a9d2 100644 --- a/clang-tools-extra/modularize/ModularizeUtilities.cpp +++ b/clang-tools-extra/modularize/ModularizeUtilities.cpp @@ -290,7 +290,7 @@ std::error_code ModularizeUtilities::loadModuleMap( Target.get(), *HeaderInfo)); // Parse module.modulemap file into module map. - if (ModMap->loadModuleMapFile(ModuleMapEntry, false, Dir)) { + if (ModMap->parseAndLoadModuleMapFile(ModuleMapEntry, false, Dir)) { return std::error_code(1, std::generic_category()); } diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 75e8fc541305b..273d6d004f0db 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -606,6 +606,7 @@ def ModuleImport : DiagGroup<"module-import">; def ModuleConflict : DiagGroup<"module-conflict">; def ModuleFileExtension : DiagGroup<"module-file-extension">; def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">; +def ModuleMap : DiagGroup<"module-map">; def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">; def NewlineEOF : DiagGroup<"newline-eof">; def Nullability : DiagGroup<"nullability">; diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index f29edfa835d42..503c68eaacf7e 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -838,6 +838,12 @@ def warn_pp_date_time : Warning< ShowInSystemHeader, DefaultIgnore, InGroup>; // Module map parsing +def remark_mmap_parse : Remark< + "parsing modulemap '%0'">, ShowInSystemHeader, InGroup; +def remark_mmap_load : Remark< + "loading modulemap '%0'">, ShowInSystemHeader, InGroup; +def remark_mmap_load_module : Remark< + "loading parsed module '%0'">, ShowInSystemHeader, InGroup; def err_mmap_unknown_token : Error<"skipping stray token">; def err_mmap_expected_module : Error<"expected module declaration">; def err_mmap_expected_module_name : Error<"expected module name">; diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h index bccec4dd951d6..2e0c8bec8bd8c 100644 --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -332,13 +332,27 @@ class HeaderSearch { /// The mapping between modules and headers. mutable ModuleMap ModMap; + struct ModuleMapDirectoryState { + OptionalFileEntryRef ModuleMapFile; + enum { + Parsed, + Loaded, + Invalid, + } Status; + }; + /// Describes whether a given directory has a module map in it. - llvm::DenseMap DirectoryHasModuleMap; + llvm::DenseMap + DirectoryModuleMap; /// Set of module map files we've already loaded, and a flag indicating /// whether they were valid or not. llvm::DenseMap LoadedModuleMaps; + /// Set of module map files we've already parsed, and a flag indicating + /// whether they were valid or not. + llvm::DenseMap ParsedModuleMaps; + // A map of discovered headers with their associated include file name. llvm::DenseMap> IncludeNames; @@ -433,11 +447,6 @@ class HeaderSearch { /// Retrieve the path to the module cache. StringRef getModuleCachePath() const { return ModuleCachePath; } - /// Consider modules when including files from this directory. - void setDirectoryHasModuleMap(const DirectoryEntry* Dir) { - DirectoryHasModuleMap[Dir] = true; - } - /// Forget everything we know about headers so far. void ClearFileInfo() { FileInfo.clear(); @@ -713,9 +722,10 @@ class HeaderSearch { /// used to resolve paths within the module (this is required when /// building the module from preprocessed source). /// \returns true if an error occurred, false otherwise. - bool loadModuleMapFile(FileEntryRef File, bool IsSystem, FileID ID = FileID(), - unsigned *Offset = nullptr, - StringRef OriginalModuleMapFile = StringRef()); + bool parseAndLoadModuleMapFile(FileEntryRef File, bool IsSystem, + FileID ID = FileID(), + unsigned *Offset = nullptr, + StringRef OriginalModuleMapFile = StringRef()); /// Collect the set of all known, top-level modules. /// @@ -915,26 +925,31 @@ class HeaderSearch { size_t getTotalMemory() const; private: - /// Describes what happened when we tried to load a module map file. - enum LoadModuleMapResult { - /// The module map file had already been loaded. - LMM_AlreadyLoaded, + /// Describes what happened when we tried to load or parse a module map file. + enum ModuleMapResult { + /// The module map file had already been processed. + MMR_AlreadyProcessed, - /// The module map file was loaded by this invocation. - LMM_NewlyLoaded, + /// The module map file was processed by this invocation. + MMR_NewlyProcessed, /// There is was directory with the given name. - LMM_NoDirectory, + MMR_NoDirectory, /// There was either no module map file or the module map file was /// invalid. - LMM_InvalidModuleMap + MMR_InvalidModuleMap }; - LoadModuleMapResult loadModuleMapFileImpl(FileEntryRef File, bool IsSystem, - DirectoryEntryRef Dir, - FileID ID = FileID(), - unsigned *Offset = nullptr); + ModuleMapResult parseAndLoadModuleMapFileImpl(FileEntryRef File, + bool IsSystem, + DirectoryEntryRef Dir, + FileID ID = FileID(), + unsigned *Offset = nullptr); + + ModuleMapResult parseModuleMapFileImpl(FileEntryRef File, bool IsSystem, + DirectoryEntryRef Dir, + FileID ID = FileID()); /// Try to load the module map file in the given directory. /// @@ -945,8 +960,8 @@ class HeaderSearch { /// /// \returns The result of attempting to load the module map file from the /// named directory. - LoadModuleMapResult loadModuleMapFile(StringRef DirName, bool IsSystem, - bool IsFramework); + ModuleMapResult parseAndLoadModuleMapFile(StringRef DirName, bool IsSystem, + bool IsFramework); /// Try to load the module map file in the given directory. /// @@ -956,8 +971,13 @@ class HeaderSearch { /// /// \returns The result of attempting to load the module map file from the /// named directory. - LoadModuleMapResult loadModuleMapFile(DirectoryEntryRef Dir, bool IsSystem, - bool IsFramework); + ModuleMapResult parseAndLoadModuleMapFile(DirectoryEntryRef Dir, + bool IsSystem, bool IsFramework); + + ModuleMapResult parseModuleMapFile(StringRef DirName, bool IsSystem, + bool IsFramework); + ModuleMapResult parseModuleMapFile(DirectoryEntryRef Dir, bool IsSystem, + bool IsFramework); }; /// Apply the header search options to get given HeaderSearch object. diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 43c3890631bd1..d6a1e14958f13 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -18,6 +18,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Lex/ModuleMapFile.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" @@ -262,6 +263,18 @@ class ModuleMap { /// Describes whether we haved loaded a particular file as a module /// map. llvm::DenseMap LoadedModuleMap; + llvm::DenseMap + ParsedModuleMap; + + std::vector> ParsedModuleMaps; + + /// Map from top level module name to a list of ModuleDecls in the order they + /// were discovered. This allows handling shadowing correctly and diagnosing + /// redefinitions. + llvm::StringMap, + 1>> + ParsedModules; /// Resolve the given export declaration into an actual export /// declaration. @@ -478,6 +491,8 @@ class ModuleMap { /// \returns The named module, if known; otherwise, returns null. Module *findModule(StringRef Name) const; + Module *findOrLoadModule(StringRef Name); + Module *findOrInferSubmodule(Module *Parent, StringRef Name); /// Retrieve a module with the given name using lexical name lookup, @@ -693,6 +708,11 @@ class ModuleMap { void addHeader(Module *Mod, Module::Header Header, ModuleHeaderRole Role, bool Imported = false); + /// Parse a module map without creating `clang::Module` instances. + bool parseModuleMapFile(FileEntryRef File, bool IsSystem, + DirectoryEntryRef Dir, FileID ID = FileID(), + SourceLocation ExternModuleLoc = SourceLocation()); + /// Load the given module map file, and record any modules we /// encounter. /// @@ -713,10 +733,11 @@ class ModuleMap { /// that caused us to load this module map file, if any. /// /// \returns true if an error occurred, false otherwise. - bool loadModuleMapFile(FileEntryRef File, bool IsSystem, - DirectoryEntryRef HomeDir, FileID ID = FileID(), - unsigned *Offset = nullptr, - SourceLocation ExternModuleLoc = SourceLocation()); + bool + parseAndLoadModuleMapFile(FileEntryRef File, bool IsSystem, + DirectoryEntryRef HomeDir, FileID ID = FileID(), + unsigned *Offset = nullptr, + SourceLocation ExternModuleLoc = SourceLocation()); /// Dump the contents of the module map, for debugging purposes. void dump(); diff --git a/clang/include/clang/Lex/ModuleMapFile.h b/clang/include/clang/Lex/ModuleMapFile.h index 1219cc2b50753..7d0e36e9ab86c 100644 --- a/clang/include/clang/Lex/ModuleMapFile.h +++ b/clang/include/clang/Lex/ModuleMapFile.h @@ -133,8 +133,17 @@ using TopLevelDecl = std::variant; /// This holds many reference types (StringRef, SourceLocation, etc.) whose /// lifetimes are bound by the SourceManager and FileManager used. struct ModuleMapFile { + /// The FileID used to parse this module map. This is always a local ID. + FileID ID; + + /// The directory in which the module map was discovered. Declarations in + /// the module map are relative to this directory. + OptionalDirectoryEntryRef Dir; + /// Beginning of the file, used for moduleMapFileRead callback. SourceLocation Start; + + bool IsSystem; std::vector Decls; void dump(llvm::raw_ostream &out) const; diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index cbc9c9dcee05f..a4ce50b02714d 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -582,13 +582,13 @@ struct ReadModuleNames : ASTReaderListener { ModuleMap &MM = PP.getHeaderSearchInfo().getModuleMap(); for (const std::string &LoadedModule : LoadedModules) MM.cacheModuleLoad(*PP.getIdentifierInfo(LoadedModule), - MM.findModule(LoadedModule)); + MM.findOrLoadModule(LoadedModule)); LoadedModules.clear(); } void markAllUnavailable() { for (const std::string &LoadedModule : LoadedModules) { - if (Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule( + if (Module *M = PP.getHeaderSearchInfo().getModuleMap().findOrLoadModule( LoadedModule)) { M->HasIncompatibleModuleFile = true; diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index 9b2aa253c90ee..f09eb9832e692 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -621,8 +621,8 @@ static bool loadModuleMapForModuleBuild(CompilerInstance &CI, bool IsSystem, } // Load the module map file. - if (HS.loadModuleMapFile(*ModuleMap, IsSystem, ModuleMapID, &Offset, - PresumedModuleMapFile)) + if (HS.parseAndLoadModuleMapFile(*ModuleMap, IsSystem, ModuleMapID, &Offset, + PresumedModuleMapFile)) return true; if (SrcMgr.getBufferOrFake(ModuleMapID).getBufferSize() == Offset) @@ -1077,8 +1077,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, // If we were asked to load any module map files, do so now. for (const auto &Filename : CI.getFrontendOpts().ModuleMapFiles) { if (auto File = CI.getFileManager().getOptionalFileRef(Filename)) - CI.getPreprocessor().getHeaderSearchInfo().loadModuleMapFile( - *File, /*IsSystem*/false); + CI.getPreprocessor().getHeaderSearchInfo().parseAndLoadModuleMapFile( + *File, /*IsSystem*/ false); else CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename; } diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp index 2665580e5afce..ea2391f6812cf 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -299,7 +299,7 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, SourceLocation ImportLoc, bool AllowSearch, bool AllowExtraModuleMapSearch) { // Look in the module map to determine if there is a module by this name. - Module *Module = ModMap.findModule(ModuleName); + Module *Module = ModMap.findOrLoadModule(ModuleName); if (Module || !AllowSearch || !HSOpts.ImplicitModuleMaps) return Module; @@ -359,11 +359,11 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, StringRef SearchName, // checked DirectoryEntryRef NormalDir = *Dir.getDirRef(); // Search for a module map file in this directory. - if (loadModuleMapFile(NormalDir, IsSystem, - /*IsFramework*/false) == LMM_NewlyLoaded) { - // We just loaded a module map file; check whether the module is - // available now. - Module = ModMap.findModule(ModuleName); + if (parseModuleMapFile(NormalDir, IsSystem, + /*IsFramework*/ false) == MMR_NewlyProcessed) { + // We just parsed a module map file; check whether the module can be + // loaded now. + Module = ModMap.findOrLoadModule(ModuleName); if (Module) break; } @@ -373,10 +373,10 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, StringRef SearchName, SmallString<128> NestedModuleMapDirName; NestedModuleMapDirName = Dir.getDirRef()->getName(); llvm::sys::path::append(NestedModuleMapDirName, ModuleName); - if (loadModuleMapFile(NestedModuleMapDirName, IsSystem, - /*IsFramework*/false) == LMM_NewlyLoaded){ - // If we just loaded a module map file, look for the module again. - Module = ModMap.findModule(ModuleName); + if (parseModuleMapFile(NestedModuleMapDirName, IsSystem, + /*IsFramework*/ false) == MMR_NewlyProcessed) { + // If we just parsed a module map file, look for the module again. + Module = ModMap.findOrLoadModule(ModuleName); if (Module) break; } @@ -393,7 +393,7 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, StringRef SearchName, loadSubdirectoryModuleMaps(Dir); // Look again for the module. - Module = ModMap.findModule(ModuleName); + Module = ModMap.findOrLoadModule(ModuleName); if (Module) break; } @@ -1559,7 +1559,7 @@ bool HeaderSearch::hasModuleMap(StringRef FileName, if (!HSOpts.ImplicitModuleMaps) return false; - SmallVector FixUpDirectories; + SmallVector FixUpDirectories; StringRef DirName = FileName; do { @@ -1574,19 +1574,20 @@ bool HeaderSearch::hasModuleMap(StringRef FileName, return false; // Try to load the module map file in this directory. - switch (loadModuleMapFile(*Dir, IsSystem, - llvm::sys::path::extension(Dir->getName()) == - ".framework")) { - case LMM_NewlyLoaded: - case LMM_AlreadyLoaded: + switch (parseAndLoadModuleMapFile( + *Dir, IsSystem, + llvm::sys::path::extension(Dir->getName()) == ".framework")) { + case MMR_NewlyProcessed: + case MMR_AlreadyProcessed: { // Success. All of the directories we stepped through inherit this module // map file. + const ModuleMapDirectoryState &MMDS = DirectoryModuleMap[*Dir]; for (unsigned I = 0, N = FixUpDirectories.size(); I != N; ++I) - DirectoryHasModuleMap[FixUpDirectories[I]] = true; + DirectoryModuleMap[FixUpDirectories[I]] = MMDS; return true; - - case LMM_NoDirectory: - case LMM_InvalidModuleMap: + } + case MMR_NoDirectory: + case MMR_InvalidModuleMap: break; } @@ -1706,7 +1707,8 @@ bool HeaderSearch::findUsableModuleForFrameworkHeader( static OptionalFileEntryRef getPrivateModuleMap(FileEntryRef File, FileManager &FileMgr, - DiagnosticsEngine &Diags) { + DiagnosticsEngine &Diags, + bool Diagnose = true) { StringRef Filename = llvm::sys::path::filename(File.getName()); SmallString<128> PrivateFilename(File.getDir().getName()); if (Filename == "module.map") @@ -1717,7 +1719,7 @@ static OptionalFileEntryRef getPrivateModuleMap(FileEntryRef File, return std::nullopt; auto PMMFile = FileMgr.getOptionalFileRef(PrivateFilename); if (PMMFile) { - if (Filename == "module.map") + if (Diagnose && Filename == "module.map") Diags.Report(diag::warn_deprecated_module_dot_map) << PrivateFilename << 1 << File.getDir().getName().ends_with(".framework"); @@ -1725,9 +1727,9 @@ static OptionalFileEntryRef getPrivateModuleMap(FileEntryRef File, return PMMFile; } -bool HeaderSearch::loadModuleMapFile(FileEntryRef File, bool IsSystem, - FileID ID, unsigned *Offset, - StringRef OriginalModuleMapFile) { +bool HeaderSearch::parseAndLoadModuleMapFile(FileEntryRef File, bool IsSystem, + FileID ID, unsigned *Offset, + StringRef OriginalModuleMapFile) { // Find the directory for the module. For frameworks, that may require going // up from the 'Modules' directory. OptionalDirectoryEntryRef Dir; @@ -1761,43 +1763,72 @@ bool HeaderSearch::loadModuleMapFile(FileEntryRef File, bool IsSystem, } assert(Dir && "module map home directory must exist"); - switch (loadModuleMapFileImpl(File, IsSystem, *Dir, ID, Offset)) { - case LMM_AlreadyLoaded: - case LMM_NewlyLoaded: + switch (parseAndLoadModuleMapFileImpl(File, IsSystem, *Dir, ID, Offset)) { + case MMR_AlreadyProcessed: + case MMR_NewlyProcessed: return false; - case LMM_NoDirectory: - case LMM_InvalidModuleMap: + case MMR_NoDirectory: + case MMR_InvalidModuleMap: return true; } llvm_unreachable("Unknown load module map result"); } -HeaderSearch::LoadModuleMapResult -HeaderSearch::loadModuleMapFileImpl(FileEntryRef File, bool IsSystem, - DirectoryEntryRef Dir, FileID ID, - unsigned *Offset) { +HeaderSearch::ModuleMapResult +HeaderSearch::parseAndLoadModuleMapFileImpl(FileEntryRef File, bool IsSystem, + DirectoryEntryRef Dir, FileID ID, + unsigned *Offset) { // Check whether we've already loaded this module map, and mark it as being // loaded in case we recursively try to load it from itself. auto AddResult = LoadedModuleMaps.insert(std::make_pair(File, true)); if (!AddResult.second) - return AddResult.first->second ? LMM_AlreadyLoaded : LMM_InvalidModuleMap; + return AddResult.first->second ? MMR_AlreadyProcessed + : MMR_InvalidModuleMap; - if (ModMap.loadModuleMapFile(File, IsSystem, Dir, ID, Offset)) { + if (ModMap.parseAndLoadModuleMapFile(File, IsSystem, Dir, ID, Offset)) { LoadedModuleMaps[File] = false; - return LMM_InvalidModuleMap; + return MMR_InvalidModuleMap; } // Try to load a corresponding private module map. if (OptionalFileEntryRef PMMFile = - getPrivateModuleMap(File, FileMgr, Diags)) { - if (ModMap.loadModuleMapFile(*PMMFile, IsSystem, Dir)) { + getPrivateModuleMap(File, FileMgr, Diags, !ParsedModuleMaps[File])) { + if (ModMap.parseAndLoadModuleMapFile(*PMMFile, IsSystem, Dir)) { LoadedModuleMaps[File] = false; - return LMM_InvalidModuleMap; + return MMR_InvalidModuleMap; + } + } + + // This directory has a module map. + return MMR_NewlyProcessed; +} + +HeaderSearch::ModuleMapResult +HeaderSearch::parseModuleMapFileImpl(FileEntryRef File, bool IsSystem, + DirectoryEntryRef Dir, FileID ID) { + // Check whether we've already parsed this module map, and mark it as being + // parsed in case we recursively try to parse it from itself. + auto AddResult = ParsedModuleMaps.insert(std::make_pair(File, true)); + if (!AddResult.second) + return AddResult.first->second ? MMR_AlreadyProcessed + : MMR_InvalidModuleMap; + + if (ModMap.parseModuleMapFile(File, IsSystem, Dir, ID)) { + ParsedModuleMaps[File] = false; + return MMR_InvalidModuleMap; + } + + // Try to parse a corresponding private module map. + if (OptionalFileEntryRef PMMFile = + getPrivateModuleMap(File, FileMgr, Diags)) { + if (ModMap.parseModuleMapFile(*PMMFile, IsSystem, Dir)) { + ParsedModuleMaps[File] = false; + return MMR_InvalidModuleMap; } } // This directory has a module map. - return LMM_NewlyLoaded; + return MMR_NewlyProcessed; } OptionalFileEntryRef @@ -1837,54 +1868,109 @@ HeaderSearch::lookupModuleMapFile(DirectoryEntryRef Dir, bool IsFramework) { Module *HeaderSearch::loadFrameworkModule(StringRef Name, DirectoryEntryRef Dir, bool IsSystem) { // Try to load a module map file. - switch (loadModuleMapFile(Dir, IsSystem, /*IsFramework*/true)) { - case LMM_InvalidModuleMap: + switch (parseAndLoadModuleMapFile(Dir, IsSystem, /*IsFramework*/ true)) { + case MMR_InvalidModuleMap: // Try to infer a module map from the framework directory. if (HSOpts.ImplicitModuleMaps) ModMap.inferFrameworkModule(Dir, IsSystem, /*Parent=*/nullptr); break; - case LMM_NoDirectory: + case MMR_NoDirectory: return nullptr; - case LMM_AlreadyLoaded: - case LMM_NewlyLoaded: + case MMR_AlreadyProcessed: + case MMR_NewlyProcessed: break; } - return ModMap.findModule(Name); + return ModMap.findOrLoadModule(Name); } -HeaderSearch::LoadModuleMapResult -HeaderSearch::loadModuleMapFile(StringRef DirName, bool IsSystem, - bool IsFramework) { +HeaderSearch::ModuleMapResult +HeaderSearch::parseAndLoadModuleMapFile(StringRef DirName, bool IsSystem, + bool IsFramework) { if (auto Dir = FileMgr.getOptionalDirectoryRef(DirName)) - return loadModuleMapFile(*Dir, IsSystem, IsFramework); + return parseAndLoadModuleMapFile(*Dir, IsSystem, IsFramework); - return LMM_NoDirectory; + return MMR_NoDirectory; } -HeaderSearch::LoadModuleMapResult -HeaderSearch::loadModuleMapFile(DirectoryEntryRef Dir, bool IsSystem, - bool IsFramework) { - auto KnownDir = DirectoryHasModuleMap.find(Dir); - if (KnownDir != DirectoryHasModuleMap.end()) - return KnownDir->second ? LMM_AlreadyLoaded : LMM_InvalidModuleMap; +HeaderSearch::ModuleMapResult +HeaderSearch::parseAndLoadModuleMapFile(DirectoryEntryRef Dir, bool IsSystem, + bool IsFramework) { + auto InsertRes = DirectoryModuleMap.insert(std::pair{ + Dir, ModuleMapDirectoryState{{}, ModuleMapDirectoryState::Invalid}}); + ModuleMapDirectoryState &MMState = InsertRes.first->second; + if (!InsertRes.second) { + switch (MMState.Status) { + case ModuleMapDirectoryState::Parsed: + break; + case ModuleMapDirectoryState::Loaded: + return MMR_AlreadyProcessed; + case ModuleMapDirectoryState::Invalid: + return MMR_InvalidModuleMap; + }; + } + + if (!MMState.ModuleMapFile) + MMState.ModuleMapFile = lookupModuleMapFile(Dir, IsFramework); + + if (MMState.ModuleMapFile) { + ModuleMapResult Result = + parseAndLoadModuleMapFileImpl(*MMState.ModuleMapFile, IsSystem, Dir); + // Add Dir explicitly in case ModuleMapFile is in a subdirectory. + // E.g. Foo.framework/Modules/module.modulemap + // ^Dir ^ModuleMapFile + if (Result == MMR_NewlyProcessed) + MMState.Status = ModuleMapDirectoryState::Loaded; + else if (Result == MMR_InvalidModuleMap) + MMState.Status = ModuleMapDirectoryState::Invalid; + return Result; + } + return MMR_InvalidModuleMap; +} + +HeaderSearch::ModuleMapResult +HeaderSearch::parseModuleMapFile(StringRef DirName, bool IsSystem, + bool IsFramework) { + if (auto Dir = FileMgr.getOptionalDirectoryRef(DirName)) + return parseModuleMapFile(*Dir, IsSystem, IsFramework); + + return MMR_NoDirectory; +} + +HeaderSearch::ModuleMapResult +HeaderSearch::parseModuleMapFile(DirectoryEntryRef Dir, bool IsSystem, + bool IsFramework) { + auto InsertRes = DirectoryModuleMap.insert(std::pair{ + Dir, ModuleMapDirectoryState{{}, ModuleMapDirectoryState::Invalid}}); + ModuleMapDirectoryState &MMState = InsertRes.first->second; + if (!InsertRes.second) { + switch (MMState.Status) { + case ModuleMapDirectoryState::Parsed: + case ModuleMapDirectoryState::Loaded: + return MMR_AlreadyProcessed; + case ModuleMapDirectoryState::Invalid: + return MMR_InvalidModuleMap; + }; + } + + if (!MMState.ModuleMapFile) + MMState.ModuleMapFile = lookupModuleMapFile(Dir, IsFramework); - if (OptionalFileEntryRef ModuleMapFile = - lookupModuleMapFile(Dir, IsFramework)) { - LoadModuleMapResult Result = - loadModuleMapFileImpl(*ModuleMapFile, IsSystem, Dir); + if (MMState.ModuleMapFile) { + ModuleMapResult Result = + parseModuleMapFileImpl(*MMState.ModuleMapFile, IsSystem, Dir); // Add Dir explicitly in case ModuleMapFile is in a subdirectory. // E.g. Foo.framework/Modules/module.modulemap // ^Dir ^ModuleMapFile - if (Result == LMM_NewlyLoaded) - DirectoryHasModuleMap[Dir] = true; - else if (Result == LMM_InvalidModuleMap) - DirectoryHasModuleMap[Dir] = false; + if (Result == MMR_NewlyProcessed) + MMState.Status = ModuleMapDirectoryState::Parsed; + else if (Result == MMR_InvalidModuleMap) + MMState.Status = ModuleMapDirectoryState::Invalid; return Result; } - return LMM_InvalidModuleMap; + return MMR_InvalidModuleMap; } void HeaderSearch::collectAllModules(SmallVectorImpl &Modules) { @@ -1923,7 +2009,8 @@ void HeaderSearch::collectAllModules(SmallVectorImpl &Modules) { continue; // Try to load a module map file for the search directory. - loadModuleMapFile(*DL.getDirRef(), IsSystem, /*IsFramework*/ false); + parseAndLoadModuleMapFile(*DL.getDirRef(), IsSystem, + /*IsFramework*/ false); // Try to load module map files for immediate subdirectories of this // search directory. @@ -1946,8 +2033,8 @@ void HeaderSearch::loadTopLevelSystemModules() { continue; // Try to load a module map file for the search directory. - loadModuleMapFile(*DL.getDirRef(), DL.isSystemHeaderDirectory(), - DL.isFramework()); + parseAndLoadModuleMapFile(*DL.getDirRef(), DL.isSystemHeaderDirectory(), + DL.isFramework()); } } @@ -1970,8 +2057,9 @@ void HeaderSearch::loadSubdirectoryModuleMaps(DirectoryLookup &SearchDir) { continue; bool IsFramework = llvm::sys::path::extension(Dir->path()) == ".framework"; if (IsFramework == SearchDir.isFramework()) - loadModuleMapFile(Dir->path(), SearchDir.isSystemHeaderDirectory(), - SearchDir.isFramework()); + parseAndLoadModuleMapFile(Dir->path(), + SearchDir.isSystemHeaderDirectory(), + SearchDir.isFramework()); } SearchDir.setSearchedAllModuleMaps(true); diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index a1394fd3900b0..2f3c79d4afdc6 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -1051,7 +1051,9 @@ Module *ModuleMap::inferFrameworkModule(DirectoryEntryRef FrameworkDir, bool IsFrameworkDir = Parent.ends_with(".framework"); if (OptionalFileEntryRef ModMapFile = HeaderInfo.lookupModuleMapFile(*ParentDir, IsFrameworkDir)) { - loadModuleMapFile(*ModMapFile, Attrs.IsSystem, *ParentDir); + // TODO: Parsing a module map should populate `InferredDirectories` + // so we don't need to do a full load here. + parseAndLoadModuleMapFile(*ModMapFile, Attrs.IsSystem, *ParentDir); inferred = InferredDirectories.find(*ParentDir); } @@ -1320,6 +1322,83 @@ void ModuleMap::addHeader(Module *Mod, Module::Header Header, Cb->moduleMapAddHeader(HeaderEntry.getName()); } +bool ModuleMap::parseModuleMapFile(FileEntryRef File, bool IsSystem, + DirectoryEntryRef Dir, FileID ID, + SourceLocation ExternModuleLoc) { + llvm::DenseMap::iterator + Known = ParsedModuleMap.find(File); + if (Known != ParsedModuleMap.end()) + return Known->second == nullptr; + + // If the module map file wasn't already entered, do so now. + if (ID.isInvalid()) { + ID = SourceMgr.translateFile(File); + if (ID.isInvalid() || SourceMgr.isLoadedFileID(ID)) { + auto FileCharacter = + IsSystem ? SrcMgr::C_System_ModuleMap : SrcMgr::C_User_ModuleMap; + ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter); + } + } + + std::optional Buffer = SourceMgr.getBufferOrNone(ID); + if (!Buffer) { + ParsedModuleMap[File] = nullptr; + return true; + } + + Diags.Report(diag::remark_mmap_parse) << File.getName(); + std::optional MaybeMMF = + modulemap::parseModuleMap(ID, Dir, SourceMgr, Diags, IsSystem, nullptr); + + if (!MaybeMMF) { + ParsedModuleMap[File] = nullptr; + return true; + } + + ParsedModuleMaps.push_back( + std::make_unique(std::move(*MaybeMMF))); + const modulemap::ModuleMapFile &MMF = *ParsedModuleMaps.back(); + std::vector PendingExternalModuleMaps; + for (const auto &Decl : MMF.Decls) { + std::visit(llvm::makeVisitor( + [&](const modulemap::ModuleDecl &MD) { + // Only use the first part of the name even for submodules. + // This will correctly load the submodule declarations when + // the module is loaded. + auto &ModuleDecls = + ParsedModules[StringRef(MD.Id.front().first)]; + ModuleDecls.push_back(std::pair(&MMF, &MD)); + }, + [&](const modulemap::ExternModuleDecl &EMD) { + PendingExternalModuleMaps.push_back(&EMD); + }), + Decl); + } + + for (const modulemap::ExternModuleDecl *EMD : PendingExternalModuleMaps) { + StringRef FileNameRef = EMD->Path; + SmallString<128> ModuleMapFileName; + if (llvm::sys::path::is_relative(FileNameRef)) { + ModuleMapFileName += Dir.getName(); + llvm::sys::path::append(ModuleMapFileName, EMD->Path); + FileNameRef = ModuleMapFileName; + } + + if (auto EFile = + SourceMgr.getFileManager().getOptionalFileRef(FileNameRef)) { + parseModuleMapFile(*EFile, IsSystem, EFile->getDir(), FileID(), + ExternModuleLoc); + } + } + + ParsedModuleMap[File] = &MMF; + + for (const auto &Cb : Callbacks) + Cb->moduleMapFileRead(SourceLocation(), File, IsSystem); + + return false; +} + FileID ModuleMap::getContainingModuleMapFileID(const Module *Module) const { if (Module->DefinitionLoc.isInvalid()) return {}; @@ -1458,7 +1537,6 @@ bool ModuleMap::resolveConflicts(Module *Mod, bool Complain) { namespace clang { class ModuleMapLoader { - modulemap::ModuleMapFile &MMF; SourceManager &SourceMgr; DiagnosticsEngine &Diags; @@ -1515,13 +1593,15 @@ class ModuleMapLoader { using Attributes = ModuleMap::Attributes; public: - ModuleMapLoader(modulemap::ModuleMapFile &MMF, SourceManager &SourceMgr, - DiagnosticsEngine &Diags, ModuleMap &Map, FileID ModuleMapFID, + ModuleMapLoader(SourceManager &SourceMgr, DiagnosticsEngine &Diags, + ModuleMap &Map, FileID ModuleMapFID, DirectoryEntryRef Directory, bool IsSystem) - : MMF(MMF), SourceMgr(SourceMgr), Diags(Diags), Map(Map), + : SourceMgr(SourceMgr), Diags(Diags), Map(Map), ModuleMapFID(ModuleMapFID), Directory(Directory), IsSystem(IsSystem) {} - bool loadModuleMapFile(); + bool loadModuleDecl(const modulemap::ModuleDecl &MD); + bool loadExternModuleDecl(const modulemap::ExternModuleDecl &EMD); + bool parseAndLoadModuleMapFile(const modulemap::ModuleMapFile &MMF); }; } // namespace clang @@ -1660,7 +1740,11 @@ void ModuleMapLoader::handleModuleDecl(const modulemap::ModuleDecl &MD) { Map.LangOpts.CurrentModule == ModuleName && SourceMgr.getDecomposedLoc(ModuleNameLoc).first != SourceMgr.getDecomposedLoc(Existing->DefinitionLoc).first; - if (LoadedFromASTFile || Inferred || PartOfFramework || ParsedAsMainInput) { + // TODO: Remove this check when we can avoid loading module maps multiple + // times. + bool SameModuleDecl = ModuleNameLoc == Existing->DefinitionLoc; + if (LoadedFromASTFile || Inferred || PartOfFramework || ParsedAsMainInput || + SameModuleDecl) { ActiveModule = PreviousActiveModule; // Skip the module definition. return; @@ -1773,7 +1857,7 @@ void ModuleMapLoader::handleExternModuleDecl( FileNameRef = ModuleMapFileName; } if (auto File = SourceMgr.getFileManager().getOptionalFileRef(FileNameRef)) - Map.loadModuleMapFile( + Map.parseAndLoadModuleMapFile( *File, IsSystem, Map.HeaderInfo.getHeaderSearchOpts().ModuleMapFileHomeIsCwd ? Directory @@ -2103,7 +2187,19 @@ void ModuleMapLoader::handleInferredModuleDecl( } } -bool ModuleMapLoader::loadModuleMapFile() { +bool ModuleMapLoader::loadModuleDecl(const modulemap::ModuleDecl &MD) { + handleModuleDecl(MD); + return HadError; +} + +bool ModuleMapLoader::loadExternModuleDecl( + const modulemap::ExternModuleDecl &EMD) { + handleExternModuleDecl(EMD); + return HadError; +} + +bool ModuleMapLoader::parseAndLoadModuleMapFile( + const modulemap::ModuleMapFile &MMF) { for (const auto &Decl : MMF.Decls) { std::visit( llvm::makeVisitor( @@ -2116,10 +2212,32 @@ bool ModuleMapLoader::loadModuleMapFile() { return HadError; } -bool ModuleMap::loadModuleMapFile(FileEntryRef File, bool IsSystem, - DirectoryEntryRef Dir, FileID ID, - unsigned *Offset, - SourceLocation ExternModuleLoc) { +Module *ModuleMap::findOrLoadModule(StringRef Name) { + llvm::StringMap::const_iterator Known = Modules.find(Name); + if (Known != Modules.end()) + return Known->getValue(); + + auto ParsedMod = ParsedModules.find(Name); + if (ParsedMod == ParsedModules.end()) + return nullptr; + + Diags.Report(diag::remark_mmap_load_module) << Name; + + for (const auto &ModuleDecl : ParsedMod->second) { + const modulemap::ModuleMapFile &MMF = *ModuleDecl.first; + ModuleMapLoader Loader(SourceMgr, Diags, const_cast(*this), + MMF.ID, *MMF.Dir, MMF.IsSystem); + if (Loader.loadModuleDecl(*ModuleDecl.second)) + return nullptr; + } + + return findModule(Name); +} + +bool ModuleMap::parseAndLoadModuleMapFile(FileEntryRef File, bool IsSystem, + DirectoryEntryRef Dir, FileID ID, + unsigned *Offset, + SourceLocation ExternModuleLoc) { assert(Target && "Missing target information"); llvm::DenseMap::iterator Known = LoadedModuleMap.find(File); @@ -2128,9 +2246,16 @@ bool ModuleMap::loadModuleMapFile(FileEntryRef File, bool IsSystem, // If the module map file wasn't already entered, do so now. if (ID.isInvalid()) { - auto FileCharacter = - IsSystem ? SrcMgr::C_System_ModuleMap : SrcMgr::C_User_ModuleMap; - ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter); + ID = SourceMgr.translateFile(File); + // TODO: The way we compute affecting module maps requires this to be a + // local FileID. This should be changed to reuse loaded FileIDs when + // available, and change the way that affecting module maps are + // computed to not require this. + if (ID.isInvalid() || SourceMgr.isLoadedFileID(ID)) { + auto FileCharacter = + IsSystem ? SrcMgr::C_System_ModuleMap : SrcMgr::C_User_ModuleMap; + ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter); + } } assert(Target && "Missing target information"); @@ -2144,8 +2269,9 @@ bool ModuleMap::loadModuleMapFile(FileEntryRef File, bool IsSystem, modulemap::parseModuleMap(ID, Dir, SourceMgr, Diags, IsSystem, Offset); bool Result = false; if (MMF) { - ModuleMapLoader Loader(*MMF, SourceMgr, Diags, *this, ID, Dir, IsSystem); - Result = Loader.loadModuleMapFile(); + Diags.Report(diag::remark_mmap_load) << File.getName(); + ModuleMapLoader Loader(SourceMgr, Diags, *this, ID, Dir, IsSystem); + Result = Loader.parseAndLoadModuleMapFile(*MMF); } LoadedModuleMap[File] = Result; diff --git a/clang/lib/Lex/ModuleMapFile.cpp b/clang/lib/Lex/ModuleMapFile.cpp index 5cf4a4c3d96c1..f457de85243cc 100644 --- a/clang/lib/Lex/ModuleMapFile.cpp +++ b/clang/lib/Lex/ModuleMapFile.cpp @@ -169,7 +169,10 @@ modulemap::parseModuleMap(FileID ID, clang::DirectoryEntryRef Dir, if (Failed) return std::nullopt; + Parser.MMF.ID = ID; + Parser.MMF.Dir = Dir; Parser.MMF.Start = Start; + Parser.MMF.IsSystem = IsSystem; return std::move(Parser.MMF); } diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 4bba57193ded6..bf1b76b52e1fc 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -395,7 +395,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, case ModuleDeclKind::PartitionInterface: { // We can't have parsed or imported a definition of this module or parsed a // module map defining it already. - if (auto *M = Map.findModule(ModuleName)) { + if (auto *M = Map.findOrLoadModule(ModuleName)) { Diag(Path[0].getLoc(), diag::err_module_redefinition) << ModuleName; if (M->DefinitionLoc.isValid()) Diag(M->DefinitionLoc, diag::note_prev_module_definition); diff --git a/clang/test/ClangScanDeps/modules-canononical-module-map-case.c b/clang/test/ClangScanDeps/modules-canononical-module-map-case.c index ccb0653dfc5ec..34d5949c6bac2 100644 --- a/clang/test/ClangScanDeps/modules-canononical-module-map-case.c +++ b/clang/test/ClangScanDeps/modules-canononical-module-map-case.c @@ -36,6 +36,17 @@ framework module FW { ], "name": "DIR/frameworks/FW.framework/Headers", "type": "directory" + }, + { + "contents": [ + { + "external-contents": "DIR/frameworks/FW.framework/Modules/module.modulemap", + "name": "module.modulemap", + "type": "file" + } + ], + "name": "DIR/frameworks/FW.framework/Modules", + "type": "directory" } ] } diff --git a/clang/test/Modules/Inputs/shadow/A1/A1.h b/clang/test/Modules/Inputs/shadow/A1/A1.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Modules/Inputs/shadow/A1/module.modulemap b/clang/test/Modules/Inputs/shadow/A1/module.modulemap index 9439a431b1dbe..3a47280776ae2 100644 --- a/clang/test/Modules/Inputs/shadow/A1/module.modulemap +++ b/clang/test/Modules/Inputs/shadow/A1/module.modulemap @@ -2,4 +2,6 @@ module A { header "A.h" } -module A1 {} +module A1 { + header "A1.h" +} diff --git a/clang/test/Modules/Inputs/shadow/A2/A2.h b/clang/test/Modules/Inputs/shadow/A2/A2.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Modules/Inputs/shadow/A2/module.modulemap b/clang/test/Modules/Inputs/shadow/A2/module.modulemap index 935d89bb425e0..9e6fe6448ead8 100644 --- a/clang/test/Modules/Inputs/shadow/A2/module.modulemap +++ b/clang/test/Modules/Inputs/shadow/A2/module.modulemap @@ -2,4 +2,6 @@ module A { header "A.h" } -module A2 {} +module A2 { + header "A2.h" +} diff --git a/clang/test/Modules/lazy-by-name-lookup.c b/clang/test/Modules/lazy-by-name-lookup.c new file mode 100644 index 0000000000000..11a3a5cda709d --- /dev/null +++ b/clang/test/Modules/lazy-by-name-lookup.c @@ -0,0 +1,31 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t \ +// RUN: -fmodules-cache-path=%t/cache %t/tu.c -fsyntax-only -Rmodule-map \ +// RUN: -verify + +//--- module.modulemap + +module A { + header "A.h" +} + +module B { + header "B.h" +} + +//--- A.h + +//--- B.h + +//--- tu.c + +#pragma clang __debug module_lookup A // does module map search for A +#pragma clang __debug module_map A // A is now in the ModuleMap, +#pragma clang __debug module_map B // expected-warning{{unknown module 'B'}} + // but B isn't. +#include // Now load B via header search + +// expected-remark@*{{parsing modulemap}} +// expected-remark@*{{loading parsed module 'A'}} +// expected-remark@*{{loading modulemap}} \ No newline at end of file diff --git a/clang/test/Modules/shadow.m b/clang/test/Modules/shadow.m index 44320af2b0c66..c45d0185d4d80 100644 --- a/clang/test/Modules/shadow.m +++ b/clang/test/Modules/shadow.m @@ -1,13 +1,14 @@ // RUN: rm -rf %t -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 -I %S/Inputs/shadow %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %S/Inputs/shadow %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION // REDEFINITION: error: redefinition of module 'A' // REDEFINITION: note: previously defined -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -I %S/Inputs/shadow %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c-header %S/Inputs/shadow/A1/module.modulemap -emit-module -o %t/A.pcm -fmodule-name=A +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-file=A=%t/A.pcm -I %S/Inputs/shadow %s -verify -@import A1; -@import A2; +#import "A1/A1.h" +#import "A2/A2.h" @import A; #import "A2/A.h" // expected-note {{implicitly imported}} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp index 16d2beb48c82c..eec24456fbbb9 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp @@ -325,7 +325,7 @@ bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, auto file = HS.lookupModuleMapFile(*dir, is_framework); if (!file) return error(); - if (!HS.loadModuleMapFile(*file, is_system)) + if (!HS.parseAndLoadModuleMapFile(*file, is_system)) return error(); } }