#include "clang/Lex/ModuleMap.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.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 "clang/Lex/Token.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string>
#include <system_error>
#include <utility>
using namespace clang;
void ModuleMapCallbacks::anchor() {}
void ModuleMap::resolveLinkAsDependencies(Module *Mod) {
auto PendingLinkAs = PendingLinkAsModule.find(Mod->Name);
if (PendingLinkAs != PendingLinkAsModule.end()) {
for (auto &Name : PendingLinkAs->second) {
auto *M = findModule(Name.getKey());
if (M)
M->UseExportAsModuleLinkName = true;
}
}
}
void ModuleMap::addLinkAsDependency(Module *Mod) {
if (findModule(Mod->ExportAsModule))
Mod->UseExportAsModuleLinkName = true;
else
PendingLinkAsModule[Mod->ExportAsModule].insert(Mod->Name);
}
Module::HeaderKind ModuleMap::headerRoleToKind(ModuleHeaderRole Role) {
switch ((int)Role) {
default: llvm_unreachable("unknown header role");
case NormalHeader:
return Module::HK_Normal;
case PrivateHeader:
return Module::HK_Private;
case TextualHeader:
return Module::HK_Textual;
case PrivateHeader | TextualHeader:
return Module::HK_PrivateTextual;
}
}
ModuleMap::ModuleHeaderRole
ModuleMap::headerKindToRole(Module::HeaderKind Kind) {
switch ((int)Kind) {
case Module::HK_Normal:
return NormalHeader;
case Module::HK_Private:
return PrivateHeader;
case Module::HK_Textual:
return TextualHeader;
case Module::HK_PrivateTextual:
return ModuleHeaderRole(PrivateHeader | TextualHeader);
case Module::HK_Excluded:
llvm_unreachable("unexpected header kind");
}
llvm_unreachable("unknown header kind");
}
Module::ExportDecl
ModuleMap::resolveExport(Module *Mod,
const Module::UnresolvedExportDecl &Unresolved,
bool Complain) const {
if (Unresolved.Id.empty()) {
assert(Unresolved.Wildcard && "Invalid unresolved export");
return Module::ExportDecl(nullptr, true);
}
Module *Context = resolveModuleId(Unresolved.Id, Mod, Complain);
if (!Context)
return {};
return Module::ExportDecl(Context, Unresolved.Wildcard);
}
Module *ModuleMap::resolveModuleId(const ModuleId &Id, Module *Mod,
bool Complain) const {
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;
}
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;
}
static void appendSubframeworkPaths(Module *Mod,
SmallVectorImpl<char> &Path) {
SmallVector<StringRef, 2> Paths;
for (; Mod; Mod = Mod->Parent) {
if (Mod->IsFramework)
Paths.push_back(Mod->Name);
}
if (Paths.empty())
return;
for (StringRef Framework : llvm::drop_begin(llvm::reverse(Paths)))
llvm::sys::path::append(Path, "Frameworks", Framework + ".framework");
}
Optional<FileEntryRef> ModuleMap::findHeader(
Module *M, const Module::UnresolvedHeaderDirective &Header,
SmallVectorImpl<char> &RelativePathName, bool &NeedsFramework) {
auto *Directory = M->Directory;
SmallString<128> FullPathName(Directory->getName());
auto GetFile = [&](StringRef Filename) -> Optional<FileEntryRef> {
auto File =
expectedToOptional(SourceMgr.getFileManager().getFileRef(Filename));
if (!File || (Header.Size && File->getSize() != *Header.Size) ||
(Header.ModTime && File->getModificationTime() != *Header.ModTime))
return None;
return *File;
};
auto GetFrameworkFile = [&]() -> Optional<FileEntryRef> {
unsigned FullPathLength = FullPathName.size();
appendSubframeworkPaths(M, RelativePathName);
unsigned RelativePathLength = RelativePathName.size();
llvm::sys::path::append(RelativePathName, "Headers", Header.FileName);
llvm::sys::path::append(FullPathName, RelativePathName);
if (auto File = GetFile(FullPathName))
return File;
if (M->IsFramework && M->Name == "Private")
RelativePathName.clear();
else
RelativePathName.resize(RelativePathLength);
FullPathName.resize(FullPathLength);
llvm::sys::path::append(RelativePathName, "PrivateHeaders",
Header.FileName);
llvm::sys::path::append(FullPathName, RelativePathName);
return GetFile(FullPathName);
};
if (llvm::sys::path::is_absolute(Header.FileName)) {
RelativePathName.clear();
RelativePathName.append(Header.FileName.begin(), Header.FileName.end());
return GetFile(Header.FileName);
}
if (M->isPartOfFramework())
return GetFrameworkFile();
llvm::sys::path::append(RelativePathName, Header.FileName);
llvm::sys::path::append(FullPathName, RelativePathName);
auto NormalHdrFile = GetFile(FullPathName);
if (!NormalHdrFile && Directory->getName().endswith(".framework")) {
FullPathName.assign(Directory->getName());
RelativePathName.clear();
if (GetFrameworkFile()) {
Diags.Report(Header.FileNameLoc,
diag::warn_mmap_incomplete_framework_module_declaration)
<< Header.FileName << M->getFullModuleName();
NeedsFramework = true;
}
return None;
}
return NormalHdrFile;
}
void ModuleMap::resolveHeader(Module *Mod,
const Module::UnresolvedHeaderDirective &Header,
bool &NeedsFramework) {
SmallString<128> RelativePathName;
if (Optional<FileEntryRef> File =
findHeader(Mod, Header, RelativePathName, NeedsFramework)) {
if (Header.IsUmbrella) {
const DirectoryEntry *UmbrellaDir = &File->getDir().getDirEntry();
if (Module *UmbrellaMod = UmbrellaDirs[UmbrellaDir])
Diags.Report(Header.FileNameLoc, diag::err_mmap_umbrella_clash)
<< UmbrellaMod->getFullModuleName();
else
setUmbrellaHeader(Mod, *File, Header.FileName, RelativePathName.str());
} else {
Module::Header H = {Header.FileName, std::string(RelativePathName.str()),
*File};
if (Header.Kind == Module::HK_Excluded)
excludeHeader(Mod, H);
else
addHeader(Mod, H, headerKindToRole(Header.Kind));
}
} else if (Header.HasBuiltinHeader && !Header.Size && !Header.ModTime) {
} else if (Header.Kind == Module::HK_Excluded) {
} else {
Mod->MissingHeaders.push_back(Header);
if (!Header.Size && !Header.ModTime)
Mod->markUnavailable(false);
}
}
bool ModuleMap::resolveAsBuiltinHeader(
Module *Mod, const Module::UnresolvedHeaderDirective &Header) {
if (Header.Kind == Module::HK_Excluded ||
llvm::sys::path::is_absolute(Header.FileName) ||
Mod->isPartOfFramework() || !Mod->IsSystem || Header.IsUmbrella ||
!BuiltinIncludeDir || BuiltinIncludeDir == Mod->Directory ||
!isBuiltinHeader(Header.FileName))
return false;
SmallString<128> Path;
llvm::sys::path::append(Path, BuiltinIncludeDir->getName(), Header.FileName);
auto File = SourceMgr.getFileManager().getFile(Path);
if (!File)
return false;
auto Role = headerKindToRole(Header.Kind);
Module::Header H = {Header.FileName, std::string(Path.str()), *File};
addHeader(Mod, H, Role);
return true;
}
ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags,
const LangOptions &LangOpts, const TargetInfo *Target,
HeaderSearch &HeaderInfo)
: SourceMgr(SourceMgr), Diags(Diags), LangOpts(LangOpts), Target(Target),
HeaderInfo(HeaderInfo) {
MMapLangOpts.LineComment = true;
}
ModuleMap::~ModuleMap() {
for (auto &M : Modules)
delete M.getValue();
for (auto *M : ShadowModules)
delete M;
}
void ModuleMap::setTarget(const TargetInfo &Target) {
assert((!this->Target || this->Target == &Target) &&
"Improper target override");
this->Target = &Target;
}
static StringRef sanitizeFilenameAsIdentifier(StringRef Name,
SmallVectorImpl<char> &Buffer) {
if (Name.empty())
return Name;
if (!isValidAsciiIdentifier(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 (isAsciiIdentifierContinue(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;
}
bool ModuleMap::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("stdatomic.h", true)
.Case("stdbool.h", true)
.Case("stddef.h", true)
.Case("stdint.h", true)
.Case("tgmath.h", true)
.Case("unwind.h", true)
.Default(false);
}
bool ModuleMap::isBuiltinHeader(const FileEntry *File) {
return File->getDir() == BuiltinIncludeDir &&
ModuleMap::isBuiltinHeader(llvm::sys::path::filename(File->getName()));
}
ModuleMap::HeadersMap::iterator
ModuleMap::findKnownHeader(const FileEntry *File) {
resolveHeaderDirectives(File);
HeadersMap::iterator Known = Headers.find(File);
if (HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps &&
Known == Headers.end() && ModuleMap::isBuiltinHeader(File)) {
HeaderInfo.loadTopLevelSystemModules();
return Headers.find(File);
}
return Known;
}
ModuleMap::KnownHeader
ModuleMap::findHeaderInUmbrellaDirs(const FileEntry *File,
SmallVectorImpl<const DirectoryEntry *> &IntermediateDirs) {
if (UmbrellaDirs.empty())
return {};
const DirectoryEntry *Dir = File->getDir();
assert(Dir && "file in no directory");
StringRef DirName = SourceMgr.getFileManager().getCanonicalName(Dir);
do {
auto KnownDir = UmbrellaDirs.find(Dir);
if (KnownDir != UmbrellaDirs.end())
return KnownHeader(KnownDir->second, NormalHeader);
IntermediateDirs.push_back(Dir);
DirName = llvm::sys::path::parent_path(DirName);
if (DirName.empty())
break;
if (auto DirEntry = SourceMgr.getFileManager().getDirectory(DirName))
Dir = *DirEntry;
else
Dir = nullptr;
} while (Dir);
return {};
}
static bool violatesPrivateInclude(Module *RequestingModule,
const FileEntry *IncFileEnt,
ModuleMap::KnownHeader Header) {
#ifndef NDEBUG
if (Header.getRole() & ModuleMap::PrivateHeader) {
bool IsPrivate = false;
SmallVectorImpl<Module::Header> *HeaderList[] = {
&Header.getModule()->Headers[Module::HK_Private],
&Header.getModule()->Headers[Module::HK_PrivateTextual]};
for (auto *Hs : HeaderList)
IsPrivate |= llvm::any_of(
*Hs, [&](const Module::Header &H) { return H.Entry == IncFileEnt; });
assert(IsPrivate && "inconsistent headers and roles");
}
#endif
return !Header.isAccessibleFrom(RequestingModule);
}
static Module *getTopLevelOrNull(Module *M) {
return M ? M->getTopLevelModule() : nullptr;
}
void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
bool RequestingModuleIsModuleInterface,
SourceLocation FilenameLoc,
StringRef Filename, FileEntryRef File) {
if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule))
return;
if (RequestingModule) {
resolveUses(RequestingModule, false);
resolveHeaderDirectives(RequestingModule, llvm::None);
}
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 (violatesPrivateInclude(RequestingModule, File, Header)) {
Private = Header.getModule();
continue;
}
if (RequestingModule && LangOpts.ModulesDeclUse &&
!RequestingModule->directlyUses(Header.getModule())) {
NotUsed = Header.getModule();
continue;
}
return;
}
Excluded = true;
}
if (Private) {
Diags.Report(FilenameLoc, diag::warn_use_of_private_header_outside_module)
<< Filename;
return;
}
if (NotUsed) {
Diags.Report(FilenameLoc, diag::err_undeclared_use_of_module)
<< RequestingModule->getTopLevelModule()->Name << Filename;
return;
}
if (Excluded || isHeaderInUmbrellaDirs(File))
return;
if (RequestingModule && LangOpts.ModulesStrictDeclUse) {
Diags.Report(FilenameLoc, diag::err_undeclared_use_of_module)
<< RequestingModule->getTopLevelModule()->Name << Filename;
} else if (RequestingModule && RequestingModuleIsModuleInterface &&
LangOpts.isCompilingModule()) {
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()
<< File.getName();
}
}
static bool isBetterKnownHeader(const ModuleMap::KnownHeader &New,
const ModuleMap::KnownHeader &Old) {
if (New.getModule()->isAvailable() && !Old.getModule()->isAvailable())
return true;
if ((New.getRole() & ModuleMap::PrivateHeader) !=
(Old.getRole() & ModuleMap::PrivateHeader))
return !(New.getRole() & ModuleMap::PrivateHeader);
if ((New.getRole() & ModuleMap::TextualHeader) !=
(Old.getRole() & ModuleMap::TextualHeader))
return !(New.getRole() & ModuleMap::TextualHeader);
return false;
}
ModuleMap::KnownHeader ModuleMap::findModuleForHeader(const FileEntry *File,
bool AllowTextual) {
auto MakeResult = [&](ModuleMap::KnownHeader R) -> ModuleMap::KnownHeader {
if (!AllowTextual && R.getRole() & ModuleMap::TextualHeader)
return {};
return R;
};
HeadersMap::iterator Known = findKnownHeader(File);
if (Known != Headers.end()) {
ModuleMap::KnownHeader Result;
for (KnownHeader &H : Known->second) {
if (H.getModule()->getTopLevelModule() == SourceModule)
return MakeResult(H);
if (!Result || isBetterKnownHeader(H, Result))
Result = H;
}
return MakeResult(Result);
}
return MakeResult(findOrCreateModuleForHeaderInUmbrellaDir(File));
}
ModuleMap::KnownHeader
ModuleMap::findOrCreateModuleForHeaderInUmbrellaDir(const FileEntry *File) {
assert(!Headers.count(File) && "already have a module for this header");
SmallVector<const DirectoryEntry *, 2> SkippedDirs;
KnownHeader H = findHeaderInUmbrellaDirs(File, SkippedDirs);
if (H) {
Module *Result = H.getModule();
Module *UmbrellaModule = Result;
while (!UmbrellaModule->getUmbrellaDir() && UmbrellaModule->Parent)
UmbrellaModule = UmbrellaModule->Parent;
if (UmbrellaModule->InferSubmodules) {
const FileEntry *UmbrellaModuleMap =
getModuleMapFileForUniquing(UmbrellaModule);
bool Explicit = UmbrellaModule->InferExplicitSubmodules;
for (const DirectoryEntry *SkippedDir : llvm::reverse(SkippedDirs)) {
SmallString<32> NameBuf;
StringRef Name = sanitizeFilenameAsIdentifier(
llvm::sys::path::stem(SkippedDir->getName()), NameBuf);
Result = findOrCreateModule(Name, Result, false,
Explicit).first;
InferredModuleAllowedBy[Result] = UmbrellaModuleMap;
Result->IsInferred = true;
UmbrellaDirs[SkippedDir] = Result;
if (UmbrellaModule->InferExportWildcard && Result->Exports.empty())
Result->Exports.push_back(Module::ExportDecl(nullptr, true));
}
SmallString<32> NameBuf;
StringRef Name = sanitizeFilenameAsIdentifier(
llvm::sys::path::stem(File->getName()), NameBuf);
Result = findOrCreateModule(Name, Result, false,
Explicit).first;
InferredModuleAllowedBy[Result] = UmbrellaModuleMap;
Result->IsInferred = true;
Result->addTopHeader(File);
if (UmbrellaModule->InferExportWildcard && Result->Exports.empty())
Result->Exports.push_back(Module::ExportDecl(nullptr, true));
} else {
for (unsigned I = 0, N = SkippedDirs.size(); I != N; ++I)
UmbrellaDirs[SkippedDirs[I]] = Result;
}
KnownHeader Header(Result, NormalHeader);
Headers[File].push_back(Header);
return Header;
}
return {};
}
ArrayRef<ModuleMap::KnownHeader>
ModuleMap::findAllModulesForHeader(const FileEntry *File) {
HeadersMap::iterator Known = findKnownHeader(File);
if (Known != Headers.end())
return Known->second;
if (findOrCreateModuleForHeaderInUmbrellaDir(File))
return Headers.find(File)->second;
return None;
}
ArrayRef<ModuleMap::KnownHeader>
ModuleMap::findResolvedModulesForHeader(const FileEntry *File) const {
resolveHeaderDirectives(File);
auto It = Headers.find(File);
if (It == Headers.end())
return None;
return It->second;
}
bool ModuleMap::isHeaderInUnavailableModule(const FileEntry *Header) const {
return isHeaderUnavailableInModule(Header, nullptr);
}
bool
ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header,
const Module *RequestingModule) const {
resolveHeaderDirectives(Header);
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))) {
if (!RequestingModule && I->getRole() == ModuleMap::TextualHeader)
continue;
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));
};
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;
Module *UmbrellaModule = Found;
while (!UmbrellaModule->getUmbrellaDir() && UmbrellaModule->Parent)
UmbrellaModule = UmbrellaModule->Parent;
if (UmbrellaModule->InferSubmodules) {
for (const DirectoryEntry *SkippedDir : llvm::reverse(SkippedDirs)) {
SmallString<32> NameBuf;
StringRef Name = sanitizeFilenameAsIdentifier(
llvm::sys::path::stem(SkippedDir->getName()), NameBuf);
Found = lookupModuleQualified(Name, Found);
if (!Found)
return false;
if (IsUnavailable(Found))
return true;
}
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);
DirName = llvm::sys::path::parent_path(DirName);
if (DirName.empty())
break;
if (auto DirEntry = SourceMgr.getFileManager().getDirectory(DirName))
Dir = *DirEntry;
else
Dir = nullptr;
} 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) {
if (Module *Sub = lookupModuleQualified(Name, Parent))
return std::make_pair(Sub, false);
Module *Result = new Module(Name, SourceLocation(), Parent, IsFramework,
IsExplicit, NumCreatedModules++);
if (!Parent) {
if (LangOpts.CurrentModule == Name)
SourceModule = Result;
Modules[Name] = Result;
ModuleScopeIDs[Result] = CurrentModuleScopeID;
}
return std::make_pair(Result, true);
}
Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
Module *Parent) {
auto *Result = new Module("<global>", Loc, Parent, false,
true, NumCreatedModules++);
Result->Kind = Module::GlobalModuleFragment;
if (!Result->Parent)
PendingSubmodules.emplace_back(Result);
return Result;
}
Module *
ModuleMap::createPrivateModuleFragmentForInterfaceUnit(Module *Parent,
SourceLocation Loc) {
auto *Result =
new Module("<private>", Loc, Parent, false,
true, NumCreatedModules++);
Result->Kind = Module::PrivateModuleFragment;
return Result;
}
Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
StringRef Name,
Module *GlobalModule) {
assert(LangOpts.CurrentModule == Name && "module name mismatch");
assert(!Modules[Name] && "redefining existing module");
auto *Result =
new Module(Name, Loc, nullptr, false,
false, NumCreatedModules++);
Result->Kind = Module::ModuleInterfaceUnit;
Modules[Name] = SourceModule = Result;
for (auto &Submodule : PendingSubmodules) {
Submodule->setParent(Result);
Submodule.release(); }
PendingSubmodules.clear();
auto *MainFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
assert(MainFile && "no input file for module interface");
Headers[MainFile].push_back(KnownHeader(Result, PrivateHeader));
return Result;
}
Module *ModuleMap::createHeaderModule(StringRef Name,
ArrayRef<Module::Header> Headers) {
assert(LangOpts.CurrentModule == Name && "module name mismatch");
assert(!Modules[Name] && "redefining existing module");
auto *Result =
new Module(Name, SourceLocation(), nullptr, false,
false, NumCreatedModules++);
Result->Kind = Module::ModuleInterfaceUnit;
Modules[Name] = SourceModule = Result;
for (const Module::Header &H : Headers) {
auto *M = new Module(H.NameAsWritten, SourceLocation(), Result,
false,
true, NumCreatedModules++);
M->Exports.push_back(Module::ExportDecl(nullptr, true));
addHeader(M, H, NormalHeader);
}
return Result;
}
Module *ModuleMap::createHeaderUnit(SourceLocation Loc, StringRef Name,
Module::Header H) {
assert(LangOpts.CurrentModule == Name && "module name mismatch");
assert(!Modules[Name] && "redefining existing module");
auto *Result = new Module(Name, Loc, nullptr, false,
false, NumCreatedModules++);
Result->Kind = Module::ModuleHeaderUnit;
Modules[Name] = SourceModule = Result;
addHeader(Result, H, NormalHeader);
return Result;
}
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);
for (const char *extension : {"", ".tbd"}) {
llvm::sys::path::replace_extension(LibName, extension);
if (FileMgr.getFile(LibName)) {
Mod->LinkLibraries.push_back(Module::LinkLibrary(Mod->Name,
true));
return;
}
}
}
Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
bool IsSystem, Module *Parent) {
Attributes Attrs;
Attrs.IsSystem = IsSystem;
return inferFrameworkModule(FrameworkDir, Attrs, Parent);
}
Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
Attributes Attrs, Module *Parent) {
StringRef FrameworkDirName =
SourceMgr.getFileManager().getCanonicalName(FrameworkDir);
SmallString<32> ModuleNameStorage;
StringRef ModuleName = sanitizeFilenameAsIdentifier(
llvm::sys::path::stem(FrameworkDirName), ModuleNameStorage);
if (Module *Mod = lookupModuleQualified(ModuleName, Parent))
return Mod;
FileManager &FileMgr = SourceMgr.getFileManager();
const FileEntry *ModuleMapFile = nullptr;
if (!Parent) {
bool canInfer = false;
if (llvm::sys::path::has_parent_path(FrameworkDirName)) {
StringRef Parent = llvm::sys::path::parent_path(FrameworkDirName);
if (auto ParentDir = FileMgr.getDirectory(Parent)) {
llvm::DenseMap<const DirectoryEntry *, InferredDirectory>::const_iterator
inferred = InferredDirectories.find(*ParentDir);
if (inferred == InferredDirectories.end()) {
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) {
StringRef Name = llvm::sys::path::stem(FrameworkDirName);
canInfer =
!llvm::is_contained(inferred->second.ExcludedModules, Name);
Attrs.IsSystem |= inferred->second.Attrs.IsSystem;
Attrs.IsExternC |= inferred->second.Attrs.IsExternC;
Attrs.IsExhaustive |= inferred->second.Attrs.IsExhaustive;
Attrs.NoUndeclaredIncludes |=
inferred->second.Attrs.NoUndeclaredIncludes;
ModuleMapFile = inferred->second.ModuleMapFile;
}
}
}
if (!canInfer)
return nullptr;
} else
ModuleMapFile = getModuleMapFileForUniquing(Parent);
SmallString<128> UmbrellaName = StringRef(FrameworkDir->getName());
llvm::sys::path::append(UmbrellaName, "Headers", ModuleName + ".h");
auto UmbrellaHeader = FileMgr.getFile(UmbrellaName);
if (!UmbrellaHeader)
return nullptr;
Module *Result = new Module(ModuleName, SourceLocation(), Parent,
true, false,
NumCreatedModules++);
InferredModuleAllowedBy[Result] = ModuleMapFile;
Result->IsInferred = true;
if (!Parent) {
if (LangOpts.CurrentModule == ModuleName)
SourceModule = Result;
Modules[ModuleName] = Result;
ModuleScopeIDs[Result] = CurrentModuleScopeID;
}
Result->IsSystem |= Attrs.IsSystem;
Result->IsExternC |= Attrs.IsExternC;
Result->ConfigMacrosExhaustive |= Attrs.IsExhaustive;
Result->NoUndeclaredIncludes |= Attrs.NoUndeclaredIncludes;
Result->Directory = FrameworkDir;
StringRef RelativePath = UmbrellaName.str().substr(
Result->getTopLevelModule()->Directory->getName().size());
RelativePath = llvm::sys::path::relative_path(RelativePath);
setUmbrellaHeader(Result, *UmbrellaHeader, ModuleName + ".h", RelativePath);
Result->Exports.push_back(Module::ExportDecl(nullptr, true));
Result->InferSubmodules = true;
Result->InferExportWildcard = true;
std::error_code EC;
SmallString<128> SubframeworksDirName
= StringRef(FrameworkDir->getName());
llvm::sys::path::append(SubframeworksDirName, "Frameworks");
llvm::sys::path::native(SubframeworksDirName);
llvm::vfs::FileSystem &FS = FileMgr.getVirtualFileSystem();
for (llvm::vfs::directory_iterator
Dir = FS.dir_begin(SubframeworksDirName, EC),
DirEnd;
Dir != DirEnd && !EC; Dir.increment(EC)) {
if (!StringRef(Dir->path()).endswith(".framework"))
continue;
if (auto SubframeworkDir =
FileMgr.getDirectory(Dir->path())) {
StringRef SubframeworkDirName =
FileMgr.getCanonicalName(*SubframeworkDir);
bool FoundParent = false;
do {
SubframeworkDirName
= llvm::sys::path::parent_path(SubframeworkDirName);
if (SubframeworkDirName.empty())
break;
if (auto SubDir = FileMgr.getDirectory(SubframeworkDirName)) {
if (*SubDir == FrameworkDir) {
FoundParent = true;
break;
}
}
} while (true);
if (!FoundParent)
continue;
inferFrameworkModule(*SubframeworkDir, Attrs, Result);
}
}
if (!Result->isSubFramework()) {
inferFrameworkLink(Result, FrameworkDir, FileMgr);
}
return Result;
}
Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework,
Module *ShadowingModule) {
Module *Result =
new Module(Name, SourceLocation(), nullptr, IsFramework,
false, NumCreatedModules++);
Result->ShadowingModule = ShadowingModule;
Result->markUnavailable(true);
ModuleScopeIDs[Result] = CurrentModuleScopeID;
ShadowModules.push_back(Result);
return Result;
}
void ModuleMap::setUmbrellaHeader(
Module *Mod, const FileEntry *UmbrellaHeader, const Twine &NameAsWritten,
const Twine &PathRelativeToRootModuleDirectory) {
Headers[UmbrellaHeader].push_back(KnownHeader(Mod, NormalHeader));
Mod->Umbrella = UmbrellaHeader;
Mod->UmbrellaAsWritten = NameAsWritten.str();
Mod->UmbrellaRelativeToRootModuleDirectory =
PathRelativeToRootModuleDirectory.str();
UmbrellaDirs[UmbrellaHeader->getDir()] = Mod;
for (const auto &Cb : Callbacks)
Cb->moduleMapAddUmbrellaHeader(&SourceMgr.getFileManager(), UmbrellaHeader);
}
void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir,
const Twine &NameAsWritten,
const Twine &PathRelativeToRootModuleDirectory) {
Mod->Umbrella = UmbrellaDir;
Mod->UmbrellaAsWritten = NameAsWritten.str();
Mod->UmbrellaRelativeToRootModuleDirectory =
PathRelativeToRootModuleDirectory.str();
UmbrellaDirs[UmbrellaDir] = Mod;
}
void ModuleMap::addUnresolvedHeader(Module *Mod,
Module::UnresolvedHeaderDirective Header,
bool &NeedsFramework) {
if (resolveAsBuiltinHeader(Mod, Header)) {
Header.Kind = headerRoleToKind(ModuleMap::ModuleHeaderRole(
headerKindToRole(Header.Kind) | ModuleMap::TextualHeader));
Header.HasBuiltinHeader = true;
}
if ((Header.Size || Header.ModTime) && !Header.IsUmbrella &&
Header.Kind != Module::HK_Excluded) {
if (Header.ModTime)
LazyHeadersByModTime[*Header.ModTime].push_back(Mod);
else
LazyHeadersBySize[*Header.Size].push_back(Mod);
Mod->UnresolvedHeaders.push_back(Header);
return;
}
resolveHeader(Mod, Header, NeedsFramework);
}
void ModuleMap::resolveHeaderDirectives(const FileEntry *File) const {
auto BySize = LazyHeadersBySize.find(File->getSize());
if (BySize != LazyHeadersBySize.end()) {
for (auto *M : BySize->second)
resolveHeaderDirectives(M, File);
LazyHeadersBySize.erase(BySize);
}
auto ByModTime = LazyHeadersByModTime.find(File->getModificationTime());
if (ByModTime != LazyHeadersByModTime.end()) {
for (auto *M : ByModTime->second)
resolveHeaderDirectives(M, File);
LazyHeadersByModTime.erase(ByModTime);
}
}
void ModuleMap::resolveHeaderDirectives(
Module *Mod, llvm::Optional<const FileEntry *> File) const {
bool NeedsFramework = false;
SmallVector<Module::UnresolvedHeaderDirective, 1> NewHeaders;
const auto Size = File ? File.value()->getSize() : 0;
const auto ModTime = File ? File.value()->getModificationTime() : 0;
for (auto &Header : Mod->UnresolvedHeaders) {
if (File && ((Header.ModTime && Header.ModTime != ModTime) ||
(Header.Size && Header.Size != Size)))
NewHeaders.push_back(Header);
else
const_cast<ModuleMap *>(this)->resolveHeader(Mod, Header, NeedsFramework);
}
Mod->UnresolvedHeaders.swap(NewHeaders);
}
void ModuleMap::addHeader(Module *Mod, Module::Header Header,
ModuleHeaderRole Role, bool Imported) {
KnownHeader KH(Mod, Role);
auto &HeaderList = Headers[Header.Entry];
if (llvm::is_contained(HeaderList, KH))
return;
HeaderList.push_back(KH);
Mod->Headers[headerRoleToKind(Role)].push_back(Header);
bool isCompilingModuleHeader =
LangOpts.isCompilingModule() && Mod->getTopLevelModule() == SourceModule;
if (!Imported || isCompilingModuleHeader) {
HeaderInfo.MarkFileModuleHeader(Header.Entry, Role,
isCompilingModuleHeader);
}
for (const auto &Cb : Callbacks)
Cb->moduleMapAddHeader(Header.Entry->getName());
}
void ModuleMap::excludeHeader(Module *Mod, Module::Header Header) {
(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::addAdditionalModuleMapFile(const Module *M,
const FileEntry *ModuleMap) {
AdditionalModMaps[M].insert(ModuleMap);
}
LLVM_DUMP_METHOD 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) {
auto Unresolved = std::move(Mod->UnresolvedExports);
Mod->UnresolvedExports.clear();
for (auto &UE : Unresolved) {
Module::ExportDecl Export = resolveExport(Mod, UE, Complain);
if (Export.getPointer() || Export.getInt())
Mod->Exports.push_back(Export);
else
Mod->UnresolvedExports.push_back(UE);
}
return !Mod->UnresolvedExports.empty();
}
bool ModuleMap::resolveUses(Module *Mod, bool Complain) {
auto Unresolved = std::move(Mod->UnresolvedDirectUses);
Mod->UnresolvedDirectUses.clear();
for (auto &UDU : Unresolved) {
Module *DirectUse = resolveModuleId(UDU, Mod, Complain);
if (DirectUse)
Mod->DirectUses.push_back(DirectUse);
else
Mod->UnresolvedDirectUses.push_back(UDU);
}
return !Mod->UnresolvedDirectUses.empty();
}
bool ModuleMap::resolveConflicts(Module *Mod, bool Complain) {
auto Unresolved = std::move(Mod->UnresolvedConflicts);
Mod->UnresolvedConflicts.clear();
for (auto &UC : Unresolved) {
if (Module *OtherMod = resolveModuleId(UC.Id, Mod, Complain)) {
Module::Conflict Conflict;
Conflict.Other = OtherMod;
Conflict.Message = UC.Message;
Mod->Conflicts.push_back(Conflict);
} else
Mod->UnresolvedConflicts.push_back(UC);
}
return !Mod->UnresolvedConflicts.empty();
}
namespace clang {
struct MMToken {
enum TokenKind {
Comma,
ConfigMacros,
Conflict,
EndOfFile,
HeaderKeyword,
Identifier,
Exclaim,
ExcludeKeyword,
ExplicitKeyword,
ExportKeyword,
ExportAsKeyword,
ExternKeyword,
FrameworkKeyword,
LinkKeyword,
ModuleKeyword,
Period,
PrivateKeyword,
UmbrellaKeyword,
UseKeyword,
RequiresKeyword,
Star,
StringLiteral,
IntegerLiteral,
TextualKeyword,
LBrace,
RBrace,
LSquare,
RSquare
} Kind;
SourceLocation::UIntTy Location;
unsigned StringLength;
union {
const char *StringData;
uint64_t IntegerValue;
};
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);
}
uint64_t getInteger() const {
return Kind == IntegerLiteral ? IntegerValue : 0;
}
StringRef getString() const {
return Kind == IntegerLiteral ? StringRef()
: StringRef(StringData, StringLength);
}
};
class ModuleMapParser {
Lexer &L;
SourceManager &SourceMgr;
const TargetInfo *Target;
DiagnosticsEngine &Diags;
ModuleMap ⤅
const FileEntry *ModuleMapFile;
SourceLocation CurrModuleDeclLoc;
const DirectoryEntry *Directory;
bool IsSystem;
bool HadError = false;
llvm::BumpPtrAllocator StringData;
MMToken Tok;
Module *ActiveModule = nullptr;
llvm::SmallPtrSet<Module *, 2> UsesRequiresExcludedHack;
SourceLocation consumeToken();
void skipUntil(MMToken::TokenKind K);
using ModuleId = SmallVector<std::pair<std::string, SourceLocation>, 2>;
bool parseModuleId(ModuleId &Id);
void parseModuleDecl();
void parseExternModuleDecl();
void parseRequiresDecl();
void parseHeaderDecl(MMToken::TokenKind, SourceLocation LeadingLoc);
void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
void parseExportDecl();
void parseExportAsDecl();
void parseUseDecl();
void parseLinkDecl();
void parseConfigMacros();
void parseConflict();
void parseInferredModuleDecl(bool Framework, bool Explicit);
void diagnosePrivateModules(SourceLocation ExplicitLoc,
SourceLocation FrameworkLoc);
using Attributes = ModuleMap::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, bool IsSystem)
: L(L), SourceMgr(SourceMgr), Target(Target), Diags(Diags), Map(Map),
ModuleMapFile(ModuleMapFile), Directory(Directory),
IsSystem(IsSystem) {
Tok.clear();
consumeToken();
}
bool parseModuleMapFile();
bool terminatedByDirective() { return false; }
SourceLocation getLocation() { return Tok.getLocation(); }
};
}
SourceLocation ModuleMapParser::consumeToken() {
SourceLocation Result = Tok.getLocation();
retry:
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("export_as", MMToken::ExportAsKeyword)
.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;
}
LangOptions LangOpts;
StringLiteralParser StringLiteral(LToken, SourceMgr, LangOpts, *Target);
if (StringLiteral.hadError)
goto retry;
unsigned Length = StringLiteral.GetStringLength();
char *Saved = StringData.Allocate<char>(Length + 1);
memcpy(Saved, StringLiteral.GetString().data(), Length);
Saved[Length] = 0;
Tok.Kind = MMToken::StringLiteral;
Tok.StringData = Saved;
Tok.StringLength = Length;
break;
}
case tok::numeric_constant: {
SmallString<32> SpellingBuffer;
SpellingBuffer.resize(LToken.getLength() + 1);
const char *Start = SpellingBuffer.data();
unsigned Length =
Lexer::getSpelling(LToken, Start, SourceMgr, Map.LangOpts);
uint64_t Value;
if (StringRef(Start, Length).getAsInteger(0, Value)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);
HadError = true;
goto retry;
}
Tok.Kind = MMToken::IntegerLiteral;
Tok.IntegerValue = Value;
break;
}
case tok::comment:
goto retry;
case tok::hash:
{
auto NextIsIdent = [&](StringRef Str) -> bool {
L.LexFromRawLexer(LToken);
return !LToken.isAtStartOfLine() && LToken.is(tok::raw_identifier) &&
LToken.getRawIdentifier() == Str;
};
if (NextIsIdent("pragma") && NextIsIdent("clang") &&
NextIsIdent("module") && NextIsIdent("contents")) {
Tok.Kind = MMToken::EndOfFile;
break;
}
}
LLVM_FALLTHROUGH;
default:
Diags.Report(Tok.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);
}
bool ModuleMapParser::parseModuleId(ModuleId &Id) {
Id.clear();
do {
if (Tok.is(MMToken::Identifier) || Tok.is(MMToken::StringLiteral)) {
Id.push_back(
std::make_pair(std::string(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 {
enum AttributeKind {
AT_unknown,
AT_system,
AT_extern_c,
AT_exhaustive,
AT_no_undeclared_includes
};
}
void ModuleMapParser::diagnosePrivateModules(SourceLocation ExplicitLoc,
SourceLocation FrameworkLoc) {
auto GenNoteAndFixIt = [&](StringRef BadName, StringRef Canonical,
const Module *M, SourceRange ReplLoc) {
auto D = Diags.Report(ActiveModule->DefinitionLoc,
diag::note_mmap_rename_top_level_private_module);
D << BadName << M->Name;
D << FixItHint::CreateReplacement(ReplLoc, Canonical);
};
for (auto E = Map.module_begin(); E != Map.module_end(); ++E) {
auto const *M = E->getValue();
if (M->Directory != ActiveModule->Directory)
continue;
SmallString<128> FullName(ActiveModule->getFullModuleName());
if (!FullName.startswith(M->Name) && !FullName.endswith("Private"))
continue;
SmallString<128> FixedPrivModDecl;
SmallString<128> Canonical(M->Name);
Canonical.append("_Private");
if (ActiveModule->Parent && ActiveModule->Name == "Private" && !M->Parent &&
M->Name == ActiveModule->Parent->Name) {
Diags.Report(ActiveModule->DefinitionLoc,
diag::warn_mmap_mismatched_private_submodule)
<< FullName;
SourceLocation FixItInitBegin = CurrModuleDeclLoc;
if (FrameworkLoc.isValid())
FixItInitBegin = FrameworkLoc;
if (ExplicitLoc.isValid())
FixItInitBegin = ExplicitLoc;
if (FrameworkLoc.isValid() || ActiveModule->Parent->IsFramework)
FixedPrivModDecl.append("framework ");
FixedPrivModDecl.append("module ");
FixedPrivModDecl.append(Canonical);
GenNoteAndFixIt(FullName, FixedPrivModDecl, M,
SourceRange(FixItInitBegin, ActiveModule->DefinitionLoc));
continue;
}
if (!ActiveModule->Parent && !M->Parent && M->Name != ActiveModule->Name &&
ActiveModule->Name != Canonical) {
Diags.Report(ActiveModule->DefinitionLoc,
diag::warn_mmap_mismatched_private_module_name)
<< ActiveModule->Name;
GenNoteAndFixIt(ActiveModule->Name, Canonical, M,
SourceRange(ActiveModule->DefinitionLoc));
}
}
}
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;
}
SourceLocation ExplicitLoc;
SourceLocation FrameworkLoc;
bool Explicit = false;
bool Framework = false;
if (Tok.is(MMToken::ExplicitKeyword)) {
ExplicitLoc = consumeToken();
Explicit = true;
}
if (Tok.is(MMToken::FrameworkKeyword)) {
FrameworkLoc = consumeToken();
Framework = true;
}
if (!Tok.is(MMToken::ModuleKeyword)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);
consumeToken();
HadError = true;
return;
}
CurrModuleDeclLoc = consumeToken();
if (Tok.is(MMToken::Star))
return parseInferredModuleDecl(Framework, Explicit);
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) {
Diags.Report(ExplicitLoc, diag::err_mmap_explicit_top_level);
Explicit = false;
ExplicitLoc = SourceLocation();
HadError = true;
}
Module *PreviousActiveModule = ActiveModule;
if (Id.size() > 1) {
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;
}
Diags.Report(Id[I].second, diag::err_mmap_missing_parent_module)
<< Id[I].first << (ActiveModule != nullptr)
<< (ActiveModule
? ActiveModule->getTopLevelModule()->getFullModuleName()
: "");
HadError = true;
}
if (TopLevelModule &&
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;
Attributes Attrs;
if (parseOptionalAttributes(Attrs))
return;
if (!Tok.is(MMToken::LBrace)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace)
<< ModuleName;
HadError = true;
return;
}
SourceLocation LBraceLoc = consumeToken();
Module *ShadowingModule = nullptr;
if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) {
bool LoadedFromASTFile = Existing->DefinitionLoc.isInvalid();
bool ParsedAsMainInput =
Map.LangOpts.getCompilingModule() == LangOptions::CMK_ModuleMap &&
Map.LangOpts.CurrentModule == ModuleName &&
SourceMgr.getDecomposedLoc(ModuleNameLoc).first !=
SourceMgr.getDecomposedLoc(Existing->DefinitionLoc).first;
if (!ActiveModule && (LoadedFromASTFile || ParsedAsMainInput)) {
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;
}
if (!Existing->Parent && Map.mayShadowNewModule(Existing)) {
ShadowingModule = Existing;
} else {
Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
<< ModuleName;
Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition);
skipUntil(MMToken::RBrace);
if (Tok.is(MMToken::RBrace))
consumeToken();
HadError = true;
return;
}
}
if (ShadowingModule) {
ActiveModule =
Map.createShadowedModule(ModuleName, Framework, ShadowingModule);
} else {
ActiveModule =
Map.findOrCreateModule(ModuleName, ActiveModule, Framework, Explicit)
.first;
}
ActiveModule->DefinitionLoc = ModuleNameLoc;
if (Attrs.IsSystem || IsSystem)
ActiveModule->IsSystem = true;
if (Attrs.IsExternC)
ActiveModule->IsExternC = true;
if (Attrs.NoUndeclaredIncludes ||
(!ActiveModule->Parent && ModuleName == "Darwin"))
ActiveModule->NoUndeclaredIncludes = true;
ActiveModule->Directory = Directory;
StringRef MapFileName(ModuleMapFile->getName());
if (MapFileName.endswith("module.private.modulemap") ||
MapFileName.endswith("module_private.map")) {
ActiveModule->ModuleMapIsPrivate = true;
}
SourceLocation StartLoc =
SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID());
if (Map.HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps &&
!Diags.isIgnored(diag::warn_mmap_mismatched_private_submodule,
StartLoc) &&
!Diags.isIgnored(diag::warn_mmap_mismatched_private_module_name,
StartLoc) &&
ActiveModule->ModuleMapIsPrivate)
diagnosePrivateModules(ExplicitLoc, FrameworkLoc);
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::ExportAsKeyword:
parseExportAsDecl();
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 (ActiveModule->IsFramework && !ActiveModule->isSubFramework() &&
ActiveModule->LinkLibraries.empty()) {
inferFrameworkLink(ActiveModule, Directory, SourceMgr.getFileManager());
}
if (!ActiveModule->IsAvailable && !ActiveModule->IsUnimportable &&
ActiveModule->Parent) {
ActiveModule->getTopLevelModule()->markUnavailable(false);
ActiveModule->getTopLevelModule()->MissingHeaders.append(
ActiveModule->MissingHeaders.begin(), ActiveModule->MissingHeaders.end());
}
ActiveModule = PreviousActiveModule;
}
void ModuleMapParser::parseExternModuleDecl() {
assert(Tok.is(MMToken::ExternKeyword));
SourceLocation ExternLoc = consumeToken();
if (!Tok.is(MMToken::ModuleKeyword)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);
consumeToken();
HadError = true;
return;
}
consumeToken();
ModuleId Id;
if (parseModuleId(Id)) {
HadError = true;
return;
}
if (!Tok.is(MMToken::StringLiteral)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_mmap_file);
HadError = true;
return;
}
std::string FileName = std::string(Tok.getString());
consumeToken();
StringRef FileNameRef = FileName;
SmallString<128> ModuleMapFileName;
if (llvm::sys::path::is_relative(FileNameRef)) {
ModuleMapFileName += Directory->getName();
llvm::sys::path::append(ModuleMapFileName, FileName);
FileNameRef = ModuleMapFileName;
}
if (auto File = SourceMgr.getFileManager().getFile(FileNameRef))
Map.parseModuleMapFile(
*File, IsSystem,
Map.HeaderInfo.getHeaderSearchOpts().ModuleMapFileHomeIsCwd
? Directory
: (*File)->getDir(),
FileID(), nullptr, ExternLoc);
}
static bool shouldAddRequirement(Module *M, StringRef Feature,
bool &IsRequiresExcludedHack) {
if (Feature == "excluded" &&
(M->fullModuleNameIs({"Darwin", "C", "excluded"}) ||
M->fullModuleNameIs({"Tcl", "Private"}))) {
IsRequiresExcludedHack = true;
return false;
} else if (Feature == "cplusplus" && M->fullModuleNameIs({"IOKit", "avc"})) {
return false;
}
return true;
}
void ModuleMapParser::parseRequiresDecl() {
assert(Tok.is(MMToken::RequiresKeyword));
consumeToken();
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;
}
std::string Feature = std::string(Tok.getString());
consumeToken();
bool IsRequiresExcludedHack = false;
bool ShouldAddRequirement =
shouldAddRequirement(ActiveModule, Feature, IsRequiresExcludedHack);
if (IsRequiresExcludedHack)
UsesRequiresExcludedHack.insert(ActiveModule);
if (ShouldAddRequirement) {
ActiveModule->addRequirement(Feature, RequiredState, Map.LangOpts,
*Map.Target);
}
if (!Tok.is(MMToken::Comma))
break;
consumeToken();
} while (true);
}
void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
SourceLocation LeadingLoc) {
ModuleMap::ModuleHeaderRole Role = ModuleMap::NormalHeader;
if (LeadingToken == MMToken::PrivateKeyword) {
Role = ModuleMap::PrivateHeader;
if (Tok.is(MMToken::TextualKeyword)) {
LeadingToken = Tok.Kind;
consumeToken();
}
}
if (LeadingToken == MMToken::TextualKeyword)
Role = ModuleMap::ModuleHeaderRole(Role | ModuleMap::TextualHeader);
if (UsesRequiresExcludedHack.count(ActiveModule)) {
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();
}
if (!Tok.is(MMToken::StringLiteral)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)
<< "header";
HadError = true;
return;
}
Module::UnresolvedHeaderDirective Header;
Header.FileName = std::string(Tok.getString());
Header.FileNameLoc = consumeToken();
Header.IsUmbrella = LeadingToken == MMToken::UmbrellaKeyword;
Header.Kind =
(LeadingToken == MMToken::ExcludeKeyword ? Module::HK_Excluded
: Map.headerRoleToKind(Role));
if (Header.IsUmbrella && ActiveModule->Umbrella) {
Diags.Report(Header.FileNameLoc, diag::err_mmap_umbrella_clash)
<< ActiveModule->getFullModuleName();
HadError = true;
return;
}
if (Tok.is(MMToken::LBrace)) {
SourceLocation LBraceLoc = consumeToken();
while (!Tok.is(MMToken::RBrace) && !Tok.is(MMToken::EndOfFile)) {
enum Attribute { Size, ModTime, Unknown };
StringRef Str = Tok.getString();
SourceLocation Loc = consumeToken();
switch (llvm::StringSwitch<Attribute>(Str)
.Case("size", Size)
.Case("mtime", ModTime)
.Default(Unknown)) {
case Size:
if (Header.Size)
Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;
if (!Tok.is(MMToken::IntegerLiteral)) {
Diags.Report(Tok.getLocation(),
diag::err_mmap_invalid_header_attribute_value) << Str;
skipUntil(MMToken::RBrace);
break;
}
Header.Size = Tok.getInteger();
consumeToken();
break;
case ModTime:
if (Header.ModTime)
Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;
if (!Tok.is(MMToken::IntegerLiteral)) {
Diags.Report(Tok.getLocation(),
diag::err_mmap_invalid_header_attribute_value) << Str;
skipUntil(MMToken::RBrace);
break;
}
Header.ModTime = Tok.getInteger();
consumeToken();
break;
case Unknown:
Diags.Report(Loc, diag::err_mmap_expected_header_attribute);
skipUntil(MMToken::RBrace);
break;
}
}
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;
}
}
bool NeedsFramework = false;
Map.addUnresolvedHeader(ActiveModule, std::move(Header), NeedsFramework);
if (NeedsFramework && ActiveModule)
Diags.Report(CurrModuleDeclLoc, diag::note_mmap_add_framework_keyword)
<< ActiveModule->getFullModuleName()
<< FixItHint::CreateReplacement(CurrModuleDeclLoc, "framework module");
}
static int compareModuleHeaders(const Module::Header *A,
const Module::Header *B) {
return A->NameAsWritten.compare(B->NameAsWritten);
}
void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) {
if (!Tok.is(MMToken::StringLiteral)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)
<< "umbrella";
HadError = true;
return;
}
std::string DirName = std::string(Tok.getString());
std::string DirNameAsWritten = DirName;
SourceLocation DirNameLoc = consumeToken();
if (ActiveModule->Umbrella) {
Diags.Report(DirNameLoc, diag::err_mmap_umbrella_clash)
<< ActiveModule->getFullModuleName();
HadError = true;
return;
}
const DirectoryEntry *Dir = nullptr;
if (llvm::sys::path::is_absolute(DirName)) {
if (auto D = SourceMgr.getFileManager().getDirectory(DirName))
Dir = *D;
} else {
SmallString<128> PathName;
PathName = Directory->getName();
llvm::sys::path::append(PathName, DirName);
if (auto D = SourceMgr.getFileManager().getDirectory(PathName))
Dir = *D;
}
if (!Dir) {
Diags.Report(DirNameLoc, diag::warn_mmap_umbrella_dir_not_found)
<< DirName;
return;
}
if (UsesRequiresExcludedHack.count(ActiveModule)) {
std::error_code EC;
SmallVector<Module::Header, 6> Headers;
llvm::vfs::FileSystem &FS =
SourceMgr.getFileManager().getVirtualFileSystem();
for (llvm::vfs::recursive_directory_iterator I(FS, Dir->getName(), EC), E;
I != E && !EC; I.increment(EC)) {
if (auto FE = SourceMgr.getFileManager().getFile(I->path())) {
Module::Header Header = {"", std::string(I->path()), *FE};
Headers.push_back(std::move(Header));
}
}
llvm::array_pod_sort(Headers.begin(), Headers.end(), compareModuleHeaders);
for (auto &Header : Headers)
Map.addHeader(ActiveModule, std::move(Header), ModuleMap::TextualHeader);
return;
}
if (Module *OwningModule = Map.UmbrellaDirs[Dir]) {
Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash)
<< OwningModule->getFullModuleName();
HadError = true;
return;
}
Map.setUmbrellaDir(ActiveModule, Dir, DirNameAsWritten, DirName);
}
void ModuleMapParser::parseExportDecl() {
assert(Tok.is(MMToken::ExportKeyword));
SourceLocation ExportLoc = consumeToken();
ModuleId ParsedModuleId;
bool Wildcard = false;
do {
if (Tok.is(MMToken::Identifier)) {
ParsedModuleId.push_back(
std::make_pair(std::string(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);
}
void ModuleMapParser::parseExportAsDecl() {
assert(Tok.is(MMToken::ExportAsKeyword));
consumeToken();
if (!Tok.is(MMToken::Identifier)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);
HadError = true;
return;
}
if (ActiveModule->Parent) {
Diags.Report(Tok.getLocation(), diag::err_mmap_submodule_export_as);
consumeToken();
return;
}
if (!ActiveModule->ExportAsModule.empty()) {
if (ActiveModule->ExportAsModule == Tok.getString()) {
Diags.Report(Tok.getLocation(), diag::warn_mmap_redundant_export_as)
<< ActiveModule->Name << Tok.getString();
} else {
Diags.Report(Tok.getLocation(), diag::err_mmap_conflicting_export_as)
<< ActiveModule->Name << ActiveModule->ExportAsModule
<< Tok.getString();
}
}
ActiveModule->ExportAsModule = std::string(Tok.getString());
Map.addLinkAsDependency(ActiveModule);
consumeToken();
}
void ModuleMapParser::parseUseDecl() {
assert(Tok.is(MMToken::UseKeyword));
auto KWLoc = consumeToken();
ModuleId ParsedModuleId;
parseModuleId(ParsedModuleId);
if (ActiveModule->Parent)
Diags.Report(KWLoc, diag::err_mmap_use_decl_submodule);
else
ActiveModule->UnresolvedDirectUses.push_back(ParsedModuleId);
}
void ModuleMapParser::parseLinkDecl() {
assert(Tok.is(MMToken::LinkKeyword));
SourceLocation LinkLoc = consumeToken();
bool IsFramework = false;
if (Tok.is(MMToken::FrameworkKeyword)) {
consumeToken();
IsFramework = true;
}
if (!Tok.is(MMToken::StringLiteral)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name)
<< IsFramework << SourceRange(LinkLoc);
HadError = true;
return;
}
std::string LibraryName = std::string(Tok.getString());
consumeToken();
ActiveModule->LinkLibraries.push_back(Module::LinkLibrary(LibraryName,
IsFramework));
}
void ModuleMapParser::parseConfigMacros() {
assert(Tok.is(MMToken::ConfigMacros));
SourceLocation ConfigMacrosLoc = consumeToken();
if (ActiveModule->Parent) {
Diags.Report(ConfigMacrosLoc, diag::err_mmap_config_macro_submodule);
}
Attributes Attrs;
if (parseOptionalAttributes(Attrs))
return;
if (Attrs.IsExhaustive && !ActiveModule->Parent) {
ActiveModule->ConfigMacrosExhaustive = true;
}
if (!Tok.is(MMToken::Identifier))
return;
if (!ActiveModule->Parent) {
ActiveModule->ConfigMacros.push_back(Tok.getString().str());
}
consumeToken();
do {
if (!Tok.is(MMToken::Comma))
break;
consumeToken();
if (!Tok.is(MMToken::Identifier)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro);
break;
}
if (!ActiveModule->Parent) {
ActiveModule->ConfigMacros.push_back(Tok.getString().str());
}
consumeToken();
} while (true);
}
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;
}
void ModuleMapParser::parseConflict() {
assert(Tok.is(MMToken::Conflict));
SourceLocation ConflictLoc = consumeToken();
Module::UnresolvedConflict Conflict;
if (parseModuleId(Conflict.Id))
return;
if (!Tok.is(MMToken::Comma)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_comma)
<< SourceRange(ConflictLoc);
return;
}
consumeToken();
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();
ActiveModule->UnresolvedConflicts.push_back(Conflict);
}
void ModuleMapParser::parseInferredModuleDecl(bool Framework, bool Explicit) {
assert(Tok.is(MMToken::Star));
SourceLocation StarLoc = consumeToken();
bool Failed = false;
if (!ActiveModule && !Framework) {
Diags.Report(StarLoc, diag::err_mmap_top_level_inferred_submodule);
Failed = true;
}
if (ActiveModule) {
if (!Failed && ActiveModule->IsAvailable &&
!ActiveModule->getUmbrellaDir()) {
Diags.Report(StarLoc, diag::err_mmap_inferred_no_umbrella);
Failed = true;
}
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;
}
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 (Failed) {
if (Tok.is(MMToken::LBrace)) {
consumeToken();
skipUntil(MMToken::RBrace);
if (Tok.is(MMToken::RBrace))
consumeToken();
}
HadError = true;
return;
}
Attributes Attrs;
if (parseOptionalAttributes(Attrs))
return;
if (ActiveModule) {
ActiveModule->InferSubmodules = true;
ActiveModule->InferredSubmoduleLoc = StarLoc;
ActiveModule->InferExplicitSubmodules = Explicit;
} else {
Map.InferredDirectories[Directory].InferModules = true;
Map.InferredDirectories[Directory].Attrs = Attrs;
Map.InferredDirectories[Directory].ModuleMapFile = ModuleMapFile;
}
if (!Tok.is(MMToken::LBrace)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace_wildcard);
HadError = true;
return;
}
SourceLocation LBraceLoc = consumeToken();
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();
if (!Tok.is(MMToken::Identifier)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_missing_exclude_name);
break;
}
Map.InferredDirectories[Directory].ExcludedModules.push_back(
std::string(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;
}
}
bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) {
bool HadError = false;
while (Tok.is(MMToken::LSquare)) {
SourceLocation LSquareLoc = consumeToken();
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;
}
AttributeKind Attribute
= llvm::StringSwitch<AttributeKind>(Tok.getString())
.Case("exhaustive", AT_exhaustive)
.Case("extern_c", AT_extern_c)
.Case("no_undeclared_includes", AT_no_undeclared_includes)
.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;
case AT_no_undeclared_includes:
Attrs.NoUndeclaredIncludes = true;
break;
}
consumeToken();
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;
}
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::ExportAsKeyword:
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::IntegerLiteral:
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, FileID ID,
unsigned *Offset,
SourceLocation ExternModuleLoc) {
assert(Target && "Missing target information");
llvm::DenseMap<const FileEntry *, bool>::iterator Known
= ParsedModuleMap.find(File);
if (Known != ParsedModuleMap.end())
return Known->second;
if (ID.isInvalid()) {
auto FileCharacter =
IsSystem ? SrcMgr::C_System_ModuleMap : SrcMgr::C_User_ModuleMap;
ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter);
}
assert(Target && "Missing target information");
llvm::Optional<llvm::MemoryBufferRef> Buffer = SourceMgr.getBufferOrNone(ID);
if (!Buffer)
return ParsedModuleMap[File] = true;
assert((!Offset || *Offset <= Buffer->getBufferSize()) &&
"invalid buffer offset");
Lexer L(SourceMgr.getLocForStartOfFile(ID), MMapLangOpts,
Buffer->getBufferStart(),
Buffer->getBufferStart() + (Offset ? *Offset : 0),
Buffer->getBufferEnd());
SourceLocation Start = L.getSourceLocation();
ModuleMapParser Parser(L, SourceMgr, Target, Diags, *this, File, Dir,
IsSystem);
bool Result = Parser.parseModuleMapFile();
ParsedModuleMap[File] = Result;
if (Offset) {
auto Loc = SourceMgr.getDecomposedLoc(Parser.getLocation());
assert(Loc.first == ID && "stopped in a different file?");
*Offset = Loc.second;
}
for (const auto &Cb : Callbacks)
Cb->moduleMapFileRead(Start, *File, IsSystem);
return Result;
}