2009-11-14 18:42:35 +08:00
|
|
|
//===--- 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"
|
2010-07-30 08:29:29 +08:00
|
|
|
#include "clang/AST/ASTConsumer.h"
|
2009-11-14 18:42:35 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2011-01-26 04:34:14 +08:00
|
|
|
#include "clang/AST/DeclGroup.h"
|
2009-11-14 18:42:35 +08:00
|
|
|
#include "clang/Frontend/ASTUnit.h"
|
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
|
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
2011-01-26 04:34:14 +08:00
|
|
|
#include "clang/Frontend/FrontendPluginRegistry.h"
|
Extend the ExternalASTSource interface to allow the AST source to
provide the layout of records, rather than letting Clang compute
the layout itself. LLDB provides the motivation for this feature:
because various layout-altering attributes (packed, aligned, etc.)
don't get reliably get placed into DWARF, the record layouts computed
by LLDB from the reconstructed records differ from the actual layouts,
and badness occurs. This interface lets the DWARF data drive layout,
so we don't need the attributes preserved to get the answer write.
The testing methodology for this change is fun. I've introduced a
variant of -fdump-record-layouts called -fdump-record-layouts-simple
that always has the simple C format and provides size/alignment/field
offsets. There is also a -cc1 option -foverride-record-layout=<file>
to take the output of -fdump-record-layouts-simple and parse it to
produce a set of overridden layouts, which is introduced into the AST
via a testing-only ExternalASTSource (called
LayoutOverrideSource). Each test contains a number of records to lay
out, which use various layout-changing attributes, and then dumps the
layouts. We then run the test again, using the preprocessor to
eliminate the layout-changing attributes entirely (which would give us
different layouts for the records), but supplying the
previously-computed record layouts. Finally, we diff the layouts
produced from the two runs to be sure that they are identical.
Note that this code makes the assumption that we don't *have* to
provide the offsets of bases or virtual bases to get the layout right,
because the alignment attributes don't affect it. I believe this
assumption holds, but if it does not, we can extend
LayoutOverrideSource to also provide base offset information.
Fixes the Clang side of <rdar://problem/10169539>.
llvm-svn: 149055
2012-01-26 15:55:45 +08:00
|
|
|
#include "clang/Frontend/LayoutOverrideSource.h"
|
2011-01-26 04:34:14 +08:00
|
|
|
#include "clang/Frontend/MultiplexConsumer.h"
|
2014-01-07 19:51:46 +08:00
|
|
|
#include "clang/Frontend/Utils.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Lex/HeaderSearch.h"
|
|
|
|
#include "clang/Lex/Preprocessor.h"
|
2010-08-21 02:27:03 +08:00
|
|
|
#include "clang/Parse/ParseAST.h"
|
2010-10-15 04:14:18 +08:00
|
|
|
#include "clang/Serialization/ASTDeserializationListener.h"
|
2011-08-06 06:17:03 +08:00
|
|
|
#include "clang/Serialization/ASTReader.h"
|
2013-01-24 06:38:11 +08:00
|
|
|
#include "clang/Serialization/GlobalModuleIndex.h"
|
2009-11-14 18:42:35 +08:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2012-10-23 14:18:24 +08:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "llvm/Support/Timer.h"
|
2009-11-14 18:42:35 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2014-06-13 01:19:42 +08:00
|
|
|
#include <system_error>
|
2009-11-14 18:42:35 +08:00
|
|
|
using namespace clang;
|
|
|
|
|
2014-07-11 23:06:24 +08:00
|
|
|
template class llvm::Registry<clang::PluginASTAction>;
|
|
|
|
|
2010-10-15 04:14:18 +08:00
|
|
|
namespace {
|
|
|
|
|
2011-10-29 06:54:31 +08:00
|
|
|
class DelegatingDeserializationListener : public ASTDeserializationListener {
|
2010-10-15 04:14:18 +08:00
|
|
|
ASTDeserializationListener *Previous;
|
2014-05-08 12:26:47 +08:00
|
|
|
bool DeletePrevious;
|
2010-10-15 04:14:18 +08:00
|
|
|
|
|
|
|
public:
|
2011-10-29 06:54:31 +08:00
|
|
|
explicit DelegatingDeserializationListener(
|
2014-05-08 12:26:47 +08:00
|
|
|
ASTDeserializationListener *Previous, bool DeletePrevious)
|
|
|
|
: Previous(Previous), DeletePrevious(DeletePrevious) {}
|
|
|
|
virtual ~DelegatingDeserializationListener() {
|
|
|
|
if (DeletePrevious)
|
|
|
|
delete Previous;
|
|
|
|
}
|
2010-10-15 04:14:18 +08:00
|
|
|
|
2014-03-13 14:07:04 +08:00
|
|
|
void ReaderInitialized(ASTReader *Reader) override {
|
2011-10-29 06:54:31 +08:00
|
|
|
if (Previous)
|
|
|
|
Previous->ReaderInitialized(Reader);
|
|
|
|
}
|
2014-03-13 14:07:04 +08:00
|
|
|
void IdentifierRead(serialization::IdentID ID,
|
|
|
|
IdentifierInfo *II) override {
|
2011-10-29 06:54:31 +08:00
|
|
|
if (Previous)
|
|
|
|
Previous->IdentifierRead(ID, II);
|
|
|
|
}
|
2014-03-13 14:07:04 +08:00
|
|
|
void TypeRead(serialization::TypeIdx Idx, QualType T) override {
|
2011-10-29 06:54:31 +08:00
|
|
|
if (Previous)
|
|
|
|
Previous->TypeRead(Idx, T);
|
|
|
|
}
|
2014-03-13 14:07:04 +08:00
|
|
|
void DeclRead(serialization::DeclID ID, const Decl *D) override {
|
2011-10-29 06:54:31 +08:00
|
|
|
if (Previous)
|
|
|
|
Previous->DeclRead(ID, D);
|
|
|
|
}
|
2014-03-13 14:07:04 +08:00
|
|
|
void SelectorRead(serialization::SelectorID ID, Selector Sel) override {
|
2011-10-29 06:54:31 +08:00
|
|
|
if (Previous)
|
|
|
|
Previous->SelectorRead(ID, Sel);
|
|
|
|
}
|
2014-03-13 14:07:04 +08:00
|
|
|
void MacroDefinitionRead(serialization::PreprocessedEntityID PPID,
|
|
|
|
MacroDefinition *MD) override {
|
2011-10-29 06:54:31 +08:00
|
|
|
if (Previous)
|
|
|
|
Previous->MacroDefinitionRead(PPID, MD);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/// \brief Dumps deserialized declarations.
|
|
|
|
class DeserializedDeclsDumper : public DelegatingDeserializationListener {
|
|
|
|
public:
|
2014-05-08 12:26:47 +08:00
|
|
|
explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous,
|
|
|
|
bool DeletePrevious)
|
|
|
|
: DelegatingDeserializationListener(Previous, DeletePrevious) {}
|
2011-10-29 06:54:31 +08:00
|
|
|
|
2014-03-13 14:07:04 +08:00
|
|
|
void DeclRead(serialization::DeclID ID, const Decl *D) override {
|
2010-10-15 04:14:18 +08:00
|
|
|
llvm::outs() << "PCH DECL: " << D->getDeclKindName();
|
|
|
|
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
|
2012-02-07 19:57:57 +08:00
|
|
|
llvm::outs() << " - " << *ND;
|
2010-10-15 04:14:18 +08:00
|
|
|
llvm::outs() << "\n";
|
|
|
|
|
2011-10-29 06:54:31 +08:00
|
|
|
DelegatingDeserializationListener::DeclRead(ID, D);
|
2010-10-15 04:14:18 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-05-30 01:05:42 +08:00
|
|
|
/// \brief Checks deserialized declarations and emits error if a name
|
|
|
|
/// matches one given in command-line using -error-on-deserialized-decl.
|
|
|
|
class DeserializedDeclsChecker : public DelegatingDeserializationListener {
|
|
|
|
ASTContext &Ctx;
|
|
|
|
std::set<std::string> NamesToCheck;
|
|
|
|
|
|
|
|
public:
|
|
|
|
DeserializedDeclsChecker(ASTContext &Ctx,
|
|
|
|
const std::set<std::string> &NamesToCheck,
|
2014-05-08 12:26:47 +08:00
|
|
|
ASTDeserializationListener *Previous,
|
|
|
|
bool DeletePrevious)
|
|
|
|
: DelegatingDeserializationListener(Previous, DeletePrevious), Ctx(Ctx),
|
|
|
|
NamesToCheck(NamesToCheck) {}
|
2012-05-30 01:05:42 +08:00
|
|
|
|
2014-03-13 14:07:04 +08:00
|
|
|
void DeclRead(serialization::DeclID ID, const Decl *D) override {
|
2012-05-30 01:05:42 +08:00
|
|
|
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
|
|
|
|
if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) {
|
|
|
|
unsigned DiagID
|
|
|
|
= Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error,
|
|
|
|
"%0 was deserialized");
|
|
|
|
Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID)
|
|
|
|
<< ND->getNameAsString();
|
|
|
|
}
|
|
|
|
|
|
|
|
DelegatingDeserializationListener::DeclRead(ID, D);
|
|
|
|
}
|
2010-10-15 04:14:25 +08:00
|
|
|
};
|
|
|
|
|
2010-10-15 04:14:18 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2014-05-22 12:46:25 +08:00
|
|
|
FrontendAction::FrontendAction() : Instance(nullptr) {}
|
2009-11-14 18:42:35 +08:00
|
|
|
|
|
|
|
FrontendAction::~FrontendAction() {}
|
|
|
|
|
2012-01-21 00:28:04 +08:00
|
|
|
void FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput,
|
2014-08-11 01:03:42 +08:00
|
|
|
std::unique_ptr<ASTUnit> AST) {
|
2012-01-21 00:28:04 +08:00
|
|
|
this->CurrentInput = CurrentInput;
|
2014-08-11 01:03:42 +08:00
|
|
|
CurrentASTUnit = std::move(AST);
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
2014-07-18 06:34:12 +08:00
|
|
|
ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
|
|
|
|
StringRef InFile) {
|
|
|
|
ASTConsumer* Consumer = CreateASTConsumer(CI, InFile);
|
2011-01-26 04:34:14 +08:00
|
|
|
if (!Consumer)
|
2014-05-22 12:46:25 +08:00
|
|
|
return nullptr;
|
2011-01-26 04:34:14 +08:00
|
|
|
|
|
|
|
if (CI.getFrontendOpts().AddPluginActions.size() == 0)
|
|
|
|
return Consumer;
|
|
|
|
|
|
|
|
// Make sure the non-plugin consumer is first, so that plugins can't
|
|
|
|
// modifiy the AST.
|
2014-07-18 06:34:12 +08:00
|
|
|
std::vector<ASTConsumer*> Consumers(1, Consumer);
|
2011-01-26 04:34:14 +08:00
|
|
|
|
|
|
|
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) {
|
2014-07-18 06:34:12 +08:00
|
|
|
if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) {
|
|
|
|
std::unique_ptr<PluginASTAction> P(it->instantiate());
|
|
|
|
FrontendAction* c = P.get();
|
|
|
|
if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i]))
|
|
|
|
Consumers.push_back(c->CreateASTConsumer(CI, InFile));
|
|
|
|
}
|
2011-01-26 04:34:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-18 06:34:12 +08:00
|
|
|
return new MultiplexConsumer(Consumers);
|
2011-01-26 04:34:14 +08:00
|
|
|
}
|
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
|
2012-01-21 00:28:04 +08:00
|
|
|
const FrontendInputFile &Input) {
|
2009-11-14 18:42:35 +08:00
|
|
|
assert(!Instance && "Already processing a source file!");
|
2012-11-10 03:40:39 +08:00
|
|
|
assert(!Input.isEmpty() && "Unexpected empty filename!");
|
2012-01-21 00:28:04 +08:00
|
|
|
setCurrentInput(Input);
|
2009-11-14 18:42:35 +08:00
|
|
|
setCompilerInstance(&CI);
|
|
|
|
|
2012-11-10 03:40:39 +08:00
|
|
|
StringRef InputFile = Input.getFile();
|
2012-08-10 09:06:08 +08:00
|
|
|
bool HasBegunSourceFile = false;
|
2011-06-18 08:53:41 +08:00
|
|
|
if (!BeginInvocation(CI))
|
|
|
|
goto failure;
|
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
// AST files follow a very different path, since they share objects via the
|
|
|
|
// AST unit.
|
2012-11-10 03:40:39 +08:00
|
|
|
if (Input.getKind() == IK_AST) {
|
2009-11-14 18:42:35 +08:00
|
|
|
assert(!usesPreprocessorOnly() &&
|
|
|
|
"Attempt to pass AST file to preprocessor only action!");
|
2010-06-08 07:24:43 +08:00
|
|
|
assert(hasASTFileSupport() &&
|
|
|
|
"This action does not have AST file support!");
|
2009-11-14 18:42:35 +08:00
|
|
|
|
2012-02-20 22:00:23 +08:00
|
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics());
|
2013-11-27 13:22:15 +08:00
|
|
|
|
2014-08-11 01:03:42 +08:00
|
|
|
std::unique_ptr<ASTUnit> AST(
|
|
|
|
ASTUnit::LoadFromASTFile(InputFile, Diags, CI.getFileSystemOpts()));
|
|
|
|
|
2009-12-03 09:45:44 +08:00
|
|
|
if (!AST)
|
2009-11-14 18:42:35 +08:00
|
|
|
goto failure;
|
|
|
|
|
2013-03-19 06:55:24 +08:00
|
|
|
// Inform the diagnostic client we are processing a source file.
|
2014-05-22 12:46:25 +08:00
|
|
|
CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), nullptr);
|
2013-03-19 06:55:24 +08:00
|
|
|
HasBegunSourceFile = true;
|
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
// 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());
|
|
|
|
|
2014-08-11 01:03:42 +08:00
|
|
|
setCurrentInput(Input, std::move(AST));
|
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
// Initialize the action.
|
2012-11-10 03:40:39 +08:00
|
|
|
if (!BeginSourceFileAction(CI, InputFile))
|
2009-11-14 18:42:35 +08:00
|
|
|
goto failure;
|
|
|
|
|
2013-01-23 08:45:44 +08:00
|
|
|
// Create the AST consumer.
|
2012-11-10 03:40:39 +08:00
|
|
|
CI.setASTConsumer(CreateWrappedASTConsumer(CI, InputFile));
|
2009-11-14 18:42:35 +08:00
|
|
|
if (!CI.hasASTConsumer())
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-04-16 02:16:25 +08:00
|
|
|
if (!CI.hasVirtualFileSystem()) {
|
|
|
|
if (IntrusiveRefCntPtr<vfs::FileSystem> VFS =
|
|
|
|
createVFSFromCompilerInvocation(CI.getInvocation(),
|
|
|
|
CI.getDiagnostics()))
|
|
|
|
CI.setVirtualFileSystem(VFS);
|
|
|
|
else
|
|
|
|
goto failure;
|
2014-02-26 02:23:47 +08:00
|
|
|
}
|
|
|
|
|
2010-06-08 07:26:47 +08:00
|
|
|
// Set up the file and source managers, if needed.
|
2010-06-08 07:23:50 +08:00
|
|
|
if (!CI.hasFileManager())
|
|
|
|
CI.createFileManager();
|
|
|
|
if (!CI.hasSourceManager())
|
2010-11-23 16:35:12 +08:00
|
|
|
CI.createSourceManager(CI.getFileManager());
|
2010-06-08 07:26:47 +08:00
|
|
|
|
|
|
|
// IR files bypass the rest of initialization.
|
2012-11-10 03:40:39 +08:00
|
|
|
if (Input.getKind() == IK_LLVM_IR) {
|
2010-06-08 07:26:47 +08:00
|
|
|
assert(hasIRSupport() &&
|
|
|
|
"This action does not have IR file support!");
|
|
|
|
|
|
|
|
// Inform the diagnostic client we are processing a source file.
|
2014-05-22 12:46:25 +08:00
|
|
|
CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), nullptr);
|
2012-08-10 09:06:08 +08:00
|
|
|
HasBegunSourceFile = true;
|
2010-06-08 07:26:47 +08:00
|
|
|
|
|
|
|
// Initialize the action.
|
2012-11-10 03:40:39 +08:00
|
|
|
if (!BeginSourceFileAction(CI, InputFile))
|
2010-06-08 07:26:47 +08:00
|
|
|
goto failure;
|
|
|
|
|
2014-04-15 02:00:01 +08:00
|
|
|
// Initialize the main file entry.
|
|
|
|
if (!CI.InitializeSourceManager(CurrentInput))
|
|
|
|
goto failure;
|
|
|
|
|
2010-06-08 07:26:47 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-23 14:18:24 +08:00
|
|
|
// If the implicit PCH include is actually a directory, rather than
|
|
|
|
// a single file, search for a suitable PCH file in that directory.
|
|
|
|
if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) {
|
|
|
|
FileManager &FileMgr = CI.getFileManager();
|
|
|
|
PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
|
|
|
|
StringRef PCHInclude = PPOpts.ImplicitPCHInclude;
|
|
|
|
if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) {
|
2014-06-12 22:02:15 +08:00
|
|
|
std::error_code EC;
|
2012-10-23 14:18:24 +08:00
|
|
|
SmallString<128> DirNative;
|
|
|
|
llvm::sys::path::native(PCHDir->getName(), DirNative);
|
|
|
|
bool Found = false;
|
|
|
|
for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd;
|
|
|
|
Dir != DirEnd && !EC; Dir.increment(EC)) {
|
|
|
|
// Check whether this is an acceptable AST file.
|
|
|
|
if (ASTReader::isAcceptableASTFile(Dir->path(), FileMgr,
|
|
|
|
CI.getLangOpts(),
|
2012-10-25 07:41:50 +08:00
|
|
|
CI.getTargetOpts(),
|
|
|
|
CI.getPreprocessorOpts())) {
|
2013-02-06 00:36:52 +08:00
|
|
|
PPOpts.ImplicitPCHInclude = Dir->path();
|
|
|
|
Found = true;
|
2012-10-23 14:18:24 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Found) {
|
|
|
|
CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-08 07:26:47 +08:00
|
|
|
// Set up the preprocessor.
|
2014-03-07 15:47:58 +08:00
|
|
|
CI.createPreprocessor(getTranslationUnitKind());
|
2010-06-08 07:23:50 +08:00
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
// Inform the diagnostic client we are processing a source file.
|
|
|
|
CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(),
|
|
|
|
&CI.getPreprocessor());
|
2012-08-10 09:06:08 +08:00
|
|
|
HasBegunSourceFile = true;
|
2009-11-14 18:42:35 +08:00
|
|
|
|
|
|
|
// Initialize the action.
|
2012-11-10 03:40:39 +08:00
|
|
|
if (!BeginSourceFileAction(CI, InputFile))
|
2009-11-14 18:42:35 +08:00
|
|
|
goto failure;
|
|
|
|
|
2014-04-15 02:00:01 +08:00
|
|
|
// Initialize the main file entry. It is important that this occurs after
|
|
|
|
// BeginSourceFileAction, which may change CurrentInput during module builds.
|
|
|
|
if (!CI.InitializeSourceManager(CurrentInput))
|
|
|
|
goto failure;
|
|
|
|
|
2013-01-23 08:45:44 +08:00
|
|
|
// Create the AST context and consumer unless this is a preprocessor only
|
|
|
|
// action.
|
2009-11-14 18:42:35 +08:00
|
|
|
if (!usesPreprocessorOnly()) {
|
|
|
|
CI.createASTContext();
|
|
|
|
|
2014-07-18 06:34:12 +08:00
|
|
|
std::unique_ptr<ASTConsumer> Consumer(
|
|
|
|
CreateWrappedASTConsumer(CI, InputFile));
|
2010-10-30 03:49:13 +08:00
|
|
|
if (!Consumer)
|
|
|
|
goto failure;
|
2010-07-30 08:29:29 +08:00
|
|
|
|
2010-10-25 01:26:36 +08:00
|
|
|
CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener());
|
2012-10-10 07:05:51 +08:00
|
|
|
|
2011-03-10 01:21:42 +08:00
|
|
|
if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) {
|
|
|
|
// Convert headers to PCH and chain them.
|
2014-07-07 19:07:10 +08:00
|
|
|
IntrusiveRefCntPtr<ExternalSemaSource> source, FinalReader;
|
|
|
|
source = createChainedIncludesSource(CI, FinalReader);
|
2011-03-10 01:21:42 +08:00
|
|
|
if (!source)
|
|
|
|
goto failure;
|
2014-07-07 19:07:10 +08:00
|
|
|
CI.setModuleManager(static_cast<ASTReader *>(FinalReader.get()));
|
2011-03-10 01:21:42 +08:00
|
|
|
CI.getASTContext().setExternalSource(source);
|
|
|
|
} else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) {
|
|
|
|
// Use PCH.
|
2009-11-14 18:42:35 +08:00
|
|
|
assert(hasPCHSupport() && "This action does not have PCH support!");
|
Revamp the SourceManager to separate the representation of parsed
source locations from source locations loaded from an AST/PCH file.
Previously, loading an AST/PCH file involved carefully pre-allocating
space at the beginning of the source manager for the source locations
and FileIDs that correspond to the prefix, and then appending the
source locations/FileIDs used for parsing the remaining translation
unit. This design forced us into loading PCH files early, as a prefix,
whic has become a rather significant limitation.
This patch splits the SourceManager space into two parts: for source
location "addresses", the lower values (growing upward) are used to
describe parsed code, while upper values (growing downward) are used
for source locations loaded from AST/PCH files. Similarly, positive
FileIDs are used to describe parsed code while negative FileIDs are
used to file/macro locations loaded from AST/PCH files. As a result,
we can load PCH/AST files even during parsing, making various
improvemnts in the future possible, e.g., teaching #include <foo.h> to
look for and load <foo.h.gch> if it happens to be already available.
This patch was originally written by Sebastian Redl, then brought
forward to the modern age by Jonathan Turner, and finally
polished/finished by me to be committed.
llvm-svn: 135484
2011-07-20 00:10:42 +08:00
|
|
|
ASTDeserializationListener *DeserialListener =
|
|
|
|
Consumer->GetASTDeserializationListener();
|
2014-05-08 12:26:47 +08:00
|
|
|
bool DeleteDeserialListener = false;
|
|
|
|
if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls) {
|
|
|
|
DeserialListener = new DeserializedDeclsDumper(DeserialListener,
|
|
|
|
DeleteDeserialListener);
|
|
|
|
DeleteDeserialListener = true;
|
|
|
|
}
|
|
|
|
if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty()) {
|
|
|
|
DeserialListener = new DeserializedDeclsChecker(
|
|
|
|
CI.getASTContext(),
|
|
|
|
CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn,
|
|
|
|
DeserialListener, DeleteDeserialListener);
|
|
|
|
DeleteDeserialListener = true;
|
|
|
|
}
|
2009-11-14 18:42:35 +08:00
|
|
|
CI.createPCHExternalASTSource(
|
2014-05-08 12:26:47 +08:00
|
|
|
CI.getPreprocessorOpts().ImplicitPCHInclude,
|
|
|
|
CI.getPreprocessorOpts().DisablePCHValidation,
|
|
|
|
CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, DeserialListener,
|
|
|
|
DeleteDeserialListener);
|
2009-11-14 18:42:35 +08:00
|
|
|
if (!CI.getASTContext().getExternalSource())
|
|
|
|
goto failure;
|
|
|
|
}
|
2010-07-10 05:00:24 +08:00
|
|
|
|
2014-07-18 06:34:12 +08:00
|
|
|
CI.setASTConsumer(Consumer.release());
|
2010-07-10 05:00:24 +08:00
|
|
|
if (!CI.hasASTConsumer())
|
|
|
|
goto failure;
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
2011-08-06 06:17:03 +08:00
|
|
|
// Initialize built-in info as long as we aren't using an external AST
|
2009-11-14 18:42:35 +08:00
|
|
|
// source.
|
|
|
|
if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) {
|
|
|
|
Preprocessor &PP = CI.getPreprocessor();
|
If a declaration is loaded, and then a module import adds a redeclaration, then
ensure that querying the first declaration for its most recent declaration
checks for redeclarations from the imported module.
This works as follows:
* The 'most recent' pointer on a canonical declaration grows a pointer to the
external AST source and a generation number (space- and time-optimized for
the case where there is no external source).
* Each time the 'most recent' pointer is queried, if it has an external source,
we check whether it's up to date, and update it if not.
* The ancillary data stored on the canonical declaration is allocated lazily
to avoid filling it in for declarations that end up being non-canonical.
We'll still perform a redundant (ASTContext) allocation if someone asks for
the most recent declaration from a decl before setPreviousDecl is called,
but such cases are probably all bugs, and are now easy to find.
Some finessing is still in order here -- in particular, we use a very general
mechanism for handling the DefinitionData pointer on CXXRecordData, and a more
targeted approach would be more compact.
Also, the MayHaveOutOfDateDef mechanism should now be expunged, since it was
addressing only a corner of the full problem space here. That's not covered
by this patch.
Early performance benchmarks show that this makes no measurable difference to
Clang performance without modules enabled (and fixes a major correctness issue
with modules enabled). I'll revert if a full performance comparison shows any
problems.
llvm-svn: 209046
2014-05-17 07:01:30 +08:00
|
|
|
|
|
|
|
// If modules are enabled, create the module manager before creating
|
|
|
|
// any builtins, so that all declarations know that they might be
|
|
|
|
// extended by an external source.
|
|
|
|
if (CI.getLangOpts().Modules)
|
|
|
|
CI.createModuleManager();
|
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(),
|
2012-03-11 15:00:24 +08:00
|
|
|
PP.getLangOpts());
|
If a declaration is loaded, and then a module import adds a redeclaration, then
ensure that querying the first declaration for its most recent declaration
checks for redeclarations from the imported module.
This works as follows:
* The 'most recent' pointer on a canonical declaration grows a pointer to the
external AST source and a generation number (space- and time-optimized for
the case where there is no external source).
* Each time the 'most recent' pointer is queried, if it has an external source,
we check whether it's up to date, and update it if not.
* The ancillary data stored on the canonical declaration is allocated lazily
to avoid filling it in for declarations that end up being non-canonical.
We'll still perform a redundant (ASTContext) allocation if someone asks for
the most recent declaration from a decl before setPreviousDecl is called,
but such cases are probably all bugs, and are now easy to find.
Some finessing is still in order here -- in particular, we use a very general
mechanism for handling the DefinitionData pointer on CXXRecordData, and a more
targeted approach would be more compact.
Also, the MayHaveOutOfDateDef mechanism should now be expunged, since it was
addressing only a corner of the full problem space here. That's not covered
by this patch.
Early performance benchmarks show that this makes no measurable difference to
Clang performance without modules enabled (and fixes a major correctness issue
with modules enabled). I'll revert if a full performance comparison shows any
problems.
llvm-svn: 209046
2014-05-17 07:01:30 +08:00
|
|
|
} else {
|
|
|
|
// FIXME: If this is a problem, recover from it by creating a multiplex
|
|
|
|
// source.
|
|
|
|
assert((!CI.getLangOpts().Modules || CI.getModuleManager()) &&
|
|
|
|
"modules enabled but created an external source that "
|
|
|
|
"doesn't support modules");
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
Extend the ExternalASTSource interface to allow the AST source to
provide the layout of records, rather than letting Clang compute
the layout itself. LLDB provides the motivation for this feature:
because various layout-altering attributes (packed, aligned, etc.)
don't get reliably get placed into DWARF, the record layouts computed
by LLDB from the reconstructed records differ from the actual layouts,
and badness occurs. This interface lets the DWARF data drive layout,
so we don't need the attributes preserved to get the answer write.
The testing methodology for this change is fun. I've introduced a
variant of -fdump-record-layouts called -fdump-record-layouts-simple
that always has the simple C format and provides size/alignment/field
offsets. There is also a -cc1 option -foverride-record-layout=<file>
to take the output of -fdump-record-layouts-simple and parse it to
produce a set of overridden layouts, which is introduced into the AST
via a testing-only ExternalASTSource (called
LayoutOverrideSource). Each test contains a number of records to lay
out, which use various layout-changing attributes, and then dumps the
layouts. We then run the test again, using the preprocessor to
eliminate the layout-changing attributes entirely (which would give us
different layouts for the records), but supplying the
previously-computed record layouts. Finally, we diff the layouts
produced from the two runs to be sure that they are identical.
Note that this code makes the assumption that we don't *have* to
provide the offsets of bases or virtual bases to get the layout right,
because the alignment attributes don't affect it. I believe this
assumption holds, but if it does not, we can extend
LayoutOverrideSource to also provide base offset information.
Fixes the Clang side of <rdar://problem/10169539>.
llvm-svn: 149055
2012-01-26 15:55:45 +08:00
|
|
|
// If there is a layout overrides file, attach an external AST source that
|
|
|
|
// provides the layouts from that file.
|
|
|
|
if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() &&
|
|
|
|
CI.hasASTContext() && !CI.getASTContext().getExternalSource()) {
|
2014-02-27 12:11:59 +08:00
|
|
|
IntrusiveRefCntPtr<ExternalASTSource>
|
Extend the ExternalASTSource interface to allow the AST source to
provide the layout of records, rather than letting Clang compute
the layout itself. LLDB provides the motivation for this feature:
because various layout-altering attributes (packed, aligned, etc.)
don't get reliably get placed into DWARF, the record layouts computed
by LLDB from the reconstructed records differ from the actual layouts,
and badness occurs. This interface lets the DWARF data drive layout,
so we don't need the attributes preserved to get the answer write.
The testing methodology for this change is fun. I've introduced a
variant of -fdump-record-layouts called -fdump-record-layouts-simple
that always has the simple C format and provides size/alignment/field
offsets. There is also a -cc1 option -foverride-record-layout=<file>
to take the output of -fdump-record-layouts-simple and parse it to
produce a set of overridden layouts, which is introduced into the AST
via a testing-only ExternalASTSource (called
LayoutOverrideSource). Each test contains a number of records to lay
out, which use various layout-changing attributes, and then dumps the
layouts. We then run the test again, using the preprocessor to
eliminate the layout-changing attributes entirely (which would give us
different layouts for the records), but supplying the
previously-computed record layouts. Finally, we diff the layouts
produced from the two runs to be sure that they are identical.
Note that this code makes the assumption that we don't *have* to
provide the offsets of bases or virtual bases to get the layout right,
because the alignment attributes don't affect it. I believe this
assumption holds, but if it does not, we can extend
LayoutOverrideSource to also provide base offset information.
Fixes the Clang side of <rdar://problem/10169539>.
llvm-svn: 149055
2012-01-26 15:55:45 +08:00
|
|
|
Override(new LayoutOverrideSource(
|
|
|
|
CI.getFrontendOpts().OverrideRecordLayoutsFile));
|
|
|
|
CI.getASTContext().setExternalSource(Override);
|
|
|
|
}
|
If a declaration is loaded, and then a module import adds a redeclaration, then
ensure that querying the first declaration for its most recent declaration
checks for redeclarations from the imported module.
This works as follows:
* The 'most recent' pointer on a canonical declaration grows a pointer to the
external AST source and a generation number (space- and time-optimized for
the case where there is no external source).
* Each time the 'most recent' pointer is queried, if it has an external source,
we check whether it's up to date, and update it if not.
* The ancillary data stored on the canonical declaration is allocated lazily
to avoid filling it in for declarations that end up being non-canonical.
We'll still perform a redundant (ASTContext) allocation if someone asks for
the most recent declaration from a decl before setPreviousDecl is called,
but such cases are probably all bugs, and are now easy to find.
Some finessing is still in order here -- in particular, we use a very general
mechanism for handling the DefinitionData pointer on CXXRecordData, and a more
targeted approach would be more compact.
Also, the MayHaveOutOfDateDef mechanism should now be expunged, since it was
addressing only a corner of the full problem space here. That's not covered
by this patch.
Early performance benchmarks show that this makes no measurable difference to
Clang performance without modules enabled (and fixes a major correctness issue
with modules enabled). I'll revert if a full performance comparison shows any
problems.
llvm-svn: 209046
2014-05-17 07:01:30 +08:00
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
// If we failed, reset state since the client will not end up calling the
|
|
|
|
// matching EndSourceFile().
|
|
|
|
failure:
|
|
|
|
if (isCurrentFileAST()) {
|
2014-05-22 12:46:25 +08:00
|
|
|
CI.setASTContext(nullptr);
|
|
|
|
CI.setPreprocessor(nullptr);
|
|
|
|
CI.setSourceManager(nullptr);
|
|
|
|
CI.setFileManager(nullptr);
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
2012-08-10 09:06:08 +08:00
|
|
|
if (HasBegunSourceFile)
|
|
|
|
CI.getDiagnosticClient().EndSourceFile();
|
2012-10-15 03:21:21 +08:00
|
|
|
CI.clearOutputFiles(/*EraseFiles=*/true);
|
2012-01-21 00:28:04 +08:00
|
|
|
setCurrentInput(FrontendInputFile());
|
2014-05-22 12:46:25 +08:00
|
|
|
setCompilerInstance(nullptr);
|
2009-11-14 18:42:35 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-08 13:48:06 +08:00
|
|
|
bool FrontendAction::Execute() {
|
2009-11-14 18:42:35 +08:00
|
|
|
CompilerInstance &CI = getCompilerInstance();
|
|
|
|
|
2009-11-29 17:57:35 +08:00
|
|
|
if (CI.hasFrontendTimer()) {
|
|
|
|
llvm::TimeRegion Timer(CI.getFrontendTimer());
|
|
|
|
ExecuteAction();
|
|
|
|
}
|
|
|
|
else ExecuteAction();
|
2012-06-08 13:48:06 +08:00
|
|
|
|
2013-01-24 06:38:11 +08:00
|
|
|
// If we are supposed to rebuild the global module index, do so now unless
|
2013-01-25 08:45:27 +08:00
|
|
|
// there were any module-build failures.
|
|
|
|
if (CI.shouldBuildGlobalModuleIndex() && CI.hasFileManager() &&
|
|
|
|
CI.hasPreprocessor()) {
|
2013-01-24 06:38:11 +08:00
|
|
|
GlobalModuleIndex::writeIndex(
|
|
|
|
CI.getFileManager(),
|
|
|
|
CI.getPreprocessor().getHeaderSearchInfo().getModuleCachePath());
|
|
|
|
}
|
|
|
|
|
2012-06-08 13:48:06 +08:00
|
|
|
return true;
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FrontendAction::EndSourceFile() {
|
|
|
|
CompilerInstance &CI = getCompilerInstance();
|
|
|
|
|
2011-02-10 02:47:31 +08:00
|
|
|
// Inform the diagnostic client we are done with this source file.
|
|
|
|
CI.getDiagnosticClient().EndSourceFile();
|
|
|
|
|
2014-08-08 04:51:16 +08:00
|
|
|
// Inform the preprocessor we are done.
|
|
|
|
if (CI.hasPreprocessor())
|
|
|
|
CI.getPreprocessor().EndSourceFile();
|
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
// Finalize the action.
|
|
|
|
EndSourceFileAction();
|
|
|
|
|
2014-04-24 10:42:04 +08:00
|
|
|
// Sema references the ast consumer, so reset sema first.
|
2009-11-14 18:42:35 +08:00
|
|
|
//
|
|
|
|
// FIXME: There is more per-file stuff we could just drop here?
|
2014-04-24 10:42:04 +08:00
|
|
|
bool DisableFree = CI.getFrontendOpts().DisableFree;
|
|
|
|
if (DisableFree) {
|
2010-08-13 07:31:19 +08:00
|
|
|
if (!isCurrentFileAST()) {
|
2014-04-24 08:51:03 +08:00
|
|
|
CI.resetAndLeakSema();
|
2011-03-22 02:40:17 +08:00
|
|
|
CI.resetAndLeakASTContext();
|
2010-08-13 07:31:19 +08:00
|
|
|
}
|
2014-07-18 06:34:12 +08:00
|
|
|
BuryPointer(CI.takeASTConsumer());
|
2009-11-14 18:42:35 +08:00
|
|
|
} else {
|
2010-08-13 07:31:19 +08:00
|
|
|
if (!isCurrentFileAST()) {
|
2014-05-22 12:46:25 +08:00
|
|
|
CI.setSema(nullptr);
|
|
|
|
CI.setASTContext(nullptr);
|
2010-08-13 07:31:19 +08:00
|
|
|
}
|
2014-05-22 12:46:25 +08:00
|
|
|
CI.setASTConsumer(nullptr);
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
|
2013-06-11 08:36:55 +08:00
|
|
|
// Cleanup the output streams, and erase the output files if instructed by the
|
|
|
|
// FrontendAction.
|
|
|
|
CI.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles());
|
2009-11-14 18:42:35 +08:00
|
|
|
|
2014-04-24 11:31:27 +08:00
|
|
|
// FIXME: Only do this if DisableFree is set.
|
|
|
|
if (isCurrentFileAST()) {
|
2014-04-24 08:51:03 +08:00
|
|
|
CI.resetAndLeakSema();
|
2011-03-22 02:40:17 +08:00
|
|
|
CI.resetAndLeakASTContext();
|
|
|
|
CI.resetAndLeakPreprocessor();
|
|
|
|
CI.resetAndLeakSourceManager();
|
|
|
|
CI.resetAndLeakFileManager();
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
2014-05-22 12:46:25 +08:00
|
|
|
setCompilerInstance(nullptr);
|
2012-01-21 00:28:04 +08:00
|
|
|
setCurrentInput(FrontendInputFile());
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
2013-06-11 08:36:55 +08:00
|
|
|
bool FrontendAction::shouldEraseOutputFiles() {
|
|
|
|
return getCompilerInstance().getDiagnostics().hasErrorOccurred();
|
|
|
|
}
|
|
|
|
|
2009-11-14 18:42:35 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Utility Actions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
void ASTFrontendAction::ExecuteAction() {
|
|
|
|
CompilerInstance &CI = getCompilerInstance();
|
2013-07-28 21:23:37 +08:00
|
|
|
if (!CI.hasPreprocessor())
|
|
|
|
return;
|
2009-11-14 18:42:35 +08:00
|
|
|
|
|
|
|
// 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?
|
2014-05-22 12:46:25 +08:00
|
|
|
CodeCompleteConsumer *CompletionConsumer = nullptr;
|
2009-11-14 18:42:35 +08:00
|
|
|
if (CI.hasCodeCompletionConsumer())
|
|
|
|
CompletionConsumer = &CI.getCodeCompletionConsumer();
|
|
|
|
|
2010-08-13 07:31:19 +08:00
|
|
|
if (!CI.hasSema())
|
2011-08-26 06:30:56 +08:00
|
|
|
CI.createSema(getTranslationUnitKind(), CompletionConsumer);
|
2010-08-13 07:31:19 +08:00
|
|
|
|
2012-04-12 18:11:59 +08:00
|
|
|
ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
|
|
|
|
CI.getFrontendOpts().SkipFunctionBodies);
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
|
|
|
|
2011-12-20 10:48:34 +08:00
|
|
|
void PluginASTAction::anchor() { }
|
|
|
|
|
2014-07-18 06:34:12 +08:00
|
|
|
ASTConsumer *
|
2009-11-14 18:42:35 +08:00
|
|
|
PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI,
|
2011-07-23 18:55:15 +08:00
|
|
|
StringRef InFile) {
|
2009-12-12 13:05:38 +08:00
|
|
|
llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!");
|
2009-11-14 18:42:35 +08:00
|
|
|
}
|
2011-06-17 00:17:05 +08:00
|
|
|
|
2014-07-18 06:34:12 +08:00
|
|
|
ASTConsumer *WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI,
|
|
|
|
StringRef InFile) {
|
2011-06-17 00:17:05 +08:00
|
|
|
return WrappedAction->CreateASTConsumer(CI, InFile);
|
|
|
|
}
|
2011-06-18 08:53:41 +08:00
|
|
|
bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) {
|
|
|
|
return WrappedAction->BeginInvocation(CI);
|
|
|
|
}
|
2011-06-17 00:17:05 +08:00
|
|
|
bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI,
|
2011-07-23 18:55:15 +08:00
|
|
|
StringRef Filename) {
|
2012-01-21 00:28:04 +08:00
|
|
|
WrappedAction->setCurrentInput(getCurrentInput());
|
2011-06-18 08:53:41 +08:00
|
|
|
WrappedAction->setCompilerInstance(&CI);
|
2011-06-17 00:17:05 +08:00
|
|
|
return WrappedAction->BeginSourceFileAction(CI, Filename);
|
|
|
|
}
|
|
|
|
void WrapperFrontendAction::ExecuteAction() {
|
|
|
|
WrappedAction->ExecuteAction();
|
|
|
|
}
|
|
|
|
void WrapperFrontendAction::EndSourceFileAction() {
|
|
|
|
WrappedAction->EndSourceFileAction();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WrapperFrontendAction::usesPreprocessorOnly() const {
|
|
|
|
return WrappedAction->usesPreprocessorOnly();
|
|
|
|
}
|
2011-08-26 06:30:56 +08:00
|
|
|
TranslationUnitKind WrapperFrontendAction::getTranslationUnitKind() {
|
|
|
|
return WrappedAction->getTranslationUnitKind();
|
2011-06-17 00:17:05 +08:00
|
|
|
}
|
|
|
|
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) {}
|
|
|
|
|