Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support undoing of #undef preprocessor tokens. #645

Merged
merged 4 commits into from
Jul 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions interpreter/cling/lib/Interpreter/DeclCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/Token.h"

using namespace clang;
Expand Down Expand Up @@ -58,6 +60,45 @@ namespace {
}

namespace cling {
///\brief Serves as DeclCollector's connector to the PPCallbacks interface.
///
class DeclCollector::PPAdapter : public clang::PPCallbacks {
cling::DeclCollector* m_Parent;

void MacroDirective(const clang::Token& MacroNameTok,
const clang::MacroDirective* MD) {
assert(m_Parent->m_CurTransaction && "Missing transction");
Transaction::MacroDirectiveInfo MDE(MacroNameTok.getIdentifierInfo(), MD);
m_Parent->m_CurTransaction->append(MDE);
}

public:
PPAdapter(cling::DeclCollector* P) : m_Parent(P) {}

/// \name PPCallbacks overrides
/// Macro support
void MacroDefined(const clang::Token& MacroNameTok,
const clang::MacroDirective* MD) final {
MacroDirective(MacroNameTok, MD);
}

/// \name PPCallbacks overrides
/// Macro support
void MacroUndefined(const clang::Token& MacroNameTok,
const clang::MacroDefinition& MD,
const clang::MacroDirective* Undef) final {
if (Undef)
MacroDirective(MacroNameTok, Undef);
}
};

void DeclCollector::Setup(IncrementalParser* IncrParser, ASTConsumer* Consumer,
clang::Preprocessor& PP) {
m_IncrParser = IncrParser;
m_Consumer = Consumer;
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(new PPAdapter(this)));
}

bool DeclCollector::comesFromASTReader(DeclGroupRef DGR) const {
assert(!DGR.isNull() && "DeclGroupRef is Null!");
assert(m_CurTransaction && "No current transaction when deserializing");
Expand Down Expand Up @@ -270,11 +311,4 @@ namespace cling {
m_Consumer->HandleCXXStaticMemberVarInstantiation(D);
}

void DeclCollector::MacroDefined(const clang::Token &MacroNameTok,
const clang::MacroDirective *MD) {
assert(m_CurTransaction && "Missing transction");
Transaction::MacroDirectiveInfo MDE(MacroNameTok.getIdentifierInfo(), MD);
m_CurTransaction->append(MDE);
}

} // namespace cling
66 changes: 16 additions & 50 deletions interpreter/cling/lib/Interpreter/DeclCollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#define CLING_DECL_COLLECTOR_H

#include "clang/AST/ASTConsumer.h"
#include "clang/Lex/PPCallbacks.h"

#include "ASTTransformer.h"

Expand All @@ -23,6 +22,7 @@ namespace clang {
class CodeGenerator;
class Decl;
class DeclGroupRef;
class Preprocessor;
class Token;
}

