diff options
author | Leander Schulten <Leander.Schulten@rwth-aachen.de> | 2021-01-30 22:11:04 +0100 |
---|---|---|
committer | Leander Schulten <Leander.Schulten@rwth-aachen.de> | 2021-02-04 11:40:05 +0000 |
commit | 29207e3eebb5ea7597d4160bbf171f1cec0edd5c (patch) | |
tree | 2bddd9ccf4f8f26ad51ca074bef3f7c83c58c32f | |
parent | 0e0c2ca91c53e7bca3c8ea0a1c047a87b0873ac7 (diff) | |
download | qt-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.cpp | 27 | ||||
-rw-r--r-- | src/libs/3rdparty/cplusplus/Symbols.h | 1 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppquickfix_test.cpp | 69 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppquickfixes.cpp | 505 |
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 ¶mName, + 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 ¶m) { model.addRow(¶m); } + void removeParameter(ParentClassConstructorParameter ¶m) { model.removeRow(¶m); } + void removeAllParameters() + { + for (auto ¶m : parameters) + model.removeRow(¶m); + } +}; + +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 ¶meter = 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 ¶m = 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(¶meterModel, 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 ¶m) { + 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 ¶m : 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; }; |