Newer
Older
//===--- FrontendAction.cpp -----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/Frontend/FrontendAction.h"

Sebastian Redl
committed
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/ChainedIncludesSource.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Parse/ParseAST.h"

Argyrios Kyrtzidis
committed
#include "clang/Serialization/ASTDeserializationListener.h"
#include "clang/Serialization/ASTReader.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;

Argyrios Kyrtzidis
committed
namespace {

Argyrios Kyrtzidis
committed
class DelegatingDeserializationListener : public ASTDeserializationListener {

Argyrios Kyrtzidis
committed
ASTDeserializationListener *Previous;
public:

Argyrios Kyrtzidis
committed
explicit DelegatingDeserializationListener(
ASTDeserializationListener *Previous)

Argyrios Kyrtzidis
committed
: Previous(Previous) { }

Argyrios Kyrtzidis
committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
virtual void ReaderInitialized(ASTReader *Reader) {
if (Previous)
Previous->ReaderInitialized(Reader);
}
virtual void IdentifierRead(serialization::IdentID ID,
IdentifierInfo *II) {
if (Previous)
Previous->IdentifierRead(ID, II);
}
virtual void TypeRead(serialization::TypeIdx Idx, QualType T) {
if (Previous)
Previous->TypeRead(Idx, T);
}
virtual void DeclRead(serialization::DeclID ID, const Decl *D) {
if (Previous)
Previous->DeclRead(ID, D);
}
virtual void SelectorRead(serialization::SelectorID ID, Selector Sel) {
if (Previous)
Previous->SelectorRead(ID, Sel);
}
virtual void MacroDefinitionRead(serialization::PreprocessedEntityID PPID,
MacroDefinition *MD) {
if (Previous)
Previous->MacroDefinitionRead(PPID, MD);
}
};
/// \brief Dumps deserialized declarations.
class DeserializedDeclsDumper : public DelegatingDeserializationListener {
public:
explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous)
: DelegatingDeserializationListener(Previous) { }

Argyrios Kyrtzidis
committed
virtual void DeclRead(serialization::DeclID ID, const Decl *D) {
llvm::outs() << "PCH DECL: " << D->getDeclKindName();
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
llvm::outs() << " - " << ND->getNameAsString();
llvm::outs() << "\n";

Argyrios Kyrtzidis
committed
DelegatingDeserializationListener::DeclRead(ID, D);

Argyrios Kyrtzidis
committed
}
};

Argyrios Kyrtzidis
committed
/// \brief Checks deserialized declarations and emits error if a name
/// matches one given in command-line using -error-on-deserialized-decl.

Argyrios Kyrtzidis
committed
class DeserializedDeclsChecker : public DelegatingDeserializationListener {

Argyrios Kyrtzidis
committed
ASTContext &Ctx;
std::set<std::string> NamesToCheck;
public:
DeserializedDeclsChecker(ASTContext &Ctx,
const std::set<std::string> &NamesToCheck,
ASTDeserializationListener *Previous)

Argyrios Kyrtzidis
committed
: DelegatingDeserializationListener(Previous),
Ctx(Ctx), NamesToCheck(NamesToCheck) { }

Argyrios Kyrtzidis
committed
virtual void DeclRead(serialization::DeclID ID, const Decl *D) {
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) {
unsigned DiagID
= Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error,

Argyrios Kyrtzidis
committed
"%0 was deserialized");
Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID)
<< ND->getNameAsString();
}

Argyrios Kyrtzidis
committed
DelegatingDeserializationListener::DeclRead(ID, D);

Argyrios Kyrtzidis
committed
}
};

Argyrios Kyrtzidis
committed
} // end anonymous namespace

Kovarththanan Rajaratnam
committed
FrontendAction::FrontendAction() : Instance(0) {}
FrontendAction::~FrontendAction() {}

Chris Lattner
committed
void FrontendAction::setCurrentFile(StringRef Value, InputKind Kind,
CurrentFile = Value;
CurrentASTUnit.reset(AST);
}
ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,

