diff --git a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt index 7a536c92be..9785ffa0c6 100644 --- a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt +++ b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt @@ -17,6 +17,8 @@ set(cpp_sources cpptypes/extensiontypes.h cpptypes/extensiontypes.cpp cpptypes/typewithspecialproperties.h + + cpptypes/typewithmanyproperties.h ) set(qml_sources @@ -50,6 +52,7 @@ set(qml_sources javaScriptFunctions.qml changingBindings.qml propertyAlias.qml + propertyAliasAttributes.qml propertyAlias_external.qml propertyChangeHandler.qml NestedHelloWorld.qml @@ -58,7 +61,7 @@ set(qml_sources listPropertySameName.qml defaultProperty.qml defaultPropertyCorrectSelection.qml - # defaultAlias.qml + defaultAlias.qml propertyReturningFunction.qml AttachedProperty.qml attachedPropertyDerived.qml @@ -99,6 +102,7 @@ set(qml_sources # support types: DefaultPropertySingleChild.qml + DefaultPropertyAliasChild.qml DefaultPropertyManyChildren.qml LocallyImported.qml LocalWithOnCompleted.qml diff --git a/tests/auto/qml/qmltc/QmltcTests/DefaultPropertyAliasChild.qml b/tests/auto/qml/qmltc/QmltcTests/DefaultPropertyAliasChild.qml new file mode 100644 index 0000000000..3672a1385b --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/DefaultPropertyAliasChild.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + id: self + + property QtObject someObject + default property alias child: self.someObject +} diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h new file mode 100644 index 0000000000..873c38771e --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h @@ -0,0 +1,90 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef PROPERTYALIASATTRIBUTES_H +#define PROPERTYALIASATTRIBUTES_H + +#include +#include +#include + +using namespace Qt::Literals; + +class TypeWithManyProperties : public QObject +{ + Q_OBJECT + QML_ELEMENT + + const QString m_readOnly = u"Hello World!"_s; + QString m_readAndWrite; + QString m_resettable; + QString m_unresettable; + QString m_hasAllAttributes; + QString m_hasAllAttributes2; + +public: + TypeWithManyProperties() + { + hasAllAttributesBindable().setBinding( + [&] { return u"From the bindable: "_s + readOnly(); }); + } + + Q_PROPERTY(QString readOnly READ readOnly); + Q_PROPERTY(QString readAndWrite READ readAndWrite WRITE setReadAndWrite); + Q_PROPERTY(QString readAndWriteMember MEMBER m_readAndWrite); + Q_PROPERTY(QString resettable READ resettable WRITE setReadAndWrite RESET resetResettable); + Q_PROPERTY(QString unresettable READ unresettable WRITE setUnresettable); + Q_PROPERTY(QString notifiable READ readAndWrite WRITE setReadAndWriteAndNotify NOTIFY + notifiableChanged); + Q_PROPERTY(QString notifiableMember MEMBER m_readAndWrite NOTIFY notifiableChanged); + Q_PROPERTY(QString latestReadAndWrite MEMBER m_readAndWrite REVISION(1, 0)); + Q_PROPERTY(QString notExisting MEMBER m_readAndWrite REVISION(6, 0)); + + Q_PROPERTY(QString hasAllAttributes READ hasAllAttributes WRITE setHasAllAttributes RESET + resetHasAllAttributes NOTIFY hasAllAttributesChanged REVISION(1, 0) + BINDABLE hasAllAttributesBindable + DESIGNABLE false SCRIPTABLE true STORED false USER true FINAL + REQUIRED); + + Q_OBJECT_BINDABLE_PROPERTY(TypeWithManyProperties, QString, hasAllAttributesProperty); + + QBindable hasAllAttributesBindable() + { + return QBindable(&hasAllAttributesProperty); + } + + Q_PROPERTY(QString hasAllAttributes2 READ hasAllAttributes2 + DESIGNABLE true SCRIPTABLE true STORED true USER false CONSTANT); + + QString readOnly() { return m_readOnly; } + + QString readAndWrite() { return m_readAndWrite; } + + void setReadAndWrite(const QString &s) { m_readAndWrite = s; } + + void setReadAndWriteAndNotify(const QString &s) + { + if (s != readAndWrite()) { + setReadAndWrite(s); + emit notifiableChanged(s); + } + } + + void resetResettable() { m_resettable = u"Reset!"_s; } + + QString resettable() { return m_resettable; } + QString unresettable() { return m_unresettable; } + void setResettable(const QString &s) { m_resettable = s; } + void setUnresettable(const QString &s) { m_unresettable = s; } + + QString hasAllAttributes2() { return u"Some Constant string"_s; } + QString hasAllAttributes() { return m_hasAllAttributes; } + void setHasAllAttributes(const QString &s) { m_hasAllAttributes = s; } + void resetHasAllAttributes() { m_hasAllAttributes = "This value has been reset."; } + +signals: + void notifiableChanged(const QString &newValue); + void hasAllAttributesChanged(const QString &newValue); +}; + +#endif // PROPERTYALIASATTRIBUTES_H diff --git a/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml b/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml index 48203c3063..1909ed72a1 100644 --- a/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml +++ b/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml @@ -1,9 +1,8 @@ -import QtQml 2.0 +import QtQml -QtObject { +DefaultPropertyAliasChild { + id: self property string hello: "Hello from parent" - property QtObject origin - default property alias child: origin QtObject { property string hello: "Hello from parent.child (alias)" diff --git a/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml new file mode 100644 index 0000000000..66cd948ac4 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml @@ -0,0 +1,37 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQml +import QmltcTests + +TypeWithManyProperties { + id: self + + property alias hasAllAttributesAlias: self.hasAllAttributes + //hasAllAttributes: "The string." + hasAllAttributesAlias: "The string." // when this is missing then qmltc does not emit any error... + property alias hasAllAttributes2Alias: self.hasAllAttributes2 + + property alias readOnlyAlias: self.readOnly + property alias readAndWriteMemberAlias: self.readAndWriteMember + property alias resettableAlias: self.resettable + property alias unresettableAlias: self.unresettable + property alias notifiableAlias: self.notifiable + property alias notifiableMemberAlias: self.notifiableMember + property alias latestReadAndWriteAlias: self.latestReadAndWrite + + // aliases cannot be readonly, they inherit it from the property pointed to + // readonly default property alias readOnlyAlias2: self.readAndWriteMember // not valid + + default property alias defaultAlias: self.readAndWriteMember + + // cannot be compiled by qmlcachegen it seems: required property cannot have initializer + // required property alias requiredAlias: self.hasAllAttributes + + function assignUndefinedToResettableAlias() { + resettableAlias = undefined + } + function assignUndefinedToUnresettableAlias() { + unresettableAlias = undefined + } +} diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp index 85cc82a1b1..c0bfb0436d 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.cpp +++ b/tests/auto/qml/qmltc/tst_qmltc.cpp @@ -33,6 +33,7 @@ #include "changingbindings.h" #include "propertyalias.h" #include "propertyalias_external.h" +#include "propertyaliasattributes.h" #include "complexaliases.h" #include "propertychangehandler.h" #include "nestedhelloworld.h" @@ -72,6 +73,7 @@ #include "valuetypelistproperty.h" #include "translations.h" #include "translationsbyid.h" +#include "defaultalias.h" #include "testprivateproperty.h" @@ -894,8 +896,19 @@ void tst_qmltc::specialProperties() // alias attributes: const QMetaObject *mo = created.metaObject(); QMetaProperty xxAlias = mo->property(mo->indexOfProperty("xxAlias")); + + QQmlComponent c(&e); + c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/specialProperties.qml")); + QScopedPointer _fromEngine(c.create()); + QVERIFY2(_fromEngine, qPrintable(c.errorString())); + QObject &fromEngine = *_fromEngine; + const QMetaObject *fromEngineMetaObject = fromEngine.metaObject(); + + QMetaProperty xxAliasFromEngine = + fromEngineMetaObject->property(mo->indexOfProperty("xxAlias")); QVERIFY(xxAlias.isValid()); - QVERIFY(xxAlias.isConstant()); + QVERIFY(xxAliasFromEngine.isValid()); + QCOMPARE(xxAlias.isConstant(), xxAliasFromEngine.isConstant()); QCOMPARE(created.xyAlias(), u"reset"); } @@ -1091,6 +1104,192 @@ void tst_qmltc::propertyAlias_external() QCOMPARE(heightAliasChangedSpy.count(), 1); } +void tst_qmltc::propertyAliasAttribute() +{ + QQmlEngine e; + PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e); + + QQmlComponent c(&e); + c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/propertyAliasAttributes.qml")); + QScopedPointer _fromEngine(c.create()); + QVERIFY2(_fromEngine, qPrintable(c.errorString())); + QObject &fromEngine = *_fromEngine; + const QMetaObject *fromEngineMetaObject = fromEngine.metaObject(); + + const QString stringA = u"The quick brown fox"_s; + const QString stringB = u"jumps over the lazy dog."_s; + + QCOMPARE(fromQmltc.readOnlyAlias(), u"Hello World!"_s); + QCOMPARE(fromEngine.property("readOnlyAlias"), u"Hello World!"_s); + + QVERIFY(!fromQmltc.setProperty("readOnlyAlias", u"Some string"_s)); + QVERIFY(!fromEngine.setProperty("readOnlyAlias", u"Some string"_s)); + + // reading and writing from alias is already covered in the alias test + // check if it works on properties with the MEMBERS attribute + fromQmltc.setReadAndWriteMemberAlias(stringA); + fromEngine.setProperty("readAndWriteMemberAlias", stringA); + + QCOMPARE(fromQmltc.property("readAndWriteMember"), stringA); + QCOMPARE(fromEngine.property("readAndWriteMember"), stringA); + + fromQmltc.setReadAndWriteMemberAlias(stringB); + fromEngine.setProperty("readAndWriteMemberAlias", stringB); + + QCOMPARE(fromQmltc.readAndWriteMemberAlias(), stringB); + QCOMPARE(fromQmltc.property("readAndWriteMember"), stringB); + + QCOMPARE(fromEngine.property("readAndWriteMemberAlias"), stringB); + QCOMPARE(fromEngine.property("readAndWriteMember"), stringB); + + // check if alias can be reset through property + fromQmltc.setResettableAlias(stringA); + fromEngine.setProperty("resettableAlias", stringB); + fromQmltc.resetResettable(); + const int resettableIdx = fromEngineMetaObject->indexOfProperty("resettable"); + QVERIFY(fromEngineMetaObject->property(resettableIdx).reset(&fromEngine)); + QCOMPARE(fromQmltc.resettable(), u"Reset!"_s); + QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s); + + // check if property can be reset through alias + fromQmltc.setResettableAlias(stringA); + fromEngine.setProperty("resettableAlias", stringA); + fromQmltc.resetResettableAlias(); + QMetaMethod resetResettableAlias = fromEngineMetaObject->method( + fromEngineMetaObject->indexOfMethod("resetResettableAlias")); + resetResettableAlias.invoke(&fromEngine); + QCOMPARE(fromQmltc.resettable(), u"Reset!"_s); + QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s); + + // check if property can be reset by assigning undefined to alias + fromQmltc.setResettableAlias(stringA); + fromEngine.setProperty("resettableAlias", stringA); + fromQmltc.assignUndefinedToResettableAlias(); + QMetaMethod assignUndefinedToResettableAlias = fromEngineMetaObject->method( + fromEngineMetaObject->indexOfMethod("assignUndefinedToResettableAlias")); + assignUndefinedToResettableAlias.invoke(&fromEngine); + QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s); + QCOMPARE(fromQmltc.resettable(), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s); + + // check if property can be reset by assigning undefined to alias of + // non-resettable prop which should not happen: instead, nothing should happen + fromQmltc.setUnresettableAlias(stringA); + fromEngine.setProperty("unresettableAlias", stringA); + fromQmltc.assignUndefinedToUnresettableAlias(); + QMetaMethod assignUndefinedToUnresettableAlias = fromEngineMetaObject->method( + fromEngineMetaObject->indexOfMethod("assignUndefinedToUnresettableAlias")); + assignUndefinedToUnresettableAlias.invoke(&fromEngine); + QCOMPARE(fromQmltc.unresettableAlias(), stringA); + QCOMPARE(fromQmltc.property("unresettable"), stringA); + QCOMPARE(fromEngine.property("unresettableAlias"), stringA); + QCOMPARE(fromEngine.property("unresettable"), stringA); + + // check if notify arrives! + fromQmltc.setReadAndWrite(stringB); + fromEngine.setProperty("readAndWrite", stringB); + qsizetype calls = 0; + QSignalSpy spyQmltc(&fromQmltc, SIGNAL(notifiableChanged(QString))); + QSignalSpy spyEngine(&fromEngine, SIGNAL(notifiableChanged(QString))); + // write through alias + fromQmltc.setNotifiableAlias(stringA); + QVERIFY(fromEngine.setProperty("notifiableAlias", stringA)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + // write through property + fromQmltc.setReadAndWriteAndNotify(stringB); + QVERIFY(fromEngine.setProperty("notifiable", stringB)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + + fromQmltc.setNotifiableMemberAlias(stringA); + QVERIFY(fromEngine.setProperty("notifiableMemberAlias", stringA)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + fromQmltc.setProperty("notifiableMember", stringB); + QVERIFY(fromEngine.setProperty("notifiableMember", stringB)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + + // check that the alias to a revisioned property works + fromQmltc.setLatestReadAndWriteAlias(stringA); + QVERIFY(fromEngine.setProperty("latestReadAndWriteAlias", stringA)); + QCOMPARE(fromQmltc.latestReadAndWriteAlias(), stringA); + QCOMPARE(fromQmltc.property("latestReadAndWrite"), stringA); + QCOMPARE(fromEngine.property("latestReadAndWriteAlias"), stringA); + QCOMPARE(fromEngine.property("latestReadAndWrite"), stringA); + + QVERIFY(fromQmltc.setProperty("latestReadAndWrite", stringB)); + QVERIFY(fromEngine.setProperty("latestReadAndWrite", stringB)); + QCOMPARE(fromQmltc.latestReadAndWriteAlias(), stringB); + QCOMPARE(fromQmltc.property("latestReadAndWrite"), stringB); + QCOMPARE(fromEngine.property("latestReadAndWriteAlias"), stringB); + QCOMPARE(fromEngine.property("latestReadAndWrite"), stringB); + + // check if metaobject of alias is correct + const QVector metaObjects = { + fromQmltc.metaObject(), + fromEngine.metaObject(), + }; + + QVERIFY(metaObjects[0]); + QVERIFY(metaObjects[1]); + + QVector> metaProperties(2); + for (int j = 0; j < metaObjects.size(); j++) { + const QMetaObject *metaObject = metaObjects[j]; + for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) { + metaProperties[j][QString::fromLatin1(metaObject->property(i).name())] = + metaObject->property(i); + } + } + + { + QVERIFY(metaProperties[0].contains("hasAllAttributesAlias")); + QVERIFY(metaProperties[1].contains("hasAllAttributesAlias")); + QMetaProperty mpQmltc = metaProperties[0].value("hasAllAttributesAlias"); + QMetaProperty mpEngine = metaProperties[1].value("hasAllAttributesAlias"); + QCOMPARE(mpQmltc.isReadable(), mpEngine.isReadable()); + QCOMPARE(mpQmltc.isWritable(), mpEngine.isWritable()); + QCOMPARE(mpQmltc.isResettable(), mpEngine.isResettable()); + QCOMPARE(mpQmltc.hasNotifySignal(), mpEngine.hasNotifySignal()); + QCOMPARE(mpQmltc.revision(), mpEngine.revision()); + QCOMPARE(mpQmltc.isDesignable(), mpEngine.isDesignable()); + QCOMPARE(mpQmltc.isScriptable(), mpEngine.isScriptable()); + QCOMPARE(mpQmltc.isStored(), mpEngine.isStored()); + QCOMPARE(mpQmltc.isUser(), mpEngine.isUser()); + QCOMPARE(mpQmltc.isBindable(), mpEngine.isBindable()); + QCOMPARE(mpQmltc.isConstant(), mpEngine.isConstant()); + QCOMPARE(mpQmltc.isFinal(), mpEngine.isFinal()); + QCOMPARE(mpQmltc.isRequired(), mpEngine.isRequired()); + } + + { + QVERIFY(metaProperties[0].contains("hasAllAttributes2Alias")); + QVERIFY(metaProperties[1].contains("hasAllAttributes2Alias")); + QMetaProperty mpQmltc = metaProperties[0].value("hasAllAttributes2Alias"); + QMetaProperty mpEngine = metaProperties[1].value("hasAllAttributes2Alias"); + QCOMPARE(mpQmltc.isReadable(), mpEngine.isReadable()); + QCOMPARE(mpQmltc.isWritable(), mpEngine.isWritable()); + QCOMPARE(mpQmltc.isResettable(), mpEngine.isResettable()); + QCOMPARE(mpQmltc.hasNotifySignal(), mpEngine.hasNotifySignal()); + QCOMPARE(mpQmltc.revision(), mpEngine.revision()); + QCOMPARE(mpQmltc.isDesignable(), mpEngine.isDesignable()); + QCOMPARE(mpQmltc.isScriptable(), mpEngine.isScriptable()); + QCOMPARE(mpQmltc.isStored(), mpEngine.isStored()); + QCOMPARE(mpQmltc.isUser(), mpEngine.isUser()); + QCOMPARE(mpQmltc.isBindable(), mpEngine.isBindable()); + QCOMPARE(mpQmltc.isConstant(), mpEngine.isConstant()); + QCOMPARE(mpQmltc.isFinal(), mpEngine.isFinal()); + QCOMPARE(mpQmltc.isRequired(), mpEngine.isRequired()); + } +} + // TODO: we need to support RESET in aliases as well? (does it make sense?) void tst_qmltc::complexAliases() { @@ -1455,7 +1654,19 @@ void tst_qmltc::defaultPropertyCorrectSelection() void tst_qmltc::defaultAlias() { - QSKIP("Not implemented - not supported"); + QQmlEngine e; + PREPEND_NAMESPACE(defaultAlias) created(&e); + + QQmlComponent c(&e); + c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/defaultAlias.qml")); + QScopedPointer fromEngine(c.create()); + QVERIFY2(fromEngine, qPrintable(c.errorString())); + + auto *child = static_cast(created.child()); + QVERIFY(fromEngine->property("child").canConvert()); + QObject *childFromEngine = fromEngine->property("child").value(); + QVERIFY(childFromEngine); + QCOMPARE(child->hello(), childFromEngine->property("hello")); } void tst_qmltc::attachedProperty() diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h index f2c68d2e8d..0f862170c4 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.h +++ b/tests/auto/qml/qmltc/tst_qmltc.h @@ -45,6 +45,7 @@ private slots: void changingBindings(); void propertyAlias(); void propertyAlias_external(); + void propertyAliasAttribute(); void complexAliases(); void propertyChangeHandler(); void nestedHelloWorld(); diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp index ee932a84cd..57e8363bae 100644 --- a/tools/qmltc/qmltccompiler.cpp +++ b/tools/qmltc/qmltccompiler.cpp @@ -733,10 +733,20 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a current.functions.emplaceBack(bindable); mocLines << u"BINDABLE"_s << bindable.name; } + // 3. add notify - which is pretty special - if (QString notifyName = result.property.notify(); !notifyName.isEmpty()) { + // step 1: generate the moc instructions + // mimic the engines behavior: do it even if the notify will never be emitted + if (const QString aliasNotifyName = alias.notify(); !aliasNotifyName.isEmpty()) { + Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise + mocLines << u"NOTIFY"_s << aliasNotifyName; + } + + // step 2: connect the notifier to the aliased property notifier, if this latter exists + // otherwise, mimic the engines behavior and generate a useless notify + if (const QString notifyName = result.property.notify(); !notifyName.isEmpty()) { auto notifyFrames = frames; notifyFrames.pop(); // we don't need the last frame at all in this case @@ -762,6 +772,7 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a current.endInit.body += notifyEpilogue; current.endInit.body << u"}"_s; } + if (QString resetName = result.property.reset(); !resetName.isEmpty()) { Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise QmltcMethod reset {}; @@ -775,8 +786,12 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a mocLines << u"RESET"_s << reset.name; } - if (result.property.isConstant()) - mocLines << u"CONSTANT"_s; + // mimic the engines behavior: aliases are never constants + // mocLines << u"CONSTANT"_s; + // mimic the engines behavior: aliases are never stored + mocLines << u"STORED"_s << u"false"_s; + // mimic the engines behavior: aliases are never designable + mocLines << u"DESIGNABLE"_s << u"false"_s; // 4. add moc entry // Q_PROPERTY(QString text READ text WRITE setText BINDABLE bindableText NOTIFY textChanged) diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index aeea6371ab..017b5d5440 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -562,8 +562,12 @@ static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedA return; if (origin.property.isWritable() && alias->write().isEmpty()) alias->setWrite(compiledData.write); - if (!origin.property.notify().isEmpty() && alias->notify().isEmpty()) + + // the engine always compiles a notify for properties/aliases defined in qml code + // Yes, this generated notify will never be emitted. + if (alias->notify().isEmpty()) alias->setNotify(compiledData.notify); + if (!origin.property.bindable().isEmpty() && alias->bindable().isEmpty()) alias->setBindable(compiledData.bindable); }