diff options
Diffstat (limited to 'lib/Lex/ModuleMap.cpp')
-rw-r--r-- | lib/Lex/ModuleMap.cpp | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp new file mode 100644 index 0000000000..69bc52636c --- /dev/null +++ b/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 ⤅ + + /// \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(<oken, 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; +} |