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 <ulf.hermann@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Maximilian Goldstein 2021-04-26 17:08:42 +02:00
parent facffe8e57
commit 077b8f8df4
13 changed files with 49 additions and 4 deletions

View File

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

View File

@ -137,4 +137,6 @@
#define QML_UNAVAILABLE \
QML_FOREIGN(QQmlTypeNotAvailable)
#define QML_CUSTOMPARSER Q_CLASSINFO("QML.HasCustomParser", "true")
#endif // QQMLREGISTRATION_H

View File

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

View File

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

View File

@ -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(),

View File

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

View File

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

View File

@ -53,6 +53,7 @@ struct QmlTypesClassDescription
bool isCreatable = true;
bool isSingleton = false;
bool isRootClass = false;
bool hasCustomParser = false;
QStringList implementsInterfaces;
enum CollectMode {

View File

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

View File

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

View File

@ -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" }
}
]
}

View File

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

View File

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