summaryrefslogtreecommitdiffstats
path: root/clang/lib/Lex/ModuleMap.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2011-11-11 19:10:28 +0000
committerDouglas Gregor <dgregor@apple.com>2011-11-11 19:10:28 +0000
commit718292f260bf608a09ceb21a2ca35ad3613b041b (patch)
tree3be65f339548915773a9698b7bc07e7317123e24 /clang/lib/Lex/ModuleMap.cpp
parentb889f8b4b83f487c6ed48c5026a83a7a58f25bef (diff)
downloadbcm5719-llvm-718292f260bf608a09ceb21a2ca35ad3613b041b.tar.gz
bcm5719-llvm-718292f260bf608a09ceb21a2ca35ad3613b041b.zip
Introduce basic support for parsing module map files.
Module map files provide a way to map between headers and modules, so that we can layer a module system on top of existing headers without changing those headers at all. This commit introduces the module map file parser and the module map that it generates, and wires up the module map file parser so that we'll automatically find module map files as part of header search. Note that we don't yet use the information stored in the module map. llvm-svn: 144402
Diffstat (limited to 'clang/lib/Lex/ModuleMap.cpp')
-rw-r--r--clang/lib/Lex/ModuleMap.cpp506
1 files changed, 506 insertions, 0 deletions
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;
+}
OpenPOWER on IntegriCloud