From 313d473d700e2dfda8e060d3ce577b598314d0d3 Mon Sep 17 00:00:00 2001 From: Sami Shalayel Date: Mon, 27 Jan 2025 15:41:40 +0100 Subject: qdslintplugin: add UnsupportedRootTypeInQmlUi warning Implement ErrUnsupportedTypeInQmlUi that warns about unsupported root types in .ui.qml files. Also reuse the same warning message. Task-number: QTBUG-129308 Change-Id: I83a2cb39367ea3fe6ac65ab1e0b16c04cf467d73 Reviewed-by: Ulf Hermann --- src/plugins/qmllint/qds/plugin.json | 5 ++ src/plugins/qmllint/qds/qdslintplugin.cpp | 67 +++++++++++++++++----- .../data/qdsPlugin/UnsupportedRootElement.ui.qml | 3 + .../data/qdsPlugin/UnsupportedRootElement2.ui.qml | 7 +++ tests/auto/qml/qmllint/tst_qmllint.cpp | 16 ++++++ 5 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement.ui.qml create mode 100644 tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement2.ui.qml diff --git a/src/plugins/qmllint/qds/plugin.json b/src/plugins/qmllint/qds/plugin.json index a77ef0398b..9d9d482510 100644 --- a/src/plugins/qmllint/qds/plugin.json +++ b/src/plugins/qmllint/qds/plugin.json @@ -29,6 +29,11 @@ "name": "InvalidIdeInVisualDesigner", "settingsName": "InvalidIdeInVisualDesigner", "description": "Warn about ambiguous id names in ui.qml files" + }, + { + "name": "UnsupportedRootTypeInQmlUi", + "settingsName": "UnsupportedRootTypeInQmlUi", + "description": "Warn about unsupported root types in ui.qml files" } ] } diff --git a/src/plugins/qmllint/qds/qdslintplugin.cpp b/src/plugins/qmllint/qds/qdslintplugin.cpp index 04a5620c33..9855ac3a52 100644 --- a/src/plugins/qmllint/qds/qdslintplugin.cpp +++ b/src/plugins/qmllint/qds/qdslintplugin.cpp @@ -7,6 +7,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -29,6 +30,9 @@ static constexpr LoggerWarningId ErrUnsupportedTypeInQmlUi{ static constexpr LoggerWarningId ErrInvalidIdeInVisualDesigner{ "QtDesignStudio.InvalidIdeInVisualDesigner" }; +static constexpr LoggerWarningId ErrUnsupportedRootTypeInQmlUi{ + "QtDesignStudio.UnsupportedRootTypeInQmlUi" +}; class FunctionCallValidator : public PropertyPass { @@ -71,7 +75,16 @@ private: std::make_pair("QtQml.Models"_L1, "Package"_L1), std::make_pair("QtQuick"_L1, "ShaderEffect"_L1), }; + using UnsupportedName = decltype(s_unsupportedElementNames)::value_type; std::array m_unsupportedElements; + + static constexpr std::array s_unsupportedRootNames = { + std::make_pair("QtQml.Models"_L1, "ListModel"_L1), + std::make_pair("QtQml.Models"_L1, "Package"_L1), + std::make_pair("QtQml"_L1, "Timer"_L1), + }; + std::array m_unsupportedRootElements; + Element m_qtObject; }; void QdsBindingValidator::onRead(const QQmlSA::Element &element, const QString &propertyName, @@ -216,25 +229,49 @@ void FunctionCallValidator::onCall(const Element &element, const QString &proper QdsElementValidator::QdsElementValidator(PassManager *manager) : ElementPass(manager) { - for (qsizetype i = 0; i < qsizetype(s_unsupportedElementNames.size()); ++i) { - if (!manager->hasImportedModule(s_unsupportedElementNames[i].first)) - continue; - m_unsupportedElements[i] = resolveType(s_unsupportedElementNames[i].first, - s_unsupportedElementNames[i].second); - } + auto loadTypes = [&manager, this](QSpan names, QSpan output) { + for (qsizetype i = 0; i < qsizetype(names.size()); ++i) { + if (!manager->hasImportedModule(names[i].first)) + continue; + output[i] = resolveType(names[i].first, names[i].second); + } + }; + loadTypes(s_unsupportedElementNames, m_unsupportedElements); + loadTypes(s_unsupportedRootNames, m_unsupportedRootElements); + m_qtObject = resolveType("QtQml"_L1, "QtObject"_L1); } void QdsElementValidator::run(const Element &element) { - for (const auto &unsupportedElement : m_unsupportedElements) { - if (!unsupportedElement || !element.inherits(unsupportedElement)) - continue; - - emitWarning(u"This type (%1) is not supported in a UI file (.ui.qml)."_s.arg( - element.baseTypeName()), - ErrUnsupportedTypeInQmlUi, element.sourceLocation()); - break; - } + enum WarningType { ForElements, ForRootElements }; + auto warnIfElementIsUnsupported = [this, &element](WarningType warningType) { + QSpan unsupportedComponents = warningType == ForElements + ? QSpan(m_unsupportedElements) + : QSpan(m_unsupportedRootElements); + const QStringView message = warningType == ForElements + ? u"This type (%1) is not supported in a UI file (.ui.qml)." + : u"This type (%1) is not supported as a root element of a UI file (.ui.qml)."; + const LoggerWarningId &id = warningType == ForElements ? ErrUnsupportedTypeInQmlUi + : ErrUnsupportedRootTypeInQmlUi; + + for (const auto &unsupportedElement : unsupportedComponents) { + if (!unsupportedElement || !element.inherits(unsupportedElement)) + continue; + + emitWarning(message.arg(element.baseTypeName()), id, element.sourceLocation()); + break; + } + + // special case: we don't want to warn on types indirectly inheriting from QtObject, for + // example Item. + if (warningType == ForRootElements && element.baseType() == m_qtObject) + emitWarning(message.arg(element.baseTypeName()), id, element.sourceLocation()); + }; + + if (element.isFileRootComponent()) + warnIfElementIsUnsupported(ForRootElements); + warnIfElementIsUnsupported(ForElements); + if (QString id = resolveElementToId(element, element); !id.isEmpty()) { static constexpr std::array unsupportedNames = { "action"_L1, "alias"_L1, "anchors"_L1, "as"_L1, "baseState"_L1, diff --git a/tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement.ui.qml b/tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement.ui.qml new file mode 100644 index 0000000000..4611af3533 --- /dev/null +++ b/tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement.ui.qml @@ -0,0 +1,3 @@ +import QtQuick + +QtObject {} diff --git a/tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement2.ui.qml b/tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement2.ui.qml new file mode 100644 index 0000000000..46c1db7a61 --- /dev/null +++ b/tests/auto/qml/qmllint/data/qdsPlugin/UnsupportedRootElement2.ui.qml @@ -0,0 +1,7 @@ +import QtQuick +import QtQml.Models + +ListModel { + +} + diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 7ca19a029b..93994a2926 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -2612,6 +2612,22 @@ void TestQmllint::qdsPlugin_data() QTest::addRow("SupportedElements") << u"qdsPlugin/SupportedElements.ui.qml"_s << Result::clean(); + + QTest::addRow("UnsupportedRootElement") + << u"qdsPlugin/UnsupportedRootElement.ui.qml"_s + << Result{ { + Message{ + u"This type (QtObject) is not supported as a root element of a UI file (.ui.qml)."_s, + 3, 1 }, + } }; + + QTest::addRow("UnsupportedRootElement2") + << u"qdsPlugin/UnsupportedRootElement2.ui.qml"_s + << Result{ { + Message{ + u"This type (ListModel) is not supported as a root element of a UI file (.ui.qml)."_s, + 4, 1 }, + } }; } void TestQmllint::qdsPlugin() -- cgit v1.2.3