summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2016-10-19 15:54:16 +0200
committerRichard Braun <rbraun@sceen.net>2016-10-19 15:54:16 +0200
commit75059a9e7a8e5c089a399239521ac91d443fc919 (patch)
tree891dbd24bdbc16f0ebaef9702c7f0fd8407c0897
Initial commit
-rw-r--r--.gitignore3
-rw-r--r--AddActionCall.cpp75
-rw-r--r--AddActionCall.h17
-rw-r--r--ConnectCall.cpp99
-rw-r--r--ConnectCall.h17
-rw-r--r--Context.cpp84
-rw-r--r--Context.h25
-rw-r--r--DisconnectCall.cpp94
-rw-r--r--DisconnectCall.h17
-rw-r--r--Makefile44
-rw-r--r--SingleShotCall.cpp75
-rw-r--r--SingleShotCall.h17
-rw-r--r--SlotCall.cpp216
-rw-r--r--SlotCall.h48
-rw-r--r--clang_headers.h10
-rw-r--r--main.cpp61
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");