Expand All @@ -34,30 +34,16 @@ namespace cling {
class IncrementalParser;
class Transaction;

///\brief Serves as DeclCollector's connector to the PPCallbacks interface.
///
class DeclCollectorPPAdapter: public clang::PPCallbacks {
DeclCollector* m_parent;
public:
DeclCollectorPPAdapter(DeclCollector* parent):
m_parent(parent)
{}

/// \name PPCallbacks overrides
/// Macro support
void MacroDefined(const clang::Token &MacroNameTok,
const clang::MacroDirective *MD) override;
/// \}
};

///\brief Collects declarations and fills them in cling::Transaction.
///
/// cling::Transaction becomes is a main building block in the interpreter.
/// cling::DeclCollector is responsible for appending all the declarations
/// seen by clang.
///
class DeclCollector: public clang::ASTConsumer {
private:
class DeclCollector : public clang::ASTConsumer {
/// \brief PPCallbacks overrides/ Macro support
class PPAdapter;

///\brief Contains the transaction AST transformers.
///
std::vector<std::unique_ptr<ASTTransformer>> m_TransactionTransformers;
Expand Down Expand Up @@ -93,11 +79,6 @@ namespace cling {

virtual ~DeclCollector();

std::unique_ptr<DeclCollectorPPAdapter> MakePPAdapter() {
return std::unique_ptr<DeclCollectorPPAdapter>
(new DeclCollectorPPAdapter(this));
}

void SetTransformers(std::vector<std::unique_ptr<ASTTransformer>>&& allTT,
std::vector<std::unique_ptr<WrapperTransformer>>&& allWT){
m_TransactionTransformers.swap(allTT);
Expand All @@ -108,29 +89,21 @@ namespace cling {
WT->SetConsumer(this);
}

void setContext(IncrementalParser* IncrParser, ASTConsumer* Consumer) {
m_IncrParser = IncrParser;
m_Consumer = Consumer;
}

/// \name PPCallbacks overrides
/// Macro support
void MacroDefined(const clang::Token &MacroNameTok,
const clang::MacroDirective *MD);
/// \}
void Setup(IncrementalParser* IncrParser, ASTConsumer* Consumer,
clang::Preprocessor& PP);

/// \{
/// \name ASTConsumer overrides

virtual bool HandleTopLevelDecl(clang::DeclGroupRef DGR);
virtual void HandleInterestingDecl(clang::DeclGroupRef DGR);
virtual void HandleTagDeclDefinition(clang::TagDecl* TD);
virtual void HandleInvalidTagDeclDefinition(clang::TagDecl* TD);
virtual void HandleVTable(clang::CXXRecordDecl* RD);
virtual void CompleteTentativeDefinition(clang::VarDecl* VD);
virtual void HandleTranslationUnit(clang::ASTContext& Ctx);
virtual void HandleCXXImplicitFunctionInstantiation(clang::FunctionDecl *D);
virtual void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D);
bool HandleTopLevelDecl(clang::DeclGroupRef DGR) final;
void HandleInterestingDecl(clang::DeclGroupRef DGR) final;
void HandleTagDeclDefinition(clang::TagDecl* TD) final;
void HandleInvalidTagDeclDefinition(clang::TagDecl* TD) final;
void HandleVTable(clang::CXXRecordDecl* RD) final;
void CompleteTentativeDefinition(clang::VarDecl* VD) final;
void HandleTranslationUnit(clang::ASTContext& Ctx) final;
void HandleCXXImplicitFunctionInstantiation(clang::FunctionDecl *D) final;
void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D) final;
/// \}

/// \{
Expand All @@ -144,13 +117,6 @@ namespace cling {
// dyn_cast/isa support
static bool classof(const clang::ASTConsumer*) { return true; }
};

inline void
DeclCollectorPPAdapter::MacroDefined(const clang::Token &MacroNameTok,
const clang::MacroDirective *MD) {
m_parent->MacroDefined(MacroNameTok, MD);
}

} // namespace cling

#endif // CLING_DECL_COLLECTOR_H
8 changes: 3 additions & 5 deletions interpreter/cling/lib/Interpreter/IncrementalParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,18 @@ namespace cling {
cling::errs() << "No AST consumer available.\n";
return;
}
// Add the callback keeping track of the macro definitions.
m_CI->getPreprocessor().addPPCallbacks(m_Consumer->MakePPAdapter());

DiagnosticsEngine& Diag = m_CI->getDiagnostics();
if (m_CI->getFrontendOpts().ProgramAction != frontend::ParseSyntaxOnly) {
m_CodeGen.reset(CreateLLVMCodeGen(
Diag, makeModuleName(), m_CI->getHeaderSearchOpts(),
m_CI->getPreprocessorOpts(), m_CI->getCodeGenOpts(),
*m_Interpreter->getLLVMContext()));
m_Consumer->setContext(this, m_CodeGen.get());
} else {
m_Consumer->setContext(this, 0);
}

// Initialize the DeclCollector and add callbacks keeping track of macros.
m_Consumer->Setup(this, m_CodeGen.get(), m_CI->getPreprocessor());

m_DiagConsumer.reset(new FilteringDiagConsumer(Diag, false));

initializeVirtualFile();
Expand Down
20 changes: 16 additions & 4 deletions interpreter/cling/lib/Interpreter/Transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,23 @@ namespace cling {
assert(MDE.m_MD && "Appending null MacroDirective?!");
assert(getState() == kCollecting
&& "Cannot append declarations in current state.");

#ifndef NDEBUG
// Check for duplicates
for (size_t i = 0, e = m_MacroDirectiveInfoQueue.size(); i < e; ++i) {
MacroDirectiveInfo &oldMacroDirectiveInfo (m_MacroDirectiveInfoQueue[i]);
assert(oldMacroDirectiveInfo != MDE && "Duplicates?!");
if (size_t i = m_MacroDirectiveInfoQueue.size()) {
// Check for duplicates
do {
MacroDirectiveInfo &prevDir (m_MacroDirectiveInfoQueue[--i]);
if (prevDir == MDE) {
const UndefMacroDirective* A =
dyn_cast<UndefMacroDirective>(MDE.m_MD);
const UndefMacroDirective* B =
dyn_cast<UndefMacroDirective>(prevDir.m_MD);
// Allow undef to follow def and vice versa, but that is all.
assert((A ? B==nullptr : B!=nullptr) && "Duplicates");
// Has previously been checked prior to here, so were done.
break;
}
} while (i != 0);
}
#endif

Expand Down
54 changes: 54 additions & 0 deletions interpreter/cling/test/CodeUnloading/Macros.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling -I%S -Xclang -verify 2>&1 | FileCheck %s
// Tests undoing of macro definitions

// Invoke the printer to get it in the undo queue early
"TEST"
// CHECK: (const char [5]) "TEST"

// Make sure one Transactin can handle redefinitions
#include "Macros.h"
// expected-warning@Macros.h:3 {{'TEST' macro redefined}}
// expected-note@Macros.h:2 {{previous definition is here}}
// expected-warning@Macros.h:4 {{'TEST' macro redefined}}
// expected-note@Macros.h:3 {{previous definition is here}}
// expected-warning@Macros.h:5 {{'TEST' macro redefined}}
// expected-note@Macros.h:4 {{previous definition is here}}
// expected-warning@Macros.h:6 {{'TEST' macro redefined}}
// expected-note@Macros.h:5 {{previous definition is here}}

TEST
// CHECK: (const char [7]) "TEST 4"

.undo //print
.undo //include
.undo // FIXME: REMOVE once print unloading is merged

TEST // expected-error@2 {{use of undeclared identifier 'TEST'}}

#define TEST "DEFINED"
#undef TEST
.undo
TEST
// CHECK: (const char [8]) "DEFINED"
.undo // print
.undo // define
.undo // FIXME: REMOVE once print unloading is merged

TEST // expected-error@2 {{use of undeclared identifier 'TEST'}}

// Make sure one Transactin can handle undef, redef
#define TESTB
#include "Macros.h"
// expected-warning@Macros.h:19 {{'TEST' macro redefined}}
// expected-note@Macros.h:18 {{previous definition is here}}

TEST // CHECK: (const char [7]) "TEST G"
.q
20 changes: 20 additions & 0 deletions interpreter/cling/test/CodeUnloading/Macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef TESTB
#define TEST "TEST 0"
#define TEST "TEST 1"
#define TEST "TEST 2"
#define TEST "TEST 3"
#define TEST "TEST 4"
#else
#define TEST "TEST A"
#undef TEST
#define TEST "TEST B"
#undef TEST
#define TEST "TEST C"
#undef TEST
#define TEST "TEST D"
#undef TEST
#define TEST "TEST E"
#undef TEST
#define TEST "TEST F"
#define TEST "TEST G"
#endif