diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index c1e3e98f43b..893cc5b8ecd 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -2698,10 +2698,6 @@ ConnectionSyntax WriteInitialization::connectionSyntax(const language::SignalSlo return ConnectionSyntax::StringBased; } - // QTBUG-110952, ambiguous overloads of display() - if (receiver.className == u"QLCDNumber" && receiver.signature.startsWith(u"display(")) - return ConnectionSyntax::StringBased; - if ((sender.name == m_mainFormVarName && m_customSignals.contains(sender.signature)) || (receiver.name == m_mainFormVarName && m_customSlots.contains(receiver.signature))) { return ConnectionSyntax::StringBased; @@ -2729,14 +2725,21 @@ void WriteInitialization::acceptConnection(DomConnection *connection) return; } const QString senderSignature = connection->elementSignal(); + const QString slotSignature = connection->elementSlot(); + const bool senderAmbiguous = m_uic->customWidgetsInfo()->isAmbiguousSignal(senderDecl.className, + senderSignature); + const bool slotAmbiguous = m_uic->customWidgetsInfo()->isAmbiguousSlot(receiverDecl.className, + slotSignature); + language::SignalSlotOptions signalOptions; - if (m_uic->customWidgetsInfo()->isAmbiguousSignal(senderDecl.className, senderSignature)) - signalOptions.setFlag(language::SignalSlotOption::Ambiguous); + signalOptions.setFlag(language::SignalSlotOption::Ambiguous, senderAmbiguous); + language::SignalSlotOptions slotOptions; + slotOptions.setFlag(language::SignalSlotOption::Ambiguous, slotAmbiguous); language::SignalSlot theSignal{senderDecl.name, senderSignature, senderDecl.className, signalOptions}; - language::SignalSlot theSlot{receiverDecl.name, connection->elementSlot(), - receiverDecl.className, {}}; + language::SignalSlot theSlot{receiverDecl.name, slotSignature, + receiverDecl.className, slotOptions}; m_output << m_indent; language::formatConnection(m_output, theSignal, theSlot, diff --git a/src/tools/uic/customwidgetsinfo.cpp b/src/tools/uic/customwidgetsinfo.cpp index 169cdad618c..6ec418634c2 100644 --- a/src/tools/uic/customwidgetsinfo.cpp +++ b/src/tools/uic/customwidgetsinfo.cpp @@ -78,19 +78,89 @@ bool CustomWidgetsInfo::isCustomWidgetContainer(const QString &className) const return false; } +// FIXME in 7.0 - QTBUG-124241 +// Remove isAmbiguous logic when widget slots have been disambiguated. +bool CustomWidgetsInfo::isAmbiguous(const QString &className, const QString &signature, + QMetaMethod::MethodType type) const +{ + using TypeMap = QHash; + struct AmbiguousInClass { + QLatin1StringView className; + TypeMap methodMap; + }; + + static const QList ambiguousList = { + + {"QAction"_L1, {{"triggered"_L1, QMetaMethod::Signal}}}, + {"QCommandLinkButton"_L1, {{"triggered"_L1, QMetaMethod::Signal}, + {"clicked"_L1, QMetaMethod::Signal}}}, + {"QPushButton"_L1, {{"triggered"_L1, QMetaMethod::Signal}, + {"clicked"_L1, QMetaMethod::Signal}}}, + {"QCheckBox"_L1, {{"triggered"_L1, QMetaMethod::Signal}, + {"clicked"_L1, QMetaMethod::Signal}}}, + {"QRadioButton"_L1, {{"triggered"_L1, QMetaMethod::Signal}, + {"clicked"_L1, QMetaMethod::Signal}}}, + {"QToolButton"_L1, {{"triggered"_L1, QMetaMethod::Signal}, + {"clicked"_L1, QMetaMethod::Signal}}}, + {"QLabel"_L1, {{"setNum"_L1, QMetaMethod::Slot}}}, + {"QGraphicsView"_L1, {{"invalidateScene"_L1, QMetaMethod::Slot}}}, + {"QListView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}}, + {"QColumnView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}}, + {"QListWidget"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}, + {"scrollToItem"_L1, QMetaMethod::Slot}}}, + {"QTableView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}}, + {"QTableWidget"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}, + {"scrollToItem"_L1, QMetaMethod::Slot}}}, + {"QTreeView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}, + {"verticalScrollbarValueChanged"_L1, QMetaMethod::Slot}, + {"expandRecursively"_L1, QMetaMethod::Slot}}}, + {"QTreeWidget"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}, + {"verticalScrollbarValueChanged"_L1, QMetaMethod::Slot} + ,{"expandRecursively"_L1, QMetaMethod::Slot} + ,{"scrollToItem"_L1, QMetaMethod::Slot}}}, + {"QUndoView"_L1, {{"dataChanged"_L1, QMetaMethod::Slot}}}, + {"QLCDNumber"_L1, {{"display"_L1, QMetaMethod::Slot}}}, + {"QMenuBar"_L1, {{"setVisible"_L1, QMetaMethod::Slot}}}, + {"QTextBrowser"_L1, {{"setSource"_L1, QMetaMethod::Slot}}}, + + /* + The following widgets with ambiguities are not used in the widget designer: + + {"QSplashScreen"_L1, {{"showMessage"_L1, QMetaMethod::Slot}}}, + {"QCompleter"_L1, {{"activated"_L1, QMetaMethod::Signal}, + {"highlighted"_L1, QMetaMethod::Signal}}}, + {"QSystemTrayIcon"_L1, {{"showMessage"_L1, QMetaMethod::Slot}}}, + {"QStyledItemDelegate"_L1, {{"closeEditor"_L1, QMetaMethod::Signal}}}, + {"QErrorMessage"_L1, {{"showMessage"_L1, QMetaMethod::Slot}}}, + {"QGraphicsDropShadowEffect"_L1, {{"setOffset"_L1, QMetaMethod::Slot}}}, + {"QGraphicsScene"_L1, {{"invalidate"_L1, QMetaMethod::Slot}}}, + {"QItemDelegate"_L1, {{"closeEditor"_L1, QMetaMethod::Signal}}} + */ + }; + + for (auto it = ambiguousList.constBegin(); it != ambiguousList.constEnd(); ++it) { + if (extends(className, it->className)) { + const qsizetype pos = signature.indexOf(u'('); + const QString method = signature.left(pos); + const auto methodIterator = it->methodMap.find(method); + return methodIterator != it->methodMap.constEnd() && type == methodIterator.value(); + } + } + return false; +} + // Is it ambiguous, resulting in different signals for Python // "QAbstractButton::clicked(checked=false)" bool CustomWidgetsInfo::isAmbiguousSignal(const QString &className, const QString &signalSignature) const { - if (signalSignature.startsWith(u"triggered") && extends(className, "QAction")) - return true; - if (signalSignature.startsWith(u"clicked(") - && extendsOneOf(className, {u"QCommandLinkButton"_s, u"QCheckBox"_s, - u"QPushButton"_s, u"QRadioButton"_s, u"QToolButton"_s})) { - return true; - } - return false; + return isAmbiguous(className, signalSignature, QMetaMethod::Signal); +} + +bool CustomWidgetsInfo::isAmbiguousSlot(const QString &className, + const QString &signalSignature) const +{ + return isAmbiguous(className, signalSignature, QMetaMethod::Slot); } QString CustomWidgetsInfo::realClassName(const QString &className) const diff --git a/src/tools/uic/customwidgetsinfo.h b/src/tools/uic/customwidgetsinfo.h index 4bd004bdc72..f336292f2ac 100644 --- a/src/tools/uic/customwidgetsinfo.h +++ b/src/tools/uic/customwidgetsinfo.h @@ -7,6 +7,7 @@ #include "treewalker.h" #include #include +#include QT_BEGIN_NAMESPACE @@ -38,10 +39,14 @@ public: bool isAmbiguousSignal(const QString &className, const QString &signalSignature) const; + bool isAmbiguousSlot(const QString &className, + const QString &slotSignature) const; private: using NameCustomWidgetMap = QMap; NameCustomWidgetMap m_customWidgets; + bool isAmbiguous(const QString &className, const QString &signature, + QMetaMethod::MethodType type) const; }; QT_END_NAMESPACE diff --git a/src/tools/uic/shared/language.cpp b/src/tools/uic/shared/language.cpp index bfdce0bc4c3..d59688e3466 100644 --- a/src/tools/uic/shared/language.cpp +++ b/src/tools/uic/shared/language.cpp @@ -4,6 +4,7 @@ #include "language.h" #include +#include namespace language { @@ -370,17 +371,40 @@ void _formatStackVariable(QTextStream &str, const char *className, QStringView v } } -enum OverloadUse { - UseOverload, - UseOverloadWhenNoArguments, // Use overload only when the argument list is empty, - // in this case there is no chance of connecting - // mismatching T against const T & - DontUseOverload +enum class OverloadUse { + Always, + WhenAmbiguousOrEmpty, // Use overload if + // - signal/slot is ambiguous + // - argument list is empty (chance of connecting mismatching T against const T &) + Never, }; // Format a member function for a signal slot connection -static void formatMemberFnPtr(QTextStream &str, const SignalSlot &s, - OverloadUse useQOverload = DontUseOverload) +static bool isConstRef(const QStringView &arg) +{ + return arg.startsWith(u'Q') && arg != "QPoint"_L1 && arg != "QSize"_L1; +} + +static QString formatOverload(const QStringView ¶meters) +{ + QString result = "qOverload<"_L1; + const auto args = QStringView{parameters}.split(u','); + for (qsizetype i = 0, size = args.size(); i < size; ++i) { + const auto &arg = args.at(i); + if (i > 0) + result += u','; + const bool constRef = isConstRef(arg); + if (constRef) + result += "const "_L1; + result += arg; + if (constRef) + result += u'&'; + } + result += u'>'; + return result; +} + +static void formatMemberFnPtr(QTextStream &str, const SignalSlot &s, OverloadUse useQOverload) { const qsizetype parenPos = s.signature.indexOf(u'('); Q_ASSERT(parenPos >= 0); @@ -388,11 +412,24 @@ static void formatMemberFnPtr(QTextStream &str, const SignalSlot &s, const auto parameters = QStringView{s.signature}.mid(parenPos + 1, s.signature.size() - parenPos - 2); - const bool withOverload = useQOverload == UseOverload || - (useQOverload == UseOverloadWhenNoArguments && parameters.isEmpty()); + + const bool isAmbiguous = s.options.testFlag(SignalSlotOption::Ambiguous); + bool withOverload = false; // just to silence the compiler + + switch (useQOverload) { + case OverloadUse::Always: + withOverload = true; + break; + case OverloadUse::Never: + withOverload = false; + break; + case OverloadUse::WhenAmbiguousOrEmpty: + withOverload = parameters.empty() || isAmbiguous; + break; + } if (withOverload) - str << "qOverload<" << parameters << ">("; + str << formatOverload(parameters) << '('; str << '&' << s.className << "::" << functionName; @@ -405,9 +442,9 @@ static void formatMemberFnPtrConnection(QTextStream &str, const SignalSlot &receiver) { str << "QObject::connect(" << sender.name << ", "; - formatMemberFnPtr(str, sender); + formatMemberFnPtr(str, sender, OverloadUse::Never); str << ", " << receiver.name << ", "; - formatMemberFnPtr(str, receiver, UseOverloadWhenNoArguments); + formatMemberFnPtr(str, receiver, OverloadUse::WhenAmbiguousOrEmpty); str << ')'; } diff --git a/tests/auto/tools/uic/baseline/qoverload.ui b/tests/auto/tools/uic/baseline/qoverload.ui new file mode 100644 index 00000000000..fb9e5294b7d --- /dev/null +++ b/tests/auto/tools/uic/baseline/qoverload.ui @@ -0,0 +1,93 @@ + + + MainWindow + + + + 0 + 0 + 400 + 300 + + + + MainWindow + + + + + + + Qt::Horizontal + + + + + + + TextLabel + + + + + + + + + + + + 0 + 0 + 400 + 29 + + + + + + TopToolBarArea + + + false + + + + + + + + + horizontalSlider + valueChanged(int) + label + setNum(int) + + + 199 + 67 + + + 199 + 172 + + + + + label + linkActivated(QString) + lcdNumber + display(QString) + + + 199 + 126 + + + 199 + 218 + + + + + diff --git a/tests/auto/tools/uic/baseline/qoverload.ui.h b/tests/auto/tools/uic/baseline/qoverload.ui.h new file mode 100644 index 00000000000..4ab0e37d1f3 --- /dev/null +++ b/tests/auto/tools/uic/baseline/qoverload.ui.h @@ -0,0 +1,98 @@ +/******************************************************************************** +** Form generated from reading UI file 'qoverload.ui' +** +** Created by: Qt User Interface Compiler version 6.8.0 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef QOVERLOAD_H +#define QOVERLOAD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Ui_MainWindow +{ +public: + QWidget *centralWidget; + QVBoxLayout *verticalLayout; + QSlider *horizontalSlider; + QLabel *label; + QLCDNumber *lcdNumber; + QMenuBar *menuBar; + QToolBar *mainToolBar; + QStatusBar *statusBar; + + void setupUi(QMainWindow *MainWindow) + { + if (MainWindow->objectName().isEmpty()) + MainWindow->setObjectName("MainWindow"); + MainWindow->resize(400, 300); + centralWidget = new QWidget(MainWindow); + centralWidget->setObjectName("centralWidget"); + verticalLayout = new QVBoxLayout(centralWidget); + verticalLayout->setSpacing(6); + verticalLayout->setContentsMargins(11, 11, 11, 11); + verticalLayout->setObjectName("verticalLayout"); + horizontalSlider = new QSlider(centralWidget); + horizontalSlider->setObjectName("horizontalSlider"); + horizontalSlider->setOrientation(Qt::Horizontal); + + verticalLayout->addWidget(horizontalSlider); + + label = new QLabel(centralWidget); + label->setObjectName("label"); + + verticalLayout->addWidget(label); + + lcdNumber = new QLCDNumber(centralWidget); + lcdNumber->setObjectName("lcdNumber"); + + verticalLayout->addWidget(lcdNumber); + + MainWindow->setCentralWidget(centralWidget); + menuBar = new QMenuBar(MainWindow); + menuBar->setObjectName("menuBar"); + menuBar->setGeometry(QRect(0, 0, 400, 29)); + MainWindow->setMenuBar(menuBar); + mainToolBar = new QToolBar(MainWindow); + mainToolBar->setObjectName("mainToolBar"); + MainWindow->addToolBar(Qt::ToolBarArea::TopToolBarArea, mainToolBar); + statusBar = new QStatusBar(MainWindow); + statusBar->setObjectName("statusBar"); + MainWindow->setStatusBar(statusBar); + + retranslateUi(MainWindow); + QObject::connect(horizontalSlider, &QSlider::valueChanged, label, qOverload(&QLabel::setNum)); + QObject::connect(label, &QLabel::linkActivated, lcdNumber, qOverload(&QLCDNumber::display)); + + QMetaObject::connectSlotsByName(MainWindow); + } // setupUi + + void retranslateUi(QMainWindow *MainWindow) + { + MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr)); + label->setText(QCoreApplication::translate("MainWindow", "TextLabel", nullptr)); + } // retranslateUi + +}; + +namespace Ui { + class MainWindow: public Ui_MainWindow {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // QOVERLOAD_H