Chris Lattner
committed
StringRef InFile) {
ASTConsumer* Consumer = CreateASTConsumer(CI, InFile);
if (!Consumer)
return 0;
if (CI.getFrontendOpts().AddPluginActions.size() == 0)
return Consumer;
// Make sure the non-plugin consumer is first, so that plugins can't
// modifiy the AST.
std::vector<ASTConsumer*> Consumers(1, Consumer);
for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size();
i != e; ++i) {
// This is O(|plugins| * |add_plugins|), but since both numbers are
// way below 50 in practice, that's ok.
for (FrontendPluginRegistry::iterator
it = FrontendPluginRegistry::begin(),
ie = FrontendPluginRegistry::end();
it != ie; ++it) {
if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) {
llvm::OwningPtr<PluginASTAction> P(it->instantiate());
FrontendAction* c = P.get();
if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i]))
Consumers.push_back(c->CreateASTConsumer(CI, InFile));
}
}
}
return new MultiplexConsumer(Consumers);
}
bool FrontendAction::BeginSourceFile(CompilerInstance &CI,

Chris Lattner
committed
StringRef Filename,

Daniel Dunbar
committed
InputKind InputKind) {
assert(!Instance && "Already processing a source file!");
assert(!Filename.empty() && "Unexpected empty filename!");
setCurrentFile(Filename, InputKind);
setCompilerInstance(&CI);

Argyrios Kyrtzidis
committed
if (!BeginInvocation(CI))
goto failure;
// AST files follow a very different path, since they share objects via the
// AST unit.

Daniel Dunbar
committed
if (InputKind == IK_AST) {
assert(!usesPreprocessorOnly() &&
"Attempt to pass AST file to preprocessor only action!");

Daniel Dunbar
committed
assert(hasASTFileSupport() &&
"This action does not have AST file support!");
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics());
std::string Error;
ASTUnit *AST = ASTUnit::LoadFromASTFile(Filename, Diags,
CI.getFileSystemOpts());

Daniel Dunbar
committed
if (!AST)
goto failure;
setCurrentFile(Filename, InputKind, AST);
// Set the shared objects, these are reset when we finish processing the
// file, otherwise the CompilerInstance will happily destroy them.
CI.setFileManager(&AST->getFileManager());
CI.setSourceManager(&AST->getSourceManager());
CI.setPreprocessor(&AST->getPreprocessor());
CI.setASTContext(&AST->getASTContext());
// Initialize the action.
if (!BeginSourceFileAction(CI, Filename))
goto failure;
/// Create the AST consumer.
CI.setASTConsumer(CreateWrappedASTConsumer(CI, Filename));
if (!CI.hasASTConsumer())
goto failure;
return true;
}
// Set up the file and source managers, if needed.

Daniel Dunbar
committed
if (!CI.hasFileManager())
CI.createFileManager();
if (!CI.hasSourceManager())
CI.createSourceManager(CI.getFileManager());
// IR files bypass the rest of initialization.
if (InputKind == IK_LLVM_IR) {
assert(hasIRSupport() &&
"This action does not have IR file support!");
// Inform the diagnostic client we are processing a source file.
CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0);
// Initialize the action.
if (!BeginSourceFileAction(CI, Filename))
goto failure;
return true;
}
// Set up the preprocessor.

Daniel Dunbar
committed
CI.createPreprocessor();
// Inform the diagnostic client we are processing a source file.
CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(),
&CI.getPreprocessor());
// Initialize the action.
if (!BeginSourceFileAction(CI, Filename))
goto failure;
/// Create the AST context and consumer unless this is a preprocessor only
/// action.
if (!usesPreprocessorOnly()) {
CI.createASTContext();
llvm::OwningPtr<ASTConsumer> Consumer(
CreateWrappedASTConsumer(CI, Filename));
if (!Consumer)
goto failure;

Sebastian Redl
committed

Argyrios Kyrtzidis
committed
CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener());

Argyrios Kyrtzidis
committed
if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) {
// Convert headers to PCH and chain them.
llvm::OwningPtr<ExternalASTSource> source;
source.reset(ChainedIncludesSource::create(CI));
if (!source)
goto failure;
CI.getASTContext().setExternalSource(source);
} else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) {
// Use PCH.
assert(hasPCHSupport() && "This action does not have PCH support!");
ASTDeserializationListener *DeserialListener =
Consumer->GetASTDeserializationListener();

Argyrios Kyrtzidis
committed
if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls)
DeserialListener = new DeserializedDeclsDumper(DeserialListener);

Argyrios Kyrtzidis
committed
if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty())
DeserialListener = new DeserializedDeclsChecker(CI.getASTContext(),
CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn,
DeserialListener);
CI.createPCHExternalASTSource(
CI.getPreprocessorOpts().ImplicitPCHInclude,

Sebastian Redl
committed
CI.getPreprocessorOpts().DisablePCHValidation,
CI.getPreprocessorOpts().DisableStatCache,

Argyrios Kyrtzidis
committed
DeserialListener);
if (!CI.getASTContext().getExternalSource())
goto failure;
}

Sebastian Redl
committed

Sebastian Redl
committed
CI.setASTConsumer(Consumer.take());

