diff options
author | Richard Braun <rbraun@sceen.net> | 2016-10-19 15:54:16 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2016-10-19 15:54:16 +0200 |
commit | 75059a9e7a8e5c089a399239521ac91d443fc919 (patch) | |
tree | 891dbd24bdbc16f0ebaef9702c7f0fd8407c0897 |
Initial commit
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | AddActionCall.cpp | 75 | ||||
-rw-r--r-- | AddActionCall.h | 17 | ||||
-rw-r--r-- | ConnectCall.cpp | 99 | ||||
-rw-r--r-- | ConnectCall.h | 17 | ||||
-rw-r--r-- | Context.cpp | 84 | ||||
-rw-r--r-- | Context.h | 25 | ||||
-rw-r--r-- | DisconnectCall.cpp | 94 | ||||
-rw-r--r-- | DisconnectCall.h | 17 | ||||
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | SingleShotCall.cpp | 75 | ||||
-rw-r--r-- | SingleShotCall.h | 17 | ||||
-rw-r--r-- | SlotCall.cpp | 216 | ||||
-rw-r--r-- | SlotCall.h | 48 | ||||
-rw-r--r-- | clang_headers.h | 10 | ||||
-rw-r--r-- | main.cpp | 61 |
16 files changed, 902 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab0ba3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +cscope.out +*.o +*.so diff --git a/AddActionCall.cpp b/AddActionCall.cpp new file mode 100644 index 0000000..ae2a404 --- /dev/null +++ b/AddActionCall.cpp @@ -0,0 +1,75 @@ +#include <string> + +#include "clang_headers.h" + +#include "AddActionCall.h" +#include "Context.h" +#include "SlotCall.h" + +using namespace std; +using namespace clang; + +AddActionCall::AddActionCall(Context &context, const CallExpr *expr) : + SlotCall(context) +{ + FunctionDecl::param_const_iterator it; + const FunctionDecl *decl; + const CXXRecordDecl *parent; + std::string slot, type; + unsigned slot_index; + + decl = expr->getDirectCallee(); + + if (decl == nullptr) { + return; + } + + if (decl->getNameAsString().compare("addAction") != 0) { + return; + } + + parent = dyn_cast_or_null<CXXRecordDecl>(decl->getParent()); + + if (parent == nullptr) { + return; + } + + if (parent->getNameAsString().compare("QMenu") != 0) { + return; + } + + for (it = decl->param_begin(); it != decl->param_end(); it++) { + if ((*it)->getType().getAsString().compare("const char *") == 0) { + break; + } + } + + if (it == decl->param_end()) { + _context.note(expr->getLocStart(), "no string, skipping"); + return; + } + + if (decl->getNumParams() == 4) { + slot = getSlot(expr->getArg(1), expr->getArg(2)); + slot_index = 2; + } else if (decl->getNumParams() == 5) { + slot = getSlot(expr->getArg(2), expr->getArg(3)); + slot_index = 3; + } else { + return; + } + + if (slot.empty()) { + return; + } + + replace(expr->getArg(slot_index), slot); + + reportMatch(); +} + +bool AddActionCall::processCallExpr(Context &context, const clang::CallExpr *expr) +{ + AddActionCall call(context, expr); + return call.matches(); +} diff --git a/AddActionCall.h b/AddActionCall.h new file mode 100644 index 0000000..29e587c --- /dev/null +++ b/AddActionCall.h @@ -0,0 +1,17 @@ +#ifndef _ADDACTIONCALL_H +#define _ADDACTIONCALL_H + +#include "Context.h" +#include "SlotCall.h" + +class AddActionCall : public SlotCall { +public: + virtual ~AddActionCall() {} + + static bool processCallExpr(Context &context, const clang::CallExpr *expr); + +private: + AddActionCall(Context &context, const clang::CallExpr *expr); +}; + +#endif // _ADDACTIONCALL_H diff --git a/ConnectCall.cpp b/ConnectCall.cpp new file mode 100644 index 0000000..8c9b3b0 --- /dev/null +++ b/ConnectCall.cpp @@ -0,0 +1,99 @@ +#include <string> + +#include "clang_headers.h" + +#include "ConnectCall.h" +#include "Context.h" +#include "SlotCall.h" + +using namespace std; +using namespace clang; + +ConnectCall::ConnectCall(Context &context, const CallExpr *expr) : + SlotCall(context) +{ + FunctionDecl::param_const_iterator it; + const FunctionDecl *decl; + const CXXRecordDecl *parent; + std::string signal, slot; + unsigned slot_index; + + decl = expr->getDirectCallee(); + + if (decl == nullptr) { + return; + } + + if (decl->getNameAsString().compare("connect") != 0) { + return; + } + + parent = dyn_cast_or_null<CXXRecordDecl>(decl->getParent()); + + if (parent == nullptr) { + return; + } + + if (parent->getNameAsString().compare("QObject") != 0) { + return; + } + + for (it = decl->param_begin(); it != decl->param_end(); it++) { + if ((*it)->getType().getAsString().compare("const char *") == 0) { + break; + } + } + + if (it == decl->param_end()) { + _context.note(expr->getLocStart(), "no string, skipping"); + return; + } + + if (expr->getNumArgs() < 2) { + return; + } + + signal = getSignal(expr->getArg(0), expr->getArg(1)); + + if (signal.empty()) { + return; + } + + if (decl->getNumParams() == 4) { + const CXXMemberCallExpr *cxx_call; + + cxx_call = dyn_cast_or_null<CXXMemberCallExpr>(expr); + + if (cxx_call == nullptr) { + _context.note(expr->getLocStart(), "unable to convert into C++ member call"); + return; + } + + slot = getSlot(cxx_call->getImplicitObjectArgument(), expr->getArg(2)); + slot_index = 2; + + if (!slot.empty()) { + slot = string("this, ") + slot; + } + } else if (decl->getNumParams() == 5) { + slot = getSlot(expr->getArg(2), expr->getArg(3)); + slot_index = 3; + } else { + return; + } + + if (slot.empty()) { + return; + } + + replace(expr->getArg(1), signal); + replace(expr->getArg(slot_index), slot); + + reportMatch(); +} + +bool ConnectCall::processCallExpr(Context &context, const clang::CallExpr *expr) +{ + ConnectCall call(context, expr); + return call.matches(); +} diff --git a/ConnectCall.h b/ConnectCall.h new file mode 100644 index 0000000..20b859e --- /dev/null +++ b/ConnectCall.h @@ -0,0 +1,17 @@ +#ifndef _CONNECTCALL_H +#define _CONNECTCALL_H + +#include "Context.h" +#include "SlotCall.h" + +class ConnectCall : public SlotCall { +public: + virtual ~ConnectCall() {} + + static bool processCallExpr(Context &context, const clang::CallExpr *expr); + +private: + ConnectCall(Context &context, const clang::CallExpr *expr); +}; + +#endif // _CONNECTCALL_H diff --git a/Context.cpp b/Context.cpp new file mode 100644 index 0000000..e6a541d --- /dev/null +++ b/Context.cpp @@ -0,0 +1,84 @@ +#include <string> + +#include "clang_headers.h" + +#include "ConnectCall.h" +#include "Context.h" +#include "SlotCall.h" + +using namespace std; +using namespace llvm; +using namespace clang; + +void +Context::note(const string &s) +{ + unsigned id = _ci.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Note, + "qtreslot: %0"); + DiagnosticBuilder builder = _ci.getDiagnostics().Report(id); + builder << s; + builder.setForceEmit(); +} + +void +Context::note(SourceLocation loc, const string &s) +{ + unsigned id = _ci.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Note, + "qtreslot: %0"); + DiagnosticBuilder builder = _ci.getDiagnostics().Report(loc, id); + builder << s; + builder.setForceEmit(); +} + +void +Context::warn(const string &s) +{ + unsigned id = _ci.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Warning, + "qtreslot: %0"); + DiagnosticBuilder builder = _ci.getDiagnostics().Report(id); + builder << s; + builder.setForceEmit(); +} + +void +Context::warn(SourceLocation loc, const string &s) +{ + unsigned id = _ci.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Warning, + "qtreslot: %0"); + DiagnosticBuilder builder = _ci.getDiagnostics().Report(loc, id); + builder << s; + builder.setForceEmit(); +} + +void +Context::processBuffer(const FileID &id, const RewriteBuffer &buffer) +{ + std::error_code error; + + // TODO Avoid writing if output already exists and is up to date. + + const FileEntry *entry = getSourceMgr().getFileEntryForID(id); + string path((_ci.getFileManager().getCanonicalName(entry->getDir()).str() + + "/" + entry->getName() + ".qtreslot")); + note(string("writing ") + path); + raw_fd_ostream file(StringRef(path), error, llvm::sys::fs::F_None); + + if (error) { + note(string("unable to open ") + path); + return; + } + + buffer.write(file); + file.close(); + + if (error) { + note(string("error when writing ") + path); + } +} + +Context::~Context() +{ + for (buffer_iterator it = buffer_begin(); it != buffer_end(); it++) { + processBuffer(it->first, it->second); + } +} diff --git a/Context.h b/Context.h new file mode 100644 index 0000000..cd7f6b9 --- /dev/null +++ b/Context.h @@ -0,0 +1,25 @@ +#ifndef _CONTEXT_H +#define _CONTEXT_H + +#include "clang_headers.h" + +class Context : public clang::Rewriter { +public: + const clang::CompilerInstance &_ci; + + Context(const clang::CompilerInstance &ci) : + clang::Rewriter(ci.getSourceManager(), ci.getLangOpts()), + _ci(ci) {} + + virtual ~Context(); + + void note(const std::string &s); + void note(clang::SourceLocation loc, const std::string &s); + void warn(const std::string &s); + void warn(clang::SourceLocation loc, const std::string &s); + +private: + void processBuffer(const clang::FileID &id, const clang::RewriteBuffer &buffer); +}; + +#endif // _CONTEXT_H diff --git a/DisconnectCall.cpp b/DisconnectCall.cpp new file mode 100644 index 0000000..694baf1 --- /dev/null +++ b/DisconnectCall.cpp @@ -0,0 +1,94 @@ +#include <string> + +#include "clang_headers.h" + +#include "Context.h" +#include "DisconnectCall.h" +#include "SlotCall.h" + +using namespace std; +using namespace clang; + +DisconnectCall::DisconnectCall(Context &context, const CallExpr *expr) : + SlotCall(context) +{ + FunctionDecl::param_const_iterator it; + const FunctionDecl *decl; + const CXXRecordDecl *parent; + std::string signal, slot; + unsigned signal_index, slot_index; + + decl = expr->getDirectCallee(); + + if (decl == nullptr) { + return; + } + + if (decl->getNameAsString().compare("disconnect") != 0) { + return; + } + + parent = dyn_cast_or_null<CXXRecordDecl>(decl->getParent()); + + if (parent == nullptr) { + return; + } + + if (parent->getNameAsString().compare("QObject") != 0) { + return; + } + + for (it = decl->param_begin(); it != decl->param_end(); it++) { + if ((*it)->getType().getAsString().compare("const char *") == 0) { + break; + } + } + + if (it == decl->param_end()) { + _context.note(expr->getLocStart(), "no string, skipping"); + return; + } + + if (decl->getNumParams() == 3) { + const CXXMemberCallExpr *cxx_call; + + cxx_call = dyn_cast_or_null<CXXMemberCallExpr>(expr); + + if (cxx_call == nullptr) { + _context.note(expr->getLocStart(), "unable to convert into C++ member call"); + return; + } + + signal = getSignal(cxx_call->getImplicitObjectArgument(), expr->getArg(0)); + signal_index = 0; + + if (!signal.empty()) { + signal = string("this, ") + signal; + } + + slot = getSlot(expr->getArg(1), expr->getArg(2)); + slot_index = 2; + } else if (decl->getNumParams() == 4) { + signal = getSignal(expr->getArg(0), expr->getArg(1)); + signal_index = 1; + slot = getSlot(expr->getArg(2), expr->getArg(3)); + slot_index = 3; + } else { + return; + } + + if (signal.empty() || slot.empty()) { + return; + } + + replace(expr->getArg(signal_index), signal); + replace(expr->getArg(slot_index), slot); + + reportMatch(); +} + +bool DisconnectCall::processCallExpr(Context &context, const clang::CallExpr *expr) +{ + DisconnectCall call(context, expr); + return call.matches(); +} diff --git a/DisconnectCall.h b/DisconnectCall.h new file mode 100644 index 0000000..a6cc3ef --- /dev/null +++ b/DisconnectCall.h @@ -0,0 +1,17 @@ +#ifndef _DISCONNECTCALL_H +#define _DISCONNECTCALL_H + +#include "Context.h" +#include "SlotCall.h" + +class DisconnectCall : public SlotCall { +public: + virtual ~DisconnectCall() {} + + static bool processCallExpr(Context &context, const clang::CallExpr *expr); + +private: + DisconnectCall(Context &context, const clang::CallExpr *expr); +}; + +#endif // _DISCONNECTCALL_H diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..674dbbd --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +PLUGIN = qtreslot + +HEADERS = \ + clang_headers.h \ + AddActionCall.h \ + ConnectCall.h \ + Context.h \ + DisconnectCall.h \ + SingleShotCall.h \ + SlotCall.h + +SOURCES = \ + main.cpp \ + AddActionCall.cpp \ + ConnectCall.cpp \ + Context.cpp \ + DisconnectCall.cpp \ + SingleShotCall.cpp \ + SlotCall.cpp + +OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES)) + +LLVM_COMPONENTS = all +LLVM_CXXFLAGS = $(shell llvm-config --cxxflags) +LLVM_LDFLAGS = $(shell llvm-config --ldflags) + +CXXFLAGS = -std=c++11 -fPIC -Wall +CXXFLAGS += -fvisibility-inlines-hidden -fno-exceptions -fno-rtti +CXXFLAGS += $(LLVM_CXXFLAGS) + +LDFLAGS = -shared -Wl,-undefined,dynamic_lookup $(LLVM_LDFLAGS) + +all: $(PLUGIN).so + +$(PLUGIN).so: $(OBJECTS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ + +%.o: %.cpp $(HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +clean: + rm -f $(PLUGIN).so $(OBJECTS) + +.PHONY: all clean diff --git a/SingleShotCall.cpp b/SingleShotCall.cpp new file mode 100644 index 0000000..3e13954 --- /dev/null +++ b/SingleShotCall.cpp @@ -0,0 +1,75 @@ +#include <string> + +#include "clang_headers.h" + +#include "Context.h" +#include "SingleShotCall.h" +#include "SlotCall.h" + +using namespace std; +using namespace clang; + +SingleShotCall::SingleShotCall(Context &context, const CallExpr *expr) : + SlotCall(context) +{ + FunctionDecl::param_const_iterator it; + const FunctionDecl *decl; + const CXXRecordDecl *parent; + std::string slot, type; + unsigned slot_index; + + decl = expr->getDirectCallee(); + + if (decl == nullptr) { + return; + } + + if (decl->getNameAsString().compare("singleShot") != 0) { + return; + } + + parent = dyn_cast_or_null<CXXRecordDecl>(decl->getParent()); + + if (parent == nullptr) { + return; + } + + if (parent->getNameAsString().compare("QTimer") != 0) { + return; + } + + for (it = decl->param_begin(); it != decl->param_end(); it++) { + if ((*it)->getType().getAsString().compare("const char *") == 0) { + break; + } + } + + if (it == decl->param_end()) { + _context.note(expr->getLocStart(), "no string, skipping"); + return; + } + + if (decl->getNumParams() == 3) { + slot = getSlot(expr->getArg(1), expr->getArg(2)); + slot_index = 2; + } else if (decl->getNumParams() == 4) { + slot = getSlot(expr->getArg(2), expr->getArg(3)); + slot_index = 3; + } else { + return; + } + + if (slot.empty()) { + return; + } + + replace(expr->getArg(slot_index), slot); + + reportMatch(); +} + +bool SingleShotCall::processCallExpr(Context &context, const clang::CallExpr *expr) +{ + SingleShotCall call(context, expr); + return call.matches(); +} diff --git a/SingleShotCall.h b/SingleShotCall.h new file mode 100644 index 0000000..0ded6ba --- /dev/null +++ b/SingleShotCall.h @@ -0,0 +1,17 @@ +#ifndef _SINGLESHOTCALL_H +#define _SINGLESHOTCALL_H + +#include "Context.h" +#include "SlotCall.h" + +class SingleShotCall : public SlotCall { +public: + virtual ~SingleShotCall() {} + + static bool processCallExpr(Context &context, const clang::CallExpr *expr); + +private: + SingleShotCall(Context &context, const clang::CallExpr *expr); +}; + +#endif // _SINGLESHOTCALL_H diff --git a/SlotCall.cpp b/SlotCall.cpp new file mode 100644 index 0000000..9296d06 --- /dev/null +++ b/SlotCall.cpp @@ -0,0 +1,216 @@ +// TODO Determine if explicit namespace is needed when getting signals and slots + +#include <stdlib.h> + +#include <string> + +#include "clang_headers.h" + +#include "AddActionCall.h" +#include "ConnectCall.h" +#include "DisconnectCall.h" +#include "SingleShotCall.h" +#include "SlotCall.h" + +using namespace std; +using namespace clang; + +const list<SlotCall::processor> SlotCall::processors = list<SlotCall::processor>({ + AddActionCall::processCallExpr, + ConnectCall::processCallExpr, + DisconnectCall::processCallExpr, + SingleShotCall::processCallExpr, +}); + +void +SlotCall::processCallExpr(Context &context, const CallExpr *expr) +{ + bool matches; + + for (processor p : processors) { + matches = p(context, expr); + + if (matches) { + return; + } + } +} + +bool +SlotCall::isnotspace(int c) +{ + return !isspace(c); +} + +void +SlotCall::ltrim(string &s) +{ + s.erase(s.begin(), find_if(s.begin(), s.end(), isnotspace)); +} + +void +SlotCall::rtrim(string &s) +{ + s.erase(find_if(s.rbegin(), s.rend(), isnotspace).base(), s.end()); +} + +void +SlotCall::trim(string &s) +{ + ltrim(s); + rtrim(s); +} + +string +SlotCall::getSource(const Expr *expr) +{ + return Lexer::getSourceText(CharSourceRange::getTokenRange(expr->getSourceRange()), + _context._ci.getSourceManager(), + _context._ci.getLangOpts()).str(); +} + +string +SlotCall::getArgument(const string &s) +{ + size_t start, end; + string arg; + + start = s.find("("); + + if (start == string::npos) { + return ""; + } + + start++; + end = s.find("(", start); + + if (end == string::npos) { + return ""; + } + + arg = s.substr(start, end - start); + trim(arg); + return arg; +} + +string +SlotCall::getRawType(const Expr *expr) +{ + const CXXRecordDecl *decl; + + decl = expr->IgnoreImpCasts()->getType()->getPointeeCXXRecordDecl(); + + if (decl == nullptr) { + return ""; + } + + return decl->getName(); +} + +bool SlotCall::checkSuppressionFromQtObjectHeader(const Expr *expr) +{ + SourceLocation fileLoc = _context.getSourceMgr().getFileLoc(expr->getLocStart()); + string s = _context.getSourceMgr().getFilename(fileLoc).str(); + + // Not as accurate as possible, but should do the job for everyone + return (s.find("/qobject.h") != string::npos); +} + +bool +SlotCall::checkSuppression(const Expr *expr) +{ + if (checkSuppressionFromQtObjectHeader(expr)) { + return true; + } + + return false; +} + +string +SlotCall::getSignal(const Expr *sender, const Expr *signal) +{ + string source, type, arg, result; + size_t pos; + + type = getRawType(sender); + + if (type.empty()) { + _context.warn(sender->getLocStart(), "unable to identify type"); + goto out; + } + + source = getSource(signal); + pos = source.find("SIGNAL"); + + if (pos == string::npos) { + if (!checkSuppression(signal)) { + _context.warn(signal->getLocStart(), "no SIGNAL macro"); + } + + goto out; + } + + arg = getArgument(source.substr(pos)); + + if (arg.empty()) { + _context.warn(signal->getLocStart(), "malformed SIGNAL macro usage"); + goto out; + } + + result.append("&").append(type).append("::").append(arg); + trim(result); + +out: + return result; +} + +string +SlotCall::getSlot(const Expr *receiver, const Expr *slot) +{ + string source, type, arg, result; + size_t pos; + + type = getRawType(receiver); + + if (type.empty()) { + _context.warn(receiver->getLocStart(), "unable to identify type"); + goto out; + } + + source = getSource(slot); + pos = source.find("SLOT"); + + if (pos == string::npos) { + if (!checkSuppression(slot)) { + _context.warn(slot->getLocStart(), "no SLOT macro"); + } + + goto out; + } + + arg = getArgument(source.substr(pos)); + + if (arg.empty()) { + _context.warn(slot->getLocStart(), "malformed SLOT macro usage"); + goto out; + } + + result.append("&").append(type).append("::").append(arg); + trim(result); + +out: + return result; +} + +void +SlotCall::replace(const clang::Expr *expr, const string &s) +{ + pair<SourceLocation, SourceLocation> loc_pair = + _context.getSourceMgr().getExpansionRange(expr->getLocStart()); + SourceRange range(loc_pair.first, loc_pair.second); + bool error = _context.ReplaceText(range, s); + + if (error) { + _context.note(expr->getLocStart(), "unable to transform expression"); + } +} diff --git a/SlotCall.h b/SlotCall.h new file mode 100644 index 0000000..8c44ea6 --- /dev/null +++ b/SlotCall.h @@ -0,0 +1,48 @@ +#ifndef _SLOTCALL_H +#define _SLOTCALL_H + +#include "Context.h" + +class SlotCall { +public: + typedef std::function<bool(Context &, const clang::CallExpr *)> processor; + + virtual ~SlotCall() {} + + static void processCallExpr(Context &context, const clang::CallExpr *expr); + +protected: + Context &_context; + + SlotCall(Context &context) : _context(context), _match(false) {} + + void reportMatch(void) { + _match = true; + } + + bool matches() { + return _match; + } + + std::string getSource(const clang::Expr *expr); + std::string getSignal(const clang::Expr *sender, const clang::Expr *signal); + std::string getSlot(const clang::Expr *receiver, const clang::Expr *slot); + void replace(const clang::Expr *expr, const std::string &s); + +private: + bool _match; + + static const std::list<processor> processors; + + static bool isnotspace(int c); + static void ltrim(std::string &s); + static void rtrim(std::string &s); + static void trim(std::string &s); + + std::string getArgument(const std::string &s); + std::string getRawType(const clang::Expr *expr); + bool checkSuppressionFromQtObjectHeader(const clang::Expr *expr); + bool checkSuppression(const clang::Expr *expr); +}; + +#endif // _SLOTCALL_H diff --git a/clang_headers.h b/clang_headers.h new file mode 100644 index 0000000..af880fb --- /dev/null +++ b/clang_headers.h @@ -0,0 +1,10 @@ +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Rewrite/Core/RewriteBuffer.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Lexer.h" +#include "llvm/Support/Casting.h" diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..e77bd9e --- /dev/null +++ b/main.cpp @@ -0,0 +1,61 @@ +#include <string> + +#include "clang_headers.h" + +#include "Context.h" +#include "SlotCall.h" + +using namespace std; +using namespace clang; + +namespace { + +class FuncCallVisitor : public RecursiveASTVisitor<FuncCallVisitor> { +public: + explicit FuncCallVisitor(Context &context) : _context(context) {} + + bool VisitCallExpr(const CallExpr *expr) { + SlotCall::processCallExpr(_context, expr); + return true; + } + +private: + Context &_context; +}; + +class FuncCallConsumer : public ASTConsumer { +public: + explicit FuncCallConsumer(CompilerInstance &ci) : + _context(ci), + _visitor(FuncCallVisitor(_context)) {} + + virtual bool HandleTopLevelDecl(DeclGroupRef dg) override { + for (Decl *decl : dg) { + _visitor.TraverseDecl(decl); + } + + return true; + } + +private: + Context _context; + FuncCallVisitor _visitor; +}; + +class QtReslot : public PluginASTAction { +protected: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, + llvm::StringRef) override { + return llvm::make_unique<FuncCallConsumer>(ci); + } + + bool ParseArgs(const CompilerInstance&, + const std::vector<std::string>&) override { + return true; + } +}; + +} + +static FrontendPluginRegistry::Add<QtReslot> +X("rewrite_signals_and_slots", "rewrite source files with Qt5 signal and slot connections"); |