forked from OSchip/llvm-project
Introduce a testbed for merging multiple ASTs into a single AST
context with the AST importer. WIP, still useless but at least it has a test. llvm-svn: 95683
This commit is contained in:
parent
499b4e3387
commit
62d311fdf4
|
@ -200,6 +200,9 @@ def verify : Flag<"-verify">,
|
|||
// CompilerInvocation out of a driver-derived argument vector.
|
||||
def cc1 : Flag<"-cc1">;
|
||||
|
||||
def ast_merge : Separate<"-ast-merge">,
|
||||
MetaVarName<"<ast file>">,
|
||||
HelpText<"Merge the given AST file into the translation unit being compiled.">;
|
||||
def code_completion_at : Separate<"-code-completion-at">,
|
||||
MetaVarName<"<file>:<line>:<column>">,
|
||||
HelpText<"Dump code-completion information at a location">;
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace clang {
|
|||
class ASTUnit;
|
||||
class ASTConsumer;
|
||||
class CompilerInstance;
|
||||
class ASTMergeAction;
|
||||
|
||||
/// FrontendAction - Abstract base class for actions which can be performed by
|
||||
/// the frontend.
|
||||
|
@ -25,6 +26,7 @@ class FrontendAction {
|
|||
std::string CurrentFile;
|
||||
llvm::OwningPtr<ASTUnit> CurrentASTUnit;
|
||||
CompilerInstance *Instance;
|
||||
friend class ASTMergeAction;
|
||||
|
||||
protected:
|
||||
/// @name Implementation Action Interface
|
||||
|
@ -104,6 +106,10 @@ public:
|
|||
return *CurrentASTUnit;
|
||||
}
|
||||
|
||||
ASTUnit *takeCurrentASTUnit() {
|
||||
return CurrentASTUnit.take();
|
||||
}
|
||||
|
||||
void setCurrentFile(llvm::StringRef Value, ASTUnit *AST = 0);
|
||||
|
||||
/// @}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#define LLVM_CLANG_FRONTEND_FRONTENDACTIONS_H
|
||||
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
class FixItRewriter;
|
||||
|
@ -119,6 +121,43 @@ public:
|
|||
virtual bool hasCodeCompletionSupport() const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Frontend action adaptor that merges ASTs together.
|
||||
*
|
||||
* This action takes an existing AST file and "merges" it into the AST
|
||||
* context, producing a merged context. This action is an action
|
||||
* adaptor, which forwards most of its calls to another action that
|
||||
* will consume the merged context.
|
||||
*/
|
||||
class ASTMergeAction : public FrontendAction {
|
||||
/// \brief The action that the merge action adapts.
|
||||
FrontendAction *AdaptedAction;
|
||||
|
||||
/// \brief The set of AST files to merge.
|
||||
std::vector<std::string> ASTFiles;
|
||||
|
||||
protected:
|
||||
virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
|
||||
llvm::StringRef InFile);
|
||||
|
||||
virtual bool BeginSourceFileAction(CompilerInstance &CI,
|
||||
llvm::StringRef Filename);
|
||||
|
||||
virtual void ExecuteAction();
|
||||
virtual void EndSourceFileAction();
|
||||
|
||||
public:
|
||||
ASTMergeAction(FrontendAction *AdaptedAction,
|
||||
std::string *ASTFiles, unsigned NumASTFiles);
|
||||
virtual ~ASTMergeAction();
|
||||
|
||||
virtual bool usesPreprocessorOnly() const;
|
||||
virtual bool usesCompleteTranslationUnit();
|
||||
virtual bool hasPCHSupport() const;
|
||||
virtual bool hasASTSupport() const;
|
||||
virtual bool hasCodeCompletionSupport() const;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Code Gen AST Actions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -110,6 +110,9 @@ public:
|
|||
/// The list of plugins to load.
|
||||
std::vector<std::string> Plugins;
|
||||
|
||||
/// \brief The list of AST files to merge.
|
||||
std::vector<std::string> ASTMergeFiles;
|
||||
|
||||
public:
|
||||
FrontendOptions() {
|
||||
DebugCodeCompletionPrinter = 1;
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace {
|
|||
explicit ASTNodeImporter(ASTImporter &Importer) : Importer(Importer) { }
|
||||
|
||||
using TypeVisitor<ASTNodeImporter, QualType>::Visit;
|
||||
using DeclVisitor<ASTNodeImporter, Decl *>::Visit;
|
||||
|
||||
// Importing types
|
||||
QualType VisitBuiltinType(BuiltinType *T);
|
||||
|
@ -440,6 +441,13 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
|
|||
if (!DC)
|
||||
return 0;
|
||||
|
||||
DeclContext *LexicalDC = DC;
|
||||
if (D->getDeclContext() != D->getLexicalDeclContext()) {
|
||||
LexicalDC = Importer.ImportContext(D->getLexicalDeclContext());
|
||||
if (!LexicalDC)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Import the name of this declaration.
|
||||
DeclarationName Name = Importer.Import(D->getDeclName());
|
||||
if (D->getDeclName() && !Name)
|
||||
|
@ -455,11 +463,11 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
|
|||
|
||||
// Try to find a variable in our own ("to") context with the same name and
|
||||
// in the same context as the variable we're importing.
|
||||
if (!D->isFileVarDecl()) {
|
||||
if (D->isFileVarDecl()) {
|
||||
VarDecl *MergeWithVar = 0;
|
||||
llvm::SmallVector<NamedDecl *, 4> ConflictingDecls;
|
||||
unsigned IDNS = Decl::IDNS_Ordinary;
|
||||
for (DeclContext::lookup_result Lookup = DC->lookup(D->getDeclName());
|
||||
for (DeclContext::lookup_result Lookup = DC->lookup(Name);
|
||||
Lookup.first != Lookup.second;
|
||||
++Lookup.first) {
|
||||
if (!(*Lookup.first)->isInIdentifierNamespace(IDNS))
|
||||
|
@ -517,16 +525,22 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
|
|||
TypeSourceInfo *TInfo = 0;
|
||||
if (TypeSourceInfo *FromTInfo = D->getTypeSourceInfo()) {
|
||||
TInfo = Importer.Import(FromTInfo);
|
||||
#if 0
|
||||
// FIXME: Tolerate failures in translation type source
|
||||
// information, at least until it is implemented.
|
||||
if (!TInfo)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Create the imported variable.
|
||||
VarDecl *ToVar = VarDecl::Create(Importer.getToContext(), DC, Loc,
|
||||
Name.getAsIdentifierInfo(), T, TInfo,
|
||||
D->getStorageClass());
|
||||
ToVar->setLexicalDeclContext(LexicalDC);
|
||||
Importer.getImportedDecls()[D] = ToVar;
|
||||
|
||||
LexicalDC->addDecl(ToVar);
|
||||
|
||||
// Merge the initializer.
|
||||
// FIXME: Can we really import any initializer? Alternatively, we could force
|
||||
// ourselves to import every declaration of a variable and then only use
|
||||
|
@ -542,7 +556,12 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
|
|||
ASTImporter::ASTImporter(ASTContext &ToContext, Diagnostic &ToDiags,
|
||||
ASTContext &FromContext, Diagnostic &FromDiags)
|
||||
: ToContext(ToContext), FromContext(FromContext),
|
||||
ToDiags(ToDiags), FromDiags(FromDiags) { }
|
||||
ToDiags(ToDiags), FromDiags(FromDiags) {
|
||||
ImportedDecls[FromContext.getTranslationUnitDecl()]
|
||||
= ToContext.getTranslationUnitDecl();
|
||||
}
|
||||
|
||||
ASTImporter::~ASTImporter() { }
|
||||
|
||||
QualType ASTImporter::Import(QualType FromT) {
|
||||
if (FromT.isNull())
|
||||
|
@ -566,6 +585,73 @@ QualType ASTImporter::Import(QualType FromT) {
|
|||
return ToContext.getQualifiedType(ToT, FromT.getQualifiers());
|
||||
}
|
||||
|
||||
TypeSourceInfo *ASTImporter::Import(TypeSourceInfo *FromTSI) {
|
||||
// FIXME: Implement!
|
||||
return 0;
|
||||
}
|
||||
|
||||
Decl *ASTImporter::Import(Decl *FromD) {
|
||||
if (!FromD)
|
||||
return 0;
|
||||
|
||||
// Check whether we've already imported this declaration.
|
||||
llvm::DenseMap<Decl *, Decl *>::iterator Pos = ImportedDecls.find(FromD);
|
||||
if (Pos != ImportedDecls.end())
|
||||
return Pos->second;
|
||||
|
||||
// Import the type
|
||||
ASTNodeImporter Importer(*this);
|
||||
Decl *ToD = Importer.Visit(FromD);
|
||||
if (!ToD)
|
||||
return 0;
|
||||
|
||||
// Record the imported declaration.
|
||||
ImportedDecls[FromD] = ToD;
|
||||
return ToD;
|
||||
}
|
||||
|
||||
DeclContext *ASTImporter::ImportContext(DeclContext *FromDC) {
|
||||
if (!FromDC)
|
||||
return FromDC;
|
||||
|
||||
return cast_or_null<DeclContext>(Import(cast<Decl>(FromDC)));
|
||||
}
|
||||
|
||||
Expr *ASTImporter::Import(Expr *FromE) {
|
||||
if (!FromE)
|
||||
return 0;
|
||||
|
||||
return cast_or_null<Expr>(Import(cast<Stmt>(FromE)));
|
||||
}
|
||||
|
||||
Stmt *ASTImporter::Import(Stmt *FromS) {
|
||||
if (!FromS)
|
||||
return 0;
|
||||
|
||||
// FIXME: Implement!
|
||||
return 0;
|
||||
}
|
||||
|
||||
NestedNameSpecifier *ASTImporter::Import(NestedNameSpecifier *FromNNS) {
|
||||
if (!FromNNS)
|
||||
return 0;
|
||||
|
||||
// FIXME: Implement!
|
||||
return 0;
|
||||
}
|
||||
|
||||
SourceLocation ASTImporter::Import(SourceLocation FromLoc) {
|
||||
if (FromLoc.isInvalid())
|
||||
return SourceLocation();
|
||||
|
||||
// FIXME: Implement!
|
||||
return SourceLocation();
|
||||
}
|
||||
|
||||
SourceRange ASTImporter::Import(SourceRange FromRange) {
|
||||
return SourceRange(Import(FromRange.getBegin()), Import(FromRange.getEnd()));
|
||||
}
|
||||
|
||||
DeclarationName ASTImporter::Import(DeclarationName FromName) {
|
||||
if (!FromName)
|
||||
return DeclarationName();
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
//===-- ASTMerge.cpp - AST Merging Frontent Action --------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "clang/Frontend/ASTUnit.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/ASTImporter.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
ASTConsumer *ASTMergeAction::CreateASTConsumer(CompilerInstance &CI,
|
||||
llvm::StringRef InFile) {
|
||||
return AdaptedAction->CreateASTConsumer(CI, InFile);
|
||||
}
|
||||
|
||||
bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI,
|
||||
llvm::StringRef Filename) {
|
||||
// FIXME: This is a hack. We need a better way to communicate the
|
||||
// AST file, compiler instance, and file name than member variables
|
||||
// of FrontendAction.
|
||||
AdaptedAction->setCurrentFile(getCurrentFile(), takeCurrentASTUnit());
|
||||
AdaptedAction->setCompilerInstance(&CI);
|
||||
return AdaptedAction->BeginSourceFileAction(CI, Filename);
|
||||
}
|
||||
|
||||
void ASTMergeAction::ExecuteAction() {
|
||||
CompilerInstance &CI = getCompilerInstance();
|
||||
|
||||
for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) {
|
||||
ASTUnit *Unit = ASTUnit::LoadFromPCHFile(ASTFiles[I], CI.getDiagnostics(),
|
||||
false, true);
|
||||
if (!Unit)
|
||||
continue;
|
||||
|
||||
ASTImporter Importer(CI.getASTContext(), CI.getDiagnostics(),
|
||||
Unit->getASTContext(), CI.getDiagnostics());
|
||||
|
||||
TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
|
||||
for (DeclContext::decl_iterator D = TU->decls_begin(),
|
||||
DEnd = TU->decls_end();
|
||||
D != DEnd; ++D) {
|
||||
// FIXME: We only merge variables whose names start with x. Why
|
||||
// would anyone want anything else?
|
||||
if (VarDecl *VD = dyn_cast<VarDecl>(*D))
|
||||
if (VD->getIdentifier() &&
|
||||
*VD->getIdentifier()->getNameStart() == 'x') {
|
||||
Decl *Merged = Importer.Import(VD);
|
||||
if (Merged)
|
||||
Merged->dump();
|
||||
}
|
||||
}
|
||||
|
||||
delete Unit;
|
||||
}
|
||||
|
||||
|
||||
return AdaptedAction->ExecuteAction();
|
||||
}
|
||||
|
||||
void ASTMergeAction::EndSourceFileAction() {
|
||||
return AdaptedAction->EndSourceFileAction();
|
||||
}
|
||||
|
||||
ASTMergeAction::ASTMergeAction(FrontendAction *AdaptedAction,
|
||||
std::string *ASTFiles, unsigned NumASTFiles)
|
||||
: AdaptedAction(AdaptedAction), ASTFiles(ASTFiles, ASTFiles + NumASTFiles) {
|
||||
assert(AdaptedAction && "ASTMergeAction needs an action to adapt");
|
||||
}
|
||||
|
||||
ASTMergeAction::~ASTMergeAction() {
|
||||
delete AdaptedAction;
|
||||
}
|
||||
|
||||
bool ASTMergeAction::usesPreprocessorOnly() const {
|
||||
return AdaptedAction->usesPreprocessorOnly();
|
||||
}
|
||||
|
||||
bool ASTMergeAction::usesCompleteTranslationUnit() {
|
||||
return AdaptedAction->usesCompleteTranslationUnit();
|
||||
}
|
||||
|
||||
bool ASTMergeAction::hasPCHSupport() const {
|
||||
return AdaptedAction->hasPCHSupport();
|
||||
}
|
||||
|
||||
bool ASTMergeAction::hasASTSupport() const {
|
||||
return AdaptedAction->hasASTSupport();
|
||||
}
|
||||
|
||||
bool ASTMergeAction::hasCodeCompletionSupport() const {
|
||||
return AdaptedAction->hasCodeCompletionSupport();
|
||||
}
|
|
@ -2,6 +2,7 @@ set(LLVM_NO_RTTI 1)
|
|||
|
||||
add_clang_library(clangFrontend
|
||||
ASTConsumers.cpp
|
||||
ASTMerge.cpp
|
||||
ASTUnit.cpp
|
||||
AnalysisConsumer.cpp
|
||||
Backend.cpp
|
||||
|
|
|
@ -367,6 +367,10 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts,
|
|||
Res.push_back("-load");
|
||||
Res.push_back(Opts.Plugins[i]);
|
||||
}
|
||||
for (unsigned i = 0, e = Opts.ASTMergeFiles.size(); i != e; ++i) {
|
||||
Res.push_back("-ast-merge");
|
||||
Res.push_back(Opts.ASTMergeFiles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts,
|
||||
|
@ -929,6 +933,7 @@ ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Diagnostic &Diags) {
|
|||
Opts.ShowTimers = Args.hasArg(OPT_ftime_report);
|
||||
Opts.ShowVersion = Args.hasArg(OPT_version);
|
||||
Opts.ViewClassInheritance = getLastArgValue(Args, OPT_cxx_inheritance_view);
|
||||
Opts.ASTMergeFiles = getAllArgValues(Args, OPT_ast_merge);
|
||||
|
||||
FrontendOptions::InputKind DashX = FrontendOptions::IK_None;
|
||||
if (const Arg *A = Args.getLastArg(OPT_x)) {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
// RUN: true
|
||||
int *x0;
|
||||
float **x1;
|
|
@ -0,0 +1,3 @@
|
|||
// RUN: true
|
||||
int *x0;
|
||||
double *x1;
|
|
@ -0,0 +1,5 @@
|
|||
// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/var1.c
|
||||
// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/var2.c
|
||||
// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: declared with incompatible types
|
|
@ -51,7 +51,7 @@ void LLVMErrorHandler(void *UserData, const std::string &Message) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
static FrontendAction *CreateFrontendAction(CompilerInstance &CI) {
|
||||
static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
|
||||
using namespace clang::frontend;
|
||||
|
||||
switch (CI.getFrontendOpts().ProgramAction) {
|
||||
|
@ -112,6 +112,21 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) {
|
|||
}
|
||||
}
|
||||
|
||||
static FrontendAction *CreateFrontendAction(CompilerInstance &CI) {
|
||||
// Create the underlying action.
|
||||
FrontendAction *Act = CreateFrontendBaseAction(CI);
|
||||
if (!Act)
|
||||
return 0;
|
||||
|
||||
// If there are any AST files to merge, create a frontend action
|
||||
// adaptor to perform the merge.
|
||||
if (!CI.getFrontendOpts().ASTMergeFiles.empty())
|
||||
Act = new ASTMergeAction(Act, &CI.getFrontendOpts().ASTMergeFiles[0],
|
||||
CI.getFrontendOpts().ASTMergeFiles.size());
|
||||
|
||||
return Act;
|
||||
}
|
||||
|
||||
// FIXME: Define the need for this testing away.
|
||||
static int cc1_test(Diagnostic &Diags,
|
||||
const char **ArgBegin, const char **ArgEnd) {
|
||||
|
|
Loading…
Reference in New Issue