llvm-project/clang/lib/Lex/ModuleMap.cpp

507 lines
14 KiB
C++
Raw Normal View History

//===--- 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;
}