Skip to content

Commit

Permalink
Allow passing additional parameters to wrapped tool (#14)
Browse files Browse the repository at this point in the history
Tools can now define an optional typedef by the name of `ArgTypes`
to signal MetaTool to expect additional args wrapped in such a
structure

Signed-off-by: Vaibhav Yenamandra <vyenamandra@bloomberg.net>
  • Loading branch information
vaibhav-y authored and ruoso committed Aug 8, 2018
1 parent 6872314 commit 04da5ce
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 10 deletions.
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,21 @@ add_library(
)
target_include_directories(
clangmetatool
PRIVATE ${CLANG_INCLUDE_DIRS}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${LLVM_INCLUDE_DIR}>
)

add_library( clangfixup INTERFACE IMPORTED )
set_property(
TARGET clangfixup
APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CLANG_INCLUDE_DIRS}
)

target_link_libraries(
clangmetatool
clangTooling
clangfixup
)

install(
Expand Down
40 changes: 39 additions & 1 deletion include/clangmetatool/meta_tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,22 @@
#include <llvm/ADT/StringRef.h>

namespace clangmetatool {
namespace {

// Code to check if there exists a member T::ArgTypes that was created as a
// typedef
template <typename... Ts>
using void_t = void;

template <typename T, typename = void>
struct has_typedef_ArgTypes : std::false_type {
};

template <typename T>
struct has_typedef_ArgTypes<T, void_t<typename T::ArgTypes>> : std::true_type {
};

}
/**
* MetaTool is a template that reduces the amount of boilerplate
* required to write a clang tool. The WrappedTool is a class that
Expand All @@ -35,11 +50,33 @@ namespace clangmetatool {
*/
template <class WrappedTool>
class MetaTool : public clang::ASTFrontendAction {
private:
static constexpr bool providesArgTypes = has_typedef_ArgTypes<WrappedTool>::value;
struct NoArgs { typedef void* ArgTypes; };
public:
typedef typename std::conditional_t<providesArgTypes, WrappedTool, NoArgs>::ArgTypes ArgTypes;
private:
std::map<std::string, clang::tooling::Replacements> &replacementsMap;
clang::ast_matchers::MatchFinder f;
WrappedTool *tool;

ArgTypes& args;


template <class A>
WrappedTool* create_tool(clang::CompilerInstance &ci, A args) {
return new WrappedTool(&ci, &f, args);
}
WrappedTool* create_tool(clang::CompilerInstance &ci, typename NoArgs::ArgTypes& args) {
return new WrappedTool(&ci, &f);
}

public:
MetaTool
(std::map<std::string, clang::tooling::Replacements> &replacementsMap,
ArgTypes& args)
:replacementsMap(replacementsMap), tool(NULL), args(args) { }

MetaTool
(std::map<std::string, clang::tooling::Replacements> &replacementsMap)
:replacementsMap(replacementsMap), tool(NULL) { }
Expand All @@ -49,13 +86,14 @@ namespace clangmetatool {
delete tool;
}


virtual bool BeginSourceFileAction(clang::CompilerInstance &ci) override {
// we don't expect to ever have the metatool be invoked more
// than once, it would eventually result in us holding
// references to unused compiler instance objects, and
// eventually segfaulting, so assert here.
assert(tool == NULL);
tool = new WrappedTool(&ci, &f);
tool = create_tool(ci, args);
return true;
}

Expand Down
15 changes: 11 additions & 4 deletions include/clangmetatool/meta_tool_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <clang/Tooling/Tooling.h>

namespace clangmetatool {

/**
* MetaToolFactory wraps around FrontendAction class that takes a
* replacementsMap as argument to the construtor. You can use it in
Expand All @@ -20,6 +19,8 @@ namespace clangmetatool {
class MetaToolFactory
: public clang::tooling::FrontendActionFactory {
private:
// T *must* provide this
typename T::ArgTypes args;

/**
* List of replacements to be used in the run.
Expand All @@ -29,18 +30,24 @@ namespace clangmetatool {

/**
* Metatool factory takes a reference to the replacements map that
* will be used for this run.
* will be used for this run in, along with any additional arguments that
* need to be passed on to the Tool
*/
MetaToolFactory
(std::map<std::string, clang::tooling::Replacements> &replacements,
typename T::ArgTypes& args)
:replacements(replacements), args(args) { }

MetaToolFactory
(std::map<std::string, clang::tooling::Replacements> &replacements)
:replacements(replacements) { }

/**
* This will create the object of your tool giving the
* replacemnets map as an argument.
*/
virtual clang::FrontendAction* create() {
return new T(replacements);
return new T(replacements, args);
}
};

Expand Down
19 changes: 17 additions & 2 deletions skeleton/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <iosfwd>
#include <map>
#include <tuple>

#include <clang/Frontend/FrontendAction.h>
#include <clang/Tooling/Core/Replacement.h>
Expand All @@ -14,7 +15,20 @@

class MyTool {
public:
MyTool(clang::CompilerInstance* ci, clang::ast_matchers::MatchFinder *f) {
/**
* This typedef is optional, causing two possible constructor signatures:
* For classes the define it:
* MyTool( clang::CompilerInstance*,
* clang::ast_matchers::MatchFinder*,
* ArgTypes )
* Or, for classes that choose not to define ArgTypes:
* MyTool( clang::CompilerInstance*,
* clang::ast_matchers::MatchFinder* )
*/
typedef std::tuple<> ArgTypes;
MyTool(clang::CompilerInstance* ci,
clang::ast_matchers::MatchFinder *f,
ArgTypes arguments) {
}
void postProcessing
(std::map<std::string, clang::tooling::Replacements> &replacementsMap) {
Expand All @@ -33,8 +47,9 @@ int main(int argc, const char* argv[]) {
clang::tooling::RefactoringTool tool(optionsParser.getCompilations(),
optionsParser.getSourcePathList());

MyTool::ArgTypes toolArgs;
clangmetatool::MetaToolFactory< clangmetatool::MetaTool<MyTool> >
raf(tool.getReplacements());
raf(tool.getReplacements(), toolArgs);

int r = tool.runAndSave(&raf);

Expand Down
101 changes: 101 additions & 0 deletions t/014-meta-tool-arbitrary-args.t.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "clangmetatool-testconfig.h"

#include <gtest/gtest.h>

#include <clangmetatool/meta_tool_factory.h>
#include <clangmetatool/meta_tool.h>

#include <clang/Frontend/FrontendAction.h>
#include <clang/Tooling/Core/Replacement.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
#include <clang/Tooling/Refactoring.h>
#include <llvm/Support/CommandLine.h>

#include <tuple>

bool constructor_called;
bool postprocessing_called;

class MyTool {
public:
typedef std::tuple<char, int, std::string> ArgTypes;

private:
clang::CompilerInstance* ci;
clang::ast_matchers::MatchFinder *f;
ArgTypes args;

public:
MyTool(clang::CompilerInstance* ci,
clang::ast_matchers::MatchFinder *f,
ArgTypes& args)
:ci(ci), f(f), args(args) {
constructor_called = true;
}
void postProcessing
(std::map<std::string, clang::tooling::Replacements> &replacementsMap) {
ASSERT_NE((void*)NULL, ci);
ASSERT_NE((void*)NULL, f);

EXPECT_EQ('$', std::get<0>(args));
EXPECT_EQ(42, std::get<1>(args));
EXPECT_EQ(std::string("the answer"), std::get<2>(args));

postprocessing_called = true;
}
};

// A struct to pass as an argument to MyTool
struct AStruct {
int dummyField;
AStruct() : dummyField(0) {};
bool operator==(const AStruct& other) const {
return dummyField == other.dummyField;
}
};

TEST(use_meta_tool, factory) {
llvm::cl::OptionCategory MyToolCategory("my-tool options");

int argc = 4;
const char* argv[] = { "foo", "/dev/null", "--", "-xc++" };

clang::tooling::CommonOptionsParser
optionsParser
( argc, argv,
MyToolCategory );
clang::tooling::RefactoringTool tool
( optionsParser.getCompilations(),
optionsParser.getSourcePathList());

constructor_called = false;
postprocessing_called = false;

MyTool::ArgTypes toolArgs('$', 42, "the answer");
clangmetatool::MetaToolFactory<
clangmetatool::MetaTool< MyTool > > raf(tool.getReplacements(), toolArgs);

int r = tool.runAndSave(&raf);
ASSERT_EQ(0, r);
ASSERT_EQ(true, constructor_called);
ASSERT_EQ(true, postprocessing_called);

}


// ----------------------------------------------------------------------------
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
9 changes: 7 additions & 2 deletions t/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ configure_file(
include/clangmetatool-testconfig.h
)

add_library( gtestfixup INTERFACE IMPORTED )
set_property( TARGET gtestfixup
APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIRS} )

foreach(
TEST
001-meta-tool
Expand All @@ -19,16 +23,15 @@ foreach(
009-definitions-one-func-in-namespace
010-definitions-two-class-defs-with-funcs
011-definitions-templates
014-meta-tool-arbitrary-args
)

add_executable(${TEST}.t ${TEST}.t.cpp)
target_include_directories(
${TEST}.t
PRIVATE
${GTEST_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/t/include
${CMAKE_SOURCE_DIR}/t/include
${CLANG_INCLUDE_DIRS}
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${LLVM_INCLUDE_DIR}>
)
Expand All @@ -37,6 +40,8 @@ foreach(
${TEST}.t
clangmetatool
clangTooling
clangfixup
gtestfixup
${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
Expand Down

0 comments on commit 04da5ce

Please sign in to comment.