forked from qt-creator/qt-creator
We'd like to split up the monster files cppquickfixes.cpp and cppquickfix_test.cpp by moving non-trivial quickfixes into dedicated files. Since there will be many of them, we want to have them in a separate directory. This patch prepares that. Change-Id: I1e78fab5cce25151662297482582c8043618332e Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
1978 lines
69 KiB
C++
1978 lines
69 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "cppinsertvirtualmethods.h"
|
|
|
|
#include "../cppcodestylesettings.h"
|
|
#include "../cppeditortr.h"
|
|
#include "../cpptoolsreuse.h"
|
|
#include "../functionutils.h"
|
|
#include "../insertionpointlocator.h"
|
|
#include "cppquickfixassistant.h"
|
|
|
|
#include <coreplugin/icore.h>
|
|
#include <texteditor/fontsettings.h>
|
|
#include <texteditor/texteditorsettings.h>
|
|
|
|
#include <cplusplus/CppRewriter.h>
|
|
#include <cplusplus/Overview.h>
|
|
|
|
#include <utils/algorithm.h>
|
|
#include <utils/changeset.h>
|
|
#include <utils/qtcassert.h>
|
|
#include <utils/utilsicons.h>
|
|
|
|
#include <QAction>
|
|
#include <QCheckBox>
|
|
#include <QComboBox>
|
|
#include <QCoreApplication>
|
|
#include <QDialog>
|
|
#include <QDialogButtonBox>
|
|
#include <QGroupBox>
|
|
#include <QLineEdit>
|
|
#include <QPointer>
|
|
#include <QQueue>
|
|
#include <QSortFilterProxyModel>
|
|
#include <QStandardItemModel>
|
|
#include <QTextDocument>
|
|
#include <QToolButton>
|
|
#include <QTreeView>
|
|
#include <QVBoxLayout>
|
|
|
|
#ifdef WITH_TESTS
|
|
#include "cppquickfix_test.h"
|
|
#include <QtTest>
|
|
#endif
|
|
|
|
using namespace CPlusPlus;
|
|
using namespace TextEditor;
|
|
using namespace Utils;
|
|
|
|
namespace CppEditor {
|
|
namespace Internal {
|
|
|
|
class InsertVirtualMethodsModel;
|
|
class VirtualMethodsSettings;
|
|
|
|
class InsertVirtualMethodsDialog : public QDialog
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
enum CustomItemRoles {
|
|
Reimplemented = Qt::UserRole
|
|
};
|
|
|
|
enum ImplementationMode {
|
|
ModeOnlyDeclarations = 0x00000001,
|
|
ModeInsideClass = 0x00000002,
|
|
ModeOutsideClass = 0x00000004,
|
|
ModeImplementationFile = 0x00000008
|
|
};
|
|
|
|
InsertVirtualMethodsDialog(QWidget *parent = nullptr);
|
|
~InsertVirtualMethodsDialog() override;
|
|
void initGui();
|
|
void initData();
|
|
virtual void saveSettings();
|
|
const VirtualMethodsSettings *settings() const;
|
|
|
|
void setHasImplementationFile(bool file);
|
|
void setHasReimplementedFunctions(bool functions);
|
|
|
|
virtual bool gather();
|
|
|
|
protected:
|
|
void setInsertOverrideReplacement(bool insert);
|
|
void setOverrideReplacement(const QString &replacements);
|
|
|
|
private:
|
|
void setHideReimplementedFunctions(bool hide);
|
|
void updateOverrideReplacementsComboBox();
|
|
|
|
private:
|
|
QTreeView *m_view = nullptr;
|
|
QLineEdit *m_filter = nullptr;
|
|
QCheckBox *m_hideReimplementedFunctions = nullptr;
|
|
QComboBox *m_insertMode = nullptr;
|
|
QCheckBox *m_virtualKeyword = nullptr;
|
|
QCheckBox *m_overrideReplacementCheckBox = nullptr;
|
|
QComboBox *m_overrideReplacementComboBox = nullptr;
|
|
QToolButton *m_clearUserAddedReplacementsButton = nullptr;
|
|
QDialogButtonBox *m_buttons = nullptr;
|
|
QList<bool> m_expansionStateNormal;
|
|
QList<bool> m_expansionStateReimp;
|
|
QStringList m_availableOverrideReplacements;
|
|
bool m_hasImplementationFile = false;
|
|
bool m_hasReimplementedFunctions = false;
|
|
|
|
protected:
|
|
VirtualMethodsSettings *m_settings;
|
|
|
|
void saveExpansionState();
|
|
void restoreExpansionState();
|
|
|
|
public:
|
|
InsertVirtualMethodsModel *classFunctionModel;
|
|
QSortFilterProxyModel *classFunctionFilterModel;
|
|
};
|
|
|
|
} // namespace Internal
|
|
} // namespace CppEditor
|
|
|
|
Q_DECLARE_METATYPE(CppEditor::Internal::InsertVirtualMethodsDialog::ImplementationMode)
|
|
|
|
namespace {
|
|
|
|
class InsertVirtualMethodsItem
|
|
{
|
|
public:
|
|
InsertVirtualMethodsItem(InsertVirtualMethodsItem *parent)
|
|
: m_parent(parent)
|
|
{}
|
|
|
|
virtual ~InsertVirtualMethodsItem() = default;
|
|
|
|
virtual QString description() const = 0;
|
|
virtual Qt::ItemFlags flags() const = 0;
|
|
virtual Qt::CheckState checkState() const = 0;
|
|
|
|
InsertVirtualMethodsItem *parent() { return m_parent; }
|
|
|
|
int row = -1;
|
|
|
|
private:
|
|
InsertVirtualMethodsItem *m_parent = nullptr;
|
|
};
|
|
|
|
class FunctionItem;
|
|
|
|
class ClassItem : public InsertVirtualMethodsItem
|
|
{
|
|
public:
|
|
ClassItem(const QString &className, const Class *clazz);
|
|
~ClassItem() override;
|
|
|
|
QString description() const override { return name; }
|
|
Qt::ItemFlags flags() const override;
|
|
Qt::CheckState checkState() const override;
|
|
void removeFunction(int row);
|
|
|
|
const Class *klass;
|
|
const QString name;
|
|
QList<FunctionItem *> functions;
|
|
};
|
|
|
|
class FunctionItem : public InsertVirtualMethodsItem
|
|
{
|
|
public:
|
|
FunctionItem(const Function *func, const QString &functionName, ClassItem *parent);
|
|
QString description() const override;
|
|
Qt::ItemFlags flags() const override;
|
|
Qt::CheckState checkState() const override { return checked ? Qt::Checked : Qt::Unchecked; }
|
|
|
|
const Function *function = nullptr;
|
|
CppEditor::InsertionPointLocator::AccessSpec accessSpec
|
|
= CppEditor::InsertionPointLocator::Invalid;
|
|
bool reimplemented = false;
|
|
bool alreadyFound = false;
|
|
bool checked = false;
|
|
FunctionItem *nextOverride = nullptr;
|
|
|
|
private:
|
|
QString name;
|
|
};
|
|
|
|
ClassItem::ClassItem(const QString &className, const Class *clazz) :
|
|
InsertVirtualMethodsItem(nullptr),
|
|
klass(clazz),
|
|
name(className)
|
|
{
|
|
}
|
|
|
|
ClassItem::~ClassItem()
|
|
{
|
|
qDeleteAll(functions);
|
|
functions.clear();
|
|
}
|
|
|
|
Qt::ItemFlags ClassItem::flags() const
|
|
{
|
|
for (FunctionItem *func : std::as_const(functions)) {
|
|
if (!func->alreadyFound)
|
|
return Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
|
|
}
|
|
|
|
return Qt::ItemIsSelectable;
|
|
}
|
|
|
|
Qt::CheckState ClassItem::checkState() const
|
|
{
|
|
if (functions.isEmpty())
|
|
return Qt::Unchecked;
|
|
Qt::CheckState state = functions.first()->checkState();
|
|
for (FunctionItem *function : std::as_const(functions)) {
|
|
Qt::CheckState functionState = function->checkState();
|
|
if (functionState != state)
|
|
return Qt::PartiallyChecked;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
void ClassItem::removeFunction(int row)
|
|
{
|
|
QTC_ASSERT(row >= 0 && row < functions.count(), return);
|
|
functions.removeAt(row);
|
|
// Update row number for all the following functions
|
|
for (int r = row, total = functions.count(); r < total; ++r)
|
|
functions[r]->row = r;
|
|
}
|
|
|
|
FunctionItem::FunctionItem(const Function *func, const QString &functionName, ClassItem *parent) :
|
|
InsertVirtualMethodsItem(parent),
|
|
function(func),
|
|
nextOverride(this)
|
|
{
|
|
name = functionName;
|
|
}
|
|
|
|
QString FunctionItem::description() const
|
|
{
|
|
return name;
|
|
}
|
|
|
|
Qt::ItemFlags FunctionItem::flags() const
|
|
{
|
|
Qt::ItemFlags res = Qt::NoItemFlags;
|
|
if (!alreadyFound)
|
|
res |= Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
|
|
return res;
|
|
}
|
|
|
|
QStringList defaultOverrideReplacements()
|
|
{
|
|
return {
|
|
QLatin1String("override"),
|
|
QLatin1String("Q_DECL_OVERRIDE")
|
|
};
|
|
}
|
|
|
|
QStringList sortedAndTrimmedStringListWithoutEmptyElements(const QStringList &list)
|
|
{
|
|
QStringList result;
|
|
for (const QString &replacement : list) {
|
|
const QString trimmedReplacement = replacement.trimmed();
|
|
if (!trimmedReplacement.isEmpty())
|
|
result << trimmedReplacement;
|
|
}
|
|
result.sort();
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace CppEditor {
|
|
namespace Internal {
|
|
|
|
const bool kInsertVirtualKeywordDefault = false;
|
|
const bool kHideReimplementedFunctionsDefault = false;
|
|
const bool kInsertOVerrideReplacementDefault = false;
|
|
const int kOverrideReplacementIndexDefault = 0;
|
|
const InsertVirtualMethodsDialog::ImplementationMode kImplementationModeDefault
|
|
= InsertVirtualMethodsDialog::ModeOnlyDeclarations;
|
|
|
|
class VirtualMethodsSettings
|
|
{
|
|
public:
|
|
void read()
|
|
{
|
|
QtcSettings *s = Core::ICore::settings();
|
|
s->beginGroup(group());
|
|
insertVirtualKeyword = s->value(insertVirtualKeywordKey(), kInsertVirtualKeywordDefault)
|
|
.toBool();
|
|
hideReimplementedFunctions
|
|
= s->value(hideReimplementedFunctionsKey(), kHideReimplementedFunctionsDefault).toBool();
|
|
insertOverrideReplacement
|
|
= s->value(insertOverrideReplacementKey(), kInsertOVerrideReplacementDefault).toBool();
|
|
overrideReplacementIndex
|
|
= s->value(overrideReplacementIndexKey(), kOverrideReplacementIndexDefault).toInt();
|
|
userAddedOverrideReplacements = s->value(userAddedOverrideReplacementsKey()).toStringList();
|
|
implementationMode = static_cast<InsertVirtualMethodsDialog::ImplementationMode>(
|
|
s->value(implementationModeKey(), int(kImplementationModeDefault)).toInt());
|
|
s->endGroup();
|
|
}
|
|
|
|
void write() const
|
|
{
|
|
QtcSettings *s = Core::ICore::settings();
|
|
s->beginGroup(group());
|
|
s->setValueWithDefault(insertVirtualKeywordKey(),
|
|
insertVirtualKeyword,
|
|
kInsertVirtualKeywordDefault);
|
|
s->setValueWithDefault(hideReimplementedFunctionsKey(),
|
|
hideReimplementedFunctions,
|
|
kHideReimplementedFunctionsDefault);
|
|
s->setValueWithDefault(insertOverrideReplacementKey(),
|
|
insertOverrideReplacement,
|
|
kInsertOVerrideReplacementDefault);
|
|
s->setValueWithDefault(overrideReplacementIndexKey(),
|
|
overrideReplacementIndex,
|
|
kOverrideReplacementIndexDefault);
|
|
s->setValueWithDefault(userAddedOverrideReplacementsKey(), userAddedOverrideReplacements);
|
|
s->setValueWithDefault(implementationModeKey(),
|
|
int(implementationMode),
|
|
int(kImplementationModeDefault));
|
|
s->endGroup();
|
|
}
|
|
|
|
QString overrideReplacement; // internal
|
|
QStringList userAddedOverrideReplacements;
|
|
InsertVirtualMethodsDialog::ImplementationMode implementationMode = kImplementationModeDefault;
|
|
int overrideReplacementIndex = kOverrideReplacementIndexDefault;
|
|
bool insertVirtualKeyword = kInsertVirtualKeywordDefault;
|
|
bool hideReimplementedFunctions = kHideReimplementedFunctionsDefault;
|
|
bool insertOverrideReplacement = kInsertOVerrideReplacementDefault;
|
|
|
|
private:
|
|
static Key group() { return "QuickFix/InsertVirtualMethods"; }
|
|
static Key insertVirtualKeywordKey() { return "insertKeywordVirtual"; }
|
|
static Key insertOverrideReplacementKey() { return "insertOverrideReplacement"; }
|
|
static Key overrideReplacementIndexKey() { return "overrideReplacementIndex"; }
|
|
static Key userAddedOverrideReplacementsKey() { return "userAddedOverrideReplacements"; }
|
|
static Key implementationModeKey() { return "implementationMode"; }
|
|
static Key hideReimplementedFunctionsKey() { return "hideReimplementedFunctions"; }
|
|
};
|
|
|
|
class InsertVirtualMethodsModel : public QAbstractItemModel
|
|
{
|
|
public:
|
|
InsertVirtualMethodsModel(QObject *parent = nullptr) : QAbstractItemModel(parent)
|
|
{
|
|
const FontSettings &fs = TextEditorSettings::fontSettings();
|
|
formatReimpFunc = fs.formatFor(C_DISABLED_CODE);
|
|
}
|
|
|
|
~InsertVirtualMethodsModel() override
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
beginResetModel();
|
|
qDeleteAll(classes);
|
|
classes.clear();
|
|
endResetModel();
|
|
}
|
|
|
|
QModelIndex index(int row, int column, const QModelIndex &parent) const override
|
|
{
|
|
if (column != 0)
|
|
return {};
|
|
if (parent.isValid()) {
|
|
auto classItem = static_cast<ClassItem *>(parent.internalPointer());
|
|
return createIndex(row, column, classItem->functions.at(row));
|
|
}
|
|
return createIndex(row, column, classes.at(row));
|
|
}
|
|
|
|
QModelIndex parent(const QModelIndex &child) const override
|
|
{
|
|
if (!child.isValid())
|
|
return {};
|
|
InsertVirtualMethodsItem *parent = itemForIndex(child)->parent();
|
|
return parent ? createIndex(parent->row, 0, parent) : QModelIndex();
|
|
}
|
|
|
|
int rowCount(const QModelIndex &parent) const override
|
|
{
|
|
if (!parent.isValid())
|
|
return classes.count();
|
|
InsertVirtualMethodsItem *item = itemForIndex(parent);
|
|
if (item->parent()) // function -> no children
|
|
return 0;
|
|
return static_cast<ClassItem *>(item)->functions.count();
|
|
}
|
|
|
|
int columnCount(const QModelIndex &) const override
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void addClass(ClassItem *classItem)
|
|
{
|
|
int row = classes.count();
|
|
classItem->row = row;
|
|
beginInsertRows(QModelIndex(), row, row);
|
|
classes.append(classItem);
|
|
endInsertRows();
|
|
}
|
|
|
|
void removeFunction(FunctionItem *funcItem)
|
|
{
|
|
auto classItem = static_cast<ClassItem *>(funcItem->parent());
|
|
beginRemoveRows(createIndex(classItem->row, 0, classItem), funcItem->row, funcItem->row);
|
|
classItem->removeFunction(funcItem->row);
|
|
endRemoveRows();
|
|
}
|
|
|
|
QVariant data(const QModelIndex &index, int role) const override
|
|
{
|
|
if (!index.isValid())
|
|
return QVariant();
|
|
|
|
InsertVirtualMethodsItem *item = itemForIndex(index);
|
|
switch (role) {
|
|
case Qt::DisplayRole:
|
|
return item->description();
|
|
case Qt::CheckStateRole:
|
|
return item->checkState();
|
|
case Qt::ForegroundRole:
|
|
if (item->parent() && static_cast<FunctionItem *>(item)->alreadyFound)
|
|
return formatReimpFunc.foreground();
|
|
break;
|
|
case Qt::BackgroundRole:
|
|
if (item->parent() && static_cast<FunctionItem *>(item)->alreadyFound) {
|
|
const QColor background = formatReimpFunc.background();
|
|
if (background.isValid())
|
|
return background;
|
|
}
|
|
break;
|
|
case InsertVirtualMethodsDialog::Reimplemented:
|
|
if (item->parent()) {
|
|
auto function = static_cast<FunctionItem *>(item);
|
|
return QVariant(function->alreadyFound);
|
|
}
|
|
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override
|
|
{
|
|
if (!index.isValid())
|
|
return false;
|
|
|
|
InsertVirtualMethodsItem *item = itemForIndex(index);
|
|
switch (role) {
|
|
case Qt::CheckStateRole: {
|
|
bool checked = value.toInt() == Qt::Checked;
|
|
if (item->parent()) {
|
|
auto funcItem = static_cast<FunctionItem *>(item);
|
|
while (funcItem->checked != checked) {
|
|
funcItem->checked = checked;
|
|
const QModelIndex funcIndex = createIndex(funcItem->row, 0, funcItem);
|
|
emit dataChanged(funcIndex, funcIndex);
|
|
const QModelIndex parentIndex =
|
|
createIndex(funcItem->parent()->row, 0, funcItem->parent());
|
|
emit dataChanged(parentIndex, parentIndex);
|
|
funcItem = funcItem->nextOverride;
|
|
}
|
|
} else {
|
|
auto classItem = static_cast<ClassItem *>(item);
|
|
for (FunctionItem *funcItem : std::as_const(classItem->functions)) {
|
|
if (funcItem->alreadyFound || funcItem->checked == checked)
|
|
continue;
|
|
QModelIndex funcIndex = createIndex(funcItem->row, 0, funcItem);
|
|
setData(funcIndex, value, role);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return QAbstractItemModel::setData(index, value, role);
|
|
}
|
|
|
|
Qt::ItemFlags flags(const QModelIndex &index) const override
|
|
{
|
|
if (!index.isValid())
|
|
return Qt::NoItemFlags;
|
|
return itemForIndex(index)->flags();
|
|
}
|
|
|
|
QList<ClassItem *> classes;
|
|
|
|
private:
|
|
Format formatReimpFunc;
|
|
|
|
InsertVirtualMethodsItem *itemForIndex(const QModelIndex &index) const
|
|
{
|
|
return static_cast<InsertVirtualMethodsItem *>(index.internalPointer());
|
|
}
|
|
};
|
|
|
|
class InsertVirtualMethodsOp : public CppQuickFixOperation
|
|
{
|
|
public:
|
|
InsertVirtualMethodsOp(const CppQuickFixInterface &interface,
|
|
InsertVirtualMethodsDialog *factory)
|
|
: CppQuickFixOperation(interface, 0)
|
|
, m_factory(factory)
|
|
{
|
|
setDescription(Tr::tr("Insert Virtual Functions of Base Classes"));
|
|
|
|
const QList<AST *> &path = interface.path();
|
|
const int pathSize = path.size();
|
|
if (pathSize < 2)
|
|
return;
|
|
|
|
// Determine if cursor is on a class or a base class
|
|
if (SimpleNameAST *nameAST = path.at(pathSize - 1)->asSimpleName()) {
|
|
if (!interface.isCursorOn(nameAST))
|
|
return;
|
|
|
|
if (!(m_classAST = path.at(pathSize - 2)->asClassSpecifier())) { // normal class
|
|
int index = pathSize - 2;
|
|
const BaseSpecifierAST *baseAST = path.at(index)->asBaseSpecifier();// simple bclass
|
|
if (!baseAST) {
|
|
if (index > 0 && path.at(index)->asQualifiedName()) // namespaced base class
|
|
baseAST = path.at(--index)->asBaseSpecifier();
|
|
}
|
|
--index;
|
|
if (baseAST && index >= 0)
|
|
m_classAST = path.at(index)->asClassSpecifier();
|
|
}
|
|
}
|
|
|
|
// Also offer the operation if we are on some "empty" part of the class declaration.
|
|
if (!m_classAST)
|
|
m_classAST = path.at(pathSize - 1)->asClassSpecifier();
|
|
|
|
if (!m_classAST || !m_classAST->base_clause_list)
|
|
return;
|
|
|
|
// Determine insert positions
|
|
const int endOfClassAST = interface.currentFile()->endOf(m_classAST);
|
|
m_insertPosDecl = endOfClassAST - 1; // Skip last "}"
|
|
m_insertPosOutside = endOfClassAST + 1; // Step over ";"
|
|
|
|
// Determine base classes
|
|
QList<const Class *> baseClasses;
|
|
QQueue<ClassOrNamespace *> baseClassQueue;
|
|
QSet<ClassOrNamespace *> visitedBaseClasses;
|
|
if (ClassOrNamespace *clazz = interface.context().lookupType(m_classAST->symbol))
|
|
baseClassQueue.enqueue(clazz);
|
|
while (!baseClassQueue.isEmpty()) {
|
|
ClassOrNamespace *clazz = baseClassQueue.dequeue();
|
|
visitedBaseClasses.insert(clazz);
|
|
const QList<ClassOrNamespace *> bases = clazz->usings();
|
|
for (const ClassOrNamespace *baseClass : bases) {
|
|
const QList<Symbol *> symbols = baseClass->symbols();
|
|
for (Symbol *symbol : symbols) {
|
|
Class *base = symbol->asClass();
|
|
if (base
|
|
&& (clazz = interface.context().lookupType(symbol))
|
|
&& !visitedBaseClasses.contains(clazz)
|
|
&& !baseClasses.contains(base)) {
|
|
baseClasses.prepend(base);
|
|
baseClassQueue.enqueue(clazz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine virtual functions
|
|
m_factory->classFunctionModel->clear();
|
|
Overview printer = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
printer.showFunctionSignatures = true;
|
|
QHash<const Function *, FunctionItem *> virtualFunctions;
|
|
for (const Class *clazz : std::as_const(baseClasses)) {
|
|
ClassItem *itemBase = new ClassItem(printer.prettyName(clazz->name()), clazz);
|
|
for (Scope::iterator it = clazz->memberBegin(); it != clazz->memberEnd(); ++it) {
|
|
if (const Function *func = (*it)->type()->asFunctionType()) {
|
|
// Filter virtual destructors
|
|
const Name *name = func->name();
|
|
if (!name || name->asDestructorNameId())
|
|
continue;
|
|
|
|
QList<const Function * > firstVirtuals;
|
|
const bool isVirtual = FunctionUtils::isVirtualFunction(
|
|
func, interface.context(), &firstVirtuals);
|
|
if (!isVirtual)
|
|
continue;
|
|
|
|
if (func->isFinal()) {
|
|
for (const Function *firstVirtual : std::as_const(firstVirtuals)) {
|
|
if (FunctionItem *first = virtualFunctions[firstVirtual]) {
|
|
FunctionItem *next = nullptr;
|
|
for (FunctionItem *removed = first; next != first; removed = next) {
|
|
next = removed->nextOverride;
|
|
m_factory->classFunctionModel->removeFunction(removed);
|
|
delete removed;
|
|
};
|
|
virtualFunctions.remove(firstVirtual);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
// Filter OQbject's
|
|
// - virtual const QMetaObject *metaObject() const;
|
|
// - virtual void *qt_metacast(const char *);
|
|
// - virtual int qt_metacall(QMetaObject::Call, int, void **);
|
|
bool skip = false;
|
|
for (const Function *firstVirtual : std::as_const(firstVirtuals)) {
|
|
if (printer.prettyName(firstVirtual->enclosingClass()->name()) == "QObject"
|
|
&& magicQObjectFunctions().contains(
|
|
printer.prettyName(func->name()))) {
|
|
skip = true;
|
|
break;
|
|
}
|
|
}
|
|
if (skip)
|
|
continue;
|
|
|
|
// Do not implement existing functions inside target class
|
|
bool funcExistsInClass = false;
|
|
const Name *funcName = func->name();
|
|
const OperatorNameId * const opName = funcName->asOperatorNameId();
|
|
Symbol *symbol = opName ? m_classAST->symbol->find(opName->kind())
|
|
: m_classAST->symbol->find(funcName->identifier());
|
|
for (; symbol; symbol = symbol->next()) {
|
|
if (!opName && (!symbol->name()
|
|
|| !funcName->identifier()->match(symbol->identifier()))) {
|
|
continue;
|
|
}
|
|
if (symbol->type().match(func->type())) {
|
|
funcExistsInClass = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Construct function item
|
|
const bool isReimplemented = !firstVirtuals.contains(func);
|
|
const bool isPureVirtual = func->isPureVirtual();
|
|
QString itemName = printer.prettyType(func->type(), func->name());
|
|
if (isPureVirtual)
|
|
itemName += QLatin1String(" = 0");
|
|
const QString itemReturnTypeString = printer.prettyType(func->returnType());
|
|
itemName += QLatin1String(" : ") + itemReturnTypeString;
|
|
if (isReimplemented)
|
|
itemName += QLatin1String(" (redeclared)");
|
|
auto funcItem = new FunctionItem(func, itemName, itemBase);
|
|
if (isReimplemented) {
|
|
factory->setHasReimplementedFunctions(true);
|
|
funcItem->reimplemented = true;
|
|
funcItem->alreadyFound = funcExistsInClass;
|
|
for (const Function *firstVirtual : std::as_const(firstVirtuals)) {
|
|
if (FunctionItem *first = virtualFunctions[firstVirtual]) {
|
|
if (!first->alreadyFound) {
|
|
while (first->checked != isPureVirtual) {
|
|
first->checked = isPureVirtual;
|
|
first = first->nextOverride;
|
|
}
|
|
}
|
|
funcItem->checked = first->checked;
|
|
|
|
FunctionItem *prev = funcItem;
|
|
for (FunctionItem *next = funcItem->nextOverride;
|
|
next && next != funcItem; next = next->nextOverride) {
|
|
prev = next;
|
|
}
|
|
prev->nextOverride = first->nextOverride;
|
|
first->nextOverride = funcItem;
|
|
}
|
|
}
|
|
} else {
|
|
if (!funcExistsInClass) {
|
|
funcItem->checked = isPureVirtual;
|
|
} else {
|
|
funcItem->alreadyFound = true;
|
|
funcItem->checked = true;
|
|
factory->setHasReimplementedFunctions(true);
|
|
}
|
|
}
|
|
|
|
funcItem->accessSpec = acessSpec(*it);
|
|
funcItem->row = itemBase->functions.count();
|
|
itemBase->functions.append(funcItem);
|
|
|
|
virtualFunctions[func] = funcItem;
|
|
|
|
// update internal counters
|
|
if (!funcExistsInClass)
|
|
++m_functionCount;
|
|
}
|
|
}
|
|
|
|
if (itemBase->functions.isEmpty())
|
|
delete itemBase;
|
|
else
|
|
m_factory->classFunctionModel->addClass(itemBase);
|
|
}
|
|
if (m_factory->classFunctionModel->classes.isEmpty() || m_functionCount == 0)
|
|
return;
|
|
|
|
bool isHeaderFile = false;
|
|
m_cppFilePath = correspondingHeaderOrSource(interface.filePath(), &isHeaderFile);
|
|
m_factory->setHasImplementationFile(isHeaderFile && !m_cppFilePath.isEmpty());
|
|
|
|
m_valid = true;
|
|
}
|
|
|
|
bool isValid() const
|
|
{
|
|
return m_valid;
|
|
}
|
|
|
|
InsertionPointLocator::AccessSpec acessSpec(const Symbol *symbol)
|
|
{
|
|
const Function *func = symbol->type()->asFunctionType();
|
|
if (!func)
|
|
return InsertionPointLocator::Invalid;
|
|
if (func->isSignal())
|
|
return InsertionPointLocator::Signals;
|
|
|
|
InsertionPointLocator::AccessSpec spec = InsertionPointLocator::Invalid;
|
|
if (symbol->isPrivate())
|
|
spec = InsertionPointLocator::Private;
|
|
else if (symbol->isProtected())
|
|
spec = InsertionPointLocator::Protected;
|
|
else if (symbol->isPublic())
|
|
spec = InsertionPointLocator::Public;
|
|
else
|
|
return InsertionPointLocator::Invalid;
|
|
|
|
if (func->isSlot()) {
|
|
switch (spec) {
|
|
case InsertionPointLocator::Private:
|
|
return InsertionPointLocator::PrivateSlot;
|
|
case InsertionPointLocator::Protected:
|
|
return InsertionPointLocator::ProtectedSlot;
|
|
case InsertionPointLocator::Public:
|
|
return InsertionPointLocator::PublicSlot;
|
|
default:
|
|
return spec;
|
|
}
|
|
}
|
|
return spec;
|
|
}
|
|
|
|
void perform() override
|
|
{
|
|
if (!m_factory->gather())
|
|
return;
|
|
|
|
m_factory->saveSettings();
|
|
|
|
// Insert declarations (and definition if Inside-/OutsideClass)
|
|
Overview printer = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
printer.showFunctionSignatures = true;
|
|
printer.showReturnTypes = true;
|
|
printer.showArgumentNames = true;
|
|
printer.showTemplateParameters = true;
|
|
Utils::ChangeSet headerChangeSet;
|
|
const CppRefactoringChanges refactoring(snapshot());
|
|
const Utils::FilePath filePath = currentFile()->filePath();
|
|
const CppRefactoringFilePtr headerFile = refactoring.cppFile(filePath);
|
|
const LookupContext targetContext(headerFile->cppDocument(), snapshot());
|
|
|
|
const Class *targetClass = m_classAST->symbol;
|
|
ClassOrNamespace *targetCoN = targetContext.lookupType(targetClass->enclosingScope());
|
|
if (!targetCoN)
|
|
targetCoN = targetContext.globalNamespace();
|
|
UseMinimalNames useMinimalNames(targetCoN);
|
|
Control *control = context().bindings()->control().get();
|
|
QList<const Function *> insertedFunctions;
|
|
for (ClassItem *classItem : std::as_const(m_factory->classFunctionModel->classes)) {
|
|
if (classItem->checkState() == Qt::Unchecked)
|
|
continue;
|
|
|
|
// Insert Declarations (+ definitions)
|
|
QString lastAccessSpecString;
|
|
bool first = true;
|
|
for (FunctionItem *funcItem : std::as_const(classItem->functions)) {
|
|
if (funcItem->reimplemented || funcItem->alreadyFound || !funcItem->checked)
|
|
continue;
|
|
|
|
const auto cmp = [funcItem](const Function *f) {
|
|
return f->name()->match(funcItem->function->name())
|
|
&& f->type().match(funcItem->function->type());
|
|
};
|
|
if (Utils::contains(insertedFunctions, cmp))
|
|
continue;
|
|
insertedFunctions.append(funcItem->function);
|
|
|
|
if (first) {
|
|
// Add comment
|
|
const QString comment = QLatin1String("\n// ") +
|
|
printer.prettyName(classItem->klass->name()) +
|
|
QLatin1String(" interface\n");
|
|
headerChangeSet.insert(m_insertPosDecl, comment);
|
|
first = false;
|
|
}
|
|
|
|
// Function type minimalization: As base class and derived class could be in
|
|
// different namespaces, we must first make the type fully qualified before
|
|
// it can get minimized.
|
|
Clone cloner(control);
|
|
Function newFunc(&cloner, nullptr, const_cast<Function *>(funcItem->function));
|
|
newFunc.setEnclosingScope(const_cast<Class *>(targetClass));
|
|
SubstitutionEnvironment envQualified;
|
|
envQualified.setContext(context());
|
|
envQualified.switchScope(classItem->klass->enclosingScope());
|
|
UseQualifiedNames useQualifiedNames;
|
|
envQualified.enter(&useQualifiedNames);
|
|
newFunc.setReturnType(rewriteType(newFunc.returnType(), &envQualified, control));
|
|
const int argc = newFunc.argumentCount();
|
|
for (int i = 0; i < argc; ++i) {
|
|
Argument * const arg = newFunc.argumentAt(i)->asArgument();
|
|
QTC_ASSERT(arg, continue);
|
|
arg->setType(rewriteType(arg->type(), &envQualified, control));
|
|
}
|
|
SubstitutionEnvironment envMinimized;
|
|
envMinimized.setContext(context());
|
|
envMinimized.switchScope(targetClass->enclosingScope());
|
|
envMinimized.enter(&useMinimalNames);
|
|
const FullySpecifiedType tn = rewriteType(newFunc.type(), &envMinimized, control);
|
|
QString declaration = printer.prettyType(tn, newFunc.unqualifiedName());
|
|
|
|
if (m_factory->settings()->insertVirtualKeyword)
|
|
declaration = QLatin1String("virtual ") + declaration;
|
|
if (m_factory->settings()->insertOverrideReplacement) {
|
|
const QString overrideReplacement = m_factory->settings()->overrideReplacement;
|
|
if (!overrideReplacement.isEmpty())
|
|
declaration += QLatin1Char(' ') + overrideReplacement;
|
|
}
|
|
if (m_factory->settings()->implementationMode
|
|
& InsertVirtualMethodsDialog::ModeInsideClass) {
|
|
declaration += QLatin1String("\n{\n}\n");
|
|
} else {
|
|
declaration += QLatin1String(";\n");
|
|
}
|
|
|
|
const QString accessSpecString =
|
|
InsertionPointLocator::accessSpecToString(funcItem->accessSpec);
|
|
if (accessSpecString != lastAccessSpecString) {
|
|
declaration = accessSpecString + QLatin1String(":\n") + declaration;
|
|
if (!lastAccessSpecString.isEmpty()) // separate if not direct after the comment
|
|
declaration = QLatin1String("\n") + declaration;
|
|
lastAccessSpecString = accessSpecString;
|
|
}
|
|
headerChangeSet.insert(m_insertPosDecl, declaration);
|
|
|
|
// Insert definition outside class
|
|
if (m_factory->settings()->implementationMode
|
|
& InsertVirtualMethodsDialog::ModeOutsideClass) {
|
|
const QString name = printer.prettyName(targetClass->name()) +
|
|
QLatin1String("::") + printer.prettyName(funcItem->function->name());
|
|
const QString defText = printer.prettyType(tn, name) + QLatin1String("\n{\n}");
|
|
headerChangeSet.insert(m_insertPosOutside, QLatin1String("\n\n") + defText);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write header file
|
|
if (!headerChangeSet.isEmpty()) {
|
|
headerFile->setChangeSet(headerChangeSet);
|
|
headerFile->setOpenEditor(true, m_insertPosDecl);
|
|
headerFile->apply();
|
|
}
|
|
|
|
// Insert in implementation file
|
|
if (m_factory->settings()->implementationMode
|
|
& InsertVirtualMethodsDialog::ModeImplementationFile) {
|
|
const Symbol *symbol = headerFile->cppDocument()->lastVisibleSymbolAt(
|
|
targetClass->line(), targetClass->column());
|
|
if (!symbol)
|
|
return;
|
|
const Class *clazz = symbol->asClass();
|
|
if (!clazz)
|
|
return;
|
|
|
|
CppRefactoringFilePtr implementationFile = refactoring.cppFile(m_cppFilePath);
|
|
Utils::ChangeSet implementationChangeSet;
|
|
const int insertPos = qMax(0, implementationFile->document()->characterCount() - 1);
|
|
|
|
// make target lookup context
|
|
Document::Ptr implementationDoc = implementationFile->cppDocument();
|
|
int line, column;
|
|
implementationDoc->translationUnit()->getPosition(insertPos, &line, &column);
|
|
Scope *targetScope = implementationDoc->scopeAt(line, column);
|
|
const LookupContext targetContext(implementationDoc, snapshot());
|
|
ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
|
|
if (!targetCoN)
|
|
targetCoN = targetContext.globalNamespace();
|
|
|
|
// Loop through inserted declarations
|
|
for (int i = targetClass->memberCount(); i < clazz->memberCount(); ++i) {
|
|
Declaration *decl = clazz->memberAt(i)->asDeclaration();
|
|
if (!decl)
|
|
continue;
|
|
|
|
// setup rewriting to get minimally qualified names
|
|
SubstitutionEnvironment env;
|
|
env.setContext(context());
|
|
env.switchScope(decl->enclosingScope());
|
|
UseMinimalNames q(targetCoN);
|
|
env.enter(&q);
|
|
Control *control = context().bindings()->control().get();
|
|
|
|
// rewrite the function type and name + create definition
|
|
const FullySpecifiedType type = rewriteType(decl->type(), &env, control);
|
|
const QString name = printer.prettyName(
|
|
LookupContext::minimalName(decl, targetCoN, control));
|
|
const QString defText = printer.prettyType(type, name) + QLatin1String("\n{\n}");
|
|
|
|
implementationChangeSet.insert(insertPos, QLatin1String("\n\n") + defText);
|
|
}
|
|
|
|
if (!implementationChangeSet.isEmpty()) {
|
|
implementationFile->setChangeSet(implementationChangeSet);
|
|
implementationFile->apply();
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
InsertVirtualMethodsDialog *m_factory = nullptr;
|
|
const ClassSpecifierAST *m_classAST = nullptr;
|
|
bool m_valid = false;
|
|
FilePath m_cppFilePath;
|
|
int m_insertPosDecl = 0;
|
|
int m_insertPosOutside = 0;
|
|
unsigned m_functionCount = 0;
|
|
};
|
|
|
|
class InsertVirtualMethodsFilterModel : public QSortFilterProxyModel
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
InsertVirtualMethodsFilterModel(QObject *parent = nullptr)
|
|
: QSortFilterProxyModel(parent)
|
|
{}
|
|
|
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
|
|
{
|
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
|
|
|
// Handle base class
|
|
if (!sourceParent.isValid()) {
|
|
// check if any child is valid
|
|
if (!sourceModel()->hasChildren(index))
|
|
return false;
|
|
if (!m_hideReimplemented)
|
|
return true;
|
|
|
|
for (int i = 0; i < sourceModel()->rowCount(index); ++i) {
|
|
const QModelIndex child = sourceModel()->index(i, 0, index);
|
|
if (!child.data(InsertVirtualMethodsDialog::Reimplemented).toBool())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (!QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent))
|
|
return false;
|
|
if (m_hideReimplemented)
|
|
return !index.data(InsertVirtualMethodsDialog::Reimplemented).toBool();
|
|
return true;
|
|
}
|
|
|
|
bool hideReimplemented() const
|
|
{
|
|
return m_hideReimplemented;
|
|
}
|
|
|
|
void setHideReimplementedFunctions(bool show)
|
|
{
|
|
m_hideReimplemented = show;
|
|
invalidateFilter();
|
|
}
|
|
|
|
private:
|
|
bool m_hideReimplemented = false;
|
|
};
|
|
|
|
InsertVirtualMethodsDialog::InsertVirtualMethodsDialog(QWidget *parent)
|
|
: QDialog(parent)
|
|
, m_settings(new VirtualMethodsSettings)
|
|
, classFunctionModel(new InsertVirtualMethodsModel(this))
|
|
, classFunctionFilterModel(new InsertVirtualMethodsFilterModel(this))
|
|
{
|
|
classFunctionFilterModel->setSourceModel(classFunctionModel);
|
|
classFunctionFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
}
|
|
|
|
InsertVirtualMethodsDialog::~InsertVirtualMethodsDialog()
|
|
{
|
|
delete m_settings;
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::initGui()
|
|
{
|
|
if (m_view)
|
|
return;
|
|
|
|
setWindowTitle(Tr::tr("Insert Virtual Functions"));
|
|
auto globalVerticalLayout = new QVBoxLayout;
|
|
|
|
// View
|
|
QGroupBox *groupBoxView = new QGroupBox(Tr::tr("&Functions to insert:"), this);
|
|
auto groupBoxViewLayout = new QVBoxLayout(groupBoxView);
|
|
m_filter = new QLineEdit(this);
|
|
m_filter->setClearButtonEnabled(true);
|
|
m_filter->setPlaceholderText(Tr::tr("Filter"));
|
|
groupBoxViewLayout->addWidget(m_filter);
|
|
m_view = new QTreeView(this);
|
|
m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
m_view->setHeaderHidden(true);
|
|
groupBoxViewLayout->addWidget(m_view);
|
|
m_hideReimplementedFunctions =
|
|
new QCheckBox(Tr::tr("&Hide reimplemented functions"), this);
|
|
groupBoxViewLayout->addWidget(m_hideReimplementedFunctions);
|
|
|
|
// Insertion options
|
|
QGroupBox *groupBoxImplementation = new QGroupBox(Tr::tr("&Insertion options:"), this);
|
|
auto groupBoxImplementationLayout = new QVBoxLayout(groupBoxImplementation);
|
|
m_insertMode = new QComboBox(this);
|
|
m_insertMode->addItem(Tr::tr("Insert only declarations"), ModeOnlyDeclarations);
|
|
m_insertMode->addItem(Tr::tr("Insert definitions inside class"), ModeInsideClass);
|
|
m_insertMode->addItem(Tr::tr("Insert definitions outside class"), ModeOutsideClass);
|
|
m_insertMode->addItem(Tr::tr("Insert definitions in implementation file"), ModeImplementationFile);
|
|
m_virtualKeyword = new QCheckBox(Tr::tr("Add \"&virtual\" to function declaration"), this);
|
|
m_overrideReplacementCheckBox = new QCheckBox(
|
|
Tr::tr("Add \"override\" equivalent to function declaration:"), this);
|
|
m_overrideReplacementComboBox = new QComboBox(this);
|
|
QSizePolicy sizePolicy = m_overrideReplacementComboBox->sizePolicy();
|
|
sizePolicy.setHorizontalPolicy(QSizePolicy::Expanding);
|
|
m_overrideReplacementComboBox->setSizePolicy(sizePolicy);
|
|
m_overrideReplacementComboBox->setEditable(true);
|
|
connect(m_overrideReplacementCheckBox, &QCheckBox::clicked,
|
|
m_overrideReplacementComboBox, &QComboBox::setEnabled);
|
|
|
|
auto clearUserAddedReplacements = new QAction(this);
|
|
clearUserAddedReplacements->setIcon(Utils::Icons::CLEAN_TOOLBAR.icon());
|
|
clearUserAddedReplacements->setText(Tr::tr("Clear Added \"override\" Equivalents"));
|
|
connect(clearUserAddedReplacements, &QAction::triggered, this, [this] {
|
|
m_availableOverrideReplacements = defaultOverrideReplacements();
|
|
updateOverrideReplacementsComboBox();
|
|
m_clearUserAddedReplacementsButton->setEnabled(false);
|
|
});
|
|
m_clearUserAddedReplacementsButton = new QToolButton(this);
|
|
m_clearUserAddedReplacementsButton->setDefaultAction(clearUserAddedReplacements);
|
|
|
|
auto overrideWidgetsLayout = new QHBoxLayout(this);
|
|
overrideWidgetsLayout->setSpacing(0);
|
|
overrideWidgetsLayout->setContentsMargins(0, 0, 0, 0);
|
|
overrideWidgetsLayout->addWidget(m_overrideReplacementCheckBox);
|
|
overrideWidgetsLayout->addWidget(m_overrideReplacementComboBox);
|
|
overrideWidgetsLayout->addWidget(m_clearUserAddedReplacementsButton);
|
|
QWidget *overrideWidgets = new QWidget(groupBoxImplementation);
|
|
overrideWidgets->setLayout(overrideWidgetsLayout);
|
|
|
|
groupBoxImplementationLayout->addWidget(m_insertMode);
|
|
groupBoxImplementationLayout->addWidget(m_virtualKeyword);
|
|
groupBoxImplementationLayout->addWidget(overrideWidgets);
|
|
groupBoxImplementationLayout->addStretch(99);
|
|
|
|
// Bottom button box
|
|
m_buttons = new QDialogButtonBox(this);
|
|
m_buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
|
|
globalVerticalLayout->addWidget(groupBoxView, 9);
|
|
globalVerticalLayout->addWidget(groupBoxImplementation, 0);
|
|
globalVerticalLayout->addWidget(m_buttons, 0);
|
|
setLayout(globalVerticalLayout);
|
|
|
|
connect(m_hideReimplementedFunctions, &QAbstractButton::toggled,
|
|
this, &InsertVirtualMethodsDialog::setHideReimplementedFunctions);
|
|
connect(m_filter, &QLineEdit::textChanged,
|
|
classFunctionFilterModel, &QSortFilterProxyModel::setFilterWildcard);
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::initData()
|
|
{
|
|
m_settings->read();
|
|
m_filter->clear();
|
|
m_hideReimplementedFunctions->setChecked(m_settings->hideReimplementedFunctions);
|
|
const QStringList alwaysPresentReplacements = defaultOverrideReplacements();
|
|
m_availableOverrideReplacements = alwaysPresentReplacements;
|
|
m_availableOverrideReplacements += m_settings->userAddedOverrideReplacements;
|
|
|
|
m_view->setModel(classFunctionFilterModel);
|
|
m_expansionStateNormal.clear();
|
|
m_expansionStateReimp.clear();
|
|
m_hideReimplementedFunctions->setEnabled(m_hasReimplementedFunctions);
|
|
m_virtualKeyword->setChecked(m_settings->insertVirtualKeyword);
|
|
m_insertMode->setCurrentIndex(m_insertMode->findData(m_settings->implementationMode));
|
|
|
|
m_overrideReplacementCheckBox->setChecked(m_settings->insertOverrideReplacement);
|
|
updateOverrideReplacementsComboBox();
|
|
const bool canClear = m_availableOverrideReplacements.size() > alwaysPresentReplacements.size();
|
|
m_clearUserAddedReplacementsButton->setEnabled(canClear);
|
|
int overrideReplacementIndex = m_settings->overrideReplacementIndex;
|
|
if (overrideReplacementIndex >= m_overrideReplacementComboBox->count())
|
|
overrideReplacementIndex = 0;
|
|
m_overrideReplacementComboBox->setCurrentIndex(overrideReplacementIndex);
|
|
|
|
setHideReimplementedFunctions(m_hideReimplementedFunctions->isChecked());
|
|
|
|
if (m_hasImplementationFile) {
|
|
if (m_insertMode->count() == 3) {
|
|
m_insertMode->addItem(Tr::tr("Insert definitions in implementation file"),
|
|
ModeImplementationFile);
|
|
}
|
|
} else {
|
|
if (m_insertMode->count() == 4)
|
|
m_insertMode->removeItem(3);
|
|
}
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::saveSettings()
|
|
{
|
|
m_settings->insertVirtualKeyword = m_virtualKeyword->isChecked();
|
|
m_settings->implementationMode = static_cast<InsertVirtualMethodsDialog::ImplementationMode>(
|
|
m_insertMode->itemData(m_insertMode->currentIndex()).toInt());
|
|
m_settings->hideReimplementedFunctions = m_hideReimplementedFunctions->isChecked();
|
|
m_settings->insertOverrideReplacement = m_overrideReplacementCheckBox->isChecked();
|
|
m_settings->overrideReplacementIndex = m_overrideReplacementComboBox->currentIndex();
|
|
if (m_overrideReplacementComboBox && m_overrideReplacementComboBox->isEnabled())
|
|
m_settings->overrideReplacement = m_overrideReplacementComboBox->currentText().trimmed();
|
|
QSet<QString> addedReplacements = Utils::toSet(m_availableOverrideReplacements);
|
|
addedReplacements.insert(m_settings->overrideReplacement);
|
|
addedReplacements.subtract(Utils::toSet(defaultOverrideReplacements()));
|
|
m_settings->userAddedOverrideReplacements =
|
|
sortedAndTrimmedStringListWithoutEmptyElements(Utils::toList(addedReplacements));
|
|
m_settings->write();
|
|
}
|
|
|
|
const VirtualMethodsSettings *InsertVirtualMethodsDialog::settings() const
|
|
{
|
|
return m_settings;
|
|
}
|
|
|
|
bool InsertVirtualMethodsDialog::gather()
|
|
{
|
|
initGui();
|
|
initData();
|
|
m_filter->setFocus();
|
|
|
|
// Expand the dialog a little bit
|
|
adjustSize();
|
|
resize(size() * 1.5);
|
|
|
|
QPointer<InsertVirtualMethodsDialog> that(this);
|
|
const int ret = exec();
|
|
if (!that)
|
|
return false;
|
|
|
|
return (ret == QDialog::Accepted);
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::setHasImplementationFile(bool file)
|
|
{
|
|
m_hasImplementationFile = file;
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::setHasReimplementedFunctions(bool functions)
|
|
{
|
|
m_hasReimplementedFunctions = functions;
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::setHideReimplementedFunctions(bool hide)
|
|
{
|
|
auto model = qobject_cast<InsertVirtualMethodsFilterModel *>(classFunctionFilterModel);
|
|
|
|
if (m_expansionStateNormal.isEmpty() && m_expansionStateReimp.isEmpty()) {
|
|
model->setHideReimplementedFunctions(hide);
|
|
m_view->expandAll();
|
|
saveExpansionState();
|
|
return;
|
|
}
|
|
|
|
if (model->hideReimplemented() == hide)
|
|
return;
|
|
|
|
saveExpansionState();
|
|
model->setHideReimplementedFunctions(hide);
|
|
restoreExpansionState();
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::updateOverrideReplacementsComboBox()
|
|
{
|
|
m_overrideReplacementComboBox->clear();
|
|
for (const QString &replacement : std::as_const(m_availableOverrideReplacements))
|
|
m_overrideReplacementComboBox->addItem(replacement);
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::saveExpansionState()
|
|
{
|
|
auto model = qobject_cast<InsertVirtualMethodsFilterModel *>(classFunctionFilterModel);
|
|
|
|
QList<bool> &state = model->hideReimplemented() ? m_expansionStateReimp
|
|
: m_expansionStateNormal;
|
|
state.clear();
|
|
for (int i = 0; i < model->rowCount(); ++i)
|
|
state << m_view->isExpanded(model->index(i, 0));
|
|
}
|
|
|
|
void InsertVirtualMethodsDialog::restoreExpansionState()
|
|
{
|
|
auto model = qobject_cast<InsertVirtualMethodsFilterModel *>(classFunctionFilterModel);
|
|
|
|
const QList<bool> &state = model->hideReimplemented() ? m_expansionStateReimp
|
|
: m_expansionStateNormal;
|
|
const int stateCount = state.count();
|
|
for (int i = 0; i < model->rowCount(); ++i) {
|
|
if (i < stateCount && !state.at(i)) {
|
|
m_view->collapse(model->index(i, 0));
|
|
continue;
|
|
}
|
|
m_view->expand(model->index(i, 0));
|
|
}
|
|
}
|
|
|
|
InsertVirtualMethods::InsertVirtualMethods(InsertVirtualMethodsDialog *dialog)
|
|
: m_dialog(dialog)
|
|
{
|
|
if (!dialog)
|
|
m_dialog = new InsertVirtualMethodsDialog;
|
|
}
|
|
|
|
InsertVirtualMethods::~InsertVirtualMethods()
|
|
{
|
|
m_dialog->deleteLater();
|
|
}
|
|
|
|
void InsertVirtualMethods::doMatch(const CppQuickFixInterface &interface,
|
|
QuickFixOperations &result)
|
|
{
|
|
QSharedPointer<InsertVirtualMethodsOp> op(new InsertVirtualMethodsOp(interface, m_dialog));
|
|
if (op->isValid())
|
|
result.append(op);
|
|
}
|
|
|
|
#ifdef WITH_TESTS
|
|
|
|
namespace Tests {
|
|
|
|
typedef QByteArray _;
|
|
|
|
/// Stub dialog of InsertVirtualMethodsDialog that does not pop up anything.
|
|
class InsertVirtualMethodsDialogTest : public InsertVirtualMethodsDialog
|
|
{
|
|
public:
|
|
InsertVirtualMethodsDialogTest(ImplementationMode mode,
|
|
bool insertVirtualKeyword,
|
|
bool insertOverrideKeyword,
|
|
QWidget *parent = 0)
|
|
: InsertVirtualMethodsDialog(parent)
|
|
{
|
|
m_settings->implementationMode = mode;
|
|
m_settings->insertVirtualKeyword = insertVirtualKeyword;
|
|
m_settings->insertOverrideReplacement = insertOverrideKeyword;
|
|
m_settings->overrideReplacement = QLatin1String("override");
|
|
}
|
|
|
|
bool gather() override { return true; }
|
|
void saveSettings() override { }
|
|
};
|
|
|
|
void InsertVirtualMethodsTest::test_data()
|
|
{
|
|
QTest::addColumn<InsertVirtualMethodsDialog::ImplementationMode>("implementationMode");
|
|
QTest::addColumn<bool>("insertVirtualKeyword");
|
|
QTest::addColumn<bool>("insertOverrideKeyword");
|
|
QTest::addColumn<QByteArray>("original");
|
|
QTest::addColumn<QByteArray>("expected");
|
|
|
|
// Check: Insert only declarations
|
|
QTest::newRow("onlyDecl")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Insert only declarations without virtual keyword but with override
|
|
QTest::newRow("onlyDeclWithoutVirtual")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << false << true << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" int virtualFuncA() override;\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Are access specifiers considered
|
|
QTest::newRow("Access")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"protected:\n"
|
|
" virtual int b() = 0;\n"
|
|
"private:\n"
|
|
" virtual int c() = 0;\n"
|
|
"public slots:\n"
|
|
" virtual int d() = 0;\n"
|
|
"protected slots:\n"
|
|
" virtual int e() = 0;\n"
|
|
"private slots:\n"
|
|
" virtual int f() = 0;\n"
|
|
"signals:\n"
|
|
" virtual int g() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"protected:\n"
|
|
" virtual int b() = 0;\n"
|
|
"private:\n"
|
|
" virtual int c() = 0;\n"
|
|
"public slots:\n"
|
|
" virtual int d() = 0;\n"
|
|
"protected slots:\n"
|
|
" virtual int e() = 0;\n"
|
|
"private slots:\n"
|
|
" virtual int f() = 0;\n"
|
|
"signals:\n"
|
|
" virtual int g() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int a();\n\n"
|
|
"protected:\n"
|
|
" virtual int b();\n\n"
|
|
"private:\n"
|
|
" virtual int c();\n\n"
|
|
"public slots:\n"
|
|
" virtual int d();\n\n"
|
|
"protected slots:\n"
|
|
" virtual int e();\n\n"
|
|
"private slots:\n"
|
|
" virtual int f();\n\n"
|
|
"signals:\n"
|
|
" virtual int g();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Is a base class of a base class considered.
|
|
QTest::newRow("Superclass")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int a();\n"
|
|
"\n"
|
|
" // BaseB interface\n"
|
|
"public:\n"
|
|
" virtual int b();\n"
|
|
"};\n"
|
|
);
|
|
|
|
|
|
// Check: Do not insert reimplemented functions twice.
|
|
QTest::newRow("SuperclassOverride")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int a();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Insert only declarations for pure virtual function
|
|
QTest::newRow("PureVirtualOnlyDecl")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Insert pure virtual functions inside class
|
|
QTest::newRow("PureVirtualInside")
|
|
<< InsertVirtualMethodsDialog::ModeInsideClass << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA()\n"
|
|
" {\n"
|
|
" }\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Overloads
|
|
QTest::newRow("Overloads")
|
|
<< InsertVirtualMethodsDialog::ModeInsideClass << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virt(int i) = 0;\n"
|
|
" virtual int virt(double d) = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virt(int i) = 0;\n"
|
|
" virtual int virt(double d) = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int virt(int i)\n"
|
|
" {\n"
|
|
" }\n"
|
|
" virtual int virt(double d)\n"
|
|
" {\n"
|
|
" }\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Insert inside class
|
|
QTest::newRow("inside")
|
|
<< InsertVirtualMethodsDialog::ModeInsideClass << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA()\n"
|
|
" {\n"
|
|
" }\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Insert outside class
|
|
QTest::newRow("outside")
|
|
<< InsertVirtualMethodsDialog::ModeOutsideClass << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA();\n"
|
|
"};\n\n"
|
|
"int Derived::virtualFuncA()\n"
|
|
"{\n"
|
|
"}\n"
|
|
);
|
|
|
|
// Check: No trigger: all implemented
|
|
QTest::newRow("notrigger_allImplemented")
|
|
<< InsertVirtualMethodsDialog::ModeOutsideClass << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA();\n"
|
|
" virtual operator==(const BaseA &);\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
" virtual operator==(const BaseA &);\n"
|
|
"};\n"
|
|
) << _();
|
|
|
|
// Check: One pure, one not
|
|
QTest::newRow("Some_Pure")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
" virtual int virtualFuncB();\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA() = 0;\n"
|
|
" virtual int virtualFuncB();\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int virtualFuncA();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Pure function in derived class
|
|
QTest::newRow("Pure_in_Derived")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a();\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a();\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int a();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: One pure function in base class, one in derived
|
|
QTest::newRow("Pure_in_Base_And_Derived")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b();\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b();\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int a();\n"
|
|
" virtual int b();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: One pure function in base class, two in derived
|
|
QTest::newRow("Pure_in_Base_And_Derived_2")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b();\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int b() = 0;\n"
|
|
" virtual int c() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b();\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int b() = 0;\n"
|
|
" virtual int c() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int a();\n"
|
|
" virtual int b();\n"
|
|
"\n"
|
|
" // BaseB interface\n"
|
|
"public:\n"
|
|
" virtual int c();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Remove final function
|
|
QTest::newRow("final_function_removed")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() final = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() final = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseB {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int b();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Remove multiple final functions
|
|
QTest::newRow("multiple_final_functions_removed")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << true << false << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int c() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseC : public BaseB {\n"
|
|
"public:\n"
|
|
" virtual int a() final = 0;\n"
|
|
" virtual int d() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseC {\n"
|
|
"};\n"
|
|
) << _(
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int b() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseB : public BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a() = 0;\n"
|
|
" virtual int c() = 0;\n"
|
|
"};\n\n"
|
|
"class BaseC : public BaseB {\n"
|
|
"public:\n"
|
|
" virtual int a() final = 0;\n"
|
|
" virtual int d() = 0;\n"
|
|
"};\n\n"
|
|
"class Der@ived : public BaseC {\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int b();\n"
|
|
"\n"
|
|
" // BaseB interface\n"
|
|
"public:\n"
|
|
" virtual int c();\n"
|
|
"\n"
|
|
" // BaseC interface\n"
|
|
"public:\n"
|
|
" virtual int d();\n"
|
|
"};\n"
|
|
);
|
|
|
|
// Check: Insert multiply-inherited virtual function only once.
|
|
QTest::newRow("multiple_inheritance_insert")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << false << true << _(
|
|
"struct Base1 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct Base2 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct @Derived : Base1, Base2 {\n"
|
|
"};\n") << _(
|
|
"struct Base1 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct Base2 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct Derived : Base1, Base2 {\n\n"
|
|
" // Base2 interface\n"
|
|
"public:\n"
|
|
" void virt() override;\n"
|
|
"};\n");
|
|
|
|
// Check: Do not insert multiply-inherited virtual function that has been re-implemented
|
|
// along the way.
|
|
QTest::newRow("multiple_inheritance_no_insert")
|
|
<< InsertVirtualMethodsDialog::ModeOnlyDeclarations << false << true << _(
|
|
"struct Base1 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct Base2 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct Derived1 : Base1, Base2 {\n"
|
|
" void virt() override;\n"
|
|
"};\n\n"
|
|
"struct @Derived2 : Derived1\n"
|
|
"};\n") << _(
|
|
"struct Base1 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct Base2 {\n"
|
|
" virtual void virt() = 0;\n"
|
|
"};\n\n"
|
|
"struct Derived1 : Base1, Base2 {\n"
|
|
" void virt() override;\n"
|
|
"};\n\n"
|
|
"struct Derived2 : Derived1\n"
|
|
"};\n");
|
|
}
|
|
|
|
void InsertVirtualMethodsTest::test()
|
|
{
|
|
QFETCH(InsertVirtualMethodsDialog::ImplementationMode, implementationMode);
|
|
QFETCH(bool, insertVirtualKeyword);
|
|
QFETCH(bool, insertOverrideKeyword);
|
|
QFETCH(QByteArray, original);
|
|
QFETCH(QByteArray, expected);
|
|
|
|
InsertVirtualMethods factory(
|
|
new Tests::InsertVirtualMethodsDialogTest(implementationMode,
|
|
insertVirtualKeyword,
|
|
insertOverrideKeyword));
|
|
Tests::QuickFixOperationTest(Tests::singleDocument(original, expected), &factory);
|
|
}
|
|
|
|
/// Check: Insert in implementation file
|
|
void InsertVirtualMethodsTest::testImplementationFile()
|
|
{
|
|
QList<Tests::TestDocumentPtr> testFiles;
|
|
QByteArray original;
|
|
QByteArray expected;
|
|
|
|
// Header File
|
|
original =
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a(const std::vector<int> &v) = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public Bas@eA {\n"
|
|
"public:\n"
|
|
" Derived();\n"
|
|
"};\n";
|
|
expected =
|
|
"class BaseA {\n"
|
|
"public:\n"
|
|
" virtual int a(const std::vector<int> &v) = 0;\n"
|
|
"};\n\n"
|
|
"class Derived : public BaseA {\n"
|
|
"public:\n"
|
|
" Derived();\n"
|
|
"\n"
|
|
" // BaseA interface\n"
|
|
"public:\n"
|
|
" virtual int a(const std::vector<int> &v);\n"
|
|
"};\n";
|
|
testFiles << Tests::CppTestDocument::create("file.h", original, expected);
|
|
|
|
// Source File
|
|
original = "#include \"file.h\"\n";
|
|
expected =
|
|
"#include \"file.h\"\n"
|
|
"\n\n"
|
|
"int Derived::a(const std::vector<int> &v)\n"
|
|
"{\n}";
|
|
testFiles << Tests::CppTestDocument::create("file.cpp", original, expected);
|
|
|
|
InsertVirtualMethods factory(new Tests::InsertVirtualMethodsDialogTest(
|
|
InsertVirtualMethodsDialog::ModeImplementationFile,
|
|
true,
|
|
false));
|
|
Tests::QuickFixOperationTest(testFiles, &factory);
|
|
}
|
|
|
|
/// Check: Qualified names.
|
|
void InsertVirtualMethodsTest::testBaseClassInNamespace()
|
|
{
|
|
QList<Tests::TestDocumentPtr> testFiles;
|
|
QByteArray original;
|
|
QByteArray expected;
|
|
|
|
// Header File
|
|
original =
|
|
"namespace BaseNS {enum BaseEnum {EnumA = 1};}\n"
|
|
"namespace BaseNS {\n"
|
|
"class Base {\n"
|
|
"public:\n"
|
|
" virtual BaseEnum a(BaseEnum e) = 0;\n"
|
|
"};\n"
|
|
"}\n"
|
|
"class Deri@ved : public BaseNS::Base {\n"
|
|
"public:\n"
|
|
" Derived();\n"
|
|
"};\n";
|
|
expected =
|
|
"namespace BaseNS {enum BaseEnum {EnumA = 1};}\n"
|
|
"namespace BaseNS {\n"
|
|
"class Base {\n"
|
|
"public:\n"
|
|
" virtual BaseEnum a(BaseEnum e) = 0;\n"
|
|
"};\n"
|
|
"}\n"
|
|
"class Deri@ved : public BaseNS::Base {\n"
|
|
"public:\n"
|
|
" Derived();\n"
|
|
"\n"
|
|
" // Base interface\n"
|
|
"public:\n"
|
|
" virtual BaseNS::BaseEnum a(BaseNS::BaseEnum e);\n"
|
|
"};\n";
|
|
testFiles << Tests::CppTestDocument::create("file.h", original, expected);
|
|
|
|
// Source File
|
|
original = "#include \"file.h\"\n";
|
|
expected =
|
|
"#include \"file.h\"\n"
|
|
"\n\n"
|
|
"BaseNS::BaseEnum Derived::a(BaseNS::BaseEnum e)\n"
|
|
"{\n}";
|
|
testFiles << Tests::CppTestDocument::create("file.cpp", original, expected);
|
|
|
|
InsertVirtualMethods factory(new Tests::InsertVirtualMethodsDialogTest(
|
|
InsertVirtualMethodsDialog::ModeImplementationFile,
|
|
true,
|
|
false));
|
|
Tests::QuickFixOperationTest(testFiles, &factory);
|
|
}
|
|
|
|
} // namespace Tests
|
|
|
|
InsertVirtualMethods *InsertVirtualMethods::createTestFactory()
|
|
{
|
|
return new InsertVirtualMethods(new Tests::InsertVirtualMethodsDialogTest(
|
|
InsertVirtualMethodsDialog::ModeOutsideClass, true, false));
|
|
}
|
|
|
|
#endif // WITH_TESTS
|
|
|
|
} // namespace Internal
|
|
} // namespace CppEditor
|
|
|
|
#include "cppinsertvirtualmethods.moc"
|