Sebastian Redl
committed
if (!CI.hasASTConsumer())
goto failure;
}
// Initialize built-in info as long as we aren't using an external AST
// source.
if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) {
Preprocessor &PP = CI.getPreprocessor();
PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(),
PP.getLangOptions());
}
return true;
// If we failed, reset state since the client will not end up calling the
// matching EndSourceFile().
failure:
if (isCurrentFileAST()) {
CI.setASTContext(0);
CI.setPreprocessor(0);
CI.setSourceManager(0);
CI.setFileManager(0);
}
CI.getDiagnosticClient().EndSourceFile();
setCompilerInstance(0);
return false;
}
void FrontendAction::Execute() {
CompilerInstance &CI = getCompilerInstance();
// Initialize the main file entry. This needs to be delayed until after PCH
// has loaded.

Argyrios Kyrtzidis
committed
if (!isCurrentFileAST()) {
if (!CI.InitializeSourceManager(getCurrentFile()))
return;
}

Kovarththanan Rajaratnam
committed
if (CI.hasFrontendTimer()) {
llvm::TimeRegion Timer(CI.getFrontendTimer());
ExecuteAction();
}
else ExecuteAction();
}
void FrontendAction::EndSourceFile() {
CompilerInstance &CI = getCompilerInstance();
// Inform the diagnostic client we are done with this source file.
CI.getDiagnosticClient().EndSourceFile();
// Finalize the action.
EndSourceFileAction();
// Release the consumer and the AST, in that order since the consumer may
// perform actions in its destructor which require the context.
//
// FIXME: There is more per-file stuff we could just drop here?
if (CI.getFrontendOpts().DisableFree) {
CI.takeASTConsumer();
if (!isCurrentFileAST()) {
CI.takeSema();
CI.resetAndLeakASTContext();
} else {
if (!isCurrentFileAST()) {
CI.setSema(0);
CI.setASTContext(0);
}
CI.setASTConsumer(0);
}
// Inform the preprocessor we are done.
if (CI.hasPreprocessor())
CI.getPreprocessor().EndSourceFile();
if (CI.getFrontendOpts().ShowStats) {
llvm::errs() << "\nSTATISTICS FOR '" << getCurrentFile() << "':\n";
CI.getPreprocessor().PrintStats();
CI.getPreprocessor().getIdentifierTable().PrintStats();
CI.getPreprocessor().getHeaderSearchInfo().PrintStats();
CI.getSourceManager().PrintStats();
llvm::errs() << "\n";
}
// Cleanup the output streams, and erase the output files if we encountered
// an error.
CI.clearOutputFiles(/*EraseFiles=*/CI.getDiagnostics().hasErrorOccurred());
if (isCurrentFileAST()) {
CI.takeSema();
CI.resetAndLeakASTContext();
CI.resetAndLeakPreprocessor();
CI.resetAndLeakSourceManager();
CI.resetAndLeakFileManager();
}
setCompilerInstance(0);
}
//===----------------------------------------------------------------------===//
// Utility Actions
//===----------------------------------------------------------------------===//
void ASTFrontendAction::ExecuteAction() {
CompilerInstance &CI = getCompilerInstance();
// FIXME: Move the truncation aspect of this into Sema, we delayed this till
// here so the source manager would be initialized.
if (hasCodeCompletionSupport() &&
!CI.getFrontendOpts().CodeCompletionAt.FileName.empty())
CI.createCodeCompletionConsumer();
// Use a code completion consumer?
CodeCompleteConsumer *CompletionConsumer = 0;
if (CI.hasCodeCompletionConsumer())
CompletionConsumer = &CI.getCodeCompletionConsumer();
if (!CI.hasSema())
CI.createSema(getTranslationUnitKind(), CompletionConsumer);
ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats);
}

David Blaikie
committed
void PluginASTAction::anchor() { }
ASTConsumer *
PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI,

Chris Lattner
committed
StringRef InFile) {

Jeffrey Yasskin
committed
llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!");
}
ASTConsumer *WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI,

Chris Lattner
committed
StringRef InFile) {
return WrappedAction->CreateASTConsumer(CI, InFile);
}

Argyrios Kyrtzidis
committed
bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) {
return WrappedAction->BeginInvocation(CI);
}
bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI,

Chris Lattner
committed
StringRef Filename) {

Argyrios Kyrtzidis
committed
WrappedAction->setCurrentFile(getCurrentFile(), getCurrentFileKind());
WrappedAction->setCompilerInstance(&CI);
return WrappedAction->BeginSourceFileAction(CI, Filename);
}
void WrapperFrontendAction::ExecuteAction() {
WrappedAction->ExecuteAction();
}
void WrapperFrontendAction::EndSourceFileAction() {
WrappedAction->EndSourceFileAction();
}
bool WrapperFrontendAction::usesPreprocessorOnly() const {
return WrappedAction->usesPreprocessorOnly();
}
TranslationUnitKind WrapperFrontendAction::getTranslationUnitKind() {
return WrappedAction->getTranslationUnitKind();
}
bool WrapperFrontendAction::hasPCHSupport() const {
return WrappedAction->hasPCHSupport();
}
bool WrapperFrontendAction::hasASTFileSupport() const {
return WrappedAction->hasASTFileSupport();
}
bool WrapperFrontendAction::hasIRSupport() const {
return WrappedAction->hasIRSupport();
}
bool WrapperFrontendAction::hasCodeCompletionSupport() const {
return WrappedAction->hasCodeCompletionSupport();
}
WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction)
: WrappedAction(WrappedAction) {}