[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>&amp;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