2009-06-20 16:27:14 +08:00
|
|
|
//===--- 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.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
#include "clang/Frontend/ASTUnit.h"
|
|
|
|
#include "clang/Frontend/PCHReader.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
2009-12-01 17:51:01 +08:00
|
|
|
#include "clang/AST/ASTConsumer.h"
|
2009-06-20 16:08:23 +08:00
|
|
|
#include "clang/AST/DeclVisitor.h"
|
|
|
|
#include "clang/AST/StmtVisitor.h"
|
2009-12-02 11:23:45 +08:00
|
|
|
#include "clang/Driver/Compilation.h"
|
|
|
|
#include "clang/Driver/Driver.h"
|
|
|
|
#include "clang/Driver/Job.h"
|
|
|
|
#include "clang/Driver/Tool.h"
|
2009-12-01 17:51:01 +08:00
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
|
|
#include "clang/Frontend/FrontendActions.h"
|
2009-12-02 11:23:45 +08:00
|
|
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
2009-12-01 17:51:01 +08:00
|
|
|
#include "clang/Frontend/FrontendOptions.h"
|
2009-06-20 16:08:23 +08:00
|
|
|
#include "clang/Lex/HeaderSearch.h"
|
|
|
|
#include "clang/Lex/Preprocessor.h"
|
2009-11-15 14:48:46 +08:00
|
|
|
#include "clang/Basic/TargetOptions.h"
|
2009-06-20 16:08:23 +08:00
|
|
|
#include "clang/Basic/TargetInfo.h"
|
|
|
|
#include "clang/Basic/Diagnostic.h"
|
2009-12-02 11:23:45 +08:00
|
|
|
#include "llvm/System/Host.h"
|
2009-10-18 19:34:14 +08:00
|
|
|
#include "llvm/System/Path.h"
|
2009-06-20 16:08:23 +08:00
|
|
|
using namespace clang;
|
|
|
|
|
2009-12-03 09:45:44 +08:00
|
|
|
ASTUnit::ASTUnit(bool _MainFileIsAST)
|
|
|
|
: tempFile(false), MainFileIsAST(_MainFileIsAST) {
|
2009-10-19 22:34:22 +08:00
|
|
|
}
|
2009-12-01 17:51:01 +08:00
|
|
|
ASTUnit::~ASTUnit() {
|
2009-10-16 06:23:48 +08:00
|
|
|
if (tempFile)
|
2009-10-18 19:34:14 +08:00
|
|
|
llvm::sys::Path(getPCHFileName()).eraseFromDisk();
|
2009-10-16 06:23:48 +08:00
|
|
|
}
|
2009-06-20 16:08:23 +08:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
/// \brief Gathers information from PCHReader that will be used to initialize
|
|
|
|
/// a Preprocessor.
|
2009-11-28 18:07:24 +08:00
|
|
|
class PCHInfoCollector : public PCHReaderListener {
|
2009-06-20 16:08:23 +08:00
|
|
|
LangOptions &LangOpt;
|
|
|
|
HeaderSearch &HSI;
|
|
|
|
std::string &TargetTriple;
|
|
|
|
std::string &Predefines;
|
|
|
|
unsigned &Counter;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
unsigned NumHeaderInfos;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
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) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
virtual bool ReadLanguageOptions(const LangOptions &LangOpts) {
|
|
|
|
LangOpt = LangOpts;
|
|
|
|
return false;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-11 08:52:11 +08:00
|
|
|
virtual bool ReadTargetTriple(llvm::StringRef Triple) {
|
2009-06-20 16:08:23 +08:00
|
|
|
TargetTriple = Triple;
|
|
|
|
return false;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-11 08:52:11 +08:00
|
|
|
virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef,
|
2009-06-20 16:08:23 +08:00
|
|
|
FileID PCHBufferID,
|
2009-11-11 13:29:04 +08:00
|
|
|
llvm::StringRef OriginalFileName,
|
2009-06-20 16:08:23 +08:00
|
|
|
std::string &SuggestedPredefines) {
|
|
|
|
Predefines = PCHPredef;
|
|
|
|
return false;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI) {
|
|
|
|
HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
virtual void ReadCounter(unsigned Value) {
|
|
|
|
Counter = Value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2009-09-04 02:19:54 +08:00
|
|
|
const std::string &ASTUnit::getOriginalSourceFileName() {
|
2009-12-02 16:44:16 +08:00
|
|
|
return OriginalSourceFile;
|
2009-09-04 02:19:54 +08:00
|
|
|
}
|
2009-06-20 16:08:23 +08:00
|
|
|
|
2009-10-16 06:23:48 +08:00
|
|
|
const std::string &ASTUnit::getPCHFileName() {
|
2009-12-03 05:47:43 +08:00
|
|
|
assert(isMainFileAST() && "Not an ASTUnit from a PCH file!");
|
2009-10-16 06:23:48 +08:00
|
|
|
return dyn_cast<PCHReader>(Ctx->getExternalSource())->getFileName();
|
|
|
|
}
|
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename,
|
2009-12-03 09:45:44 +08:00
|
|
|
Diagnostic &Diags,
|
2009-10-17 08:34:24 +08:00
|
|
|
bool OnlyLocalDecls,
|
|
|
|
bool UseBumpAllocator) {
|
2009-12-03 09:45:44 +08:00
|
|
|
llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true));
|
2009-10-17 04:01:17 +08:00
|
|
|
AST->OnlyLocalDecls = OnlyLocalDecls;
|
2009-10-19 22:34:22 +08:00
|
|
|
AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager()));
|
2009-06-20 16:08:23 +08:00
|
|
|
|
|
|
|
// Gather Info for preprocessor construction later on.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
LangOptions LangInfo;
|
|
|
|
HeaderSearch &HeaderInfo = *AST->HeaderInfo.get();
|
|
|
|
std::string TargetTriple;
|
|
|
|
std::string Predefines;
|
|
|
|
unsigned Counter;
|
|
|
|
|
2009-09-03 13:59:50 +08:00
|
|
|
llvm::OwningPtr<PCHReader> Reader;
|
2009-06-20 16:08:23 +08:00
|
|
|
llvm::OwningPtr<ExternalASTSource> Source;
|
|
|
|
|
2009-10-20 05:44:57 +08:00
|
|
|
Reader.reset(new PCHReader(AST->getSourceManager(), AST->getFileManager(),
|
2009-12-03 09:45:44 +08:00
|
|
|
Diags));
|
2009-09-03 13:59:35 +08:00
|
|
|
Reader->setListener(new PCHInfoCollector(LangInfo, HeaderInfo, TargetTriple,
|
|
|
|
Predefines, Counter));
|
|
|
|
|
|
|
|
switch (Reader->ReadPCH(Filename)) {
|
2009-06-20 16:08:23 +08:00
|
|
|
case PCHReader::Success:
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
case PCHReader::Failure:
|
2009-06-26 02:22:30 +08:00
|
|
|
case PCHReader::IgnorePCH:
|
2009-12-03 09:45:44 +08:00
|
|
|
Diags.Report(diag::err_fe_unable_to_load_pch);
|
2009-06-20 16:08:23 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-12-02 16:44:16 +08:00
|
|
|
AST->OriginalSourceFile = Reader->getOriginalSourceFile();
|
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
// PCH loaded successfully. Now create the preprocessor.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
// Get information about the target being compiled for.
|
2009-11-15 14:48:46 +08:00
|
|
|
//
|
|
|
|
// FIXME: This is broken, we should store the TargetOptions in the PCH.
|
|
|
|
TargetOptions TargetOpts;
|
|
|
|
TargetOpts.ABI = "";
|
|
|
|
TargetOpts.CPU = "";
|
|
|
|
TargetOpts.Features.clear();
|
|
|
|
TargetOpts.Triple = TargetTriple;
|
2009-12-03 09:45:44 +08:00
|
|
|
AST->Target.reset(TargetInfo::CreateTargetInfo(Diags, TargetOpts));
|
|
|
|
AST->PP.reset(new Preprocessor(Diags, LangInfo, *AST->Target.get(),
|
2009-09-21 11:03:39 +08:00
|
|
|
AST->getSourceManager(), HeaderInfo));
|
2009-06-20 16:08:23 +08:00
|
|
|
Preprocessor &PP = *AST->PP.get();
|
|
|
|
|
2009-09-21 11:03:47 +08:00
|
|
|
PP.setPredefines(Reader->getSuggestedPredefines());
|
2009-06-20 16:08:23 +08:00
|
|
|
PP.setCounterValue(Counter);
|
2009-09-03 13:59:35 +08:00
|
|
|
Reader->setPreprocessor(PP);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
// Create and initialize the ASTContext.
|
|
|
|
|
|
|
|
AST->Ctx.reset(new ASTContext(LangInfo,
|
2009-09-21 11:03:39 +08:00
|
|
|
AST->getSourceManager(),
|
2009-06-20 16:08:23 +08:00
|
|
|
*AST->Target.get(),
|
|
|
|
PP.getIdentifierTable(),
|
|
|
|
PP.getSelectorTable(),
|
|
|
|
PP.getBuiltinInfo(),
|
2009-10-17 08:34:24 +08:00
|
|
|
/* FreeMemory = */ !UseBumpAllocator,
|
2009-06-20 16:08:23 +08:00
|
|
|
/* size_reserve = */0));
|
|
|
|
ASTContext &Context = *AST->Ctx.get();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-09-03 13:59:35 +08:00
|
|
|
Reader->InitializeContext(Context);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-06-20 16:08:23 +08:00
|
|
|
// 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.
|
2009-09-03 13:59:35 +08:00
|
|
|
Source.reset(Reader.take());
|
2009-06-20 16:08:23 +08:00
|
|
|
Context.setExternalSource(Source);
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
return AST.take();
|
2009-06-20 16:08:23 +08:00
|
|
|
}
|
2009-12-01 17:51:01 +08:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2009-12-04 16:17:33 +08:00
|
|
|
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;
|
|
|
|
|
2009-12-01 17:51:01 +08:00
|
|
|
virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
|
|
|
|
llvm::StringRef InFile) {
|
2009-12-04 16:17:33 +08:00
|
|
|
return new TopLevelDeclTrackerConsumer(Unit);
|
2009-12-01 17:51:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2009-12-04 16:17:33 +08:00
|
|
|
TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {}
|
|
|
|
|
2009-12-01 17:51:01 +08:00
|
|
|
virtual bool hasCodeCompletionSupport() const { return false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ASTUnit *ASTUnit::LoadFromCompilerInvocation(const CompilerInvocation &CI,
|
|
|
|
Diagnostic &Diags,
|
2009-12-03 05:47:32 +08:00
|
|
|
bool OnlyLocalDecls) {
|
2009-12-01 17:51:01 +08:00
|
|
|
// Create the compiler instance to use for building the AST.
|
2009-12-02 16:43:56 +08:00
|
|
|
CompilerInstance Clang;
|
2009-12-01 17:51:01 +08:00
|
|
|
llvm::OwningPtr<ASTUnit> AST;
|
2009-12-04 16:17:33 +08:00
|
|
|
llvm::OwningPtr<TopLevelDeclTrackerAction> Act;
|
2009-12-01 17:51:01 +08:00
|
|
|
|
|
|
|
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.
|
2009-12-03 05:47:43 +08:00
|
|
|
AST.reset(new ASTUnit(false));
|
2009-12-01 17:51:01 +08:00
|
|
|
|
2009-12-03 05:47:32 +08:00
|
|
|
AST->OnlyLocalDecls = OnlyLocalDecls;
|
2009-12-02 16:44:16 +08:00
|
|
|
AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
|
|
|
|
|
2009-12-01 17:51:01 +08:00
|
|
|
// 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();
|
|
|
|
|
2009-12-04 16:17:33 +08:00
|
|
|
Act.reset(new TopLevelDeclTrackerAction(*AST));
|
|
|
|
if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
|
2009-12-01 17:51:01 +08:00
|
|
|
/*IsAST=*/false))
|
|
|
|
goto error;
|
|
|
|
|
2009-12-04 16:17:33 +08:00
|
|
|
Act->Execute();
|
2009-12-01 17:51:01 +08:00
|
|
|
|
2009-12-02 05:57:33 +08:00
|
|
|
// Steal the created target, context, and preprocessor, and take back the
|
|
|
|
// source and file managers.
|
2009-12-01 17:51:01 +08:00
|
|
|
AST->Ctx.reset(Clang.takeASTContext());
|
|
|
|
AST->PP.reset(Clang.takePreprocessor());
|
|
|
|
Clang.takeSourceManager();
|
|
|
|
Clang.takeFileManager();
|
2009-12-02 05:57:33 +08:00
|
|
|
AST->Target.reset(Clang.takeTarget());
|
2009-12-01 17:51:01 +08:00
|
|
|
|
2009-12-04 16:17:33 +08:00
|
|
|
Act->EndSourceFile();
|
2009-12-01 17:51:01 +08:00
|
|
|
|
|
|
|
Clang.takeDiagnosticClient();
|
|
|
|
Clang.takeDiagnostics();
|
|
|
|
|
|
|
|
return AST.take();
|
|
|
|
|
|
|
|
error:
|
|
|
|
Clang.takeSourceManager();
|
|
|
|
Clang.takeFileManager();
|
|
|
|
Clang.takeDiagnosticClient();
|
|
|
|
Clang.takeDiagnostics();
|
|
|
|
return 0;
|
|
|
|
}
|
2009-12-02 11:23:45 +08:00
|
|
|
|
|
|
|
ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
|
|
|
|
const char **ArgEnd,
|
|
|
|
Diagnostic &Diags,
|
2009-12-13 11:46:13 +08:00
|
|
|
llvm::StringRef ResourceFilesPath,
|
2009-12-02 11:23:45 +08:00
|
|
|
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");
|
|
|
|
|
2009-12-13 11:46:13 +08:00
|
|
|
// FIXME: We shouldn't have to pass in the path info.
|
|
|
|
driver::Driver TheDriver("clang", "/", llvm::sys::getHostTriple(),
|
2009-12-02 11:23:45 +08:00
|
|
|
"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(),
|
2009-12-13 11:45:58 +08:00
|
|
|
Diags);
|
|
|
|
|
2009-12-13 11:46:13 +08:00
|
|
|
// Set the builtin include path.
|
|
|
|
llvm::sys::Path P(ResourceFilesPath);
|
|
|
|
P.appendComponent("include");
|
|
|
|
CI.getHeaderSearchOpts().BuiltinIncludePath = P.str();
|
2009-12-02 11:23:45 +08:00
|
|
|
|
2009-12-03 05:47:32 +08:00
|
|
|
CI.getFrontendOpts().DisableFree = UseBumpAllocator;
|
|
|
|
return LoadFromCompilerInvocation(CI, Diags, OnlyLocalDecls);
|
2009-12-02 11:23:45 +08:00
|
|
|
}
|