[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [kdev-clang/wip/maciej/tooling] /: Encapsulate field refactoring
From: Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5 () gmail ! com>
Date: 2015-08-01 0:49:42
Message-ID: E1ZLKze-0002E0-Ro () scm ! kde ! org
[Download RAW message or body]
Git commit 19977ae8c3c7aa5ca9bbf3539b86add4247d7fee by Maciej Poleski.
Committed on 01/08/2015 at 00:49.
Pushed by mpoleski into branch 'wip/maciej/tooling'.
Encapsulate field refactoring
M +3 -0 refactoring/CMakeLists.txt
A +87 -0 refactoring/encapsulatefielddialog.cpp [License: LGPL (v2+)]
C +17 -18 refactoring/encapsulatefielddialog.h [from: \
refactoring/tudecldispatcher.h - 060% similarity] A +160 -0 \
refactoring/encapsulatefielddialog.ui A +490 -0 \
refactoring/encapsulatefieldrefactoring.cpp [License: LGPL (v2+)] A +70 -0 \
refactoring/encapsulatefieldrefactoring.h [License: LGPL (v2+)] A +113 -0 \
refactoring/encapsulatefieldrefactoring_changepack.cpp [License: LGPL (v2+)] A \
+157 -0 refactoring/encapsulatefieldrefactoring_changepack.h [License: LGPL \
(v2+)] M +0 -8 refactoring/redeclarationchain.cpp
M +30 -19 refactoring/refactoringmanager.cpp
M +12 -1 refactoring/tudecldispatcher.cpp
M +12 -1 refactoring/tudecldispatcher.h
M +55 -1 refactoring/utils.cpp
M +6 -0 refactoring/utils.h
M +9 -0 tests/CMakeLists.txt
A +242 -0 tests/test_encapsulatefield.cpp [License: LGPL (v2+)]
C +14 -19 tests/test_encapsulatefield.h [from: refactoring/tudecldispatcher.cpp \
- 061% similarity]
http://commits.kde.org/kdev-clang/19977ae8c3c7aa5ca9bbf3539b86add4247d7fee
diff --git a/refactoring/CMakeLists.txt b/refactoring/CMakeLists.txt
index 13f20dd..6c96491 100644
--- a/refactoring/CMakeLists.txt
+++ b/refactoring/CMakeLists.txt
@@ -23,6 +23,9 @@ set(SRCS
changesignaturerefactoring.cpp
changesignaturerefactoringinfopack.cpp
changesignaturerefactoringchangepack.cpp
+ encapsulatefieldrefactoring.cpp
+ encapsulatefielddialog.cpp
+ encapsulatefieldrefactoring_changepack.cpp
)
add_library(kdevclangrefactor STATIC
diff --git a/refactoring/encapsulatefielddialog.cpp \
b/refactoring/encapsulatefielddialog.cpp new file mode 100644
index 0000000..71f8ce2
--- /dev/null
+++ b/refactoring/encapsulatefielddialog.cpp
@@ -0,0 +1,87 @@
+/*
+ This file is part of KDevelop
+
+ Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// Qt
+#include <QPushButton>
+
+#include "encapsulatefielddialog.h"
+#include "encapsulatefieldrefactoring_changepack.h"
+
+using namespace clang;
+
+Q_DECLARE_METATYPE(clang::AccessSpecifier);
+
+EncapsulateFieldDialog::EncapsulateFieldDialog(ChangePack *changePack)
+ : QDialog(nullptr)
+ , m_changePack(changePack)
+{
+ setupUi(this);
+ fieldLineEdit->setText(QString::fromStdString(m_changePack->fieldDescription()));
+ setterGroupBox->setEnabled(m_changePack->createSetter());
+
+ getterDefinitionPlainTextEdit->setPlainText(
+ QString::fromStdString(m_changePack->accessorCode()));
+ setterDefinitionPlainTextEdit->setPlainText(
+ QString::fromStdString(m_changePack->mutatorCode()));
+
+ Q_ASSERT(m_changePack->getterAccess() != AS_none);
+ Q_ASSERT(m_changePack->setterAccess() != AS_none);
+
+ getterVisibilityComboBox->addItem(QStringLiteral("public"), \
AccessSpecifier::AS_public); + if (m_changePack->getterAccess() != \
AccessSpecifier::AS_public) { + \
getterVisibilityComboBox->addItem(QStringLiteral("protected"), + \
AccessSpecifier::AS_protected); + }
+ if (m_changePack->getterAccess() == AccessSpecifier::AS_private) {
+ getterVisibilityComboBox->addItem(QStringLiteral("private"), \
AccessSpecifier::AS_private); + }
+ getterVisibilityComboBox->setCurrentIndex(getterVisibilityComboBox->count() - \
1); + setterVisibilityComboBox->addItem(QStringLiteral("public"), \
AccessSpecifier::AS_public); + if (m_changePack->setterAccess() != \
AccessSpecifier::AS_public) { + \
setterVisibilityComboBox->addItem(QStringLiteral("protected"), + \
AccessSpecifier::AS_protected); + }
+ if (m_changePack->setterAccess() == AccessSpecifier::AS_private) {
+ setterVisibilityComboBox->addItem(QStringLiteral("private"), \
AccessSpecifier::AS_private); + }
+ setterVisibilityComboBox->setCurrentIndex(setterVisibilityComboBox->count() - \
1); +
+ // set above default
+
+ connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, \
[this]() + {
+ m_changePack->setAccessorCode(getterDefinitionPlainTextEdit->toPlainText().toStdString());
+ m_changePack->setGetterName(
+ functionName(m_changePack->accessorCode(), m_changePack->getterName()));
+ m_changePack->setGetterAccess(
+ getterVisibilityComboBox->currentData().value<AccessSpecifier>());
+ m_changePack->setCreateSetter(setterGroupBox->isChecked());
+ if (m_changePack->createSetter()) {
+ m_changePack->setMutatorCode(
+ setterDefinitionPlainTextEdit->toPlainText().toStdString());
+ m_changePack->setSetterName(
+ functionName(m_changePack->mutatorCode(), \
m_changePack->setterName())); + m_changePack->setSetterAccess(
+ setterVisibilityComboBox->currentData().value<AccessSpecifier>());
+ }
+ });
+}
+
diff --git a/refactoring/tudecldispatcher.h b/refactoring/encapsulatefielddialog.h
similarity index 60%
copy from refactoring/tudecldispatcher.h
copy to refactoring/encapsulatefielddialog.h
index f9e0158..1f30fe9 100644
--- a/refactoring/tudecldispatcher.h
+++ b/refactoring/encapsulatefielddialog.h
@@ -19,33 +19,32 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef KDEV_CLANG_TUDECLDISPATCHER_H
-#define KDEV_CLANG_TUDECLDISPATCHER_H
+#ifndef KDEV_CLANG_ENCAPSULATEFIELDDIALOG_H
+#define KDEV_CLANG_ENCAPSULATEFIELDDIALOG_H
-// C++
-#include <memory>
-#include <unordered_map>
+#include <QDialog>
-// Clang
-#include <clang/AST/DeclBase.h>
+#include "encapsulatefieldrefactoring.h"
+#include "ui_encapsulatefielddialog.h"
+class EncapsulateFieldDialog : public QDialog, private Ui::EncapsulateFieldDialog
+{
+ Q_OBJECT;
+ Q_DISABLE_COPY(EncapsulateFieldDialog);
-class DeclarationComparator;
+ using ChangePack = EncapsulateFieldRefactoring::ChangePack;
-/**
- * Helps in making decision if given declarations are syntactically equivalent
- */
-class TUDeclDispatcher
-{
public:
- TUDeclDispatcher(const DeclarationComparator *declComparator);
+ EncapsulateFieldDialog(ChangePack *changePack);
- bool equivalent(const clang::Decl *decl) const;
+ const ChangePack *changePack() const
+ {
+ return m_changePack;
+ }
private:
- const DeclarationComparator *m_declComparator;
- mutable std::unordered_map<const clang::Decl *, bool> m_cache;
+ ChangePack *m_changePack;
};
-#endif //KDEV_CLANG_TUDECLDISPATCHER_H
+#endif //KDEV_CLANG_ENCAPSULATEFIELDDIALOG_H
diff --git a/refactoring/encapsulatefielddialog.ui \
b/refactoring/encapsulatefielddialog.ui new file mode 100644
index 0000000..501fa70
--- /dev/null
+++ b/refactoring/encapsulatefielddialog.ui
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EncapsulateFieldDialog</class>
+ <widget class="QDialog" name="EncapsulateFieldDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>676</width>
+ <height>512</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Encapsulate Field</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Encapsulate field</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="fieldLineEdit">
+ <property name="font">
+ <font>
+ <family>Monospace</family>
+ </font>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="getterGroupBox">
+ <property name="title">
+ <string>Generate getter</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Definition:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPlainTextEdit" name="getterDefinitionPlainTextEdit">
+ <property name="font">
+ <font>
+ <family>Monospace</family>
+ </font>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Visibility:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="getterVisibilityComboBox"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="setterGroupBox">
+ <property name="title">
+ <string>&Generate setter</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Definition:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPlainTextEdit" name="setterDefinitionPlainTextEdit">
+ <property name="font">
+ <font>
+ <family>Monospace</family>
+ </font>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Visibility:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="setterVisibilityComboBox"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>EncapsulateFieldDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>EncapsulateFieldDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/refactoring/encapsulatefieldrefactoring.cpp \
b/refactoring/encapsulatefieldrefactoring.cpp new file mode 100644
index 0000000..4fb397f
--- /dev/null
+++ b/refactoring/encapsulatefieldrefactoring.cpp
@@ -0,0 +1,490 @@
+/*
+ This file is part of KDevelop
+
+ Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// Clang
+#include <clang/AST/Decl.h>
+#include <clang/ASTMatchers/ASTMatchers.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+#include <clang/Tooling/Refactoring.h>
+
+// KF5
+#include <KF5/KI18n/klocalizedstring.h>
+
+#include "encapsulatefieldrefactoring.h"
+#include "encapsulatefielddialog.h"
+#include "encapsulatefieldrefactoring_changepack.h"
+#include "refactoringcontext.h"
+#include "tudecldispatcher.h"
+#include "utils.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace std;
+
+namespace
+{
+class Translator : public MatchFinder::MatchCallback
+{
+ using ChangePack = EncapsulateFieldRefactoring::ChangePack;
+public:
+ Translator(Replacements &replacements, const ChangePack *changePack,
+ const DeclarationComparator *declDispatcher,
+ const DeclarationComparator *recordDeclDispatcher, const string \
&recordName); +
+ virtual void run(const MatchFinder::MatchResult &Result) override;
+ virtual void onEndOfTranslationUnit() override;
+
+ void handleDeclRefExpr(const DeclRefExpr *declRefExpr, SourceManager \
*sourceManager, + const LangOptions &langOpts);
+ void handleMemberExpr(const MemberExpr *memberExpr, SourceManager \
*sourceManager, + const LangOptions &langOpts);
+ void handleAssignToDeclRefExpr(const BinaryOperator *assignmentOperator,
+ SourceManager *sourceManager, const LangOptions \
&langOpts); + void handleAssignToMemberExpr(const BinaryOperator \
*assignmentOperator, + SourceManager *sourceManager, \
const LangOptions &langOpts); + void handleAssignOperatorCall(const \
CXXOperatorCallExpr *operatorCallExpr, + \
SourceManager *sourceManager, const LangOptions &langOpts); + void \
handleAccessSpecDecl(const AccessSpecDecl *accessSpecDecl, SourceManager \
*sourceManager, + const LangOptions &langOpts);
+ void handleFieldDecl(const Decl *fieldDecl, SourceManager *sourceManager,
+ const LangOptions &langOpts);
+
+private:
+ Replacements &m_replacements;
+ const ChangePack *m_changePack;
+ TUDeclDispatcher m_declDispatcher;
+ TUDeclDispatcher m_recordDeclDispatcher;
+ const string &m_recordName;
+ vector<tuple<AccessSpecifier, SourceLocation>> m_accessSpecifiers;
+ SourceLocation m_lastLocationInRecordDecl;
+ SourceLocation m_firstLocationInRecordDecl;
+ ASTContext *m_astContext;
+ bool m_recordHandled = false;
+ bool m_skipRecord = false;
+};
+}
+
+EncapsulateFieldRefactoring::EncapsulateFieldRefactoring(const DeclaratorDecl *decl)
+ : Refactoring(nullptr)
+ , m_changePack(ChangePack::fromDeclaratorDecl(decl))
+ , m_declDispatcher(decl)
+ , m_recordDeclDispatcher(llvm::dyn_cast<Decl>(decl->getDeclContext()))
+ , m_recordName(llvm::dyn_cast<RecordDecl>(decl->getDeclContext())->getName())
+{
+}
+
+EncapsulateFieldRefactoring::~EncapsulateFieldRefactoring() = default;
+
+llvm::ErrorOr<clang::tooling::Replacements> EncapsulateFieldRefactoring::invoke(
+ RefactoringContext *ctx)
+{
+ unique_ptr<EncapsulateFieldDialog> dialog(new \
EncapsulateFieldDialog(m_changePack.get())); + if (dialog->exec() == 0) {
+ return cancelledResult();
+ }
+
+ auto &tool = ctx->cache->refactoringTool();
+
+ Refactorings::EncapsulateField::run(tool, m_changePack.get(), &m_declDispatcher,
+ &m_recordDeclDispatcher, m_recordName);
+
+ return tool.getReplacements();
+}
+
+namespace Refactorings
+{
+namespace EncapsulateField
+{
+using ChangePack = EncapsulateFieldRefactoring::ChangePack;
+
+int run(RefactoringTool &tool, const ChangePack *changePack,
+ const DeclarationComparator *declDispatcher,
+ const DeclarationComparator *recordDeclDispatcher, const string &recordName)
+{
+ auto pureDeclRefExpr = declRefExpr().bind("DeclRefExpr"); // static
+ auto pureMemberExpr = memberExpr().bind("MemberExpr"); // instance
+
+ auto assignDeclRefExpr = binaryOperator(hasOperatorName("="), \
hasLHS(declRefExpr())) + .bind("AssignDeclRefExpr");
+ auto assignMemberExpr = binaryOperator(hasOperatorName("="), \
hasLHS(memberExpr())) + .bind("AssignMemberExpr");
+ auto assignCXXOperatorCallExpr =
+ operatorCallExpr(
+ hasOverloadedOperatorName("="),
+ argumentCountIs(2),
+ hasArgument(0, anyOf(
+ declRefExpr(),
+ memberExpr()))).bind("AssignCXXOperatorCallExpr");
+
+ auto nonAssignDeclRefExpr =
+ declRefExpr(expr().bind("DeclRefExpr"),
+ unless(anyOf(
+ hasParent(
+ binaryOperator(hasOperatorName("="),
+ hasLHS(equalsBoundNode("DeclRefExpr")))),
+ hasParent(
+ operatorCallExpr(hasOverloadedOperatorName("="),
+ argumentCountIs(2),
+ hasArgument(0, \
equalsBoundNode("DeclRefExpr"))))))); + auto nonAssignMemberExpr =
+ memberExpr(expr().bind("MemberExpr"),
+ unless(anyOf(
+ hasParent(
+ binaryOperator(hasOperatorName("="),
+ hasLHS(equalsBoundNode("MemberExpr")))),
+ hasParent(
+ operatorCallExpr(hasOverloadedOperatorName("="),
+ argumentCountIs(2),
+ hasArgument(0, \
equalsBoundNode("MemberExpr"))))))); + // Name clash above is by design
+ // Note: It may be considered to support also CompoundAssignOperator
+ // rewriting a+=b to setA(getA()+b), but such implementation would have to \
explicitly list + // all operators
+
+ auto accessSpec = accessSpecDecl().bind("AccessSpecDecl");
+ auto fieldDecl = decl().bind("FieldDecl");
+
+ MatchFinder finder;
+ Translator callback(tool.getReplacements(), changePack, declDispatcher,
+ recordDeclDispatcher, recordName);
+ // Choose appropriate set depending on static/instance and get/set
+ if (changePack->createSetter()) {
+ finder.addMatcher(nonAssignDeclRefExpr, &callback);
+ finder.addMatcher(nonAssignMemberExpr, &callback);
+ finder.addMatcher(assignDeclRefExpr, &callback);
+ finder.addMatcher(assignMemberExpr, &callback);
+ finder.addMatcher(assignCXXOperatorCallExpr, &callback);
+ } else {
+ finder.addMatcher(pureDeclRefExpr, &callback);
+ finder.addMatcher(pureMemberExpr, &callback);
+ }
+ finder.addMatcher(accessSpec, &callback);
+ finder.addMatcher(fieldDecl, &callback);
+ return tool.run(newFrontendActionFactory(&finder).get());
+}
+}
+}
+
+QString EncapsulateFieldRefactoring::name() const
+{
+ return i18n("encapsulate \
[%1]").arg(QString::fromStdString(m_changePack->fieldName())); +}
+
+Translator::Translator(Replacements &replacements, const ChangePack *changePack,
+ const DeclarationComparator *declDispatcher,
+ const DeclarationComparator *recordDeclDispatcher, const \
string &recordName) + : m_replacements(replacements)
+ , m_changePack(changePack)
+ , m_declDispatcher(declDispatcher)
+ , m_recordDeclDispatcher(recordDeclDispatcher)
+ , m_recordName(recordName)
+{
+}
+
+void Translator::run(const MatchFinder::MatchResult &Result)
+{
+ // Name clash is used here
+ auto declRef = Result.Nodes.getNodeAs<DeclRefExpr>("DeclRefExpr");
+ if (declRef && m_declDispatcher.equivalent(declRef->getDecl())) {
+ handleDeclRefExpr(declRef, Result.SourceManager, \
Result.Context->getLangOpts()); + return;
+ }
+ auto memberExpr = Result.Nodes.getNodeAs<MemberExpr>("MemberExpr");
+ if (memberExpr && m_declDispatcher.equivalent(memberExpr->getMemberDecl())) {
+ handleMemberExpr(memberExpr, Result.SourceManager, \
Result.Context->getLangOpts()); + return;
+ }
+ auto assignDeclRefExpr = \
Result.Nodes.getNodeAs<BinaryOperator>("AssignDeclRefExpr"); + if \
(assignDeclRefExpr && m_declDispatcher.equivalent( + \
llvm::dyn_cast<DeclRefExpr>(assignDeclRefExpr->getLHS())->getDecl())) { + \
handleAssignToDeclRefExpr(assignDeclRefExpr, Result.SourceManager, + \
Result.Context->getLangOpts()); + return;
+ }
+ auto assignMemberExpr = \
Result.Nodes.getNodeAs<BinaryOperator>("AssignMemberExpr"); + if (assignMemberExpr \
&& m_declDispatcher.equivalent( + \
llvm::dyn_cast<MemberExpr>(assignMemberExpr->getLHS())->getMemberDecl())) { + \
handleAssignToMemberExpr(assignMemberExpr, Result.SourceManager, + \
Result.Context->getLangOpts()); + return;
+ }
+ auto assignOperatorCall = Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
+ "AssignCXXOperatorCallExpr");
+ if (assignOperatorCall) {
+ auto assignee = assignOperatorCall->getArg(0);
+ const Decl *decl;
+ if (llvm::isa<MemberExpr>(assignee)) {
+ decl = llvm::dyn_cast<MemberExpr>(assignee)->getMemberDecl();
+ } else {
+ Q_ASSERT(llvm::isa<DeclRefExpr>(assignee));
+ decl = llvm::dyn_cast<DeclRefExpr>(assignee)->getDecl();
+ }
+ if (m_declDispatcher.equivalent(decl)) {
+ handleAssignOperatorCall(assignOperatorCall, Result.SourceManager,
+ Result.Context->getLangOpts());
+ }
+ return;
+ }
+ auto accessSpecDecl = Result.Nodes.getNodeAs<AccessSpecDecl>("AccessSpecDecl");
+ if (accessSpecDecl && \
m_recordDeclDispatcher.equivalent(accessSpecDecl->getDeclContext())) { + if \
(!m_skipRecord) { + handleAccessSpecDecl(accessSpecDecl, \
Result.SourceManager, + \
Result.Context->getLangOpts()); + }
+ return;
+ }
+ auto fieldDecl = Result.Nodes.getNodeAs<Decl>("FieldDecl");
+ if (fieldDecl && m_declDispatcher.equivalent(fieldDecl)) {
+ if (!m_skipRecord) {
+ handleFieldDecl(fieldDecl, Result.SourceManager, \
Result.Context->getLangOpts()); + }
+ return;
+ }
+}
+
+void Translator::handleDeclRefExpr(const DeclRefExpr *declRefExpr, SourceManager \
*sourceManager, + const LangOptions &langOpts)
+{
+ m_replacements.insert(
+ Replacement(*sourceManager, \
CharSourceRange::getTokenRange(declRefExpr->getLocation()), + \
m_changePack->getterName() + "()")); + // TODO Clang 3.7: use @p langOpts above
+}
+
+void Translator::handleMemberExpr(const MemberExpr *memberExpr, SourceManager \
*sourceManager, + const LangOptions &langOpts)
+{
+ m_replacements.insert(
+ Replacement(*sourceManager, \
CharSourceRange::getTokenRange(memberExpr->getMemberLoc()), + \
m_changePack->getterName() + "()")); + // TODO Clang 3.7: use @p langOpts above
+}
+
+void Translator::handleAssignToDeclRefExpr(const BinaryOperator *assignmentOperator,
+ SourceManager *sourceManager,
+ const LangOptions &langOpts)
+{
+ m_replacements.insert(
+ Replacement(*sourceManager,
+ CharSourceRange::getTokenRange(
+ \
llvm::dyn_cast<DeclRefExpr>(assignmentOperator->getLHS())->getLocation(), + \
assignmentOperator->getLocEnd()), + m_changePack->setterName() + \
"(" + + codeFromASTNode(assignmentOperator->getRHS(), \
*sourceManager, langOpts) + ")")); +}
+
+void Translator::handleAssignToMemberExpr(const BinaryOperator *assignmentOperator,
+ SourceManager *sourceManager,
+ const LangOptions &langOpts)
+{
+ m_replacements.insert(
+ Replacement(*sourceManager,
+ CharSourceRange::getTokenRange(
+ \
llvm::dyn_cast<MemberExpr>(assignmentOperator->getLHS())->getMemberLoc(), + \
assignmentOperator->getLocEnd()), + m_changePack->setterName() + \
"(" + + codeFromASTNode(assignmentOperator->getRHS(), \
*sourceManager, langOpts) + ")")); +}
+
+void Translator::handleAssignOperatorCall(const CXXOperatorCallExpr \
*operatorCallExpr, + SourceManager \
*sourceManager, + const LangOptions \
&langOpts) +{
+ auto assignee = operatorCallExpr->getArg(0);
+ SourceLocation location;
+ if (llvm::isa<MemberExpr>(assignee)) {
+ location = llvm::dyn_cast<MemberExpr>(assignee)->getMemberLoc();
+ } else {
+ Q_ASSERT(llvm::isa<DeclRefExpr>(assignee));
+ location = llvm::dyn_cast<DeclRefExpr>(assignee)->getLocation();
+ }
+ m_replacements.insert(
+ Replacement(*sourceManager,
+ CharSourceRange::getTokenRange(location, \
operatorCallExpr->getLocEnd()), + m_changePack->setterName() + "(" \
+ + codeFromASTNode(operatorCallExpr->getArg(1), *sourceManager, \
langOpts) + ")")); +}
+
+void Translator::onEndOfTranslationUnit()
+{
+ if (!m_skipRecord && m_recordHandled) {
+ m_skipRecord = true;
+
+ // insert code
+ auto accessComparator = [](AccessSpecifier spec)
+ {
+ return [spec](const tuple<AccessSpecifier, SourceLocation> &t)
+ {
+ return get<0>(t) == spec;
+ };
+ };
+ auto locationFromIterator = [this](
+ decltype(m_accessSpecifiers)::reverse_iterator i)
+ {
+ if (i != m_accessSpecifiers.rbegin()) {
+ return get<1>(*(i - 1));
+ } else {
+ return m_lastLocationInRecordDecl;
+ }
+ };
+ SourceLocation publicLocation;
+ SourceLocation protectedLocation;
+ SourceLocation privateLocation = !m_accessSpecifiers.empty() ? \
get<1>(m_accessSpecifiers[0]) + \
: m_lastLocationInRecordDecl; + auto i = find_if(m_accessSpecifiers.rbegin(), \
m_accessSpecifiers.rend(), + \
accessComparator(AccessSpecifier::AS_public)); + if (i != \
m_accessSpecifiers.rend()) { + publicLocation = locationFromIterator(i);
+ }
+ i = find_if(m_accessSpecifiers.rbegin(), m_accessSpecifiers.rend(),
+ accessComparator(AccessSpecifier::AS_protected));
+ if (i != m_accessSpecifiers.rend()) {
+ protectedLocation = locationFromIterator(i);
+ }
+ i = find_if(m_accessSpecifiers.rbegin(), m_accessSpecifiers.rend(),
+ accessComparator(AccessSpecifier::AS_private));
+ if (i != m_accessSpecifiers.rend()) {
+ privateLocation = locationFromIterator(i);
+ }
+
+ if (m_changePack->getterAccess() == AS_public ||
+ (m_changePack->createSetter() && m_changePack->setterAccess() == \
AS_public)) { + string change;
+ if (m_changePack->getterAccess() == AS_public) {
+ change = m_changePack->accessorCode();
+ }
+ if (m_changePack->createSetter() && m_changePack->setterAccess() == \
AS_public) { + change += "\n" + m_changePack->mutatorCode();
+ }
+ if (publicLocation.isInvalid()) {
+ publicLocation = m_firstLocationInRecordDecl;
+ change = "public:\n" + change + "private:\n";
+ }
+ m_replacements.insert(
+ Replacement(m_astContext->getSourceManager(), publicLocation, 0, \
change + "\n")); + }
+ if (m_changePack->getterAccess() == AS_protected ||
+ (m_changePack->createSetter() && m_changePack->setterAccess() == \
AS_protected)) { + string change;
+ if (m_changePack->getterAccess() == AS_protected) {
+ change = m_changePack->accessorCode();
+ }
+ if (m_changePack->createSetter() && m_changePack->setterAccess() == \
AS_protected) { + change += "\n" + m_changePack->mutatorCode();
+ }
+ if (protectedLocation.isInvalid()) {
+ if (publicLocation.isInvalid()) {
+ publicLocation = m_firstLocationInRecordDecl;
+ }
+ protectedLocation = publicLocation;
+ change = "protected:\n" + change + "private:\n";
+ }
+ m_replacements.insert(
+ Replacement(m_astContext->getSourceManager(), protectedLocation, 0, \
change + "\n")); + }
+ if (m_changePack->getterAccess() == AS_private ||
+ (m_changePack->createSetter() && m_changePack->setterAccess() == \
AS_private)) { + string change;
+ if (m_changePack->getterAccess() == AS_private) {
+ change = m_changePack->accessorCode();
+ }
+ if (m_changePack->createSetter() && m_changePack->setterAccess() == \
AS_private) { + change += "\n" + m_changePack->mutatorCode();
+ }
+ // It is always possible to get private location (FIXME: but beware \
difference struct/class) + m_replacements.insert(
+ Replacement(m_astContext->getSourceManager(), privateLocation, 0, \
change + "\n")); + }
+
+ m_accessSpecifiers.clear();
+ m_accessSpecifiers.shrink_to_fit(); // This vector will not be used anymore
+ }
+}
+
+void Translator::handleAccessSpecDecl(const AccessSpecDecl *accessSpecDecl,
+ SourceManager *sourceManager, const \
LangOptions &langOpts) +{
+ Q_UNUSED(sourceManager);
+ m_recordHandled = true;
+ m_accessSpecifiers.emplace_back(accessSpecDecl->getAccess(), \
accessSpecDecl->getLocStart()); +}
+
+static string describeAccessSpecifier(AccessSpecifier accessSpecifier)
+{
+ switch (accessSpecifier) {
+ case AS_public:
+ return "public";
+ case AS_protected:
+ return "protected";
+ case AS_private:
+ return "private";
+ default:
+ Q_ASSERT(false && "This code is not meant to work on AS_none access \
specifier"); + return "none";
+ }
+}
+
+namespace clang
+{
+namespace arcmt
+{
+namespace trans
+{
+// From ARCMigrate
+SourceLocation findSemiAfterLocation(SourceLocation loc, ASTContext &Ctx,
+ bool IsDecl = false);
+}
+}
+}
+
+using clang::arcmt::trans::findSemiAfterLocation;
+
+void Translator::handleFieldDecl(const Decl *fieldDecl, SourceManager \
*sourceManager, + const LangOptions &langOpts)
+{
+ m_recordHandled = true;
+ auto oldAccess = fieldDecl->getAccess();
+ if (oldAccess != AS_private) {
+ string oldAccessSpec = describeAccessSpecifier(oldAccess) + ":";
+ m_replacements.insert(
+ Replacement(*sourceManager,
+ findSemiAfterLocation(fieldDecl->getLocEnd(), \
fieldDecl->getASTContext(), + true),
+ 1, ";\n\n" + oldAccessSpec));
+ m_replacements.insert(
+ Replacement(*sourceManager, fieldDecl->getLocStart(), 0, "private:\n"));
+ // skipping whitespaces (to some extent...) above would be nice, but also \
unwisely complicated + }
+ auto recordDecl = llvm::dyn_cast<RecordDecl>(fieldDecl->getDeclContext());
+ m_lastLocationInRecordDecl = recordDecl->getRBraceLoc();
+ for (auto decl : recordDecl->decls()) {
+ if (decl->getLocStart() != recordDecl->getLocStart()) {
+ m_firstLocationInRecordDecl = decl->getLocStart();
+ break;
+ }
+ }
+ if (m_firstLocationInRecordDecl.isInvalid()) {
+ m_firstLocationInRecordDecl = m_lastLocationInRecordDecl;
+ }
+ m_astContext = &fieldDecl->getASTContext();
+}
+
diff --git a/refactoring/encapsulatefieldrefactoring.h \
b/refactoring/encapsulatefieldrefactoring.h new file mode 100644
index 0000000..b50bd3d
--- /dev/null
+++ b/refactoring/encapsulatefieldrefactoring.h
@@ -0,0 +1,70 @@
+/*
+ This file is part of KDevelop
+
+ Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDEV_CLANG_ENCAPSULATEFIELDREFACTORING_H
+#define KDEV_CLANG_ENCAPSULATEFIELDREFACTORING_H
+
+// Clang
+#include <clang/AST/DeclBase.h>
+
+#include "refactoring.h"
+#include "redeclarationchain.h"
+
+class DeclarationComparator;
+
+/**
+ * This refactoring makes slightly stronger assumptions about code base.
+ * Particularly it assumes, that all RecordDecls being subject of this refactoring \
have _one_ + * definition (for example in some header file, which can be then \
included in many places). + */
+class EncapsulateFieldRefactoring : public Refactoring
+{
+ Q_OBJECT;
+ Q_DISABLE_COPY(EncapsulateFieldRefactoring);
+public:
+ class ChangePack;
+
+ EncapsulateFieldRefactoring(const clang::DeclaratorDecl *decl);
+ ~EncapsulateFieldRefactoring();
+
+ virtual llvm::ErrorOr<clang::tooling::Replacements> invoke(RefactoringContext \
*ctx); + virtual QString name() const;
+
+private:
+ std::unique_ptr<ChangePack> m_changePack;
+ RedeclarationChain m_declDispatcher;
+ RedeclarationChain m_recordDeclDispatcher;
+ std::string m_recordName;
+};
+
+namespace Refactorings
+{
+namespace EncapsulateField
+{
+using ChangePack = EncapsulateFieldRefactoring::ChangePack;
+
+int run(clang::tooling::RefactoringTool &tool, const ChangePack *changePack,
+ const DeclarationComparator *declDispatcher,
+ const DeclarationComparator *recordDeclDispatcher, const std::string \
&recordName); +}
+}
+
+#endif //KDEV_CLANG_ENCAPSULATEFIELDREFACTORING_H
diff --git a/refactoring/encapsulatefieldrefactoring_changepack.cpp \
b/refactoring/encapsulatefieldrefactoring_changepack.cpp new file mode 100644
index 0000000..3f42af6
--- /dev/null
+++ b/refactoring/encapsulatefieldrefactoring_changepack.cpp
@@ -0,0 +1,113 @@
+/*
+ This file is part of KDevelop
+
+ Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// Clang
+#include <clang/AST/Decl.h>
+
+#include "encapsulatefieldrefactoring_changepack.h"
+#include "utils.h"
+
+using namespace std;
+using namespace clang;
+
+
+EncapsulateFieldRefactoring::ChangePack::ChangePack(const std::string \
&fieldDescription, + const \
std::string &fieldType, + const \
std::string &fieldName, + const \
std::string &getterName, + const \
std::string &setterName, + const \
std::string &recordName, + \
clang::AccessSpecifier getterAccess, + \
clang::AccessSpecifier setterAccess, + \
bool createSetter, bool isStatic) + : m_fieldDescription(fieldDescription)
+ , m_fieldType(fieldType)
+ , m_fieldName(fieldName)
+ , m_getterName(getterName)
+ , m_setterName(setterName)
+ , m_recordName(recordName)
+ , m_getterAccess(getterAccess)
+ , m_setterAccess(setterAccess)
+ , m_createSetter(createSetter)
+ , m_isStatic(isStatic)
+{
+ m_accessorCode = accessorImplementation();
+ m_mutatorCode = mutatorImplementation();
+}
+
+unique_ptr<EncapsulateFieldRefactoring::ChangePack>
+EncapsulateFieldRefactoring::ChangePack::fromDeclaratorDecl(const DeclaratorDecl \
*decl) +{
+ auto currentAccess = decl->getAccess();
+ const auto &srcMgr = decl->getASTContext().getSourceManager();
+ const auto &langOpts = decl->getASTContext().getLangOpts();
+ string fieldDescription = codeFromASTNode(decl, srcMgr, langOpts);
+ auto fieldQualType = decl->getType().getNonReferenceType();
+ fieldQualType.removeLocalConst();
+ auto fieldTypeString = fieldQualType.getAsString();
+ auto fieldName = decl->getName();
+ string getterName = suggestGetterName(fieldName);
+ string setterName = suggestSetterName(fieldName);
+ string recordName = \
llvm::dyn_cast<RecordDecl>(decl->getDeclContext())->getName(); + return \
unique_ptr<ChangePack>( + new ChangePack{fieldDescription, fieldTypeString, \
fieldName, getterName, setterName, + recordName, currentAccess, \
currentAccess, true, + !llvm::isa<FieldDecl>(decl)});
+}
+
+std::string EncapsulateFieldRefactoring::ChangePack::accessorImplementation() const
+{
+ string result;
+ if (isStatic()) {
+ result = "static ";
+ }
+ result += "const " + fieldType() + "&";
+ result += " " + getterName() + "()";
+ if (!isStatic()) {
+ result += " const\n";
+ } else {
+ result += "\n";
+ }
+ result += "{\n";
+ result += "\treturn " + fieldName() + ";\n";
+ result += "}\n";
+ return result;
+};
+
+std::string EncapsulateFieldRefactoring::ChangePack::mutatorImplementation() const
+{
+ string result;
+ if (isStatic()) {
+ result = "static ";
+ }
+ result += "void " + setterName() + "(const " + fieldType() +
+ " &" + fieldName() + ")\n";
+ result += "{\n";
+ result += "\t";
+ if (!isStatic()) {
+ result += "this->";
+ } else {
+ result += m_recordName + "::";
+ }
+ result += fieldName() + " = " + fieldName() + ";\n";
+ result += "}\n";
+ return result;
+};
diff --git a/refactoring/encapsulatefieldrefactoring_changepack.h \
b/refactoring/encapsulatefieldrefactoring_changepack.h new file mode 100644
index 0000000..1a4fe43
--- /dev/null
+++ b/refactoring/encapsulatefieldrefactoring_changepack.h
@@ -0,0 +1,157 @@
+/*
+ This file is part of KDevelop
+
+ Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDEV_CLANG_ENCAPSULATEFIELDREFACTORING_CHANGEPACK_H
+#define KDEV_CLANG_ENCAPSULATEFIELDREFACTORING_CHANGEPACK_H
+
+// C++
+#include <string>
+#include <memory>
+
+// Clang
+#include <clang/AST/DeclBase.h>
+#include <clang/Basic/Specifiers.h>
+
+#include "encapsulatefieldrefactoring.h"
+
+class EncapsulateFieldRefactoring::ChangePack
+{
+public:
+ ChangePack(const std::string &fieldDescription, const std::string &fieldType,
+ const std::string &fieldName, const std::string &getterName,
+ const std::string &setterName, const std::string &recordName,
+ clang::AccessSpecifier getterAccess, clang::AccessSpecifier \
setterAccess, + bool createSetter, bool isStatic);
+
+ static std::unique_ptr<ChangePack> fromDeclaratorDecl(const \
clang::DeclaratorDecl *decl); +
+ const std::string &fieldDescription() const
+ {
+ return m_fieldDescription;
+ }
+
+ bool isStatic() const
+ {
+ return m_isStatic;
+ }
+
+ const std::string &fieldType() const
+ {
+ return m_fieldType;
+ }
+
+ const std::string &fieldName() const
+ {
+ return m_fieldName;
+ }
+
+ const std::string &getterName() const
+ {
+ return m_getterName;
+ }
+
+ void setGetterName(const std::string &getterName)
+ {
+ m_getterName = getterName;
+ }
+
+ const std::string &setterName() const
+ {
+ return m_setterName;
+ }
+
+ void setSetterName(const std::string &setterName)
+ {
+ m_setterName = setterName;
+ }
+
+ const clang::AccessSpecifier &getterAccess() const
+ {
+ return m_getterAccess;
+ }
+
+ void setGetterAccess(const clang::AccessSpecifier &getterAccess)
+ {
+ m_getterAccess = getterAccess;
+ }
+
+ const clang::AccessSpecifier &setterAccess() const
+ {
+ return m_setterAccess;
+ }
+
+ void setSetterAccess(const clang::AccessSpecifier &setterAccess)
+ {
+ m_setterAccess = setterAccess;
+ }
+
+ bool createSetter() const
+ {
+ return m_createSetter;
+ }
+
+ void setCreateSetter(bool createSetter)
+ {
+ m_createSetter = createSetter;
+ }
+
+ const std::string &accessorCode() const
+ {
+ return m_accessorCode;
+ }
+
+ const std::string &mutatorCode() const
+ {
+ return m_mutatorCode;
+ }
+
+ void setAccessorCode(const std::string &accessorCode)
+ {
+ m_accessorCode = accessorCode;
+ }
+
+ void setMutatorCode(const std::string &mutatorCode)
+ {
+ m_mutatorCode = mutatorCode;
+ }
+
+private:
+ std::string accessorImplementation() const;
+ std::string mutatorImplementation() const;
+
+private:
+ const std::string m_fieldDescription;
+ const std::string m_fieldType;
+ const std::string m_fieldName;
+ std::string m_getterName;
+ std::string m_setterName;
+ std::string m_recordName;
+ clang::AccessSpecifier m_getterAccess;
+ clang::AccessSpecifier m_setterAccess;
+ bool m_createSetter;
+ bool m_isStatic;
+
+ std::string m_accessorCode;
+ std::string m_mutatorCode;
+};
+
+
+#endif //KDEV_CLANG_ENCAPSULATEFIELDREFACTORING_CHANGEPACK_H
diff --git a/refactoring/redeclarationchain.cpp b/refactoring/redeclarationchain.cpp
index 5e2ae45..906de55 100644
--- a/refactoring/redeclarationchain.cpp
+++ b/refactoring/redeclarationchain.cpp
@@ -28,8 +28,6 @@ using namespace clang;
RedeclarationChain::RedeclarationChain(const clang::Decl *declNode)
{
- Q_ASSERT((llvm::dyn_cast<NamedDecl>(declNode) == nullptr) ||
- (llvm::dyn_cast<NamedDecl>(declNode)->getLinkageInternal() != \
ExternalLinkage)); for (auto decl : declNode->redecls()) {
auto loc = lexicalLocation(decl);
if (loc.fileName.empty()) {
@@ -51,11 +49,5 @@ bool RedeclarationChain::intersects(const RedeclarationChain \
&other) const
bool RedeclarationChain::equivalentTo(const Decl *decl) const
{
- if (const NamedDecl *namedDecl = llvm::dyn_cast<NamedDecl>(decl)) {
- auto linkage = namedDecl->getLinkageInternal();
- if (linkage == ExternalLinkage) {
- return false;
- }
- }
return intersects(RedeclarationChain(decl));
}
diff --git a/refactoring/refactoringmanager.cpp b/refactoring/refactoringmanager.cpp
index 60e13ec..e6b074e 100644
--- a/refactoring/refactoringmanager.cpp
+++ b/refactoring/refactoringmanager.cpp
@@ -35,6 +35,7 @@
#include "renamefielddeclturefactoring.h"
#include "declarationcomparator.h"
#include "changesignaturerefactoring.h"
+#include "encapsulatefieldrefactoring.h"
#include "debug.h"
using namespace std;
@@ -80,11 +81,13 @@ private:
template<class Node>
unsigned fileOffset(const Node &node) const;
- Refactoring *refactoringForVarDecl(const VarDecl *varDecl) const;
+ Refactoring *renameVarDeclRefactoring(const VarDecl *varDecl) const;
- Refactoring *refactoringForFieldDecl(const FieldDecl *fieldDecl) const;
+ Refactoring *renameFieldDeclRefactoring(const FieldDecl *fieldDecl) const;
- Refactoring *refactoringForFunctionDecl(const FunctionDecl *functionDecl) const;
+ Refactoring *changeSignatureRefactoring(const FunctionDecl *functionDecl) const;
+
+ Refactoring *encapsulateFieldRefactoring(const DeclaratorDecl *decl) const;
/// Request ClangTool to stop after this translation unit
void done();
@@ -134,8 +137,8 @@ class ExplorerActionFactory : public FrontendActionFactory
public:
ExplorerActionFactory(const std::string &fileName, unsigned offset)
- : m_fileName(fileName),
- m_offset(offset)
+ : m_fileName(fileName)
+ , m_offset(offset)
{
}
@@ -184,9 +187,9 @@ std::vector<Refactoring *> \
RefactoringManager::allApplicableRefactorings(Refacto
ExplorerASTConsumer::ExplorerASTConsumer(ExplorerActionFactory &factory, \
CompilerInstance &CI)
- : m_visitor(*this),
- m_factory(factory),
- m_CI(CI)
+ : m_visitor(*this)
+ , m_factory(factory)
+ , m_CI(CI)
{
}
@@ -250,7 +253,7 @@ void ExplorerRecursiveASTVisitor::addRefactoring(Refactoring \
*refactoring)
////////////////////// DECISIONS ARE MADE BELOW ///////////////////////
-Refactoring *ExplorerRecursiveASTVisitor::refactoringForVarDecl(const VarDecl \
*varDecl) const +Refactoring \
*ExplorerRecursiveASTVisitor::renameVarDeclRefactoring(const VarDecl *varDecl) const \
{ auto name = varDecl->getName().str();
return new RenameVarDeclRefactoring(declarationComparator(varDecl), name);
@@ -266,7 +269,7 @@ bool ExplorerRecursiveASTVisitor::VisitDeclRefExpr(DeclRefExpr \
*declRefExpr)
refactorDebug() << "Found DeclRefExpr, but its declaration is not \
VarDecl"; return true;
}
- addRefactoring(refactoringForVarDecl(varDecl));
+ addRefactoring(renameVarDeclRefactoring(varDecl));
// other options here...
}
return true;
@@ -277,13 +280,17 @@ bool ExplorerRecursiveASTVisitor::VisitVarDecl(VarDecl \
*varDecl)
auto range = tokenRangeToCharRange(varDecl->getLocation(), m_ASTConsumer.m_CI);
if (isInRange(range)) {
done();
- addRefactoring(refactoringForVarDecl(varDecl));
+ addRefactoring(renameVarDeclRefactoring(varDecl));
+ if (varDecl->isStaticDataMember()) {
+ addRefactoring(encapsulateFieldRefactoring(varDecl));
+ }
// other options here...
}
return true;
}
-Refactoring *ExplorerRecursiveASTVisitor::refactoringForFieldDecl(const FieldDecl \
*fieldDecl) const +Refactoring \
*ExplorerRecursiveASTVisitor::renameFieldDeclRefactoring( + const FieldDecl \
*fieldDecl) const {
auto canonicalDecl = fieldDecl->getCanonicalDecl();
if (canonicalDecl->getLinkageInternal() == ExternalLinkage) {
@@ -305,7 +312,8 @@ bool ExplorerRecursiveASTVisitor::VisitFieldDecl(FieldDecl \
*fieldDecl) {
if (isInRange(fieldDecl->getLocation())) {
done();
- addRefactoring(refactoringForFieldDecl(fieldDecl));
+ addRefactoring(renameFieldDeclRefactoring(fieldDecl));
+ addRefactoring(encapsulateFieldRefactoring(fieldDecl));
// other options here...
}
return true;
@@ -316,17 +324,15 @@ bool ExplorerRecursiveASTVisitor::VisitMemberExpr(MemberExpr \
*memberExpr) if (isInRange(memberExpr->getMemberLoc())) {
done();
const FieldDecl *fieldDecl = \
llvm::dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
- if (!fieldDecl) {
- // can be CXXMethodDecl as well, but that case is (will be) handled \
separately
- return true;
+ if (fieldDecl) {
+ addRefactoring(renameFieldDeclRefactoring(fieldDecl));
}
- addRefactoring(refactoringForFieldDecl(fieldDecl));
// other options here...
}
return true;
}
-Refactoring *ExplorerRecursiveASTVisitor::refactoringForFunctionDecl(
+Refactoring *ExplorerRecursiveASTVisitor::changeSignatureRefactoring(
const FunctionDecl *functionDecl) const
{
auto canonicalDecl = functionDecl->getCanonicalDecl();
@@ -341,10 +347,15 @@ bool \
ExplorerRecursiveASTVisitor::VisitFunctionDecl(FunctionDecl *functionDecl) \
m_ASTConsumer.m_CI); if (isInRange(range)) {
done();
- addRefactoring(refactoringForFunctionDecl(functionDecl));
+ addRefactoring(changeSignatureRefactoring(functionDecl));
// other options here...
}
return true;
}
+Refactoring *ExplorerRecursiveASTVisitor::encapsulateFieldRefactoring(
+ const DeclaratorDecl *decl) const
+{
+ return new EncapsulateFieldRefactoring(decl);
+}
diff --git a/refactoring/tudecldispatcher.cpp b/refactoring/tudecldispatcher.cpp
index c5c0576..b4efbcc 100644
--- a/refactoring/tudecldispatcher.cpp
+++ b/refactoring/tudecldispatcher.cpp
@@ -27,7 +27,9 @@
#include "utils.h"
TUDeclDispatcher::TUDeclDispatcher(const DeclarationComparator *declComparator)
- : m_declComparator(declComparator) { }
+ : m_declComparator(declComparator)
+{
+}
bool TUDeclDispatcher::equivalent(const clang::Decl *decl) const
{
@@ -41,3 +43,12 @@ bool TUDeclDispatcher::equivalent(const clang::Decl *decl) const
return result;
}
}
+
+bool TUDeclDispatcher::equivalentImpl(const clang::DeclContext *declContext) const
+{
+ auto asDecl = llvm::dyn_cast<clang::Decl>(declContext);
+ Q_ASSERT(asDecl != nullptr);
+ // every (instance) decl context should be decl
+ // http://clang.llvm.org/doxygen/classclang_1_1DeclContext.html#details
+ return equivalent(asDecl);
+}
diff --git a/refactoring/tudecldispatcher.h b/refactoring/tudecldispatcher.h
index f9e0158..60aa0fb 100644
--- a/refactoring/tudecldispatcher.h
+++ b/refactoring/tudecldispatcher.h
@@ -29,7 +29,6 @@
// Clang
#include <clang/AST/DeclBase.h>
-
class DeclarationComparator;
/**
@@ -42,6 +41,18 @@ public:
bool equivalent(const clang::Decl *decl) const;
+ template<class T>
+ typename std::enable_if<std::is_base_of<clang::DeclContext, T>::value &&
+ (!std::is_base_of<clang::Decl, T>::value), bool>::type
+ equivalent(const T *declContext) const
+ {
+ // required because there are classes which derive from both Decl and \
DeclContext + return equivalentImpl(declContext);
+ };
+
+private:
+ bool equivalentImpl(const clang::DeclContext *declContext) const;
+
private:
const DeclarationComparator *m_declComparator;
mutable std::unordered_map<const clang::Decl *, bool> m_cache;
diff --git a/refactoring/utils.cpp b/refactoring/utils.cpp
index b17cf28..7a559ad 100644
--- a/refactoring/utils.cpp
+++ b/refactoring/utils.cpp
@@ -303,7 +303,7 @@ bool operator==(const LexicalLocation &lhs, const LexicalLocation \
&rhs) LexicalLocation lexicalLocation(const Decl *decl)
{
const auto &srcMgr = decl->getASTContext().getSourceManager();
- auto location = srcMgr.getDecomposedLoc(decl->getLocStart());
+ auto location = srcMgr.getDecomposedLoc(decl->getLocation());
auto fileEntry = srcMgr.getFileEntryForID(location.first);
if (fileEntry == nullptr) {
return {"", location.second};
@@ -365,3 +365,57 @@ void dumpTokenRange(clang::SourceRange range, const \
SourceManager &sourceManager
refactorDebug() << textFromTokenRange(range, sourceManager, langOpts);
}
+std::string suggestGetterName(const std::string &fieldName)
+{
+ Q_ASSERT(!fieldName.empty());
+ auto i = fieldName.find('_');
+ if (i != fieldName.npos && i + 1 < fieldName.size()) {
+ return fieldName.substr(i + 1);
+ } else if (i != fieldName.npos && fieldName.size() > 1) {
+ return fieldName.substr(0, i);
+ } else {
+ return std::string("get") + std::toupper(fieldName[0], std::locale()) + \
fieldName.substr(1); + }
+}
+
+std::string suggestSetterName(const std::string &fieldName)
+{
+ Q_ASSERT(!fieldName.empty());
+ auto i = fieldName.find('_');
+ std::string result;
+ if (i != fieldName.npos && i + 1 < fieldName.size()) {
+ result = fieldName.substr(i + 1);
+ } else if (i != fieldName.npos && fieldName.size() > 1) {
+ result = fieldName.substr(0, i);
+ } else {
+ result = fieldName;
+ }
+ result[0] = std::toupper(result[0], std::locale());
+ return "set" + result;
+}
+
+std::string functionName(const std::string &functionDeclaration, const std::string \
&fallbackName) +{
+ auto ast = buildASTFromCode(functionDeclaration);
+ if (!ast) {
+ refactorWarning() << "Unable to parse function:\n" << functionDeclaration;
+ refactorWarning() << "Using fallback" << fallbackName;
+ return fallbackName;
+ }
+ TranslationUnitDecl *tuDecl = ast->getASTContext().getTranslationUnitDecl();
+ FunctionDecl *functionDecl = nullptr;
+ for (Decl *decl : tuDecl->decls()) {
+ if (FunctionDecl *fdecl = dyn_cast<FunctionDecl>(decl)) {
+ if (fdecl->isThisDeclarationADefinition()) {
+ functionDecl = fdecl;
+ break;
+ }
+ }
+ }
+ if (!functionDecl) {
+ refactorWarning() << "Didn't find function definition here:\n" << \
functionDeclaration; + refactorWarning() << "Using fallback" << fallbackName;
+ return fallbackName;
+ }
+ return functionDecl->getName();
+}
diff --git a/refactoring/utils.h b/refactoring/utils.h
index d6b0f82..1d21555 100644
--- a/refactoring/utils.h
+++ b/refactoring/utils.h
@@ -184,4 +184,10 @@ std::string codeFromASTNode(const Node *node, const \
clang::SourceManager &source
return textFromTokenRange(node->getSourceRange(), sourceManager, langOpts);
}
+std::string suggestGetterName(const std::string &fieldName);
+
+std::string suggestSetterName(const std::string &fieldName);
+
+std::string functionName(const std::string &functionDeclaration, const std::string \
&fallbackName); +
#endif //KDEV_CLANG_UTILS_H
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index ee5e5f7..1d7534b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -87,6 +87,15 @@ ecm_add_test(test_renamevar.cpp
kdevclangrefactor
)
+ecm_add_test(test_encapsulatefield.cpp
+ TEST_NAME test_encapsulatefield
+ LINK_LIBRARIES
+ KDev::Tests
+ Qt5::Test
+ refactorenv
+ kdevclangrefactor
+)
+
endif()
if(KDEVPLATFORM_JSONTESTS_LIBRARIES)
diff --git a/tests/test_encapsulatefield.cpp b/tests/test_encapsulatefield.cpp
new file mode 100644
index 0000000..d5b2ff3
--- /dev/null
+++ b/tests/test_encapsulatefield.cpp
@@ -0,0 +1,242 @@
+/*
+ This file is part of KDevelop
+
+ Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <functional>
+#include <clang/AST/Decl.h>
+#include <clang/AST/DeclCXX.h>
+#include <clang/Tooling/Refactoring.h>
+#include <QtTest>
+#include "test_encapsulatefield.h"
+#include "refactoringenvironment.h"
+#include "../refactoring/encapsulatefieldrefactoring.h"
+#include "../refactoring/encapsulatefieldrefactoring_changepack.h"
+
+using namespace std;
+using namespace clang;
+using namespace clang::tooling;
+
+QTEST_GUILESS_MAIN(TestEncapsulateField)
+
+void TestEncapsulateField::testSimpleConstRef()
+{
+ RefactoringEnvironment env("-std=c++11");
+ env.addHeader("klass.h",
+ R"(class C
+ {
+ public:
+ int x;
+ };)");
+ env.addFile("use.cpp",
+ R"(#include "klass.h"
+ int f()
+ {
+ C c;
+ c.x=5;
+ return c.x*2;
+ })");
+ auto replacements = env.runTool(
+ [](RefactoringTool &tool)
+ {
+ using ChangePack = EncapsulateFieldRefactoring::ChangePack;
+ ChangePack changePack("", "int", "x", "accessor", "mutator", "C", \
AS_public, AS_public, + true, false);
+ auto declDispatcher = declarationComparator(
+ [](const Decl *decl)
+ {
+ if (!llvm::isa<FieldDecl>(decl)) {
+ return false;
+ }
+ return llvm::dyn_cast<FieldDecl>(decl)->getName() == "x";
+ });
+ auto recordDeclDispatcher = declarationComparator(
+ [](const Decl *decl)
+ {
+ if (!llvm::isa<CXXRecordDecl>(decl)) {
+ return false;
+ }
+ return llvm::dyn_cast<CXXRecordDecl>(decl)->getName() == "C";
+ }
+ );
+ Refactorings::EncapsulateField::run(tool, &changePack, \
declDispatcher.get(), + \
recordDeclDispatcher.get(), "C"); + });
+ env.verifyResult(replacements, "klass.h",
+ R"(class C
+ {
+ public:
+ private:
+ int x;
+ public:
+ const int& accessor() const
+ {
+ return x;
+ }
+
+ void mutator(const int &x)
+ {
+ this->x = x;
+ }
+ };)");
+ env.verifyResult(replacements, "use.cpp",
+ R"(#include "klass.h"
+ int f()
+ {
+ C c;
+ c.mutator(5);
+ return c.accessor()*2;
+ })");
+}
+
+void TestEncapsulateField::testStaticByValue()
+{
+ RefactoringEnvironment env("-std=c++11");
+ env.addHeader("klass.h",
+ R"(class C
+ {
+ public:
+ static int x;
+ };)");
+ env.addFile("use.cpp",
+ R"(#include "klass.h"
+ int f()
+ {
+ C c;
+ C::x=5;
+ return C::x*2;
+ })");
+ auto replacements = env.runTool(
+ [](RefactoringTool &tool)
+ {
+ using ChangePack = EncapsulateFieldRefactoring::ChangePack;
+ ChangePack changePack("", "int", "x", "accessor", "mutator", "C", \
AS_public, AS_public, + true, true);
+ auto declDispatcher = declarationComparator(
+ [](const Decl *decl)
+ {
+ if (!llvm::isa<VarDecl>(decl)) {
+ return false;
+ }
+ return llvm::dyn_cast<VarDecl>(decl)->getName() == "x";
+ });
+ auto recordDeclDispatcher = declarationComparator(
+ [](const Decl *decl)
+ {
+ if (!llvm::isa<CXXRecordDecl>(decl)) {
+ return false;
+ }
+ return llvm::dyn_cast<CXXRecordDecl>(decl)->getName() == "C";
+ }
+ );
+ Refactorings::EncapsulateField::run(tool, &changePack, \
declDispatcher.get(), + \
recordDeclDispatcher.get(), "C"); + });
+ env.verifyResult(replacements, "klass.h",
+ R"(class C
+ {
+ public:
+ private:
+ static int x;
+ public:
+ static const int& accessor()
+ {
+ return x;
+ }
+
+ static void mutator(const int &x)
+ {
+ C::x = x;
+ }
+ };)");
+ env.verifyResult(replacements, "use.cpp",
+ R"(#include "klass.h"
+ int f()
+ {
+ C c;
+ C::mutator(5);
+ return C::accessor()*2;
+ })");
+}
+
+void TestEncapsulateField::testStaticNoMutator()
+{
+ RefactoringEnvironment env("-std=c++11");
+ env.addHeader("klass.h",
+ R"(class C
+ {
+ public:
+ static int x;
+ };)");
+ env.addFile("use.cpp",
+ R"(#include "klass.h"
+ int f()
+ {
+ C c;
+ C::x=5;
+ return C::x*2;
+ })");
+ auto replacements = env.runTool(
+ [](RefactoringTool &tool)
+ {
+ using ChangePack = EncapsulateFieldRefactoring::ChangePack;
+ ChangePack changePack("", "int", "x", "accessor", "mutator", "C", \
AS_public, AS_none, + false, true);
+ auto declDispatcher = declarationComparator(
+ [](const Decl *decl)
+ {
+ if (!llvm::isa<VarDecl>(decl)) {
+ return false;
+ }
+ return llvm::dyn_cast<VarDecl>(decl)->getName() == "x";
+ });
+ auto recordDeclDispatcher = declarationComparator(
+ [](const Decl *decl)
+ {
+ if (!llvm::isa<CXXRecordDecl>(decl)) {
+ return false;
+ }
+ return llvm::dyn_cast<CXXRecordDecl>(decl)->getName() == "C";
+ }
+ );
+ Refactorings::EncapsulateField::run(tool, &changePack, \
declDispatcher.get(), + \
recordDeclDispatcher.get(), "C"); + });
+ env.verifyResult(replacements, "klass.h",
+ R"(class C
+ {
+ public:
+ private:
+ static int x;
+ public:
+ static const int& accessor()
+ {
+ return x;
+ }
+ };)");
+ env.verifyResult(replacements, "use.cpp",
+ R"(#include "klass.h"
+ int f()
+ {
+ C c;
+ C::accessor()=5;
+ return C::accessor()*2;
+ })");
+}
+
diff --git a/refactoring/tudecldispatcher.cpp b/tests/test_encapsulatefield.h
similarity index 61%
copy from refactoring/tudecldispatcher.cpp
copy to tests/test_encapsulatefield.h
index c5c0576..626976c 100644
--- a/refactoring/tudecldispatcher.cpp
+++ b/tests/test_encapsulatefield.h
@@ -19,25 +19,20 @@
Boston, MA 02110-1301, USA.
*/
-// Clang
-#include <clang/AST/Decl.h>
+#ifndef KDEV_CLANG_TEST_ENCAPSULATEFIELD_H
+#define KDEV_CLANG_TEST_ENCAPSULATEFIELD_H
-#include "tudecldispatcher.h"
-#include "declarationcomparator.h"
-#include "utils.h"
+#include <QObject>
-TUDeclDispatcher::TUDeclDispatcher(const DeclarationComparator *declComparator)
- : m_declComparator(declComparator) { }
-
-bool TUDeclDispatcher::equivalent(const clang::Decl *decl) const
+class TestEncapsulateField : public QObject
{
- decl = decl->getCanonicalDecl();
- auto i = m_cache.find(decl);
- if (i != m_cache.end()) {
- return i->second;
- } else {
- bool result = m_declComparator->equivalentTo(decl);
- m_cache[decl] = result;
- return result;
- }
-}
+ Q_OBJECT;
+
+private slots:
+ void testSimpleConstRef();
+ void testStaticByValue();
+ void testStaticNoMutator();
+};
+
+
+#endif //KDEV_CLANG_TEST_ENCAPSULATEFIELD_H
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic