From 077b8f8df437d1cfcf67b153dd4535c36786bdee Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Mon, 26 Apr 2021 17:08:42 +0200 Subject: [PATCH] qmltypes: Add information about QQmlCustomParser Adds a macro called QML_CUSTOMPARSER which will result in a hasCustomParser flag to be set in qmltypes. Also ensures qmllint does not warn about unknown properties when a class utilizes custom parsers. Fixes: QTBUG-93027 Change-Id: I7559d79c9bf06a0a7a93f54370948f12d09dd64e Reviewed-by: Ulf Hermann Reviewed-by: Fabian Kosmale --- src/imports/tooling/Component.qml | 1 + src/qml/qml/qqmlregistration.h | 2 ++ src/qml/types/qqmlconnections_p.h | 1 + src/qmlcompiler/qqmljsscope_p.h | 8 +++++++- src/qmlcompiler/qqmljstypedescriptionreader.cpp | 8 ++++++-- src/qmlmodels/qqmllistmodel_p.h | 1 + .../qmltypesclassdescription.cpp | 3 +++ src/qmltyperegistrar/qmltypesclassdescription.h | 1 + src/qmltyperegistrar/qmltypescreator.cpp | 3 +++ src/quick/util/qquickpropertychanges_p.h | 2 +- tests/auto/qml/qmllint/data/customParser.qml | 17 +++++++++++++++++ tests/auto/qml/qmllint/tst_qmllint.cpp | 1 + tools/qmllint/findwarnings.cpp | 5 +++++ 13 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/auto/qml/qmllint/data/customParser.qml diff --git a/src/imports/tooling/Component.qml b/src/imports/tooling/Component.qml index 942d838900..b9aa8794d5 100644 --- a/src/imports/tooling/Component.qml +++ b/src/imports/tooling/Component.qml @@ -54,6 +54,7 @@ QtObject { property bool isSingleton: false property bool isCreatable: name.length > 0 property bool isComposite: false + property bool hasCustomParser: false property string accessSemantics: "reference" property string defaultProperty } diff --git a/src/qml/qml/qqmlregistration.h b/src/qml/qml/qqmlregistration.h index 5df8f4badf..bad782767e 100644 --- a/src/qml/qml/qqmlregistration.h +++ b/src/qml/qml/qqmlregistration.h @@ -137,4 +137,6 @@ #define QML_UNAVAILABLE \ QML_FOREIGN(QQmlTypeNotAvailable) +#define QML_CUSTOMPARSER Q_CLASSINFO("QML.HasCustomParser", "true") + #endif // QQMLREGISTRATION_H diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index 8ed874d9fc..0cf6d045af 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -73,6 +73,7 @@ class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatu Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) QML_NAMED_ELEMENT(Connections) QML_ADDED_IN_VERSION(2, 0) + QML_CUSTOMPARSER public: QQmlConnections(QObject *parent=nullptr); diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index f895f5e746..aa6bc99a70 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -112,7 +112,8 @@ public: Creatable = 0x1, Composite = 0x2, Singleton = 0x4, - Script = 0x8, + Script = 0x8, + CustomParser = 0x10, }; Q_DECLARE_FLAGS(Flags, Flag) Q_FLAGS(Flags); @@ -264,10 +265,15 @@ public: bool isCreatable() const { return m_flags & Creatable; } bool isComposite() const { return m_flags & Composite; } bool isScript() const { return m_flags & Script; } + bool hasCustomParser() const { return m_flags & CustomParser; } void setIsSingleton(bool v) { m_flags = v ? (m_flags | Singleton) : (m_flags & ~Singleton); } void setIsCreatable(bool v) { m_flags = v ? (m_flags | Creatable) : (m_flags & ~Creatable); } void setIsComposite(bool v) { m_flags = v ? (m_flags | Composite) : (m_flags & ~Composite); } void setIsScript(bool v) { m_flags = v ? (m_flags | Script) : (m_flags & ~Script); } + void setHasCustomParser(bool v) + { + m_flags = v ? (m_flags | CustomParser) : (m_flags & ~CustomParser); + } void setAccessSemantics(AccessSemantics semantics) { m_semantics = semantics; } AccessSemantics accessSemantics() const { return m_semantics; } diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp index 61587e818c..106bfac654 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp +++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp @@ -234,6 +234,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast) scope->setIsCreatable(readBoolBinding(script)); } else if (name == QLatin1String("isComposite")) { scope->setIsComposite(readBoolBinding(script)); + } else if (name == QLatin1String("hasCustomParser")) { + scope->setHasCustomParser(readBoolBinding(script)); } else if (name == QLatin1String("accessSemantics")) { const QString semantics = readStringBinding(script); if (semantics == QLatin1String("reference")) { @@ -253,8 +255,10 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast) } else { addWarning(script->firstSourceLocation(), tr("Expected only name, prototype, defaultProperty, attachedType, " - "valueType, exports, interfaces, isSingleton, isCreatable, isComposite and " - "exportMetaObjectRevisions script bindings, not \"%1\".").arg(name)); + "valueType, exports, interfaces, isSingleton, isCreatable, " + "isComposite, hasCustomParser and " + "exportMetaObjectRevisions script bindings, not \"%1\".") + .arg(name)); } } else { addWarning(member->firstSourceLocation(), diff --git a/src/qmlmodels/qqmllistmodel_p.h b/src/qmlmodels/qqmllistmodel_p.h index bf4279cd05..2e8181e10c 100644 --- a/src/qmlmodels/qqmllistmodel_p.h +++ b/src/qmlmodels/qqmllistmodel_p.h @@ -85,6 +85,7 @@ class Q_QMLMODELS_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel Q_PROPERTY(QObject *agent READ agent CONSTANT REVISION(2, 14)) QML_NAMED_ELEMENT(ListModel) QML_ADDED_IN_VERSION(2, 0) + QML_CUSTOMPARSER public: QQmlListModel(QObject *parent=nullptr); diff --git a/src/qmltyperegistrar/qmltypesclassdescription.cpp b/src/qmltyperegistrar/qmltypesclassdescription.cpp index 5d022d6488..4eb65adea8 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.cpp +++ b/src/qmltyperegistrar/qmltypesclassdescription.cpp @@ -177,6 +177,9 @@ void QmlTypesClassDescription::collect( foreignTypeName = value; } else if (name == QLatin1String("QML.Root")) { isRootClass = true; + } else if (name == QLatin1String("QML.HasCustomParser")) { + if (value == QLatin1String("true")) + hasCustomParser = true; } } diff --git a/src/qmltyperegistrar/qmltypesclassdescription.h b/src/qmltyperegistrar/qmltypesclassdescription.h index 04d3adac77..42eeaafee4 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.h +++ b/src/qmltyperegistrar/qmltypesclassdescription.h @@ -53,6 +53,7 @@ struct QmlTypesClassDescription bool isCreatable = true; bool isSingleton = false; bool isRootClass = false; + bool hasCustomParser = false; QStringList implementsInterfaces; enum CollectMode { diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp index 414ded0eb1..ce67db1a2e 100644 --- a/src/qmltyperegistrar/qmltypescreator.cpp +++ b/src/qmltyperegistrar/qmltypescreator.cpp @@ -103,6 +103,9 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle if (collector.isSingleton) m_qml.writeScriptBinding(QLatin1String("isSingleton"), QLatin1String("true")); + if (collector.hasCustomParser) + m_qml.writeScriptBinding(QLatin1String("hasCustomParser"), QLatin1String("true")); + m_qml.writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjects); if (!collector.attachedType.isEmpty()) diff --git a/src/quick/util/qquickpropertychanges_p.h b/src/quick/util/qquickpropertychanges_p.h index ff48de96c6..fa38065ebe 100644 --- a/src/quick/util/qquickpropertychanges_p.h +++ b/src/quick/util/qquickpropertychanges_p.h @@ -61,12 +61,12 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPropertyChanges : public QQuickStateOperation { Q_OBJECT Q_DECLARE_PRIVATE(QQuickPropertyChanges) - Q_PROPERTY(QObject *target READ object WRITE setObject) Q_PROPERTY(bool restoreEntryValues READ restoreEntryValues WRITE setRestoreEntryValues) Q_PROPERTY(bool explicit READ isExplicit WRITE setIsExplicit) QML_NAMED_ELEMENT(PropertyChanges) QML_ADDED_IN_VERSION(2, 0) + QML_CUSTOMPARSER public: QQuickPropertyChanges(); diff --git a/tests/auto/qml/qmllint/data/customParser.qml b/tests/auto/qml/qmllint/data/customParser.qml new file mode 100644 index 0000000000..a83ae7e823 --- /dev/null +++ b/tests/auto/qml/qmllint/data/customParser.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 + +Rectangle { + id: root + width: 100; height: 100 + + states: [ + State { + name: "red_color" + PropertyChanges { target: root; color: "red" } + }, + State { + name: "blue_color" + PropertyChanges { target: root; color: "blue" } + } + ] +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index c5e7181a92..65f3197253 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -709,6 +709,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("multilineStringEscaped") << QStringLiteral("multilineStringEscaped.qml"); QTest::newRow("propertyOverride") << QStringLiteral("propertyOverride.qml"); QTest::newRow("propertyBindingValue") << QStringLiteral("propertyBindingValue.qml"); + QTest::newRow("customParser") << QStringLiteral("customParser.qml"); } void TestQmllint::cleanQmlCode() diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp index 51db82cbde..825baf1be1 100644 --- a/tools/qmllint/findwarnings.cpp +++ b/tools/qmllint/findwarnings.cpp @@ -287,6 +287,11 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb) } if (!qmlScope->hasProperty(name.toString())) { + // These warnings do not apply for custom parsers and need to be handled on a case by + // case basis + if (qmlScope->baseType()->hasCustomParser()) + return true; + // TODO: Can this be in a better suited category? m_logger.log( QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "