diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp | 2338 |
1 files changed, 2338 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp b/contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp new file mode 100644 index 0000000..ef322d8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp @@ -0,0 +1,2338 @@ +//===--- ModuleMap.cpp - Describe the layout of modules ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ModuleMap implementation, which describes the layout +// of a module as it relates to headers. +// +//===----------------------------------------------------------------------===// +#include "clang/Lex/ModuleMap.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/LiteralSupport.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <stdlib.h> +#if defined(LLVM_ON_UNIX) +#include <limits.h> +#endif +using namespace clang; + +Module::ExportDecl +ModuleMap::resolveExport(Module *Mod, + const Module::UnresolvedExportDecl &Unresolved, + bool Complain) const { + // We may have just a wildcard. + if (Unresolved.Id.empty()) { + assert(Unresolved.Wildcard && "Invalid unresolved export"); + return Module::ExportDecl(nullptr, true); + } + + // Resolve the module-id. + Module *Context = resolveModuleId(Unresolved.Id, Mod, Complain); + if (!Context) + return Module::ExportDecl(); + + return Module::ExportDecl(Context, Unresolved.Wildcard); +} + +Module *ModuleMap::resolveModuleId(const ModuleId &Id, Module *Mod, + bool Complain) const { + // Find the starting module. + Module *Context = lookupModuleUnqualified(Id[0].first, Mod); + if (!Context) { + if (Complain) + Diags.Report(Id[0].second, diag::err_mmap_missing_module_unqualified) + << Id[0].first << Mod->getFullModuleName(); + + return nullptr; + } + + // Dig into the module path. + for (unsigned I = 1, N = Id.size(); I != N; ++I) { + Module *Sub = lookupModuleQualified(Id[I].first, Context); + if (!Sub) { + if (Complain) + Diags.Report(Id[I].second, diag::err_mmap_missing_module_qualified) + << Id[I].first << Context->getFullModuleName() + << SourceRange(Id[0].second, Id[I-1].second); + + return nullptr; + } + + Context = Sub; + } + + return Context; +} + +ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags, + const LangOptions &LangOpts, const TargetInfo *Target, + HeaderSearch &HeaderInfo) + : SourceMgr(SourceMgr), Diags(Diags), LangOpts(LangOpts), Target(Target), + HeaderInfo(HeaderInfo), BuiltinIncludeDir(nullptr), + CompilingModule(nullptr), SourceModule(nullptr) {} + +ModuleMap::~ModuleMap() { + for (llvm::StringMap<Module *>::iterator I = Modules.begin(), + IEnd = Modules.end(); + I != IEnd; ++I) { + delete I->getValue(); + } +} + +void ModuleMap::setTarget(const TargetInfo &Target) { + assert((!this->Target || this->Target == &Target) && + "Improper target override"); + this->Target = &Target; +} + +/// \brief "Sanitize" a filename so that it can be used as an identifier. +static StringRef sanitizeFilenameAsIdentifier(StringRef Name, + SmallVectorImpl<char> &Buffer) { + if (Name.empty()) + return Name; + + if (!isValidIdentifier(Name)) { + // If we don't already have something with the form of an identifier, + // create a buffer with the sanitized name. + Buffer.clear(); + if (isDigit(Name[0])) + Buffer.push_back('_'); + Buffer.reserve(Buffer.size() + Name.size()); + for (unsigned I = 0, N = Name.size(); I != N; ++I) { + if (isIdentifierBody(Name[I])) + Buffer.push_back(Name[I]); + else + Buffer.push_back('_'); + } + + Name = StringRef(Buffer.data(), Buffer.size()); + } + + while (llvm::StringSwitch<bool>(Name) +#define KEYWORD(Keyword,Conditions) .Case(#Keyword, true) +#define ALIAS(Keyword, AliasOf, Conditions) .Case(Keyword, true) +#include "clang/Basic/TokenKinds.def" + .Default(false)) { + if (Name.data() != Buffer.data()) + Buffer.append(Name.begin(), Name.end()); + Buffer.push_back('_'); + Name = StringRef(Buffer.data(), Buffer.size()); + } + + return Name; +} + +/// \brief Determine whether the given file name is the name of a builtin +/// header, supplied by Clang to replace, override, or augment existing system +/// headers. +static bool isBuiltinHeader(StringRef FileName) { + return llvm::StringSwitch<bool>(FileName) + .Case("float.h", true) + .Case("iso646.h", true) + .Case("limits.h", true) + .Case("stdalign.h", true) + .Case("stdarg.h", true) + .Case("stdbool.h", true) + .Case("stddef.h", true) + .Case("stdint.h", true) + .Case("tgmath.h", true) + .Case("unwind.h", true) + .Default(false); +} + +ModuleMap::HeadersMap::iterator +ModuleMap::findKnownHeader(const FileEntry *File) { + HeadersMap::iterator Known = Headers.find(File); + if (Known == Headers.end() && File->getDir() == BuiltinIncludeDir && + isBuiltinHeader(llvm::sys::path::filename(File->getName()))) { + HeaderInfo.loadTopLevelSystemModules(); + return Headers.find(File); + } + return Known; +} + +ModuleMap::KnownHeader +ModuleMap::findHeaderInUmbrellaDirs(const FileEntry *File, + SmallVectorImpl<const DirectoryEntry *> &IntermediateDirs) { + const DirectoryEntry *Dir = File->getDir(); + assert(Dir && "file in no directory"); + + // Note: as an egregious but useful hack we use the real path here, because + // frameworks moving from top-level frameworks to embedded frameworks tend + // to be symlinked from the top-level location to the embedded location, + // and we need to resolve lookups as if we had found the embedded location. + StringRef DirName = SourceMgr.getFileManager().getCanonicalName(Dir); + + // Keep walking up the directory hierarchy, looking for a directory with + // an umbrella header. + do { + auto KnownDir = UmbrellaDirs.find(Dir); + if (KnownDir != UmbrellaDirs.end()) + return KnownHeader(KnownDir->second, NormalHeader); + + IntermediateDirs.push_back(Dir); + + // Retrieve our parent path. + DirName = llvm::sys::path::parent_path(DirName); + if (DirName.empty()) + break; + + // Resolve the parent path to a directory entry. + Dir = SourceMgr.getFileManager().getDirectory(DirName); + } while (Dir); + return KnownHeader(); +} + +// Returns true if RequestingModule directly uses RequestedModule. +static bool directlyUses(const Module *RequestingModule, + const Module *RequestedModule) { + return std::find(RequestingModule->DirectUses.begin(), + RequestingModule->DirectUses.end(), + RequestedModule) != RequestingModule->DirectUses.end(); +} + +static bool violatesPrivateInclude(Module *RequestingModule, + const FileEntry *IncFileEnt, + ModuleMap::ModuleHeaderRole Role, + Module *RequestedModule) { + bool IsPrivateRole = Role & ModuleMap::PrivateHeader; +#ifndef NDEBUG + // Check for consistency between the module header role + // as obtained from the lookup and as obtained from the module. + // This check is not cheap, so enable it only for debugging. + bool IsPrivate = false; + SmallVectorImpl<Module::Header> *HeaderList[] = + {&RequestedModule->Headers[Module::HK_Private], + &RequestedModule->Headers[Module::HK_PrivateTextual]}; + for (auto *Hdrs : HeaderList) + IsPrivate |= + std::find_if(Hdrs->begin(), Hdrs->end(), [&](const Module::Header &H) { + return H.Entry == IncFileEnt; + }) != Hdrs->end(); + assert(IsPrivate == IsPrivateRole && "inconsistent headers and roles"); +#endif + return IsPrivateRole && + RequestedModule->getTopLevelModule() != RequestingModule; +} + +static Module *getTopLevelOrNull(Module *M) { + return M ? M->getTopLevelModule() : nullptr; +} + +void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule, + SourceLocation FilenameLoc, + StringRef Filename, + const FileEntry *File) { + // No errors for indirect modules. This may be a bit of a problem for modules + // with no source files. + if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule)) + return; + + if (RequestingModule) + resolveUses(RequestingModule, /*Complain=*/false); + + bool Excluded = false; + Module *Private = nullptr; + Module *NotUsed = nullptr; + + HeadersMap::iterator Known = findKnownHeader(File); + if (Known != Headers.end()) { + for (const KnownHeader &Header : Known->second) { + // If 'File' is part of 'RequestingModule' we can definitely include it. + if (Header.getModule() == RequestingModule) + return; + + // Remember private headers for later printing of a diagnostic. + if (violatesPrivateInclude(RequestingModule, File, Header.getRole(), + Header.getModule())) { + Private = Header.getModule(); + continue; + } + + // If uses need to be specified explicitly, we are only allowed to return + // modules that are explicitly used by the requesting module. + if (RequestingModule && LangOpts.ModulesDeclUse && + !directlyUses(RequestingModule, Header.getModule())) { + NotUsed = Header.getModule(); + continue; + } + + // We have found a module that we can happily use. + return; + } + + Excluded = true; + } + + // We have found a header, but it is private. + if (Private) { + Diags.Report(FilenameLoc, diag::error_use_of_private_header_outside_module) + << Filename; + return; + } + + // We have found a module, but we don't use it. + if (NotUsed) { + Diags.Report(FilenameLoc, diag::error_undeclared_use_of_module) + << RequestingModule->getFullModuleName() << Filename; + return; + } + + if (Excluded || isHeaderInUmbrellaDirs(File)) + return; + + // At this point, only non-modular includes remain. + + if (LangOpts.ModulesStrictDeclUse) { + Diags.Report(FilenameLoc, diag::error_undeclared_use_of_module) + << RequestingModule->getFullModuleName() << Filename; + } else if (RequestingModule) { + diag::kind DiagID = RequestingModule->getTopLevelModule()->IsFramework ? + diag::warn_non_modular_include_in_framework_module : + diag::warn_non_modular_include_in_module; + Diags.Report(FilenameLoc, DiagID) << RequestingModule->getFullModuleName(); + } +} + +ModuleMap::KnownHeader +ModuleMap::findModuleForHeader(const FileEntry *File, + Module *RequestingModule, + bool IncludeTextualHeaders) { + HeadersMap::iterator Known = findKnownHeader(File); + + auto MakeResult = [&](ModuleMap::KnownHeader R) -> ModuleMap::KnownHeader { + if (!IncludeTextualHeaders && (R.getRole() & ModuleMap::TextualHeader)) + return ModuleMap::KnownHeader(); + return R; + }; + + if (Known != Headers.end()) { + ModuleMap::KnownHeader Result; + + // Iterate over all modules that 'File' is part of to find the best fit. + for (SmallVectorImpl<KnownHeader>::iterator I = Known->second.begin(), + E = Known->second.end(); + I != E; ++I) { + // Cannot use a module if it is unavailable. + if (!I->getModule()->isAvailable()) + continue; + + // If 'File' is part of 'RequestingModule', 'RequestingModule' is the + // module we are looking for. + if (I->getModule() == RequestingModule) + return MakeResult(*I); + + // If uses need to be specified explicitly, we are only allowed to return + // modules that are explicitly used by the requesting module. + if (RequestingModule && LangOpts.ModulesDeclUse && + !directlyUses(RequestingModule, I->getModule())) + continue; + + // Prefer a public header over a private header. + if (!Result || (Result.getRole() & ModuleMap::PrivateHeader)) + Result = *I; + } + return MakeResult(Result); + } + + SmallVector<const DirectoryEntry *, 2> SkippedDirs; + KnownHeader H = findHeaderInUmbrellaDirs(File, SkippedDirs); + if (H) { + Module *Result = H.getModule(); + + // Search up the module stack until we find a module with an umbrella + // directory. + Module *UmbrellaModule = Result; + while (!UmbrellaModule->getUmbrellaDir() && UmbrellaModule->Parent) + UmbrellaModule = UmbrellaModule->Parent; + + if (UmbrellaModule->InferSubmodules) { + const FileEntry *UmbrellaModuleMap = + getModuleMapFileForUniquing(UmbrellaModule); + + // Infer submodules for each of the directories we found between + // the directory of the umbrella header and the directory where + // the actual header is located. + bool Explicit = UmbrellaModule->InferExplicitSubmodules; + + for (unsigned I = SkippedDirs.size(); I != 0; --I) { + // Find or create the module that corresponds to this directory name. + SmallString<32> NameBuf; + StringRef Name = sanitizeFilenameAsIdentifier( + llvm::sys::path::stem(SkippedDirs[I-1]->getName()), NameBuf); + Result = findOrCreateModule(Name, Result, /*IsFramework=*/false, + Explicit).first; + InferredModuleAllowedBy[Result] = UmbrellaModuleMap; + Result->IsInferred = true; + + // Associate the module and the directory. + UmbrellaDirs[SkippedDirs[I-1]] = Result; + + // If inferred submodules export everything they import, add a + // wildcard to the set of exports. + if (UmbrellaModule->InferExportWildcard && Result->Exports.empty()) + Result->Exports.push_back(Module::ExportDecl(nullptr, true)); + } + + // Infer a submodule with the same name as this header file. + SmallString<32> NameBuf; + StringRef Name = sanitizeFilenameAsIdentifier( + llvm::sys::path::stem(File->getName()), NameBuf); + Result = findOrCreateModule(Name, Result, /*IsFramework=*/false, + Explicit).first; + InferredModuleAllowedBy[Result] = UmbrellaModuleMap; + Result->IsInferred = true; + Result->addTopHeader(File); + + // If inferred submodules export everything they import, add a + // wildcard to the set of exports. + if (UmbrellaModule->InferExportWildcard && Result->Exports.empty()) + Result->Exports.push_back(Module::ExportDecl(nullptr, true)); + } else { + // Record each of the directories we stepped through as being part of + // the module we found, since the umbrella header covers them all. + for (unsigned I = 0, N = SkippedDirs.size(); I != N; ++I) + UmbrellaDirs[SkippedDirs[I]] = Result; + } + + Headers[File].push_back(KnownHeader(Result, NormalHeader)); + + // If a header corresponds to an unavailable module, don't report + // that it maps to anything. + if (!Result->isAvailable()) + return KnownHeader(); + + return MakeResult(Headers[File].back()); + } + + return KnownHeader(); +} + +bool ModuleMap::isHeaderInUnavailableModule(const FileEntry *Header) const { + return isHeaderUnavailableInModule(Header, nullptr); +} + +bool +ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header, + const Module *RequestingModule) const { + HeadersMap::const_iterator Known = Headers.find(Header); + if (Known != Headers.end()) { + for (SmallVectorImpl<KnownHeader>::const_iterator + I = Known->second.begin(), + E = Known->second.end(); + I != E; ++I) { + if (I->isAvailable() && (!RequestingModule || + I->getModule()->isSubModuleOf(RequestingModule))) + return false; + } + return true; + } + + const DirectoryEntry *Dir = Header->getDir(); + SmallVector<const DirectoryEntry *, 2> SkippedDirs; + StringRef DirName = Dir->getName(); + + auto IsUnavailable = [&](const Module *M) { + return !M->isAvailable() && (!RequestingModule || + M->isSubModuleOf(RequestingModule)); + }; + + // Keep walking up the directory hierarchy, looking for a directory with + // an umbrella header. + do { + llvm::DenseMap<const DirectoryEntry *, Module *>::const_iterator KnownDir + = UmbrellaDirs.find(Dir); + if (KnownDir != UmbrellaDirs.end()) { + Module *Found = KnownDir->second; + if (IsUnavailable(Found)) + return true; + + // Search up the module stack until we find a module with an umbrella + // directory. + Module *UmbrellaModule = Found; + while (!UmbrellaModule->getUmbrellaDir() && UmbrellaModule->Parent) + UmbrellaModule = UmbrellaModule->Parent; + + if (UmbrellaModule->InferSubmodules) { + for (unsigned I = SkippedDirs.size(); I != 0; --I) { + // Find or create the module that corresponds to this directory name. + SmallString<32> NameBuf; + StringRef Name = sanitizeFilenameAsIdentifier( + llvm::sys::path::stem(SkippedDirs[I-1]->getName()), + NameBuf); + Found = lookupModuleQualified(Name, Found); + if (!Found) + return false; + if (IsUnavailable(Found)) + return true; + } + + // Infer a submodule with the same name as this header file. + SmallString<32> NameBuf; + StringRef Name = sanitizeFilenameAsIdentifier( + llvm::sys::path::stem(Header->getName()), + NameBuf); + Found = lookupModuleQualified(Name, Found); + if (!Found) + return false; + } + + return IsUnavailable(Found); + } + + SkippedDirs.push_back(Dir); + + // Retrieve our parent path. + DirName = llvm::sys::path::parent_path(DirName); + if (DirName.empty()) + break; + + // Resolve the parent path to a directory entry. + Dir = SourceMgr.getFileManager().getDirectory(DirName); + } while (Dir); + + return false; +} + +Module *ModuleMap::findModule(StringRef Name) const { + llvm::StringMap<Module *>::const_iterator Known = Modules.find(Name); + if (Known != Modules.end()) + return Known->getValue(); + + return nullptr; +} + +Module *ModuleMap::lookupModuleUnqualified(StringRef Name, + Module *Context) const { + for(; Context; Context = Context->Parent) { + if (Module *Sub = lookupModuleQualified(Name, Context)) + return Sub; + } + + return findModule(Name); +} + +Module *ModuleMap::lookupModuleQualified(StringRef Name, Module *Context) const{ + if (!Context) + return findModule(Name); + + return Context->findSubmodule(Name); +} + +std::pair<Module *, bool> +ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, + bool IsExplicit) { + // Try to find an existing module with this name. + if (Module *Sub = lookupModuleQualified(Name, Parent)) + return std::make_pair(Sub, false); + + // Create a new module with this name. + Module *Result = new Module(Name, SourceLocation(), Parent, + IsFramework, IsExplicit); + if (LangOpts.CurrentModule == Name) { + SourceModule = Result; + SourceModuleName = Name; + } + if (!Parent) { + Modules[Name] = Result; + if (!LangOpts.CurrentModule.empty() && !CompilingModule && + Name == LangOpts.CurrentModule) { + CompilingModule = Result; + } + } + return std::make_pair(Result, true); +} + +/// \brief For a framework module, infer the framework against which we +/// should link. +static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir, + FileManager &FileMgr) { + assert(Mod->IsFramework && "Can only infer linking for framework modules"); + assert(!Mod->isSubFramework() && + "Can only infer linking for top-level frameworks"); + + SmallString<128> LibName; + LibName += FrameworkDir->getName(); + llvm::sys::path::append(LibName, Mod->Name); + if (FileMgr.getFile(LibName)) { + Mod->LinkLibraries.push_back(Module::LinkLibrary(Mod->Name, + /*IsFramework=*/true)); + } +} + +Module * +ModuleMap::inferFrameworkModule(StringRef ModuleName, + const DirectoryEntry *FrameworkDir, + bool IsSystem, + Module *Parent) { + Attributes Attrs; + Attrs.IsSystem = IsSystem; + return inferFrameworkModule(ModuleName, FrameworkDir, Attrs, Parent); +} + +Module *ModuleMap::inferFrameworkModule(StringRef ModuleName, + const DirectoryEntry *FrameworkDir, + Attributes Attrs, Module *Parent) { + + // Check whether we've already found this module. + if (Module *Mod = lookupModuleQualified(ModuleName, Parent)) + return Mod; + + FileManager &FileMgr = SourceMgr.getFileManager(); + + // If the framework has a parent path from which we're allowed to infer + // a framework module, do so. + const FileEntry *ModuleMapFile = nullptr; + if (!Parent) { + // Determine whether we're allowed to infer a module map. + + // Note: as an egregious but useful hack we use the real path here, because + // we might be looking at an embedded framework that symlinks out to a + // top-level framework, and we need to infer as if we were naming the + // top-level framework. + StringRef FrameworkDirName + = SourceMgr.getFileManager().getCanonicalName(FrameworkDir); + + // In case this is a case-insensitive filesystem, make sure the canonical + // directory name matches ModuleName exactly. Modules are case-sensitive. + // FIXME: we should be able to give a fix-it hint for the correct spelling. + if (llvm::sys::path::stem(FrameworkDirName) != ModuleName) + return nullptr; + + bool canInfer = false; + if (llvm::sys::path::has_parent_path(FrameworkDirName)) { + // Figure out the parent path. + StringRef Parent = llvm::sys::path::parent_path(FrameworkDirName); + if (const DirectoryEntry *ParentDir = FileMgr.getDirectory(Parent)) { + // Check whether we have already looked into the parent directory + // for a module map. + llvm::DenseMap<const DirectoryEntry *, InferredDirectory>::const_iterator + inferred = InferredDirectories.find(ParentDir); + if (inferred == InferredDirectories.end()) { + // We haven't looked here before. Load a module map, if there is + // one. + bool IsFrameworkDir = Parent.endswith(".framework"); + if (const FileEntry *ModMapFile = + HeaderInfo.lookupModuleMapFile(ParentDir, IsFrameworkDir)) { + parseModuleMapFile(ModMapFile, Attrs.IsSystem, ParentDir); + inferred = InferredDirectories.find(ParentDir); + } + + if (inferred == InferredDirectories.end()) + inferred = InferredDirectories.insert( + std::make_pair(ParentDir, InferredDirectory())).first; + } + + if (inferred->second.InferModules) { + // We're allowed to infer for this directory, but make sure it's okay + // to infer this particular module. + StringRef Name = llvm::sys::path::stem(FrameworkDirName); + canInfer = std::find(inferred->second.ExcludedModules.begin(), + inferred->second.ExcludedModules.end(), + Name) == inferred->second.ExcludedModules.end(); + + Attrs.IsSystem |= inferred->second.Attrs.IsSystem; + Attrs.IsExternC |= inferred->second.Attrs.IsExternC; + Attrs.IsExhaustive |= inferred->second.Attrs.IsExhaustive; + ModuleMapFile = inferred->second.ModuleMapFile; + } + } + } + + // If we're not allowed to infer a framework module, don't. + if (!canInfer) + return nullptr; + } else + ModuleMapFile = getModuleMapFileForUniquing(Parent); + + + // Look for an umbrella header. + SmallString<128> UmbrellaName = StringRef(FrameworkDir->getName()); + llvm::sys::path::append(UmbrellaName, "Headers", ModuleName + ".h"); + const FileEntry *UmbrellaHeader = FileMgr.getFile(UmbrellaName); + + // FIXME: If there's no umbrella header, we could probably scan the + // framework to load *everything*. But, it's not clear that this is a good + // idea. + if (!UmbrellaHeader) + return nullptr; + + Module *Result = new Module(ModuleName, SourceLocation(), Parent, + /*IsFramework=*/true, /*IsExplicit=*/false); + InferredModuleAllowedBy[Result] = ModuleMapFile; + Result->IsInferred = true; + if (LangOpts.CurrentModule == ModuleName) { + SourceModule = Result; + SourceModuleName = ModuleName; + } + + Result->IsSystem |= Attrs.IsSystem; + Result->IsExternC |= Attrs.IsExternC; + Result->ConfigMacrosExhaustive |= Attrs.IsExhaustive; + + if (!Parent) + Modules[ModuleName] = Result; + + // umbrella header "umbrella-header-name" + Result->Umbrella = UmbrellaHeader; + Headers[UmbrellaHeader].push_back(KnownHeader(Result, NormalHeader)); + UmbrellaDirs[UmbrellaHeader->getDir()] = Result; + + // export * + Result->Exports.push_back(Module::ExportDecl(nullptr, true)); + + // module * { export * } + Result->InferSubmodules = true; + Result->InferExportWildcard = true; + + // Look for subframeworks. + std::error_code EC; + SmallString<128> SubframeworksDirName + = StringRef(FrameworkDir->getName()); + llvm::sys::path::append(SubframeworksDirName, "Frameworks"); + llvm::sys::path::native(SubframeworksDirName); + for (llvm::sys::fs::directory_iterator + Dir(SubframeworksDirName.str(), EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + if (!StringRef(Dir->path()).endswith(".framework")) + continue; + + if (const DirectoryEntry *SubframeworkDir + = FileMgr.getDirectory(Dir->path())) { + // Note: as an egregious but useful hack, we use the real path here and + // check whether it is actually a subdirectory of the parent directory. + // This will not be the case if the 'subframework' is actually a symlink + // out to a top-level framework. + StringRef SubframeworkDirName = FileMgr.getCanonicalName(SubframeworkDir); + bool FoundParent = false; + do { + // Get the parent directory name. + SubframeworkDirName + = llvm::sys::path::parent_path(SubframeworkDirName); + if (SubframeworkDirName.empty()) + break; + + if (FileMgr.getDirectory(SubframeworkDirName) == FrameworkDir) { + FoundParent = true; + break; + } + } while (true); + + if (!FoundParent) + continue; + + // FIXME: Do we want to warn about subframeworks without umbrella headers? + SmallString<32> NameBuf; + inferFrameworkModule(sanitizeFilenameAsIdentifier( + llvm::sys::path::stem(Dir->path()), NameBuf), + SubframeworkDir, Attrs, Result); + } + } + + // If the module is a top-level framework, automatically link against the + // framework. + if (!Result->isSubFramework()) { + inferFrameworkLink(Result, FrameworkDir, FileMgr); + } + + return Result; +} + +void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader){ + Headers[UmbrellaHeader].push_back(KnownHeader(Mod, NormalHeader)); + Mod->Umbrella = UmbrellaHeader; + UmbrellaDirs[UmbrellaHeader->getDir()] = Mod; +} + +void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir) { + Mod->Umbrella = UmbrellaDir; + UmbrellaDirs[UmbrellaDir] = Mod; +} + +static Module::HeaderKind headerRoleToKind(ModuleMap::ModuleHeaderRole Role) { + switch ((int)Role) { + default: llvm_unreachable("unknown header role"); + case ModuleMap::NormalHeader: + return Module::HK_Normal; + case ModuleMap::PrivateHeader: + return Module::HK_Private; + case ModuleMap::TextualHeader: + return Module::HK_Textual; + case ModuleMap::PrivateHeader | ModuleMap::TextualHeader: + return Module::HK_PrivateTextual; + } +} + +void ModuleMap::addHeader(Module *Mod, Module::Header Header, + ModuleHeaderRole Role) { + if (!(Role & TextualHeader)) { + bool isCompilingModuleHeader = Mod->getTopLevelModule() == CompilingModule; + HeaderInfo.MarkFileModuleHeader(Header.Entry, Role, + isCompilingModuleHeader); + } + Headers[Header.Entry].push_back(KnownHeader(Mod, Role)); + + Mod->Headers[headerRoleToKind(Role)].push_back(std::move(Header)); +} + +void ModuleMap::excludeHeader(Module *Mod, Module::Header Header) { + // Add this as a known header so we won't implicitly add it to any + // umbrella directory module. + // FIXME: Should we only exclude it from umbrella modules within the + // specified module? + (void) Headers[Header.Entry]; + + Mod->Headers[Module::HK_Excluded].push_back(std::move(Header)); +} + +const FileEntry * +ModuleMap::getContainingModuleMapFile(const Module *Module) const { + if (Module->DefinitionLoc.isInvalid()) + return nullptr; + + return SourceMgr.getFileEntryForID( + SourceMgr.getFileID(Module->DefinitionLoc)); +} + +const FileEntry *ModuleMap::getModuleMapFileForUniquing(const Module *M) const { + if (M->IsInferred) { + assert(InferredModuleAllowedBy.count(M) && "missing inferred module map"); + return InferredModuleAllowedBy.find(M)->second; + } + return getContainingModuleMapFile(M); +} + +void ModuleMap::setInferredModuleAllowedBy(Module *M, const FileEntry *ModMap) { + assert(M->IsInferred && "module not inferred"); + InferredModuleAllowedBy[M] = ModMap; +} + +void ModuleMap::dump() { + llvm::errs() << "Modules:"; + for (llvm::StringMap<Module *>::iterator M = Modules.begin(), + MEnd = Modules.end(); + M != MEnd; ++M) + M->getValue()->print(llvm::errs(), 2); + + llvm::errs() << "Headers:"; + for (HeadersMap::iterator H = Headers.begin(), HEnd = Headers.end(); + H != HEnd; ++H) { + llvm::errs() << " \"" << H->first->getName() << "\" -> "; + for (SmallVectorImpl<KnownHeader>::const_iterator I = H->second.begin(), + E = H->second.end(); + I != E; ++I) { + if (I != H->second.begin()) + llvm::errs() << ","; + llvm::errs() << I->getModule()->getFullModuleName(); + } + llvm::errs() << "\n"; + } +} + +bool ModuleMap::resolveExports(Module *Mod, bool Complain) { + bool HadError = false; + for (unsigned I = 0, N = Mod->UnresolvedExports.size(); I != N; ++I) { + Module::ExportDecl Export = resolveExport(Mod, Mod->UnresolvedExports[I], + Complain); + if (Export.getPointer() || Export.getInt()) + Mod->Exports.push_back(Export); + else + HadError = true; + } + Mod->UnresolvedExports.clear(); + return HadError; +} + +bool ModuleMap::resolveUses(Module *Mod, bool Complain) { + bool HadError = false; + for (unsigned I = 0, N = Mod->UnresolvedDirectUses.size(); I != N; ++I) { + Module *DirectUse = + resolveModuleId(Mod->UnresolvedDirectUses[I], Mod, Complain); + if (DirectUse) + Mod->DirectUses.push_back(DirectUse); + else + HadError = true; + } + Mod->UnresolvedDirectUses.clear(); + return HadError; +} + +bool ModuleMap::resolveConflicts(Module *Mod, bool Complain) { + bool HadError = false; + for (unsigned I = 0, N = Mod->UnresolvedConflicts.size(); I != N; ++I) { + Module *OtherMod = resolveModuleId(Mod->UnresolvedConflicts[I].Id, + Mod, Complain); + if (!OtherMod) { + HadError = true; + continue; + } + + Module::Conflict Conflict; + Conflict.Other = OtherMod; + Conflict.Message = Mod->UnresolvedConflicts[I].Message; + Mod->Conflicts.push_back(Conflict); + } + Mod->UnresolvedConflicts.clear(); + return HadError; +} + +Module *ModuleMap::inferModuleFromLocation(FullSourceLoc Loc) { + if (Loc.isInvalid()) + return nullptr; + + // Use the expansion location to determine which module we're in. + FullSourceLoc ExpansionLoc = Loc.getExpansionLoc(); + if (!ExpansionLoc.isFileID()) + return nullptr; + + const SourceManager &SrcMgr = Loc.getManager(); + FileID ExpansionFileID = ExpansionLoc.getFileID(); + + while (const FileEntry *ExpansionFile + = SrcMgr.getFileEntryForID(ExpansionFileID)) { + // Find the module that owns this header (if any). + if (Module *Mod = findModuleForHeader(ExpansionFile).getModule()) + return Mod; + + // No module owns this header, so look up the inclusion chain to see if + // any included header has an associated module. + SourceLocation IncludeLoc = SrcMgr.getIncludeLoc(ExpansionFileID); + if (IncludeLoc.isInvalid()) + return nullptr; + + ExpansionFileID = SrcMgr.getFileID(IncludeLoc); + } + + return nullptr; +} + +//----------------------------------------------------------------------------// +// Module map file parser +//----------------------------------------------------------------------------// + +namespace clang { + /// \brief A token in a module map file. + struct MMToken { + enum TokenKind { + Comma, + ConfigMacros, + Conflict, + EndOfFile, + HeaderKeyword, + Identifier, + Exclaim, + ExcludeKeyword, + ExplicitKeyword, + ExportKeyword, + ExternKeyword, + FrameworkKeyword, + LinkKeyword, + ModuleKeyword, + Period, + PrivateKeyword, + UmbrellaKeyword, + UseKeyword, + RequiresKeyword, + Star, + StringLiteral, + TextualKeyword, + LBrace, + RBrace, + LSquare, + RSquare + } Kind; + + unsigned Location; + unsigned StringLength; + const char *StringData; + + void clear() { + Kind = EndOfFile; + Location = 0; + StringLength = 0; + StringData = nullptr; + } + + bool is(TokenKind K) const { return Kind == K; } + + SourceLocation getLocation() const { + return SourceLocation::getFromRawEncoding(Location); + } + + StringRef getString() const { + return StringRef(StringData, StringLength); + } + }; + + class ModuleMapParser { + Lexer &L; + SourceManager &SourceMgr; + + /// \brief Default target information, used only for string literal + /// parsing. + const TargetInfo *Target; + + DiagnosticsEngine &Diags; + ModuleMap ⤅ + + /// \brief The current module map file. + const FileEntry *ModuleMapFile; + + /// \brief The directory that file names in this module map file should + /// be resolved relative to. + const DirectoryEntry *Directory; + + /// \brief The directory containing Clang-supplied headers. + const DirectoryEntry *BuiltinIncludeDir; + + /// \brief Whether this module map is in a system header directory. + bool IsSystem; + + /// \brief Whether an error occurred. + bool HadError; + + /// \brief Stores string data for the various string literals referenced + /// during parsing. + llvm::BumpPtrAllocator StringData; + + /// \brief The current token. + MMToken Tok; + + /// \brief The active module. + Module *ActiveModule; + + /// \brief Consume the current token and return its location. + SourceLocation consumeToken(); + + /// \brief Skip tokens until we reach the a token with the given kind + /// (or the end of the file). + void skipUntil(MMToken::TokenKind K); + + typedef SmallVector<std::pair<std::string, SourceLocation>, 2> ModuleId; + bool parseModuleId(ModuleId &Id); + void parseModuleDecl(); + void parseExternModuleDecl(); + void parseRequiresDecl(); + void parseHeaderDecl(clang::MMToken::TokenKind, + SourceLocation LeadingLoc); + void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); + void parseExportDecl(); + void parseUseDecl(); + void parseLinkDecl(); + void parseConfigMacros(); + void parseConflict(); + void parseInferredModuleDecl(bool Framework, bool Explicit); + + typedef ModuleMap::Attributes Attributes; + bool parseOptionalAttributes(Attributes &Attrs); + + public: + explicit ModuleMapParser(Lexer &L, SourceManager &SourceMgr, + const TargetInfo *Target, + DiagnosticsEngine &Diags, + ModuleMap &Map, + const FileEntry *ModuleMapFile, + const DirectoryEntry *Directory, + const DirectoryEntry *BuiltinIncludeDir, + bool IsSystem) + : L(L), SourceMgr(SourceMgr), Target(Target), Diags(Diags), Map(Map), + ModuleMapFile(ModuleMapFile), Directory(Directory), + BuiltinIncludeDir(BuiltinIncludeDir), IsSystem(IsSystem), + HadError(false), ActiveModule(nullptr) + { + Tok.clear(); + consumeToken(); + } + + bool parseModuleMapFile(); + }; +} + +SourceLocation ModuleMapParser::consumeToken() { +retry: + SourceLocation Result = Tok.getLocation(); + Tok.clear(); + + Token LToken; + L.LexFromRawLexer(LToken); + Tok.Location = LToken.getLocation().getRawEncoding(); + switch (LToken.getKind()) { + case tok::raw_identifier: { + StringRef RI = LToken.getRawIdentifier(); + Tok.StringData = RI.data(); + Tok.StringLength = RI.size(); + Tok.Kind = llvm::StringSwitch<MMToken::TokenKind>(RI) + .Case("config_macros", MMToken::ConfigMacros) + .Case("conflict", MMToken::Conflict) + .Case("exclude", MMToken::ExcludeKeyword) + .Case("explicit", MMToken::ExplicitKeyword) + .Case("export", MMToken::ExportKeyword) + .Case("extern", MMToken::ExternKeyword) + .Case("framework", MMToken::FrameworkKeyword) + .Case("header", MMToken::HeaderKeyword) + .Case("link", MMToken::LinkKeyword) + .Case("module", MMToken::ModuleKeyword) + .Case("private", MMToken::PrivateKeyword) + .Case("requires", MMToken::RequiresKeyword) + .Case("textual", MMToken::TextualKeyword) + .Case("umbrella", MMToken::UmbrellaKeyword) + .Case("use", MMToken::UseKeyword) + .Default(MMToken::Identifier); + break; + } + + case tok::comma: + Tok.Kind = MMToken::Comma; + break; + + case tok::eof: + Tok.Kind = MMToken::EndOfFile; + break; + + case tok::l_brace: + Tok.Kind = MMToken::LBrace; + break; + + case tok::l_square: + Tok.Kind = MMToken::LSquare; + break; + + case tok::period: + Tok.Kind = MMToken::Period; + break; + + case tok::r_brace: + Tok.Kind = MMToken::RBrace; + break; + + case tok::r_square: + Tok.Kind = MMToken::RSquare; + break; + + case tok::star: + Tok.Kind = MMToken::Star; + break; + + case tok::exclaim: + Tok.Kind = MMToken::Exclaim; + break; + + case tok::string_literal: { + if (LToken.hasUDSuffix()) { + Diags.Report(LToken.getLocation(), diag::err_invalid_string_udl); + HadError = true; + goto retry; + } + + // Parse the string literal. + LangOptions LangOpts; + StringLiteralParser StringLiteral(LToken, SourceMgr, LangOpts, *Target); + if (StringLiteral.hadError) + goto retry; + + // Copy the string literal into our string data allocator. + unsigned Length = StringLiteral.GetStringLength(); + char *Saved = StringData.Allocate<char>(Length + 1); + memcpy(Saved, StringLiteral.GetString().data(), Length); + Saved[Length] = 0; + + // Form the token. + Tok.Kind = MMToken::StringLiteral; + Tok.StringData = Saved; + Tok.StringLength = Length; + break; + } + + case tok::comment: + goto retry; + + default: + Diags.Report(LToken.getLocation(), diag::err_mmap_unknown_token); + HadError = true; + goto retry; + } + + return Result; +} + +void ModuleMapParser::skipUntil(MMToken::TokenKind K) { + unsigned braceDepth = 0; + unsigned squareDepth = 0; + do { + switch (Tok.Kind) { + case MMToken::EndOfFile: + return; + + case MMToken::LBrace: + if (Tok.is(K) && braceDepth == 0 && squareDepth == 0) + return; + + ++braceDepth; + break; + + case MMToken::LSquare: + if (Tok.is(K) && braceDepth == 0 && squareDepth == 0) + return; + + ++squareDepth; + break; + + case MMToken::RBrace: + if (braceDepth > 0) + --braceDepth; + else if (Tok.is(K)) + return; + break; + + case MMToken::RSquare: + if (squareDepth > 0) + --squareDepth; + else if (Tok.is(K)) + return; + break; + + default: + if (braceDepth == 0 && squareDepth == 0 && Tok.is(K)) + return; + break; + } + + consumeToken(); + } while (true); +} + +/// \brief Parse a module-id. +/// +/// module-id: +/// identifier +/// identifier '.' module-id +/// +/// \returns true if an error occurred, false otherwise. +bool ModuleMapParser::parseModuleId(ModuleId &Id) { + Id.clear(); + do { + if (Tok.is(MMToken::Identifier) || Tok.is(MMToken::StringLiteral)) { + Id.push_back(std::make_pair(Tok.getString(), Tok.getLocation())); + consumeToken(); + } else { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name); + return true; + } + + if (!Tok.is(MMToken::Period)) + break; + + consumeToken(); + } while (true); + + return false; +} + +namespace { + /// \brief Enumerates the known attributes. + enum AttributeKind { + /// \brief An unknown attribute. + AT_unknown, + /// \brief The 'system' attribute. + AT_system, + /// \brief The 'extern_c' attribute. + AT_extern_c, + /// \brief The 'exhaustive' attribute. + AT_exhaustive + }; +} + +/// \brief Parse a module declaration. +/// +/// module-declaration: +/// 'extern' 'module' module-id string-literal +/// 'explicit'[opt] 'framework'[opt] 'module' module-id attributes[opt] +/// { module-member* } +/// +/// module-member: +/// requires-declaration +/// header-declaration +/// submodule-declaration +/// export-declaration +/// link-declaration +/// +/// submodule-declaration: +/// module-declaration +/// inferred-submodule-declaration +void ModuleMapParser::parseModuleDecl() { + assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) || + Tok.is(MMToken::FrameworkKeyword) || Tok.is(MMToken::ExternKeyword)); + if (Tok.is(MMToken::ExternKeyword)) { + parseExternModuleDecl(); + return; + } + + // Parse 'explicit' or 'framework' keyword, if present. + SourceLocation ExplicitLoc; + bool Explicit = false; + bool Framework = false; + + // Parse 'explicit' keyword, if present. + if (Tok.is(MMToken::ExplicitKeyword)) { + ExplicitLoc = consumeToken(); + Explicit = true; + } + + // Parse 'framework' keyword, if present. + if (Tok.is(MMToken::FrameworkKeyword)) { + consumeToken(); + Framework = true; + } + + // Parse 'module' keyword. + if (!Tok.is(MMToken::ModuleKeyword)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); + consumeToken(); + HadError = true; + return; + } + consumeToken(); // 'module' keyword + + // If we have a wildcard for the module name, this is an inferred submodule. + // Parse it. + if (Tok.is(MMToken::Star)) + return parseInferredModuleDecl(Framework, Explicit); + + // Parse the module name. + ModuleId Id; + if (parseModuleId(Id)) { + HadError = true; + return; + } + + if (ActiveModule) { + if (Id.size() > 1) { + Diags.Report(Id.front().second, diag::err_mmap_nested_submodule_id) + << SourceRange(Id.front().second, Id.back().second); + + HadError = true; + return; + } + } else if (Id.size() == 1 && Explicit) { + // Top-level modules can't be explicit. + Diags.Report(ExplicitLoc, diag::err_mmap_explicit_top_level); + Explicit = false; + ExplicitLoc = SourceLocation(); + HadError = true; + } + + Module *PreviousActiveModule = ActiveModule; + if (Id.size() > 1) { + // This module map defines a submodule. Go find the module of which it + // is a submodule. + ActiveModule = nullptr; + const Module *TopLevelModule = nullptr; + for (unsigned I = 0, N = Id.size() - 1; I != N; ++I) { + if (Module *Next = Map.lookupModuleQualified(Id[I].first, ActiveModule)) { + if (I == 0) + TopLevelModule = Next; + ActiveModule = Next; + continue; + } + + if (ActiveModule) { + Diags.Report(Id[I].second, diag::err_mmap_missing_module_qualified) + << Id[I].first + << ActiveModule->getTopLevelModule()->getFullModuleName(); + } else { + Diags.Report(Id[I].second, diag::err_mmap_expected_module_name); + } + HadError = true; + return; + } + + if (ModuleMapFile != Map.getContainingModuleMapFile(TopLevelModule)) { + assert(ModuleMapFile != Map.getModuleMapFileForUniquing(TopLevelModule) && + "submodule defined in same file as 'module *' that allowed its " + "top-level module"); + Map.addAdditionalModuleMapFile(TopLevelModule, ModuleMapFile); + } + } + + StringRef ModuleName = Id.back().first; + SourceLocation ModuleNameLoc = Id.back().second; + + // Parse the optional attribute list. + Attributes Attrs; + parseOptionalAttributes(Attrs); + + // Parse the opening brace. + if (!Tok.is(MMToken::LBrace)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace) + << ModuleName; + HadError = true; + return; + } + SourceLocation LBraceLoc = consumeToken(); + + // Determine whether this (sub)module has already been defined. + if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) { + if (Existing->DefinitionLoc.isInvalid() && !ActiveModule) { + // Skip the module definition. + skipUntil(MMToken::RBrace); + if (Tok.is(MMToken::RBrace)) + consumeToken(); + else { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); + Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); + HadError = true; + } + return; + } + + Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) + << ModuleName; + Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); + + // Skip the module definition. + skipUntil(MMToken::RBrace); + if (Tok.is(MMToken::RBrace)) + consumeToken(); + + HadError = true; + return; + } + + // Start defining this module. + ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework, + Explicit).first; + ActiveModule->DefinitionLoc = ModuleNameLoc; + if (Attrs.IsSystem || IsSystem) + ActiveModule->IsSystem = true; + if (Attrs.IsExternC) + ActiveModule->IsExternC = true; + ActiveModule->Directory = Directory; + + bool Done = false; + do { + switch (Tok.Kind) { + case MMToken::EndOfFile: + case MMToken::RBrace: + Done = true; + break; + + case MMToken::ConfigMacros: + parseConfigMacros(); + break; + + case MMToken::Conflict: + parseConflict(); + break; + + case MMToken::ExplicitKeyword: + case MMToken::ExternKeyword: + case MMToken::FrameworkKeyword: + case MMToken::ModuleKeyword: + parseModuleDecl(); + break; + + case MMToken::ExportKeyword: + parseExportDecl(); + break; + + case MMToken::UseKeyword: + parseUseDecl(); + break; + + case MMToken::RequiresKeyword: + parseRequiresDecl(); + break; + + case MMToken::TextualKeyword: + parseHeaderDecl(MMToken::TextualKeyword, consumeToken()); + break; + + case MMToken::UmbrellaKeyword: { + SourceLocation UmbrellaLoc = consumeToken(); + if (Tok.is(MMToken::HeaderKeyword)) + parseHeaderDecl(MMToken::UmbrellaKeyword, UmbrellaLoc); + else + parseUmbrellaDirDecl(UmbrellaLoc); + break; + } + + case MMToken::ExcludeKeyword: + parseHeaderDecl(MMToken::ExcludeKeyword, consumeToken()); + break; + + case MMToken::PrivateKeyword: + parseHeaderDecl(MMToken::PrivateKeyword, consumeToken()); + break; + + case MMToken::HeaderKeyword: + parseHeaderDecl(MMToken::HeaderKeyword, consumeToken()); + break; + + case MMToken::LinkKeyword: + parseLinkDecl(); + break; + + default: + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member); + consumeToken(); + break; + } + } while (!Done); + + if (Tok.is(MMToken::RBrace)) + consumeToken(); + else { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); + Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); + HadError = true; + } + + // If the active module is a top-level framework, and there are no link + // libraries, automatically link against the framework. + if (ActiveModule->IsFramework && !ActiveModule->isSubFramework() && + ActiveModule->LinkLibraries.empty()) { + inferFrameworkLink(ActiveModule, Directory, SourceMgr.getFileManager()); + } + + // If the module meets all requirements but is still unavailable, mark the + // whole tree as unavailable to prevent it from building. + if (!ActiveModule->IsAvailable && !ActiveModule->IsMissingRequirement && + ActiveModule->Parent) { + ActiveModule->getTopLevelModule()->markUnavailable(); + ActiveModule->getTopLevelModule()->MissingHeaders.append( + ActiveModule->MissingHeaders.begin(), ActiveModule->MissingHeaders.end()); + } + + // We're done parsing this module. Pop back to the previous module. + ActiveModule = PreviousActiveModule; +} + +/// \brief Parse an extern module declaration. +/// +/// extern module-declaration: +/// 'extern' 'module' module-id string-literal +void ModuleMapParser::parseExternModuleDecl() { + assert(Tok.is(MMToken::ExternKeyword)); + consumeToken(); // 'extern' keyword + + // Parse 'module' keyword. + if (!Tok.is(MMToken::ModuleKeyword)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); + consumeToken(); + HadError = true; + return; + } + consumeToken(); // 'module' keyword + + // Parse the module name. + ModuleId Id; + if (parseModuleId(Id)) { + HadError = true; + return; + } + + // Parse the referenced module map file name. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_mmap_file); + HadError = true; + return; + } + std::string FileName = Tok.getString(); + consumeToken(); // filename + + StringRef FileNameRef = FileName; + SmallString<128> ModuleMapFileName; + if (llvm::sys::path::is_relative(FileNameRef)) { + ModuleMapFileName += Directory->getName(); + llvm::sys::path::append(ModuleMapFileName, FileName); + FileNameRef = ModuleMapFileName.str(); + } + if (const FileEntry *File = SourceMgr.getFileManager().getFile(FileNameRef)) + Map.parseModuleMapFile( + File, /*IsSystem=*/false, + Map.HeaderInfo.getHeaderSearchOpts().ModuleMapFileHomeIsCwd + ? Directory + : File->getDir()); +} + +/// \brief Parse a requires declaration. +/// +/// requires-declaration: +/// 'requires' feature-list +/// +/// feature-list: +/// feature ',' feature-list +/// feature +/// +/// feature: +/// '!'[opt] identifier +void ModuleMapParser::parseRequiresDecl() { + assert(Tok.is(MMToken::RequiresKeyword)); + + // Parse 'requires' keyword. + consumeToken(); + + // Parse the feature-list. + do { + bool RequiredState = true; + if (Tok.is(MMToken::Exclaim)) { + RequiredState = false; + consumeToken(); + } + + if (!Tok.is(MMToken::Identifier)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_feature); + HadError = true; + return; + } + + // Consume the feature name. + std::string Feature = Tok.getString(); + consumeToken(); + + // Add this feature. + ActiveModule->addRequirement(Feature, RequiredState, + Map.LangOpts, *Map.Target); + + if (!Tok.is(MMToken::Comma)) + break; + + // Consume the comma. + consumeToken(); + } while (true); +} + +/// \brief Append to \p Paths the set of paths needed to get to the +/// subframework in which the given module lives. +static void appendSubframeworkPaths(Module *Mod, + SmallVectorImpl<char> &Path) { + // Collect the framework names from the given module to the top-level module. + SmallVector<StringRef, 2> Paths; + for (; Mod; Mod = Mod->Parent) { + if (Mod->IsFramework) + Paths.push_back(Mod->Name); + } + + if (Paths.empty()) + return; + + // Add Frameworks/Name.framework for each subframework. + for (unsigned I = Paths.size() - 1; I != 0; --I) + llvm::sys::path::append(Path, "Frameworks", Paths[I-1] + ".framework"); +} + +/// \brief Parse a header declaration. +/// +/// header-declaration: +/// 'textual'[opt] 'header' string-literal +/// 'private' 'textual'[opt] 'header' string-literal +/// 'exclude' 'header' string-literal +/// 'umbrella' 'header' string-literal +/// +/// FIXME: Support 'private textual header'. +void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken, + SourceLocation LeadingLoc) { + // We've already consumed the first token. + ModuleMap::ModuleHeaderRole Role = ModuleMap::NormalHeader; + if (LeadingToken == MMToken::PrivateKeyword) { + Role = ModuleMap::PrivateHeader; + // 'private' may optionally be followed by 'textual'. + if (Tok.is(MMToken::TextualKeyword)) { + LeadingToken = Tok.Kind; + consumeToken(); + } + } + if (LeadingToken == MMToken::TextualKeyword) + Role = ModuleMap::ModuleHeaderRole(Role | ModuleMap::TextualHeader); + + if (LeadingToken != MMToken::HeaderKeyword) { + if (!Tok.is(MMToken::HeaderKeyword)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) + << (LeadingToken == MMToken::PrivateKeyword ? "private" : + LeadingToken == MMToken::ExcludeKeyword ? "exclude" : + LeadingToken == MMToken::TextualKeyword ? "textual" : "umbrella"); + return; + } + consumeToken(); + } + + // Parse the header name. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) + << "header"; + HadError = true; + return; + } + Module::UnresolvedHeaderDirective Header; + Header.FileName = Tok.getString(); + Header.FileNameLoc = consumeToken(); + + // Check whether we already have an umbrella. + if (LeadingToken == MMToken::UmbrellaKeyword && ActiveModule->Umbrella) { + Diags.Report(Header.FileNameLoc, diag::err_mmap_umbrella_clash) + << ActiveModule->getFullModuleName(); + HadError = true; + return; + } + + // Look for this file. + const FileEntry *File = nullptr; + const FileEntry *BuiltinFile = nullptr; + SmallString<128> RelativePathName; + if (llvm::sys::path::is_absolute(Header.FileName)) { + RelativePathName = Header.FileName; + File = SourceMgr.getFileManager().getFile(RelativePathName); + } else { + // Search for the header file within the search directory. + SmallString<128> FullPathName(Directory->getName()); + unsigned FullPathLength = FullPathName.size(); + + if (ActiveModule->isPartOfFramework()) { + appendSubframeworkPaths(ActiveModule, RelativePathName); + + // Check whether this file is in the public headers. + llvm::sys::path::append(RelativePathName, "Headers", Header.FileName); + llvm::sys::path::append(FullPathName, RelativePathName.str()); + File = SourceMgr.getFileManager().getFile(FullPathName); + + if (!File) { + // Check whether this file is in the private headers. + // FIXME: Should we retain the subframework paths here? + RelativePathName.clear(); + FullPathName.resize(FullPathLength); + llvm::sys::path::append(RelativePathName, "PrivateHeaders", + Header.FileName); + llvm::sys::path::append(FullPathName, RelativePathName.str()); + File = SourceMgr.getFileManager().getFile(FullPathName); + } + } else { + // Lookup for normal headers. + llvm::sys::path::append(RelativePathName, Header.FileName); + llvm::sys::path::append(FullPathName, RelativePathName.str()); + File = SourceMgr.getFileManager().getFile(FullPathName); + + // If this is a system module with a top-level header, this header + // may have a counterpart (or replacement) in the set of headers + // supplied by Clang. Find that builtin header. + if (ActiveModule->IsSystem && LeadingToken != MMToken::UmbrellaKeyword && + BuiltinIncludeDir && BuiltinIncludeDir != Directory && + isBuiltinHeader(Header.FileName)) { + SmallString<128> BuiltinPathName(BuiltinIncludeDir->getName()); + llvm::sys::path::append(BuiltinPathName, Header.FileName); + BuiltinFile = SourceMgr.getFileManager().getFile(BuiltinPathName); + + // If Clang supplies this header but the underlying system does not, + // just silently swap in our builtin version. Otherwise, we'll end + // up adding both (later). + if (!File && BuiltinFile) { + File = BuiltinFile; + RelativePathName = BuiltinPathName; + BuiltinFile = nullptr; + } + } + } + } + + // FIXME: We shouldn't be eagerly stat'ing every file named in a module map. + // Come up with a lazy way to do this. + if (File) { + if (LeadingToken == MMToken::UmbrellaKeyword) { + const DirectoryEntry *UmbrellaDir = File->getDir(); + if (Module *UmbrellaModule = Map.UmbrellaDirs[UmbrellaDir]) { + Diags.Report(LeadingLoc, diag::err_mmap_umbrella_clash) + << UmbrellaModule->getFullModuleName(); + HadError = true; + } else { + // Record this umbrella header. + Map.setUmbrellaHeader(ActiveModule, File); + } + } else if (LeadingToken == MMToken::ExcludeKeyword) { + Module::Header H = {RelativePathName.str(), File}; + Map.excludeHeader(ActiveModule, H); + } else { + // If there is a builtin counterpart to this file, add it now, before + // the "real" header, so we build the built-in one first when building + // the module. + if (BuiltinFile) { + // FIXME: Taking the name from the FileEntry is unstable and can give + // different results depending on how we've previously named that file + // in this build. + Module::Header H = { BuiltinFile->getName(), BuiltinFile }; + Map.addHeader(ActiveModule, H, Role); + } + + // Record this header. + Module::Header H = { RelativePathName.str(), File }; + Map.addHeader(ActiveModule, H, Role); + } + } else if (LeadingToken != MMToken::ExcludeKeyword) { + // Ignore excluded header files. They're optional anyway. + + // If we find a module that has a missing header, we mark this module as + // unavailable and store the header directive for displaying diagnostics. + Header.IsUmbrella = LeadingToken == MMToken::UmbrellaKeyword; + ActiveModule->markUnavailable(); + ActiveModule->MissingHeaders.push_back(Header); + } +} + +/// \brief Parse an umbrella directory declaration. +/// +/// umbrella-dir-declaration: +/// umbrella string-literal +void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { + // Parse the directory name. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) + << "umbrella"; + HadError = true; + return; + } + + std::string DirName = Tok.getString(); + SourceLocation DirNameLoc = consumeToken(); + + // Check whether we already have an umbrella. + if (ActiveModule->Umbrella) { + Diags.Report(DirNameLoc, diag::err_mmap_umbrella_clash) + << ActiveModule->getFullModuleName(); + HadError = true; + return; + } + + // Look for this file. + const DirectoryEntry *Dir = nullptr; + if (llvm::sys::path::is_absolute(DirName)) + Dir = SourceMgr.getFileManager().getDirectory(DirName); + else { + SmallString<128> PathName; + PathName = Directory->getName(); + llvm::sys::path::append(PathName, DirName); + Dir = SourceMgr.getFileManager().getDirectory(PathName); + } + + if (!Dir) { + Diags.Report(DirNameLoc, diag::err_mmap_umbrella_dir_not_found) + << DirName; + HadError = true; + return; + } + + if (Module *OwningModule = Map.UmbrellaDirs[Dir]) { + Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash) + << OwningModule->getFullModuleName(); + HadError = true; + return; + } + + // Record this umbrella directory. + Map.setUmbrellaDir(ActiveModule, Dir); +} + +/// \brief Parse a module export declaration. +/// +/// export-declaration: +/// 'export' wildcard-module-id +/// +/// wildcard-module-id: +/// identifier +/// '*' +/// identifier '.' wildcard-module-id +void ModuleMapParser::parseExportDecl() { + assert(Tok.is(MMToken::ExportKeyword)); + SourceLocation ExportLoc = consumeToken(); + + // Parse the module-id with an optional wildcard at the end. + ModuleId ParsedModuleId; + bool Wildcard = false; + do { + // FIXME: Support string-literal module names here. + if (Tok.is(MMToken::Identifier)) { + ParsedModuleId.push_back(std::make_pair(Tok.getString(), + Tok.getLocation())); + consumeToken(); + + if (Tok.is(MMToken::Period)) { + consumeToken(); + continue; + } + + break; + } + + if(Tok.is(MMToken::Star)) { + Wildcard = true; + consumeToken(); + break; + } + + Diags.Report(Tok.getLocation(), diag::err_mmap_module_id); + HadError = true; + return; + } while (true); + + Module::UnresolvedExportDecl Unresolved = { + ExportLoc, ParsedModuleId, Wildcard + }; + ActiveModule->UnresolvedExports.push_back(Unresolved); +} + +/// \brief Parse a module uses declaration. +/// +/// uses-declaration: +/// 'uses' wildcard-module-id +void ModuleMapParser::parseUseDecl() { + assert(Tok.is(MMToken::UseKeyword)); + consumeToken(); + // Parse the module-id. + ModuleId ParsedModuleId; + parseModuleId(ParsedModuleId); + + ActiveModule->UnresolvedDirectUses.push_back(ParsedModuleId); +} + +/// \brief Parse a link declaration. +/// +/// module-declaration: +/// 'link' 'framework'[opt] string-literal +void ModuleMapParser::parseLinkDecl() { + assert(Tok.is(MMToken::LinkKeyword)); + SourceLocation LinkLoc = consumeToken(); + + // Parse the optional 'framework' keyword. + bool IsFramework = false; + if (Tok.is(MMToken::FrameworkKeyword)) { + consumeToken(); + IsFramework = true; + } + + // Parse the library name + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name) + << IsFramework << SourceRange(LinkLoc); + HadError = true; + return; + } + + std::string LibraryName = Tok.getString(); + consumeToken(); + ActiveModule->LinkLibraries.push_back(Module::LinkLibrary(LibraryName, + IsFramework)); +} + +/// \brief Parse a configuration macro declaration. +/// +/// module-declaration: +/// 'config_macros' attributes[opt] config-macro-list? +/// +/// config-macro-list: +/// identifier (',' identifier)? +void ModuleMapParser::parseConfigMacros() { + assert(Tok.is(MMToken::ConfigMacros)); + SourceLocation ConfigMacrosLoc = consumeToken(); + + // Only top-level modules can have configuration macros. + if (ActiveModule->Parent) { + Diags.Report(ConfigMacrosLoc, diag::err_mmap_config_macro_submodule); + } + + // Parse the optional attributes. + Attributes Attrs; + parseOptionalAttributes(Attrs); + if (Attrs.IsExhaustive && !ActiveModule->Parent) { + ActiveModule->ConfigMacrosExhaustive = true; + } + + // If we don't have an identifier, we're done. + // FIXME: Support macros with the same name as a keyword here. + if (!Tok.is(MMToken::Identifier)) + return; + + // Consume the first identifier. + if (!ActiveModule->Parent) { + ActiveModule->ConfigMacros.push_back(Tok.getString().str()); + } + consumeToken(); + + do { + // If there's a comma, consume it. + if (!Tok.is(MMToken::Comma)) + break; + consumeToken(); + + // We expect to see a macro name here. + // FIXME: Support macros with the same name as a keyword here. + if (!Tok.is(MMToken::Identifier)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro); + break; + } + + // Consume the macro name. + if (!ActiveModule->Parent) { + ActiveModule->ConfigMacros.push_back(Tok.getString().str()); + } + consumeToken(); + } while (true); +} + +/// \brief Format a module-id into a string. +static std::string formatModuleId(const ModuleId &Id) { + std::string result; + { + llvm::raw_string_ostream OS(result); + + for (unsigned I = 0, N = Id.size(); I != N; ++I) { + if (I) + OS << "."; + OS << Id[I].first; + } + } + + return result; +} + +/// \brief Parse a conflict declaration. +/// +/// module-declaration: +/// 'conflict' module-id ',' string-literal +void ModuleMapParser::parseConflict() { + assert(Tok.is(MMToken::Conflict)); + SourceLocation ConflictLoc = consumeToken(); + Module::UnresolvedConflict Conflict; + + // Parse the module-id. + if (parseModuleId(Conflict.Id)) + return; + + // Parse the ','. + if (!Tok.is(MMToken::Comma)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_comma) + << SourceRange(ConflictLoc); + return; + } + consumeToken(); + + // Parse the message. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_message) + << formatModuleId(Conflict.Id); + return; + } + Conflict.Message = Tok.getString().str(); + consumeToken(); + + // Add this unresolved conflict. + ActiveModule->UnresolvedConflicts.push_back(Conflict); +} + +/// \brief Parse an inferred module declaration (wildcard modules). +/// +/// module-declaration: +/// 'explicit'[opt] 'framework'[opt] 'module' * attributes[opt] +/// { inferred-module-member* } +/// +/// inferred-module-member: +/// 'export' '*' +/// 'exclude' identifier +void ModuleMapParser::parseInferredModuleDecl(bool Framework, bool Explicit) { + assert(Tok.is(MMToken::Star)); + SourceLocation StarLoc = consumeToken(); + bool Failed = false; + + // Inferred modules must be submodules. + if (!ActiveModule && !Framework) { + Diags.Report(StarLoc, diag::err_mmap_top_level_inferred_submodule); + Failed = true; + } + + if (ActiveModule) { + // Inferred modules must have umbrella directories. + if (!Failed && ActiveModule->IsAvailable && + !ActiveModule->getUmbrellaDir()) { + Diags.Report(StarLoc, diag::err_mmap_inferred_no_umbrella); + Failed = true; + } + + // Check for redefinition of an inferred module. + if (!Failed && ActiveModule->InferSubmodules) { + Diags.Report(StarLoc, diag::err_mmap_inferred_redef); + if (ActiveModule->InferredSubmoduleLoc.isValid()) + Diags.Report(ActiveModule->InferredSubmoduleLoc, + diag::note_mmap_prev_definition); + Failed = true; + } + + // Check for the 'framework' keyword, which is not permitted here. + if (Framework) { + Diags.Report(StarLoc, diag::err_mmap_inferred_framework_submodule); + Framework = false; + } + } else if (Explicit) { + Diags.Report(StarLoc, diag::err_mmap_explicit_inferred_framework); + Explicit = false; + } + + // If there were any problems with this inferred submodule, skip its body. + if (Failed) { + if (Tok.is(MMToken::LBrace)) { + consumeToken(); + skipUntil(MMToken::RBrace); + if (Tok.is(MMToken::RBrace)) + consumeToken(); + } + HadError = true; + return; + } + + // Parse optional attributes. + Attributes Attrs; + parseOptionalAttributes(Attrs); + + if (ActiveModule) { + // Note that we have an inferred submodule. + ActiveModule->InferSubmodules = true; + ActiveModule->InferredSubmoduleLoc = StarLoc; + ActiveModule->InferExplicitSubmodules = Explicit; + } else { + // We'll be inferring framework modules for this directory. + Map.InferredDirectories[Directory].InferModules = true; + Map.InferredDirectories[Directory].Attrs = Attrs; + Map.InferredDirectories[Directory].ModuleMapFile = ModuleMapFile; + // FIXME: Handle the 'framework' keyword. + } + + // Parse the opening brace. + if (!Tok.is(MMToken::LBrace)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace_wildcard); + HadError = true; + return; + } + SourceLocation LBraceLoc = consumeToken(); + + // Parse the body of the inferred submodule. + bool Done = false; + do { + switch (Tok.Kind) { + case MMToken::EndOfFile: + case MMToken::RBrace: + Done = true; + break; + + case MMToken::ExcludeKeyword: { + if (ActiveModule) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_inferred_member) + << (ActiveModule != nullptr); + consumeToken(); + break; + } + + consumeToken(); + // FIXME: Support string-literal module names here. + if (!Tok.is(MMToken::Identifier)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_missing_exclude_name); + break; + } + + Map.InferredDirectories[Directory].ExcludedModules + .push_back(Tok.getString()); + consumeToken(); + break; + } + + case MMToken::ExportKeyword: + if (!ActiveModule) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_inferred_member) + << (ActiveModule != nullptr); + consumeToken(); + break; + } + + consumeToken(); + if (Tok.is(MMToken::Star)) + ActiveModule->InferExportWildcard = true; + else + Diags.Report(Tok.getLocation(), + diag::err_mmap_expected_export_wildcard); + consumeToken(); + break; + + case MMToken::ExplicitKeyword: + case MMToken::ModuleKeyword: + case MMToken::HeaderKeyword: + case MMToken::PrivateKeyword: + case MMToken::UmbrellaKeyword: + default: + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_inferred_member) + << (ActiveModule != nullptr); + consumeToken(); + break; + } + } while (!Done); + + if (Tok.is(MMToken::RBrace)) + consumeToken(); + else { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); + Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); + HadError = true; + } +} + +/// \brief Parse optional attributes. +/// +/// attributes: +/// attribute attributes +/// attribute +/// +/// attribute: +/// [ identifier ] +/// +/// \param Attrs Will be filled in with the parsed attributes. +/// +/// \returns true if an error occurred, false otherwise. +bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { + bool HadError = false; + + while (Tok.is(MMToken::LSquare)) { + // Consume the '['. + SourceLocation LSquareLoc = consumeToken(); + + // Check whether we have an attribute name here. + if (!Tok.is(MMToken::Identifier)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_attribute); + skipUntil(MMToken::RSquare); + if (Tok.is(MMToken::RSquare)) + consumeToken(); + HadError = true; + } + + // Decode the attribute name. + AttributeKind Attribute + = llvm::StringSwitch<AttributeKind>(Tok.getString()) + .Case("exhaustive", AT_exhaustive) + .Case("extern_c", AT_extern_c) + .Case("system", AT_system) + .Default(AT_unknown); + switch (Attribute) { + case AT_unknown: + Diags.Report(Tok.getLocation(), diag::warn_mmap_unknown_attribute) + << Tok.getString(); + break; + + case AT_system: + Attrs.IsSystem = true; + break; + + case AT_extern_c: + Attrs.IsExternC = true; + break; + + case AT_exhaustive: + Attrs.IsExhaustive = true; + break; + } + consumeToken(); + + // Consume the ']'. + if (!Tok.is(MMToken::RSquare)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rsquare); + Diags.Report(LSquareLoc, diag::note_mmap_lsquare_match); + skipUntil(MMToken::RSquare); + HadError = true; + } + + if (Tok.is(MMToken::RSquare)) + consumeToken(); + } + + return HadError; +} + +/// \brief Parse a module map file. +/// +/// module-map-file: +/// module-declaration* +bool ModuleMapParser::parseModuleMapFile() { + do { + switch (Tok.Kind) { + case MMToken::EndOfFile: + return HadError; + + case MMToken::ExplicitKeyword: + case MMToken::ExternKeyword: + case MMToken::ModuleKeyword: + case MMToken::FrameworkKeyword: + parseModuleDecl(); + break; + + case MMToken::Comma: + case MMToken::ConfigMacros: + case MMToken::Conflict: + case MMToken::Exclaim: + case MMToken::ExcludeKeyword: + case MMToken::ExportKeyword: + case MMToken::HeaderKeyword: + case MMToken::Identifier: + case MMToken::LBrace: + case MMToken::LinkKeyword: + case MMToken::LSquare: + case MMToken::Period: + case MMToken::PrivateKeyword: + case MMToken::RBrace: + case MMToken::RSquare: + case MMToken::RequiresKeyword: + case MMToken::Star: + case MMToken::StringLiteral: + case MMToken::TextualKeyword: + case MMToken::UmbrellaKeyword: + case MMToken::UseKeyword: + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); + HadError = true; + consumeToken(); + break; + } + } while (true); +} + +bool ModuleMap::parseModuleMapFile(const FileEntry *File, bool IsSystem, + const DirectoryEntry *Dir) { + llvm::DenseMap<const FileEntry *, bool>::iterator Known + = ParsedModuleMap.find(File); + if (Known != ParsedModuleMap.end()) + return Known->second; + + assert(Target && "Missing target information"); + auto FileCharacter = IsSystem ? SrcMgr::C_System : SrcMgr::C_User; + FileID ID = SourceMgr.createFileID(File, SourceLocation(), FileCharacter); + const llvm::MemoryBuffer *Buffer = SourceMgr.getBuffer(ID); + if (!Buffer) + return ParsedModuleMap[File] = true; + + // Parse this module map file. + Lexer L(ID, SourceMgr.getBuffer(ID), SourceMgr, MMapLangOpts); + ModuleMapParser Parser(L, SourceMgr, Target, Diags, *this, File, Dir, + BuiltinIncludeDir, IsSystem); + bool Result = Parser.parseModuleMapFile(); + ParsedModuleMap[File] = Result; + return Result; +} |