CppEditor: Add Base Class Support for Generate Constructor QuickFix

Change-Id: Idd92229134609c0ac87aad030a6bb645ff4cce1b
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Leander Schulten
2021-01-30 22:11:04 +01:00
parent 0e0c2ca91c
commit 29207e3eeb
4 changed files with 558 additions and 44 deletions

View File

@@ -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()));
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;
};