summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/include/clang/Basic/DiagnosticLexKinds.td15
-rw-r--r--clang/include/clang/Lex/HeaderSearch.h22
-rw-r--r--clang/include/clang/Lex/ModuleMap.h124
-rw-r--r--clang/lib/Lex/CMakeLists.txt1
-rw-r--r--clang/lib/Lex/HeaderSearch.cpp92
-rw-r--r--clang/lib/Lex/ModuleMap.cpp506
-rw-r--r--clang/test/Modules/Inputs/normal-module-map/a1.h1
-rw-r--r--clang/test/Modules/Inputs/normal-module-map/a2.h1
-rw-r--r--clang/test/Modules/Inputs/normal-module-map/b1.h2
-rw-r--r--clang/test/Modules/Inputs/normal-module-map/module.map8
-rw-r--r--clang/test/Modules/Inputs/normal-module-map/nested/module.map4
-rw-r--r--clang/test/Modules/Inputs/normal-module-map/nested/nested1.h1
-rw-r--r--clang/test/Modules/Inputs/normal-module-map/nested/nested2.h1
-rw-r--r--clang/test/Modules/normal-module-map.cpp10
14 files changed, 786 insertions, 2 deletions
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index f1e8c75d6b9..97095e792c8 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -374,4 +374,19 @@ def err_pp_include_in_arc_cf_code_audited : Error<
def err_pp_eof_in_arc_cf_code_audited : Error<
"'#pragma clang arc_cf_code_audited' was not ended within this file">;
+// Module map parsing
+def err_mmap_unknown_token : Error<"skipping stray token">;
+def err_mmap_expected_module : Error<"expected module declaration">;
+def err_mmap_expected_module_after_explicit : Error<
+ "expected 'module' keyword after 'explicit'">;
+def err_mmap_expected_module_name : Error<"expected module name">;
+def err_mmap_expected_lbrace : Error<"expected '{' to start module '%0'">;
+def err_mmap_expected_rbrace : Error<"expected '}'">;
+def note_mmap_lbrace_match : Note<"to match this '{'">;
+def err_mmap_expected_member : Error<
+ "expected umbrella header, header, or submodule">;
+def err_mmap_expected_header : Error<"expected a header name after '%0'">;
+def err_mmap_module_redefinition : Error<
+ "redefinition of module '%0'">;
+def note_mmap_prev_definition : Note<"previously defined here">;
}
diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h
index 0b213738580..995deab9dc7 100644
--- a/clang/include/clang/Lex/HeaderSearch.h
+++ b/clang/include/clang/Lex/HeaderSearch.h
@@ -15,6 +15,7 @@
#define LLVM_CLANG_LEX_HEADERSEARCH_H
#include "clang/Lex/DirectoryLookup.h"
+#include "clang/Lex/ModuleMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Allocator.h"
@@ -151,7 +152,6 @@ class HeaderSearch {
llvm::StringMap<std::pair<unsigned, unsigned>, llvm::BumpPtrAllocator>
LookupFileCache;
-
/// FrameworkMap - This is a collection mapping a framework or subframework
/// name like "Carbon" to the Carbon.framework directory.
llvm::StringMap<const DirectoryEntry *, llvm::BumpPtrAllocator>
@@ -161,6 +161,12 @@ class HeaderSearch {
/// headermaps. This vector owns the headermap.
std::vector<std::pair<const FileEntry*, const HeaderMap*> > HeaderMaps;
+ /// \brief The mapping between modules and headers.
+ ModuleMap ModMap;
+
+ /// \brief Describes whether a given directory has a module map in it.
+ llvm::DenseMap<const DirectoryEntry *, bool> DirectoryHasModuleMap;
+
/// \brief Uniqued set of framework names, which is used to track which
/// headers were included as framework headers.
llvm::StringSet<llvm::BumpPtrAllocator> FrameworkNames;
@@ -347,6 +353,20 @@ public:
void IncrementFrameworkLookupCount() { ++NumFrameworkLookups; }
+ /// \brief Determine whether there is a module map that may map the header
+ /// with the given file name to a (sub)module.
+ ///
+ /// \param Filename The name of the file.
+ ///
+ /// \param Root The "root" directory, at which we should stop looking for
+ /// module maps.
+ bool hasModuleMap(StringRef Filename, const DirectoryEntry *Root);
+
+ /// \brief Retrieve the module that corresponds to the given file, if any.
+ ///
+ /// FIXME: This will need to be generalized for submodules.
+ StringRef getModuleForHeader(const FileEntry *File);
+
typedef std::vector<HeaderFileInfo>::const_iterator header_file_iterator;
header_file_iterator header_file_begin() const { return FileInfo.begin(); }
header_file_iterator header_file_end() const { return FileInfo.end(); }
diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h
new file mode 100644
index 00000000000..2834f6a11bc
--- /dev/null
+++ b/clang/include/clang/Lex/ModuleMap.h
@@ -0,0 +1,124 @@
+//===--- ModuleMap.h - 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 interface, which describes the layout of a
+// module as it relates to headers.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LLVM_CLANG_LEX_MODULEMAP_H
+#define LLVM_CLANG_LEX_MODULEMAP_H
+
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringMap.h"
+#include <string>
+
+namespace clang {
+
+class FileEntry;
+class FileManager;
+class DiagnosticConsumer;
+class DiagnosticsEngine;
+class ModuleMapParser;
+
+class ModuleMap {
+public:
+ /// \brief Describes a module or submodule.
+ struct Module {
+ /// \brief The name of this module.
+ std::string Name;
+
+ /// \brief The location of the module definition.
+ SourceLocation DefinitionLoc;
+
+ /// \brief The parent of this module. This will be NULL for the top-level
+ /// module.
+ Module *Parent;
+
+ /// \brief The umbrella header, if any.
+ ///
+ /// Only the top-level module can have an umbrella header.
+ const FileEntry *UmbrellaHeader;
+
+ /// \brief The submodules of this module, indexed by name.
+ llvm::StringMap<Module *> SubModules;
+
+ /// \brief The headers that are part of this module.
+ llvm::SmallVector<const FileEntry *, 2> Headers;
+
+ /// \brief Whether this is an explicit submodule.
+ bool IsExplicit;
+
+ /// \brief Construct a top-level module.
+ explicit Module(StringRef Name, SourceLocation DefinitionLoc)
+ : Name(Name), DefinitionLoc(DefinitionLoc), Parent(0), UmbrellaHeader(0),
+ IsExplicit(false) { }
+
+ /// \brief Construct a new module or submodule.
+ Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
+ bool IsExplicit)
+ : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent),
+ UmbrellaHeader(0), IsExplicit(IsExplicit) {
+ }
+
+ /// \brief Determine whether this module is a submodule.
+ bool isSubModule() const { return Parent != 0; }
+
+ /// \brief Retrieve the full name of this module, including the path from
+ /// its top-level module.
+ std::string getFullModuleName() const;
+ };
+
+private:
+ SourceManager *SourceMgr;
+ llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
+ LangOptions LangOpts;
+
+ /// \brief The top-level modules that are known
+ llvm::StringMap<Module *> Modules;
+
+ /// \brief Mapping from each header to the module that owns the contents of the
+ /// that header.
+ llvm::DenseMap<const FileEntry *, Module *> Headers;
+
+ friend class ModuleMapParser;
+
+public:
+ /// \brief Construct a new module map.
+ ///
+ /// \param FileMgr The file manager used to find module files and headers.
+ /// This file manager should be shared with the header-search mechanism, since
+ /// they will refer to the same headers.
+ ///
+ /// \param DC A diagnostic consumer that will be cloned for use in generating
+ /// diagnostics.
+ ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC);
+
+ ~ModuleMap();
+
+ /// \brief Parse the given module map file, and record any modules we
+ /// encounter.
+ ///
+ /// \param File The file to be parsed.
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ bool parseModuleMapFile(const FileEntry *File);
+
+ /// \brief Dump the contents of the module map, for debugging purposes.
+ void dump();
+};
+
+}
+#endif
diff --git a/clang/lib/Lex/CMakeLists.txt b/clang/lib/Lex/CMakeLists.txt
index 80e2820101f..39c4fd590dc 100644
--- a/clang/lib/Lex/CMakeLists.txt
+++ b/clang/lib/Lex/CMakeLists.txt
@@ -11,6 +11,7 @@ add_clang_library(clangLex
LiteralSupport.cpp
MacroArgs.cpp
MacroInfo.cpp
+ ModuleMap.cpp
PPCaching.cpp
PPDirectives.cpp
PPExpressions.cpp
diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp
index 837b913d475..6403cfb8683 100644
--- a/clang/lib/Lex/HeaderSearch.cpp
+++ b/clang/lib/Lex/HeaderSearch.cpp
@@ -13,6 +13,7 @@
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/HeaderMap.h"
+#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/IdentifierTable.h"
#include "llvm/Support/FileSystem.h"
@@ -37,7 +38,8 @@ HeaderFileInfo::getControllingMacro(ExternalIdentifierLookup *External) {
ExternalHeaderFileInfoSource::~ExternalHeaderFileInfoSource() {}
HeaderSearch::HeaderSearch(FileManager &FM, DiagnosticsEngine &Diags)
- : FileMgr(FM), Diags(Diags), FrameworkMap(64)
+ : FileMgr(FM), Diags(Diags), FrameworkMap(64),
+ ModMap(FileMgr, *Diags.getClient())
{
AngledDirIdx = 0;
SystemDirIdx = 0;
@@ -192,6 +194,24 @@ const FileEntry *DirectoryLookup::LookupFile(
RelativePath->clear();
RelativePath->append(Filename.begin(), Filename.end());
}
+
+ // If we have a module map that might map this header, load it and
+ // check whether we'll have a suggestion for a module.
+ if (SuggestedModule && HS.hasModuleMap(TmpDir, getDir())) {
+ const FileEntry *File = HS.getFileMgr().getFile(TmpDir.str(),
+ /*openFile=*/false);
+ if (!File)
+ return File;
+
+ // If there is a module that corresponds to this header,
+ // suggest it.
+ StringRef Module = HS.getModuleForHeader(File);
+ if (!Module.empty() && Module != BuildingModule)
+ *SuggestedModule = Module;
+
+ return File;
+ }
+
return HS.getFileMgr().getFile(TmpDir.str(), /*openFile=*/true);
}
@@ -688,3 +708,73 @@ size_t HeaderSearch::getTotalMemory() const {
StringRef HeaderSearch::getUniqueFrameworkName(StringRef Framework) {
return FrameworkNames.GetOrCreateValue(Framework).getKey();
}
+
+bool HeaderSearch::hasModuleMap(StringRef FileName,
+ const DirectoryEntry *Root) {
+ llvm::SmallVector<const DirectoryEntry *, 2> FixUpDirectories;
+
+ StringRef DirName = FileName;
+ do {
+ // Get the parent directory name.
+ DirName = llvm::sys::path::parent_path(DirName);
+ if (DirName.empty())
+ return false;
+
+ // Determine whether this directory exists.
+ const DirectoryEntry *Dir = FileMgr.getDirectory(DirName);
+ if (!Dir)
+ return false;
+
+ llvm::DenseMap<const DirectoryEntry *, bool>::iterator
+ KnownDir = DirectoryHasModuleMap.find(Dir);
+ if (KnownDir != DirectoryHasModuleMap.end()) {
+ // We have seen this directory before. If it has no module map file,
+ // we're done.
+ if (!KnownDir->second)
+ return false;
+
+ // All of the directories we stepped through inherit this module map
+ // file.
+ for (unsigned I = 0, N = FixUpDirectories.size(); I != N; ++I)
+ DirectoryHasModuleMap[FixUpDirectories[I]] = true;
+
+ return true;
+ }
+
+ // We have not checked for a module map file in this directory yet;
+ // do so now.
+ llvm::SmallString<128> ModuleMapFileName;
+ ModuleMapFileName += Dir->getName();
+ llvm::sys::path::append(ModuleMapFileName, "module.map");
+ if (const FileEntry *ModuleMapFile = FileMgr.getFile(ModuleMapFileName)) {
+ // We have found a module map file. Try to parse it.
+ if (!ModMap.parseModuleMapFile(ModuleMapFile)) {
+ // This directory has a module map.
+ DirectoryHasModuleMap[Dir] = true;
+
+ // All of the directories we stepped through inherit this module map
+ // file.
+ for (unsigned I = 0, N = FixUpDirectories.size(); I != N; ++I)
+ DirectoryHasModuleMap[FixUpDirectories[I]] = true;
+
+ return true;
+ }
+ }
+
+ // This directory did not have a module map file.
+ DirectoryHasModuleMap[Dir] = false;
+
+ // Keep track of all of the directories we checked, so we can mark them as
+ // having module maps if we eventually do find a module map.
+ FixUpDirectories.push_back(Dir);
+ } while (true);
+
+ return false;
+}
+
+StringRef HeaderSearch::getModuleForHeader(const FileEntry *File) {
+ // FIXME: Actually look for the corresponding module for this header.
+ return StringRef();
+}
+
+
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
new file mode 100644
index 00000000000..69bc52636c2
--- /dev/null
+++ b/clang/lib/Lex/ModuleMap.cpp
@@ -0,0 +1,506 @@
+//===--- 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/Lex/Lexer.h"
+#include "clang/Lex/LiteralSupport.h"
+#include "clang/Lex/LexDiagnostic.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+using namespace clang;
+
+//----------------------------------------------------------------------------//
+// Module
+//----------------------------------------------------------------------------//
+
+std::string ModuleMap::Module::getFullModuleName() const {
+ llvm::SmallVector<StringRef, 2> Names;
+
+ // Build up the set of module names (from innermost to outermost).
+ for (const Module *M = this; M; M = M->Parent)
+ Names.push_back(M->Name);
+
+ std::string Result;
+ for (llvm::SmallVector<StringRef, 2>::reverse_iterator I = Names.rbegin(),
+ IEnd = Names.rend();
+ I != IEnd; ++I) {
+ if (!Result.empty())
+ Result += '.';
+
+ Result += *I;
+ }
+
+ return Result;
+}
+
+//----------------------------------------------------------------------------//
+// Module map
+//----------------------------------------------------------------------------//
+
+ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC) {
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(new DiagnosticIDs);
+ Diags = llvm::IntrusiveRefCntPtr<DiagnosticsEngine>(
+ new DiagnosticsEngine(DiagIDs));
+ Diags->setClient(DC.clone(*Diags), /*ShouldOwnClient=*/true);
+ SourceMgr = new SourceManager(*Diags, FileMgr);
+}
+
+ModuleMap::~ModuleMap() {
+ delete SourceMgr;
+}
+
+static void indent(llvm::raw_ostream &OS, unsigned Spaces) {
+ OS << std::string(' ', Spaces);
+}
+
+static void dumpModule(llvm::raw_ostream &OS, ModuleMap::Module *M,
+ unsigned Indent) {
+ indent(OS, Indent);
+ if (M->IsExplicit)
+ OS << "explicit ";
+ OS << M->Name << " {\n";
+
+ if (M->UmbrellaHeader) {
+ indent(OS, Indent + 2);
+ OS << "umbrella \"" << M->UmbrellaHeader->getName() << "\"\n";
+ }
+
+ for (unsigned I = 0, N = M->Headers.size(); I != N; ++I) {
+ indent(OS, Indent + 2);
+ OS << "header \"" << M->Headers[I]->getName() << "\"\n";
+ }
+
+ for (llvm::StringMap<ModuleMap::Module *>::iterator
+ MI = M->SubModules.begin(),
+ MIEnd = M->SubModules.end();
+ MI != MIEnd; ++MI)
+ dumpModule(llvm::errs(), MI->getValue(), Indent + 2);
+
+ indent(OS, Indent);
+ OS << "}\n";
+}
+
+void ModuleMap::dump() {
+ llvm::errs() << "Modules:";
+ for (llvm::StringMap<Module *>::iterator M = Modules.begin(),
+ MEnd = Modules.end();
+ M != MEnd; ++M)
+ dumpModule(llvm::errs(), M->getValue(), 2);
+
+ llvm::errs() << "Headers:";
+ for (llvm::DenseMap<const FileEntry *, Module *>::iterator
+ H = Headers.begin(),
+ HEnd = Headers.end();
+ H != HEnd; ++H) {
+ llvm::errs() << " \"" << H->first->getName() << "\" -> "
+ << H->second->getFullModuleName() << "\n";
+ }
+}
+
+//----------------------------------------------------------------------------//
+// Module map file parser
+//----------------------------------------------------------------------------//
+
+namespace clang {
+ /// \brief A token in a module map file.
+ struct MMToken {
+ enum TokenKind {
+ EndOfFile,
+ HeaderKeyword,
+ Identifier,
+ ExplicitKeyword,
+ ModuleKeyword,
+ UmbrellaKeyword,
+ StringLiteral,
+ LBrace,
+ RBrace
+ } Kind;
+
+ unsigned Location;
+ unsigned StringLength;
+ const char *StringData;
+
+ void clear() {
+ Kind = EndOfFile;
+ Location = 0;
+ StringLength = 0;
+ StringData = 0;
+ }
+
+ 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;
+ DiagnosticsEngine &Diags;
+ ModuleMap &Map;
+
+ /// \brief Whether an error occurred.
+ bool HadError;
+
+ /// \brief Default target information, used only for string literal
+ /// parsing.
+ TargetInfo *Target;
+
+ /// \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.
+ ModuleMap::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);
+
+ void parseModuleDecl();
+ void parseUmbrellaDecl();
+ void parseHeaderDecl();
+
+ public:
+ typedef ModuleMap::Module Module;
+
+ explicit ModuleMapParser(Lexer &L, SourceManager &SourceMgr,
+ DiagnosticsEngine &Diags,
+ ModuleMap &Map)
+ : L(L), SourceMgr(SourceMgr), Diags(Diags), Map(Map), HadError(false),
+ ActiveModule(0)
+ {
+ TargetOptions TargetOpts;
+ TargetOpts.Triple = llvm::sys::getDefaultTargetTriple();
+ Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
+
+ 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:
+ Tok.StringData = LToken.getRawIdentifierData();
+ Tok.StringLength = LToken.getLength();
+ Tok.Kind = llvm::StringSwitch<MMToken::TokenKind>(Tok.getString())
+ .Case("header", MMToken::HeaderKeyword)
+ .Case("explicit", MMToken::ExplicitKeyword)
+ .Case("module", MMToken::ModuleKeyword)
+ .Case("umbrella", MMToken::UmbrellaKeyword)
+ .Default(MMToken::Identifier);
+ break;
+
+ case tok::eof:
+ Tok.Kind = MMToken::EndOfFile;
+ break;
+
+ case tok::l_brace:
+ Tok.Kind = MMToken::LBrace;
+ break;
+
+ case tok::r_brace:
+ Tok.Kind = MMToken::RBrace;
+ break;
+
+ case tok::string_literal: {
+ // Parse the string literal.
+ LangOptions LangOpts;
+ StringLiteralParser StringLiteral(&LToken, 1, 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;
+ do {
+ switch (Tok.Kind) {
+ case MMToken::EndOfFile:
+ return;
+
+ case MMToken::LBrace:
+ if (Tok.is(K) && braceDepth == 0)
+ return;
+
+ ++braceDepth;
+ break;
+
+ case MMToken::RBrace:
+ if (braceDepth > 0)
+ --braceDepth;
+ else if (Tok.is(K))
+ return;
+ break;
+
+ default:
+ if (braceDepth == 0 && Tok.is(K))
+ return;
+ break;
+ }
+
+ consumeToken();
+ } while (true);
+}
+
+/// \brief Parse a module declaration.
+///
+/// module-declaration:
+/// 'module' identifier { module-member* }
+///
+/// module-member:
+/// umbrella-declaration
+/// header-declaration
+/// 'explicit'[opt] module-declaration
+void ModuleMapParser::parseModuleDecl() {
+ assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword));
+
+ // Parse 'explicit' keyword, if present.
+ bool Explicit = false;
+ if (Tok.is(MMToken::ExplicitKeyword)) {
+ consumeToken();
+ Explicit = true;
+ }
+
+ // Parse 'module' keyword.
+ if (!Tok.is(MMToken::ModuleKeyword)) {
+ Diags.Report(Tok.getLocation(),
+ diag::err_mmap_expected_module_after_explicit);
+ consumeToken();
+ HadError = true;
+ return;
+ }
+ consumeToken(); // 'module' keyword
+
+ // Parse the module name.
+ if (!Tok.is(MMToken::Identifier)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name);
+ HadError = true;
+ return;
+ }
+ StringRef ModuleName = Tok.getString();
+ SourceLocation ModuleNameLoc = consumeToken();
+
+ // 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.
+ llvm::StringMap<Module *> &ModuleSpace
+ = ActiveModule? ActiveModule->SubModules : Map.Modules;
+ llvm::StringMap<Module *>::iterator ExistingModule
+ = ModuleSpace.find(ModuleName);
+ if (ExistingModule != ModuleSpace.end()) {
+ Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
+ << ModuleName;
+ Diags.Report(ExistingModule->getValue()->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 = new Module(ModuleName, ModuleNameLoc, ActiveModule, Explicit);
+ ModuleSpace[ModuleName] = ActiveModule;
+
+ bool Done = false;
+ do {
+ switch (Tok.Kind) {
+ case MMToken::EndOfFile:
+ case MMToken::RBrace:
+ Done = true;
+ break;
+
+ case MMToken::ExplicitKeyword:
+ case MMToken::ModuleKeyword:
+ parseModuleDecl();
+ break;
+
+ case MMToken::HeaderKeyword:
+ parseHeaderDecl();
+ break;
+
+ case MMToken::UmbrellaKeyword:
+ parseUmbrellaDecl();
+ 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;
+ }
+
+ // We're done parsing this module. Pop back to our parent scope.
+ ActiveModule = ActiveModule->Parent;
+}
+
+/// \brief Parse an umbrella header declaration.
+///
+/// umbrella-declaration:
+/// 'umbrella' string-literal
+void ModuleMapParser::parseUmbrellaDecl() {
+ assert(Tok.is(MMToken::UmbrellaKeyword));
+ SourceLocation UmbrellaLoc = consumeToken();
+
+ // Parse the header name.
+ if (!Tok.is(MMToken::StringLiteral)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)
+ << "umbrella";
+ HadError = true;
+ return;
+ }
+ StringRef FileName = Tok.getString();
+ SourceLocation FileNameLoc = consumeToken();
+
+ // FIXME: Record the umbrella header.
+}
+
+/// \brief Parse a header declaration.
+///
+/// header-declaration:
+/// 'header' string-literal
+void ModuleMapParser::parseHeaderDecl() {
+ assert(Tok.is(MMToken::HeaderKeyword));
+ SourceLocation HeaderLoc = consumeToken();
+
+ // Parse the header name.
+ if (!Tok.is(MMToken::StringLiteral)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)
+ << "header";
+ HadError = true;
+ return;
+ }
+ StringRef FileName = Tok.getString();
+ SourceLocation FileNameLoc = consumeToken();
+
+ // FIXME: Record the header.
+}
+
+/// \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::ModuleKeyword:
+ parseModuleDecl();
+ break;
+
+ case MMToken::ExplicitKeyword:
+ case MMToken::HeaderKeyword:
+ case MMToken::Identifier:
+ case MMToken::LBrace:
+ case MMToken::RBrace:
+ case MMToken::StringLiteral:
+ case MMToken::UmbrellaKeyword:
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);
+ HadError = true;
+ consumeToken();
+ break;
+ }
+ } while (true);
+
+ return HadError;
+}
+
+bool ModuleMap::parseModuleMapFile(const FileEntry *File) {
+ FileID ID = SourceMgr->createFileID(File, SourceLocation(), SrcMgr::C_User);
+ const llvm::MemoryBuffer *Buffer = SourceMgr->getBuffer(ID);
+ if (!Buffer)
+ return true;
+
+ // Parse this module map file.
+ Lexer L(ID, SourceMgr->getBuffer(ID), *SourceMgr, LangOpts);
+ Diags->getClient()->BeginSourceFile(LangOpts);
+ ModuleMapParser Parser(L, *SourceMgr, *Diags, *this);
+ bool Result = Parser.parseModuleMapFile();
+ Diags->getClient()->EndSourceFile();
+
+ return Result;
+}
diff --git a/clang/test/Modules/Inputs/normal-module-map/a1.h b/clang/test/Modules/Inputs/normal-module-map/a1.h
new file mode 100644
index 00000000000..f2d5a49f37b
--- /dev/null
+++ b/clang/test/Modules/Inputs/normal-module-map/a1.h
@@ -0,0 +1 @@
+int a1;
diff --git a/clang/test/Modules/Inputs/normal-module-map/a2.h b/clang/test/Modules/Inputs/normal-module-map/a2.h
new file mode 100644
index 00000000000..5c4e7ff4769
--- /dev/null
+++ b/clang/test/Modules/Inputs/normal-module-map/a2.h
@@ -0,0 +1 @@
+int a2;
diff --git a/clang/test/Modules/Inputs/normal-module-map/b1.h b/clang/test/Modules/Inputs/normal-module-map/b1.h
new file mode 100644
index 00000000000..2ed111281eb
--- /dev/null
+++ b/clang/test/Modules/Inputs/normal-module-map/b1.h
@@ -0,0 +1,2 @@
+int b1;
+
diff --git a/clang/test/Modules/Inputs/normal-module-map/module.map b/clang/test/Modules/Inputs/normal-module-map/module.map
new file mode 100644
index 00000000000..c372662c253
--- /dev/null
+++ b/clang/test/Modules/Inputs/normal-module-map/module.map
@@ -0,0 +1,8 @@
+module libA {
+ header "a1.h"
+ header "a2.h"
+}
+
+module libB {
+ header "b1.h"
+}
diff --git a/clang/test/Modules/Inputs/normal-module-map/nested/module.map b/clang/test/Modules/Inputs/normal-module-map/nested/module.map
new file mode 100644
index 00000000000..fd463c2f083
--- /dev/null
+++ b/clang/test/Modules/Inputs/normal-module-map/nested/module.map
@@ -0,0 +1,4 @@
+module libNested {
+ header "nested1.h"
+ header "nested2.h"
+} \ No newline at end of file
diff --git a/clang/test/Modules/Inputs/normal-module-map/nested/nested1.h b/clang/test/Modules/Inputs/normal-module-map/nested/nested1.h
new file mode 100644
index 00000000000..3790d1a196e
--- /dev/null
+++ b/clang/test/Modules/Inputs/normal-module-map/nested/nested1.h
@@ -0,0 +1 @@
+int nested1;
diff --git a/clang/test/Modules/Inputs/normal-module-map/nested/nested2.h b/clang/test/Modules/Inputs/normal-module-map/nested/nested2.h
new file mode 100644
index 00000000000..d56d601e208
--- /dev/null
+++ b/clang/test/Modules/Inputs/normal-module-map/nested/nested2.h
@@ -0,0 +1 @@
+int nested2;
diff --git a/clang/test/Modules/normal-module-map.cpp b/clang/test/Modules/normal-module-map.cpp
new file mode 100644
index 00000000000..2935c8baa9f
--- /dev/null
+++ b/clang/test/Modules/normal-module-map.cpp
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -x objective-c -fmodule-cache-path %t -fauto-module-import -I %S/Inputs/normal-module-map -verify %s
+
+#include "a1.h"
+#include "b1.h"
+#include "nested/nested2.h"
+
+int test() {
+ return a1 + b1 + nested2;
+}
OpenPOWER on IntegriCloud