aboutsummaryrefslogtreecommitdiff
path: root/lib/Lex/ModuleMap.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2011-12-31 04:05:44 +0000
committerDouglas Gregor <dgregor@apple.com>2011-12-31 04:05:44 +0000
commit51f564f80d9f71e175635b452ffeeeff899e9bf1 (patch)
treeada57bc51cc68158361f5d1a49d8de00b6a1e564 /lib/Lex/ModuleMap.cpp
parent868f65c6bf904591f62a0d69866825d2d3dfd16f (diff)
Implement support for module requirements, which indicate the language
features needed for a particular module to be available. This allows mixed-language modules, where certain headers only work under some language variants (e.g., in C++, std.tuple might only be available in C++11 mode). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147387 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Lex/ModuleMap.cpp')
-rw-r--r--lib/Lex/ModuleMap.cpp140
1 files changed, 134 insertions, 6 deletions
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index 7d093c863c..f43abc8615 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -69,7 +69,10 @@ ModuleMap::resolveExport(Module *Mod,
return Module::ExportDecl(Context, Unresolved.Wildcard);
}
-ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC) {
+ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC,
+ const LangOptions &LangOpts)
+ : LangOpts(LangOpts)
+{
llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(new DiagnosticIDs);
Diags = llvm::IntrusiveRefCntPtr<DiagnosticsEngine>(
new DiagnosticsEngine(DiagIDs));
@@ -90,8 +93,14 @@ ModuleMap::~ModuleMap() {
Module *ModuleMap::findModuleForHeader(const FileEntry *File) {
llvm::DenseMap<const FileEntry *, Module *>::iterator Known
= Headers.find(File);
- if (Known != Headers.end())
+ if (Known != Headers.end()) {
+ // If a header corresponds to an unavailable module, don't report
+ // that it maps to anything.
+ if (!Known->second->isAvailable())
+ return 0;
+
return Known->second;
+ }
const DirectoryEntry *Dir = File->getDir();
llvm::SmallVector<const DirectoryEntry *, 2> SkippedDirs;
@@ -110,7 +119,7 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) {
Module *UmbrellaModule = Result;
while (!UmbrellaModule->getUmbrellaDir() && UmbrellaModule->Parent)
UmbrellaModule = UmbrellaModule->Parent;
-
+
if (UmbrellaModule->InferSubmodules) {
// Infer submodules for each of the directories we found between
// the directory of the umbrella header and the directory where
@@ -149,6 +158,12 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) {
}
Headers[File] = Result;
+
+ // If a header corresponds to an unavailable module, don't report
+ // that it maps to anything.
+ if (!Result->isAvailable())
+ return 0;
+
return Result;
}
@@ -166,6 +181,67 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) {
return 0;
}
+bool ModuleMap::isHeaderInUnavailableModule(const FileEntry *Header) {
+ llvm::DenseMap<const FileEntry *, Module *>::iterator Known
+ = Headers.find(Header);
+ if (Known != Headers.end())
+ return !Known->second->isAvailable();
+
+ const DirectoryEntry *Dir = Header->getDir();
+ llvm::SmallVector<const DirectoryEntry *, 2> SkippedDirs;
+ StringRef DirName = Dir->getName();
+
+ // Keep walking up the directory hierarchy, looking for a directory with
+ // an umbrella header.
+ do {
+ llvm::DenseMap<const DirectoryEntry *, Module *>::iterator KnownDir
+ = UmbrellaDirs.find(Dir);
+ if (KnownDir != UmbrellaDirs.end()) {
+ Module *Found = KnownDir->second;
+ if (!Found->isAvailable())
+ 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.
+ StringRef Name = llvm::sys::path::stem(SkippedDirs[I-1]->getName());
+ Found = lookupModuleQualified(Name, Found);
+ if (!Found)
+ return false;
+ if (!Found->isAvailable())
+ return true;
+ }
+
+ // Infer a submodule with the same name as this header file.
+ StringRef Name = llvm::sys::path::stem(Header->getName());
+ Found = lookupModuleQualified(Name, Found);
+ if (!Found)
+ return false;
+ }
+
+ return !Found->isAvailable();
+ }
+
+ 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) {
llvm::StringMap<Module *>::iterator Known = Modules.find(Name);
if (Known != Modules.end())
@@ -375,6 +451,7 @@ namespace clang {
/// \brief A token in a module map file.
struct MMToken {
enum TokenKind {
+ Comma,
EndOfFile,
HeaderKeyword,
Identifier,
@@ -384,6 +461,7 @@ namespace clang {
ModuleKeyword,
Period,
UmbrellaKeyword,
+ RequiresKeyword,
Star,
StringLiteral,
LBrace,
@@ -449,6 +527,7 @@ namespace clang {
ModuleId;
bool parseModuleId(ModuleId &Id);
void parseModuleDecl();
+ void parseRequiresDecl();
void parseHeaderDecl(SourceLocation UmbrellaLoc);
void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
void parseExportDecl();
@@ -494,10 +573,15 @@ retry:
.Case("export", MMToken::ExportKeyword)
.Case("framework", MMToken::FrameworkKeyword)
.Case("module", MMToken::ModuleKeyword)
+ .Case("requires", MMToken::RequiresKeyword)
.Case("umbrella", MMToken::UmbrellaKeyword)
.Default(MMToken::Identifier);
break;
-
+
+ case tok::comma:
+ Tok.Kind = MMToken::Comma;
+ break;
+
case tok::eof:
Tok.Kind = MMToken::EndOfFile;
break;
@@ -614,6 +698,7 @@ bool ModuleMapParser::parseModuleId(ModuleId &Id) {
/// 'explicit'[opt] 'framework'[opt] 'module' module-id { module-member* }
///
/// module-member:
+/// requires-declaration
/// header-declaration
/// submodule-declaration
/// export-declaration
@@ -755,6 +840,10 @@ void ModuleMapParser::parseModuleDecl() {
parseExportDecl();
break;
+ case MMToken::RequiresKeyword:
+ parseRequiresDecl();
+ break;
+
case MMToken::UmbrellaKeyword: {
SourceLocation UmbrellaLoc = consumeToken();
if (Tok.is(MMToken::HeaderKeyword))
@@ -787,6 +876,43 @@ void ModuleMapParser::parseModuleDecl() {
ActiveModule = PreviousActiveModule;
}
+/// \brief Parse a requires declaration.
+///
+/// requires-declaration:
+/// 'requires' feature-list
+///
+/// feature-list:
+/// identifier ',' feature-list
+/// identifier
+void ModuleMapParser::parseRequiresDecl() {
+ assert(Tok.is(MMToken::RequiresKeyword));
+
+ // Parse 'requires' keyword.
+ consumeToken();
+
+ // Parse the feature-list.
+ do {
+ 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, Map.LangOpts);
+
+ 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.
void appendSubframeworkPaths(Module *Mod, llvm::SmallVectorImpl<char> &Path) {
@@ -1123,12 +1249,14 @@ bool ModuleMapParser::parseModuleMapFile() {
parseModuleDecl();
break;
+ case MMToken::Comma:
case MMToken::ExportKeyword:
case MMToken::HeaderKeyword:
case MMToken::Identifier:
case MMToken::LBrace:
case MMToken::Period:
case MMToken::RBrace:
+ case MMToken::RequiresKeyword:
case MMToken::Star:
case MMToken::StringLiteral:
case MMToken::UmbrellaKeyword:
@@ -1149,8 +1277,8 @@ bool ModuleMap::parseModuleMapFile(const FileEntry *File) {
return true;
// Parse this module map file.
- Lexer L(ID, SourceMgr->getBuffer(ID), *SourceMgr, LangOpts);
- Diags->getClient()->BeginSourceFile(LangOpts);
+ Lexer L(ID, SourceMgr->getBuffer(ID), *SourceMgr, MMapLangOpts);
+ Diags->getClient()->BeginSourceFile(MMapLangOpts);
ModuleMapParser Parser(L, *SourceMgr, *Diags, *this, File->getDir());
bool Result = Parser.parseModuleMapFile();
Diags->getClient()->EndSourceFile();