// 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">;
class ASTUnit;
class ASTConsumer;
class CompilerInstance;
+class ASTMergeAction;
/// FrontendAction - Abstract base class for actions which can be performed by
/// the frontend.
std::string CurrentFile;
llvm::OwningPtr<ASTUnit> CurrentASTUnit;
CompilerInstance *Instance;
+ friend class ASTMergeAction;
protected:
/// @name Implementation Action Interface
return *CurrentASTUnit;
}
+ ASTUnit *takeCurrentASTUnit() {
+ return CurrentASTUnit.take();
+ }
+
void setCurrentFile(llvm::StringRef Value, ASTUnit *AST = 0);
/// @}
#define LLVM_CLANG_FRONTEND_FRONTENDACTIONS_H
#include "clang/Frontend/FrontendAction.h"
+#include <string>
+#include <vector>
namespace clang {
class FixItRewriter;
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
//===----------------------------------------------------------------------===//
/// 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;
explicit ASTNodeImporter(ASTImporter &Importer) : Importer(Importer) { }
using TypeVisitor<ASTNodeImporter, QualType>::Visit;
+ using DeclVisitor<ASTNodeImporter, Decl *>::Visit;
// Importing types
QualType VisitBuiltinType(BuiltinType *T);
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)
// 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))
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
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())
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();
--- /dev/null
+//===-- 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();
+}
add_clang_library(clangFrontend
ASTConsumers.cpp
+ ASTMerge.cpp
ASTUnit.cpp
AnalysisConsumer.cpp
Backend.cpp
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,
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)) {
--- /dev/null
+// RUN: true
+int *x0;
+float **x1;
--- /dev/null
+// RUN: true
+int *x0;
+double *x1;
--- /dev/null
+// 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
exit(1);
}
-static FrontendAction *CreateFrontendAction(CompilerInstance &CI) {
+static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
using namespace clang::frontend;
switch (CI.getFrontendOpts().ProgramAction) {
}
}
+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) {