Newer
Older
//===--- ASTUnit.cpp - ASTUnit utility ------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// ASTUnit Implementation.
//
//===----------------------------------------------------------------------===//
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/PCHReader.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Job.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/FrontendOptions.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
Daniel Dunbar
committed
ASTUnit::ASTUnit(bool _MainFileIsAST)
: tempFile(false), MainFileIsAST(_MainFileIsAST) {
ASTUnit::~ASTUnit() {
llvm::sys::Path(getPCHFileName()).eraseFromDisk();
namespace {
/// \brief Gathers information from PCHReader that will be used to initialize
/// a Preprocessor.
class PCHInfoCollector : public PCHReaderListener {
LangOptions &LangOpt;
HeaderSearch &HSI;
std::string &TargetTriple;
std::string &Predefines;
unsigned &Counter;
public:
PCHInfoCollector(LangOptions &LangOpt, HeaderSearch &HSI,
std::string &TargetTriple, std::string &Predefines,
unsigned &Counter)
: LangOpt(LangOpt), HSI(HSI), TargetTriple(TargetTriple),
Predefines(Predefines), Counter(Counter), NumHeaderInfos(0) {}
virtual bool ReadLanguageOptions(const LangOptions &LangOpts) {
LangOpt = LangOpts;
return false;
}
virtual bool ReadTargetTriple(llvm::StringRef Triple) {
TargetTriple = Triple;
return false;
}
virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef,
Daniel Dunbar
committed
llvm::StringRef OriginalFileName,
std::string &SuggestedPredefines) {
Predefines = PCHPredef;
return false;
}
virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI) {
HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++);
}
virtual void ReadCounter(unsigned Value) {
Counter = Value;
}
};
} // anonymous namespace
Steve Naroff
committed
const std::string &ASTUnit::getOriginalSourceFileName() {
return OriginalSourceFile;
Steve Naroff
committed
}
const std::string &ASTUnit::getPCHFileName() {
Daniel Dunbar
committed
assert(isMainFileAST() && "Not an ASTUnit from a PCH file!");
return dyn_cast<PCHReader>(Ctx->getExternalSource())->getFileName();
}
ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename,
Daniel Dunbar
committed
Diagnostic &Diags,
Ted Kremenek
committed
bool OnlyLocalDecls,
bool UseBumpAllocator) {
Daniel Dunbar
committed
llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true));
AST->OnlyLocalDecls = OnlyLocalDecls;
AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager()));
// Gather Info for preprocessor construction later on.
LangOptions LangInfo;
HeaderSearch &HeaderInfo = *AST->HeaderInfo.get();
std::string TargetTriple;
std::string Predefines;
unsigned Counter;
Daniel Dunbar
committed
llvm::OwningPtr<PCHReader> Reader;
llvm::OwningPtr<ExternalASTSource> Source;
Ted Kremenek
committed
Reader.reset(new PCHReader(AST->getSourceManager(), AST->getFileManager(),
Daniel Dunbar
committed
Diags));
Daniel Dunbar
committed
Reader->setListener(new PCHInfoCollector(LangInfo, HeaderInfo, TargetTriple,
Predefines, Counter));
switch (Reader->ReadPCH(Filename)) {
Argyrios Kyrtzidis
committed
case PCHReader::IgnorePCH:
Daniel Dunbar
committed
Diags.Report(diag::err_fe_unable_to_load_pch);
AST->OriginalSourceFile = Reader->getOriginalSourceFile();
// PCH loaded successfully. Now create the preprocessor.
// Get information about the target being compiled for.
//
// FIXME: This is broken, we should store the TargetOptions in the PCH.
TargetOptions TargetOpts;
TargetOpts.ABI = "";
TargetOpts.CPU = "";
TargetOpts.Features.clear();
TargetOpts.Triple = TargetTriple;
Daniel Dunbar
committed
AST->Target.reset(TargetInfo::CreateTargetInfo(Diags, TargetOpts));
AST->PP.reset(new Preprocessor(Diags, LangInfo, *AST->Target.get(),
Daniel Dunbar
committed
AST->getSourceManager(), HeaderInfo));
Preprocessor &PP = *AST->PP.get();
Daniel Dunbar
committed
PP.setPredefines(Reader->getSuggestedPredefines());
Daniel Dunbar
committed
Reader->setPreprocessor(PP);
// Create and initialize the ASTContext.
AST->Ctx.reset(new ASTContext(LangInfo,
Daniel Dunbar
committed
AST->getSourceManager(),
*AST->Target.get(),
PP.getIdentifierTable(),
PP.getSelectorTable(),
PP.getBuiltinInfo(),
Ted Kremenek
committed
/* FreeMemory = */ !UseBumpAllocator,
/* size_reserve = */0));
ASTContext &Context = *AST->Ctx.get();
Daniel Dunbar
committed
Reader->InitializeContext(Context);
// Attach the PCH reader to the AST context as an external AST
// source, so that declarations will be deserialized from the
// PCH file as needed.
Daniel Dunbar
committed
Source.reset(Reader.take());
Context.setExternalSource(Source);
namespace {
Daniel Dunbar
committed
class TopLevelDeclTrackerConsumer : public ASTConsumer {
ASTUnit &Unit;
public:
TopLevelDeclTrackerConsumer(ASTUnit &_Unit) : Unit(_Unit) {}
void HandleTopLevelDecl(DeclGroupRef D) {
for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it)
Unit.getTopLevelDecls().push_back(*it);
}
};
class TopLevelDeclTrackerAction : public ASTFrontendAction {
public:
ASTUnit &Unit;
virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
llvm::StringRef InFile) {
Daniel Dunbar
committed
return new TopLevelDeclTrackerConsumer(Unit);
}
public:
Daniel Dunbar
committed
TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {}
virtual bool hasCodeCompletionSupport() const { return false; }
};
}
ASTUnit *ASTUnit::LoadFromCompilerInvocation(const CompilerInvocation &CI,
Diagnostic &Diags,
Daniel Dunbar
committed
bool OnlyLocalDecls) {
// Create the compiler instance to use for building the AST.
CompilerInstance Clang;
llvm::OwningPtr<ASTUnit> AST;
Daniel Dunbar
committed
llvm::OwningPtr<TopLevelDeclTrackerAction> Act;
Clang.getInvocation() = CI;
Clang.setDiagnostics(&Diags);
Clang.setDiagnosticClient(Diags.getClient());
// Create the target instance.
Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
Clang.getTargetOpts()));
if (!Clang.hasTarget())
goto error;
// Inform the target of the language options.
//
// FIXME: We shouldn't need to do this, the target should be immutable once
// created. This complexity should be lifted elsewhere.
Clang.getTarget().setForcedLangOptions(Clang.getLangOpts());
assert(Clang.getFrontendOpts().Inputs.size() == 1 &&
"Invocation must have exactly one source file!");
assert(Clang.getFrontendOpts().Inputs[0].first != FrontendOptions::IK_AST &&
"FIXME: AST inputs not yet supported here!");
// Create the AST unit.
Daniel Dunbar
committed
AST.reset(new ASTUnit(false));
Daniel Dunbar
committed
AST->OnlyLocalDecls = OnlyLocalDecls;
AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
// Create a file manager object to provide access to and cache the filesystem.
Clang.setFileManager(&AST->getFileManager());
// Create the source manager.
Clang.setSourceManager(&AST->getSourceManager());
// Create the preprocessor.
Clang.createPreprocessor();
Daniel Dunbar
committed
Act.reset(new TopLevelDeclTrackerAction(*AST));
if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
/*IsAST=*/false))
goto error;
Daniel Dunbar
committed
Act->Execute();
// Steal the created target, context, and preprocessor, and take back the
// source and file managers.
AST->Ctx.reset(Clang.takeASTContext());
AST->PP.reset(Clang.takePreprocessor());
Clang.takeSourceManager();
Clang.takeFileManager();
AST->Target.reset(Clang.takeTarget());
Daniel Dunbar
committed
Act->EndSourceFile();
Clang.takeDiagnosticClient();
Clang.takeDiagnostics();
return AST.take();
error:
Clang.takeSourceManager();
Clang.takeFileManager();
Clang.takeDiagnosticClient();
Clang.takeDiagnostics();
return 0;
}
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
const char **ArgEnd,
Diagnostic &Diags,
const char *Argv0,
void *MainAddr,
bool OnlyLocalDecls,
bool UseBumpAllocator) {
llvm::SmallVector<const char *, 16> Args;
Args.push_back("<clang>"); // FIXME: Remove dummy argument.
Args.insert(Args.end(), ArgBegin, ArgEnd);
// FIXME: Find a cleaner way to force the driver into restricted modes. We
// also want to force it to use clang.
Args.push_back("-fsyntax-only");
llvm::sys::Path Path = llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
driver::Driver TheDriver(Path.getBasename().c_str(),Path.getDirname().c_str(),
llvm::sys::getHostTriple().c_str(),
"a.out", false, Diags);
llvm::OwningPtr<driver::Compilation> C(
TheDriver.BuildCompilation(Args.size(), Args.data()));
// We expect to get back exactly one command job, if we didn't something
// failed.
const driver::JobList &Jobs = C->getJobs();
if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) {
llvm::SmallString<256> Msg;
llvm::raw_svector_ostream OS(Msg);
C->PrintJob(OS, C->getJobs(), "; ", true);
Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
return 0;
}
const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
Diags.Report(diag::err_fe_expected_clang_command);
return 0;
}
const driver::ArgStringList &CCArgs = Cmd->getArguments();
CompilerInvocation CI;
CompilerInvocation::CreateFromArgs(CI, (const char**) CCArgs.data(),
(const char**) CCArgs.data()+CCArgs.size(),
Argv0, MainAddr, Diags);
Daniel Dunbar
committed
CI.getFrontendOpts().DisableFree = UseBumpAllocator;
return LoadFromCompilerInvocation(CI, Diags, OnlyLocalDecls);