summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeander Schulten <Leander.Schulten@rwth-aachen.de>2021-01-30 22:11:04 +0100
committerLeander Schulten <Leander.Schulten@rwth-aachen.de>2021-02-04 11:40:05 +0000
commit29207e3eebb5ea7597d4160bbf171f1cec0edd5c (patch)
tree2bddd9ccf4f8f26ad51ca074bef3f7c83c58c32f
parent0e0c2ca91c53e7bca3c8ea0a1c047a87b0873ac7 (diff)
downloadqt-creator-29207e3eebb5ea7597d4160bbf171f1cec0edd5c.tar.gz
CppEditor: Add Base Class Support for Generate Constructor QuickFix
Change-Id: Idd92229134609c0ac87aad030a6bb645ff4cce1b Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.cpp27
-rw-r--r--src/libs/3rdparty/cplusplus/Symbols.h1
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.cpp69
-rw-r--r--src/plugins/cppeditor/cppquickfixes.cpp505
4 files changed, 558 insertions, 44 deletions
diff --git a/src/libs/3rdparty/cplusplus/Bind.cpp b/src/libs/3rdparty/cplusplus/Bind.cpp
index 5313bd2d91..4fb9427ace 100644
--- a/src/libs/3rdparty/cplusplus/Bind.cpp
+++ b/src/libs/3rdparty/cplusplus/Bind.cpp
@@ -1253,12 +1253,39 @@ const StringLiteral *Bind::asStringLiteral(const AST *ast)
const int firstToken = ast->firstToken();
const int lastToken = ast->lastToken();
std::string buffer;
+
+ const auto token = tokenAt(ast->firstToken());
+
+ if (token.isCharLiteral()) {
+ if (token.kind() == T_WIDE_CHAR_LITERAL)
+ buffer += 'L';
+ else if (token.kind() == T_UTF16_CHAR_LITERAL)
+ buffer += 'u';
+ else if (token.kind() == T_UTF32_CHAR_LITERAL)
+ buffer += 'U';
+ buffer += '\'';
+ } else if (token.isStringLiteral()) {
+ if (token.kind() == T_WIDE_STRING_LITERAL)
+ buffer += 'L';
+ else if (token.kind() == T_UTF16_STRING_LITERAL)
+ buffer += 'u';
+ else if (token.kind() == T_UTF32_STRING_LITERAL)
+ buffer += 'U';
+ else if (token.kind() == T_UTF8_STRING_LITERAL)
+ buffer += "u8";
+ buffer += '"';
+ }
for (int index = firstToken; index != lastToken; ++index) {
const Token &tk = tokenAt(index);
if (index != firstToken && (tk.whitespace() || tk.newline()))
buffer += ' ';
buffer += tk.spell();
}
+ if (token.isCharLiteral())
+ buffer += '\'';
+ else if (token.isStringLiteral())
+ buffer += '"';
+
return control()->stringLiteral(buffer.c_str(), int(buffer.size()));
}
diff --git a/src/libs/3rdparty/cplusplus/Symbols.h b/src/libs/3rdparty/cplusplus/Symbols.h
index bfb645d1c0..eb926a1b86 100644
--- a/src/libs/3rdparty/cplusplus/Symbols.h
+++ b/src/libs/3rdparty/cplusplus/Symbols.h
@@ -544,6 +544,7 @@ public:
int baseClassCount() const;
BaseClass *baseClassAt(int index) const;
void addBaseClass(BaseClass *baseClass);
+ const std::vector<BaseClass *> &baseClasses() const { return _baseClasses; }
// Symbol's interface
FullySpecifiedType type() const override;
diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp
index e2bc42cf4c..7bfdf5b7ba 100644
--- a/src/plugins/cppeditor/cppquickfix_test.cpp
+++ b/src/plugins/cppeditor/cppquickfix_test.cpp
@@ -7878,6 +7878,75 @@ public:
QTest::newRow("default parameters")
<< header << expected << QByteArray() << QByteArray() << Inside;
+ header = R"--(
+struct Bar{
+ Bar(int i);
+};
+class@ Foo : public Bar{
+ int test;
+public:
+};
+)--";
+ expected = R"--(
+struct Bar{
+ Bar(int i);
+};
+class Foo : public Bar{
+ int test;
+public:
+ Foo(int test, int i) : Bar(i),
+ test(test)
+ {}
+};
+)--";
+ QTest::newRow("parent constructor")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+struct Bar{
+ Bar(int use_i = 6);
+};
+class@ Foo : public Bar{
+ int test;
+public:
+};
+)--";
+ expected = R"--(
+struct Bar{
+ Bar(int use_i = 6);
+};
+class Foo : public Bar{
+ int test;
+public:
+ Foo(int test, int use_i = 6) : Bar(use_i),
+ test(test)
+ {}
+};
+)--";
+ QTest::newRow("parent constructor with default")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
+ header = R"--(
+struct Bar{
+ Bar(int use_i = L'A', int use_i2 = u8"B");
+};
+class@ Foo : public Bar{
+public:
+};
+)--";
+ expected = R"--(
+struct Bar{
+ Bar(int use_i = L'A', int use_i2 = u8"B");
+};
+class Foo : public Bar{
+public:
+ Foo(int use_i = L'A', int use_i2 = u8"B") : Bar(use_i, use_i2)
+ {}
+};
+)--";
+ QTest::newRow("parent constructor with char/string default value")
+ << header << expected << QByteArray() << QByteArray() << Inside;
+
const QByteArray common = R"--(
namespace N{
template<typename T>
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index 84bceab08b..9da58027f3 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -67,6 +67,9 @@
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
+#ifdef WITH_TESTS
+#include <QAbstractItemModelTester>
+#endif
#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
@@ -86,6 +89,7 @@
#include <QRegularExpression>
#include <QSharedPointer>
#include <QStack>
+#include <QStyledItemDelegate>
#include <QTableView>
#include <QTextCodec>
#include <QTextCursor>
@@ -8429,6 +8433,8 @@ void RemoveUsingNamespace::match(const CppQuickFixInterface &interface, QuickFix
namespace {
+struct ParentClassConstructorInfo;
+
class ConstructorMemberInfo
{
public:
@@ -8439,7 +8445,20 @@ public:
, type(symbol->type())
, numberOfMember(numberOfMember)
{}
-
+ ConstructorMemberInfo(const QString &memberName,
+ const QString &paramName,
+ const QString &defaultValue,
+ Symbol *symbol,
+ const ParentClassConstructorInfo *parentClassConstructor)
+ : parentClassConstructor(parentClassConstructor)
+ , memberVariableName(memberName)
+ , parameterName(paramName)
+ , defaultValue(defaultValue)
+ , init(defaultValue.isEmpty())
+ , symbol(symbol)
+ , type(symbol->type())
+ {}
+ const ParentClassConstructorInfo *parentClassConstructor = nullptr;
QString memberVariableName;
QString parameterName;
QString defaultValue;
@@ -8449,12 +8468,12 @@ public:
FullySpecifiedType type;
int numberOfMember; // first member, second member, ...
};
-using ConstructorMemberCandidates = std::vector<ConstructorMemberInfo>;
class ConstructorParams : public QAbstractTableModel
{
Q_OBJECT
- std::vector<ConstructorMemberInfo *> &infos;
+ std::list<ConstructorMemberInfo> candidates;
+ std::vector<ConstructorMemberInfo *> infos;
void validateOrder()
{
@@ -8474,10 +8493,46 @@ class ConstructorParams : public QAbstractTableModel
public:
enum Column { ShouldInitColumn, MemberNameColumn, ParameterNameColumn, DefaultValueColumn };
- ConstructorParams(QObject *parent, std::vector<ConstructorMemberInfo *> &infos)
- : QAbstractTableModel(parent)
- , infos(infos)
- {}
+ template<typename... _Args>
+ void emplaceBackParameter(_Args &&...__args)
+ {
+ candidates.emplace_back(std::forward<_Args>(__args)...);
+ infos.push_back(&candidates.back());
+ }
+ const std::vector<ConstructorMemberInfo *> &getInfos() const { return infos; }
+ void addRow(ConstructorMemberInfo *info)
+ {
+ beginInsertRows({}, rowCount(), rowCount());
+ infos.push_back(info);
+ endInsertRows();
+ validateOrder();
+ }
+ void removeRow(ConstructorMemberInfo *info)
+ {
+ for (auto iter = infos.begin(); iter != infos.end(); ++iter) {
+ if (*iter == info) {
+ const auto index = iter - infos.begin();
+ beginRemoveRows({}, index, index);
+ infos.erase(iter);
+ endRemoveRows();
+ validateOrder();
+ return;
+ }
+ }
+ }
+
+ int selectedCount() const
+ {
+ return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
+ return mi->init && !mi->parentClassConstructor;
+ });
+ }
+ int memberCount() const
+ {
+ return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
+ return !mi->parentClassConstructor;
+ });
+ }
int rowCount(const QModelIndex & /*parent*/ = {}) const override { return infos.size(); }
int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 4; }
@@ -8485,7 +8540,8 @@ public:
{
if (index.row() < 0 || index.row() >= rowCount())
return {};
- if (role == Qt::CheckStateRole && index.column() == ShouldInitColumn)
+ if (role == Qt::CheckStateRole && index.column() == ShouldInitColumn
+ && !infos[index.row()]->parentClassConstructor)
return infos[index.row()]->init ? Qt::Checked : Qt::Unchecked;
if (role == Qt::DisplayRole && index.column() == MemberNameColumn)
return infos[index.row()]->memberVariableName;
@@ -8495,15 +8551,17 @@ public:
if ((role == Qt::DisplayRole || role == Qt::EditRole)
&& index.column() == DefaultValueColumn)
return infos[index.row()]->defaultValue;
- if ((role == Qt::ToolTipRole) && index.column() == DefaultValueColumn)
+ if ((role == Qt::ToolTipRole) && index.column() > 0)
return Overview{}.prettyType(infos[index.row()]->symbol->type());
return {};
}
bool setData(const QModelIndex &index, const QVariant &value, int role) override
{
if (index.column() == ShouldInitColumn && role == Qt::CheckStateRole) {
+ if (infos[index.row()]->parentClassConstructor)
+ return false;
infos[index.row()]->init = value.toInt() == Qt::Checked;
- emit dataChanged(this->index(index.row(), 1), this->index(index.row(), 2));
+ emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
validateOrder();
return true;
}
@@ -8530,7 +8588,7 @@ public:
f |= Qt::ItemIsSelectable;
}
- if (index.column() == ShouldInitColumn)
+ if (index.column() == ShouldInitColumn && !infos[index.row()]->parentClassConstructor)
return f | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
if (!infos[index.row()]->init)
return f;
@@ -8597,14 +8655,14 @@ public:
class TableViewStyle : public QProxyStyle
{
public:
- TableViewStyle(QStyle *style = 0)
+ TableViewStyle(QStyle *style)
: QProxyStyle(style)
{}
void drawPrimitive(PrimitiveElement element,
const QStyleOption *option,
QPainter *painter,
- const QWidget *widget = 0) const
+ const QWidget *widget) const override
{
if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
QStyleOption opt(*option);
@@ -8621,23 +8679,250 @@ signals:
void validOrder(bool valid);
};
+class TopMarginDelegate : public QStyledItemDelegate
+{
+public:
+ TopMarginDelegate(QObject *parent = nullptr)
+ : QStyledItemDelegate(parent)
+ {}
+
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override
+ {
+ Q_ASSERT(index.isValid());
+ QStyleOptionViewItem opt = option;
+ initStyleOption(&opt, index);
+ const QWidget *widget = option.widget;
+ QStyle *style = widget ? widget->style() : QApplication::style();
+ if (opt.rect.height() > 20)
+ opt.rect.adjust(0, 5, 0, 0);
+ style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
+ }
+};
+
+struct ParentClassConstructorParameter : public ConstructorMemberInfo
+{
+ QString originalDefaultValue;
+ QString declaration; // displayed in the treeView
+ ParentClassConstructorParameter(const QString &name,
+ const QString &defaultValue,
+ Symbol *symbol,
+ const ParentClassConstructorInfo *parentClassConstructor);
+
+ ParentClassConstructorParameter(const ParentClassConstructorParameter &) = delete;
+ ParentClassConstructorParameter(ParentClassConstructorParameter &&) = default;
+};
+
+struct ParentClassConstructorInfo
+{
+ ParentClassConstructorInfo(const QString &name, ConstructorParams &model)
+ : className(name)
+ , model(model)
+ {}
+ bool useInConstructor = false;
+ const QString className;
+ QString declaration;
+ std::vector<ParentClassConstructorParameter> parameters;
+ ConstructorParams &model;
+
+ ParentClassConstructorInfo(const ParentClassConstructorInfo &) = delete;
+ ParentClassConstructorInfo(ParentClassConstructorInfo &&) = default;
+
+ void addParameter(ParentClassConstructorParameter &param) { model.addRow(&param); }
+ void removeParameter(ParentClassConstructorParameter &param) { model.removeRow(&param); }
+ void removeAllParameters()
+ {
+ for (auto &param : parameters)
+ model.removeRow(&param);
+ }
+};
+
+ParentClassConstructorParameter::ParentClassConstructorParameter(
+ const QString &name,
+ const QString &defaultValue,
+ Symbol *symbol,
+ const ParentClassConstructorInfo *parentClassConstructor)
+ : ConstructorMemberInfo(parentClassConstructor->className + "::" + name,
+ name,
+ defaultValue,
+ symbol,
+ parentClassConstructor)
+ , originalDefaultValue(defaultValue)
+ , declaration(Overview{}.prettyType(symbol->type(), name)
+ + (defaultValue.isEmpty() ? QString{} : " = " + defaultValue))
+{}
+
+using ParentClassConstructors = std::vector<ParentClassConstructorInfo>;
+
+class ParentClassesModel : public QAbstractItemModel
+{
+ ParentClassConstructors &constructors;
+
+public:
+ ParentClassesModel(QObject *parent, ParentClassConstructors &constructors)
+ : QAbstractItemModel(parent)
+ , constructors(constructors)
+ {}
+ QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override
+ {
+ if (!parent.isValid())
+ return createIndex(row, column, nullptr);
+ if (parent.internalPointer())
+ return {};
+ auto index = createIndex(row, column, &constructors.at(parent.row()));
+ return index;
+ }
+ QModelIndex parent(const QModelIndex &index) const override
+ {
+ if (!index.isValid())
+ return {};
+ auto *parent = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+ if (!parent)
+ return {};
+ int i = 0;
+ for (const auto &info : constructors) {
+ if (&info == parent)
+ return createIndex(i, 0, nullptr);
+ ++i;
+ }
+ return {};
+ }
+ int rowCount(const QModelIndex &parent = {}) const override
+ {
+ if (!parent.isValid())
+ return static_cast<int>(constructors.size());
+ auto info = static_cast<ParentClassConstructorInfo *>(parent.internalPointer());
+ if (!info)
+ return static_cast<int>(constructors.at(parent.row()).parameters.size());
+ return 0;
+ }
+ int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 1; }
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ if (!index.isValid())
+ return {};
+ auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+
+ if (info) {
+ const auto &parameter = info->parameters.at(index.row());
+ if (role == Qt::CheckStateRole)
+ return parameter.init ? Qt::Checked : Qt::Unchecked;
+ if (role == Qt::DisplayRole)
+ return parameter.declaration;
+ return {};
+ }
+ const auto &constructor = constructors.at(index.row());
+ if (role == Qt::CheckStateRole)
+ return constructor.useInConstructor ? Qt::PartiallyChecked : Qt::Unchecked;
+ if (role == Qt::DisplayRole)
+ return constructor.declaration;
+
+ // Highlight the selected items
+ if (role == Qt::FontRole && constructor.useInConstructor) {
+ QFont font = QApplication::font();
+ font.setBold(true);
+ return font;
+ }
+ // Create a margin between sets of constructors for base classes
+ if (role == Qt::SizeHintRole && index.row() > 0
+ && constructor.className != constructors.at(index.row() - 1).className) {
+ return QSize(-1, 25);
+ }
+ return {};
+ }
+ bool setData(const QModelIndex &index, const QVariant &value, int /*role*/) override
+ {
+ if (index.isValid() && index.column() == 0) {
+ auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+ if (info) {
+ const bool nowUse = value.toBool();
+ auto &param = info->parameters.at(index.row());
+ param.init = nowUse;
+ if (nowUse)
+ info->addParameter(param);
+ else
+ info->removeParameter(param);
+ return true;
+ }
+ auto &newConstructor = constructors.at(index.row());
+ // You have to select a base class constructor
+ if (newConstructor.useInConstructor)
+ return false;
+ auto c = std::find_if(constructors.begin(), constructors.end(), [&](const auto &c) {
+ return c.className == newConstructor.className && c.useInConstructor;
+ });
+ QTC_ASSERT(c == constructors.end(), return false;);
+ c->useInConstructor = false;
+ newConstructor.useInConstructor = true;
+ emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
+ auto parentIndex = this->index(index.row(), 0);
+ emit dataChanged(this->index(0, 0, parentIndex),
+ this->index(rowCount(parentIndex), columnCount()));
+ const int oldIndex = c - constructors.begin();
+ emit dataChanged(this->index(oldIndex, 0), this->index(oldIndex, columnCount()));
+ parentIndex = this->index(oldIndex, 0);
+ emit dataChanged(this->index(0, 0, parentIndex),
+ this->index(rowCount(parentIndex), columnCount()));
+ // update other table
+ c->removeAllParameters();
+ for (auto &p : newConstructor.parameters)
+ if (p.init)
+ newConstructor.addParameter(p);
+ return true;
+ }
+ return false;
+ }
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override
+ {
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0:
+ return tr("Base Class Constructors");
+ }
+ }
+ return {};
+ }
+ Qt::ItemFlags flags(const QModelIndex &index) const override
+ {
+ if (index.isValid()) {
+ Qt::ItemFlags f;
+ auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
+ if (!info || info->useInConstructor) {
+ f |= Qt::ItemIsEnabled;
+ }
+ f |= Qt::ItemIsUserCheckable;
+
+ return f;
+ }
+ return {};
+ }
+};
+
class GenerateConstructorDialog : public QDialog
{
Q_DECLARE_TR_FUNCTIONS(GenerateConstructorDialog)
public:
- GenerateConstructorDialog(std::vector<ConstructorMemberInfo *> &candidates)
- : QDialog()
+ GenerateConstructorDialog(ConstructorParams *constructorParamsModel,
+ ParentClassConstructors &constructors)
{
setWindowTitle(tr("Constructor"));
- const auto model = new ConstructorParams(this, candidates);
+
+ const auto treeModel = new ParentClassesModel(this, constructors);
+ const auto treeView = new QTreeView(this);
+ treeView->setModel(treeModel);
+ treeView->setItemDelegate(new TopMarginDelegate(this));
+ treeView->expandAll();
+
const auto view = new QTableView(this);
- view->setModel(model);
+ view->setModel(constructorParamsModel);
int optimalWidth = 0;
- for (int i = 0; i < model->columnCount(QModelIndex{}); ++i) {
+ for (int i = 0; i < constructorParamsModel->columnCount(QModelIndex{}); ++i) {
view->resizeColumnToContents(i);
optimalWidth += view->columnWidth(i);
}
view->resizeRowsToContents();
+ view->verticalHeader()->setDefaultSectionSize(view->rowHeight(0));
view->setSelectionBehavior(QAbstractItemView::SelectRows);
view->setSelectionMode(QAbstractItemView::SingleSelection);
view->setDragEnabled(true);
@@ -8659,7 +8944,7 @@ public:
QSizePolicy labelSizePolicy = errorLabel->sizePolicy();
labelSizePolicy.setRetainSizeWhenHidden(true);
errorLabel->setSizePolicy(labelSizePolicy);
- connect(model,
+ connect(constructorParamsModel,
&ConstructorParams::validOrder,
[=, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) {
button->setEnabled(valid);
@@ -8669,7 +8954,7 @@ public:
// setup select all/none checkbox
QCheckBox *const checkBox = new QCheckBox(tr("Initialize all members"));
checkBox->setChecked(true);
- connect(checkBox, &QCheckBox::stateChanged, [model](int state) {
+ connect(checkBox, &QCheckBox::stateChanged, [model = constructorParamsModel](int state) {
if (state != Qt::PartiallyChecked) {
for (int i = 0; i < model->rowCount(); ++i)
model->setData(model->index(i, ConstructorParams::ShouldInitColumn),
@@ -8681,20 +8966,19 @@ public:
if (checkBox->checkState() == Qt::PartiallyChecked)
checkBox->setCheckState(Qt::Checked);
});
- connect(model, &QAbstractItemModel::dataChanged, this, [&candidates, checkBox] {
- const auto selectedCount = Utils::count(candidates, [](const ConstructorMemberInfo *mi) {
- return mi->init;
- });
-
- const auto state = [&candidates, selectedCount]() {
- if (selectedCount == 0)
- return Qt::Unchecked;
- if (static_cast<int>(candidates.size()) == selectedCount)
- return Qt::Checked;
- return Qt::PartiallyChecked;
- }();
- checkBox->setCheckState(state);
- });
+ connect(constructorParamsModel,
+ &QAbstractItemModel::dataChanged,
+ this,
+ [model = constructorParamsModel, checkBox] {
+ const auto state = [model, selectedCount = model->selectedCount()]() {
+ if (selectedCount == 0)
+ return Qt::Unchecked;
+ if (static_cast<int>(model->memberCount() == selectedCount))
+ return Qt::Checked;
+ return Qt::PartiallyChecked;
+ }();
+ checkBox->setCheckState(state);
+ });
using A = InsertionPointLocator::AccessSpec;
auto accessCombo = new QComboBox;
@@ -8716,6 +9000,7 @@ public:
mainLayout->addLayout(row);
mainLayout->addWidget(checkBox);
mainLayout->addWidget(view);
+ mainLayout->addWidget(treeView);
mainLayout->addWidget(errorLabel);
mainLayout->addWidget(buttonBox);
int left, right;
@@ -8756,29 +9041,108 @@ public:
if (s->isDeclaration() && (s->isPrivate() || s->isProtected()) && !s->isStatic()) {
const auto name = QString::fromUtf8(s->identifier()->chars(),
s->identifier()->size());
- m_candidates.emplace_back(name, s, memberCounter++);
+ parameterModel.emplaceBackParameter(name, s, memberCounter++);
+ }
+ }
+ Overview o = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ o.showArgumentNames = true;
+ o.showReturnTypes = true;
+ o.showDefaultArguments = true;
+ o.showTemplateParameters = true;
+ o.showFunctionSignatures = true;
+ LookupContext context(currentFile()->cppDocument(), interface.snapshot());
+ for (BaseClass *bc : theClass->baseClasses()) {
+ const QString className = o.prettyName(bc->name());
+
+ ClassOrNamespace *localLookupType = context.lookupType(bc);
+ QList<LookupItem> localLookup = localLookupType->lookup(bc->name());
+ for (auto &li : localLookup) {
+ Symbol *d = li.declaration();
+ if (!d->asClass())
+ continue;
+ for (auto i = d->asClass()->memberBegin(); i != d->asClass()->memberEnd(); ++i) {
+ Symbol *s = *i;
+ if (s->isProtected() || s->isPublic()) {
+ if (s->name()->match(d->name())) {
+ // we have found a constructor
+ Function *func = s->type().type()->asFunctionType();
+ if (!func)
+ continue;
+ const bool isFirst = parentClassConstructors.empty()
+ || parentClassConstructors.back().className
+ != className;
+ parentClassConstructors.emplace_back(className, parameterModel);
+ ParentClassConstructorInfo &constructor = parentClassConstructors.back();
+ constructor.declaration = className + o.prettyType(func->type());
+ constructor.declaration.replace("std::__1::__get_nullptr_t()",
+ "nullptr");
+ constructor.useInConstructor = isFirst;
+ for (auto arg = func->memberBegin(); arg != func->memberEnd(); ++arg) {
+ Symbol *param = *arg;
+ Argument *argument = param->asArgument();
+ if (!argument) // can also be a block
+ continue;
+ const QString name = o.prettyName(param->name());
+ const StringLiteral *ini = argument->initializer();
+ QString defaultValue;
+ if (ini)
+ defaultValue = QString::fromUtf8(ini->chars(), ini->size())
+ .replace("std::__1::__get_nullptr_t()",
+ "nullptr");
+ constructor.parameters.emplace_back(name,
+ defaultValue,
+ param,
+ &constructor);
+ // do not show constructors like QObject(QObjectPrivate & dd, ...)
+ ReferenceType *ref = param->type()->asReferenceType();
+ if (ref && name == "dd") {
+ auto type = o.prettyType(ref->elementType());
+ if (type.startsWith("Q") && type.endsWith("Private")) {
+ parentClassConstructors.pop_back();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
}
}
+
+ // add params to parameter lists
+ for (auto &c : parentClassConstructors)
+ if (c.useInConstructor)
+ for (auto &p : c.parameters)
+ if (p.init)
+ c.addParameter(p);
}
- bool isApplicable() const { return !m_candidates.empty(); }
+ bool isApplicable() const
+ {
+ return parameterModel.rowCount() > 0
+ || Utils::anyOf(parentClassConstructors,
+ [](const auto &parent) { return !parent.parameters.empty(); });
+ }
void setTest(bool isTest = true) { m_test = isTest; }
private:
void perform() override
{
- std::vector<ConstructorMemberInfo *> infos;
- for (auto &info : m_candidates)
- infos.push_back(&info);
+ auto infos = parameterModel.getInfos();
InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
if (!m_test) {
- GenerateConstructorDialog dlg(infos);
+ GenerateConstructorDialog dlg(&parameterModel, parentClassConstructors);
if (dlg.exec() == QDialog::Rejected)
return;
accessSpec = dlg.accessSpec();
+ infos = parameterModel.getInfos();
} else {
+#ifdef WITH_TESTS
+ ParentClassesModel model(nullptr, parentClassConstructors);
+ QAbstractItemModelTester tester(&model);
+#endif
if (infos.size() >= 3) {
// if we are testing and have 3 or more members => change the order
// move first element to the back
@@ -8789,6 +9153,16 @@ private:
if (info->memberVariableName.startsWith("di_"))
info->defaultValue = "42";
}
+ for (auto &c : parentClassConstructors) {
+ if (c.useInConstructor) {
+ for (auto &p : c.parameters) {
+ if (!p.init && p.parameterName.startsWith("use_")) {
+ infos.push_back(&p);
+ p.init = true;
+ }
+ }
+ }
+ }
}
if (infos.empty())
return;
@@ -8805,7 +9179,8 @@ private:
, m_classAST(classAST)
, m_accessSpec(accessSpec)
{}
- void generateConstructor(std::vector<ConstructorMemberInfo *> members)
+ void generateConstructor(std::vector<ConstructorMemberInfo *> members,
+ const ParentClassConstructors &parentClassConstructors)
{
auto constructorLocation = m_settings->determineSetterLocation(members.size());
if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile
@@ -8865,7 +9240,46 @@ private:
}
}
Utils::sort(members, &ConstructorMemberInfo::numberOfMember);
+ // first, do the base classes
+ for (const auto &parent : parentClassConstructors) {
+ if (!parent.useInConstructor)
+ continue;
+ // Check if we really need a constructor
+ if (Utils::anyOf(parent.parameters, [](const auto &param) {
+ return param.init || param.originalDefaultValue.isEmpty();
+ })) {
+ int defaultAtEndCount = 0;
+ for (auto i = parent.parameters.crbegin(); i != parent.parameters.crend();
+ ++i) {
+ if (i->init || i->originalDefaultValue.isEmpty())
+ break;
+ ++defaultAtEndCount;
+ }
+ const int numberOfParameters = static_cast<int>(parent.parameters.size())
+ - defaultAtEndCount;
+ constructorBody += parent.className + "(";
+ int counter = 0;
+ for (const auto &param : parent.parameters) {
+ if (++counter > numberOfParameters)
+ break;
+ if (param.init) {
+ if (param.customValueType)
+ constructorBody += "std::move(" + param.parameterName + ')';
+ else
+ constructorBody += param.parameterName;
+ } else if (!param.originalDefaultValue.isEmpty())
+ constructorBody += param.originalDefaultValue;
+ else
+ constructorBody += "/* insert value */";
+ constructorBody += ", ";
+ }
+ constructorBody.resize(constructorBody.length() - 2);
+ constructorBody += "),\n";
+ }
+ }
for (auto &member : members) {
+ if (member->parentClassConstructor)
+ continue;
QString param = member->parameterName;
if (member->customValueType)
param = "std::move(" + member->parameterName + ')';
@@ -8909,12 +9323,15 @@ private:
m_classAST,
accessSpec);
- auto members = Utils::filtered(infos, [](const auto mi) { return mi->init; });
- helper.generateConstructor(std::move(members));
+ auto members = Utils::filtered(infos, [](const auto mi) {
+ return mi->init || mi->parentClassConstructor;
+ });
+ helper.generateConstructor(std::move(members), parentClassConstructors);
helper.applyChanges();
}
- ConstructorMemberCandidates m_candidates;
+ ConstructorParams parameterModel;
+ ParentClassConstructors parentClassConstructors;
const ClassSpecifierAST *m_classAST = nullptr;
bool m_test = false;
};