diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2024-02-20 15:04:21 +0100 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-03-26 12:32:36 +0100 |
| commit | 38e35c2dd7711d9f63ec19fad72b102f7932207e (patch) | |
| tree | 3caf188df3d08d8a4cdf70d69f2d87cbd366251e | |
| parent | 24498d711a8bb1f6c2c3ca77004329d41bff3308 (diff) | |
QmlCompiler: Respect scoped enums
Correctly propagate the isClass and RegisterEnumClassesUnscoped
information from metatypes to qmltypes, then read it correctly.
For historical reasons, enums are unscoped by default, even if they are
declared as "enum class".
Furthermore, QML enums can be accessed in both scoped and unscoped way.
Scoped C++ enums can only be accessed by explicitly stating the scope,
and unscoped C++ enums can only be accessed without scope.
We cannot test this using qmllint because qmllint in 6.2 cannot properly
analyze enums, yet. We need it to fix qmlsc miscompiling the relevant
code, though.
Task-number: QTBUG-107143
Change-Id: If1ee9a10479cffb46067ccb5e683906905c24160
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 7da544e862a92cc0a9d97cbed68a35c532699116)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 806af3541056a580d0c2c6c5d7a518efbc473b68)
(cherry picked from commit 65fa7ff2c98c433843ee25b5a80dc79427aca9f8)
(cherry picked from commit e28fee2d26f6002a524d2bf8cc32c4514f6f557e)
| -rw-r--r-- | src/imports/tooling/Enum.qml | 1 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor.cpp | 1 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljsmetatypes_p.h | 14 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstypedescriptionreader.cpp | 4 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qmltypesclassdescription.cpp | 24 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qmltypesclassdescription.h | 1 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qmltypescreator.cpp | 22 | ||||
| -rw-r--r-- | src/qmltyperegistrar/qmltypescreator.h | 5 |
8 files changed, 60 insertions, 12 deletions
diff --git a/src/imports/tooling/Enum.qml b/src/imports/tooling/Enum.qml index 5d8f0e958d..36375dc380 100644 --- a/src/imports/tooling/Enum.qml +++ b/src/imports/tooling/Enum.qml @@ -42,5 +42,6 @@ import QML Member { property string alias property bool isFlag: false + property bool isScoped: false property var values: [] } diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 21b7d32554..a860b29dea 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -1340,6 +1340,7 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied) { QQmlJSMetaEnum qmlEnum(uied->name.toString()); + qmlEnum.setIsQml(true); for (const auto *member = uied->members; member; member = member->next) { qmlEnum.addKey(member->member.toString()); qmlEnum.addValue(int(member->value)); diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h index 601ad4f872..79746bca7b 100644 --- a/src/qmlcompiler/qqmljsmetatypes_p.h +++ b/src/qmlcompiler/qqmljsmetatypes_p.h @@ -66,6 +66,8 @@ class QQmlJSMetaEnum QString m_alias; QSharedPointer<const QQmlJSScope> m_type; bool m_isFlag = false; + bool m_isScoped = false; + bool m_isQml = false; public: QQmlJSMetaEnum() = default; @@ -82,6 +84,12 @@ public: bool isFlag() const { return m_isFlag; } void setIsFlag(bool isFlag) { m_isFlag = isFlag; } + bool isScoped() const { return m_isScoped; } + void setIsScoped(bool v) { m_isScoped = v; } + + bool isQml() const { return m_isQml; } + void setIsQml(bool v) { m_isQml = v; } + void addKey(const QString &key) { m_keys.append(key); } QStringList keys() const { return m_keys; } @@ -102,7 +110,8 @@ public: && a.m_name == b.m_name && a.m_alias == b.m_alias && a.m_isFlag == b.m_isFlag - && a.m_type == b.m_type; + && a.m_type == b.m_type + && a.m_isScoped == b.m_isScoped; } friend bool operator!=(const QQmlJSMetaEnum &a, const QQmlJSMetaEnum &b) @@ -112,7 +121,8 @@ public: friend size_t qHash(const QQmlJSMetaEnum &e, size_t seed = 0) { - return qHashMulti(seed, e.m_keys, e.m_values, e.m_name, e.m_alias, e.m_isFlag, e.m_type); + return qHashMulti( + seed, e.m_keys, e.m_values, e.m_name, e.m_alias, e.m_isFlag, e.m_type, e.m_isScoped); } }; diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp index b4f37338d8..ec9e55bd14 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp +++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp @@ -418,9 +418,11 @@ void QQmlJSTypeDescriptionReader::readEnum(UiObjectDefinition *ast, const QQmlJS metaEnum.setIsFlag(readBoolBinding(script)); } else if (name == QLatin1String("values")) { readEnumValues(script, &metaEnum); + } else if (name == QLatin1String("isScoped")) { + metaEnum.setIsScoped(readBoolBinding(script)); } else { addWarning(script->firstSourceLocation(), - tr("Expected only name and values script bindings.")); + tr("Expected only name, alias, isFlag, values, or isScoped.")); } } diff --git a/src/qmltyperegistrar/qmltypesclassdescription.cpp b/src/qmltyperegistrar/qmltypesclassdescription.cpp index 6385f9cbe8..cbb74463d1 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.cpp +++ b/src/qmltyperegistrar/qmltypesclassdescription.cpp @@ -112,10 +112,16 @@ void QmlTypesClassDescription::collectLocalAnonymous( const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray(); for (const QJsonValue classInfo : classInfos) { const QJsonObject obj = classInfo.toObject(); - if (obj[QStringLiteral("name")].toString() == QStringLiteral("DefaultProperty")) - defaultProp = obj[QStringLiteral("value")].toString(); - if (obj[QStringLiteral("name")].toString() == QStringLiteral("ParentProperty")) - parentProp = obj[QStringLiteral("value")].toString(); + const QString name = obj[QStringLiteral("name")].toString(); + const auto value = [&]() { return obj[QStringLiteral("value")].toString(); }; + if (name == QStringLiteral("DefaultProperty")) { + defaultProp = value(); + } else if (name == QStringLiteral("ParentProperty")) { + parentProp = value(); + } else if (name == QStringLiteral("RegisterEnumClassesUnscoped") + && value() == QStringLiteral("false")) { + registerEnumClassesScoped = true; + } } collectInterfaces(classDef); @@ -143,6 +149,9 @@ void QmlTypesClassDescription::collect( } else if (name == QLatin1String("ParentProperty")) { if (mode != RelatedType && parentProp.isEmpty()) parentProp = value; + } else if (name == QLatin1String("RegisterEnumClassesUnscoped")) { + if (mode != RelatedType && value == QLatin1String("false")) + registerEnumClassesScoped = true; } else if (name == QLatin1String("QML.AddedInVersion")) { const QTypeRevision revision = QTypeRevision::fromEncodedVersion(value.toInt()); if (mode == TopLevel) { @@ -192,10 +201,12 @@ void QmlTypesClassDescription::collect( if (const QJsonObject *other = findType(foreign, foreignTypeName)) { classDef = other; - // Default properties are always local. + // Default properties and enum classes are always local. defaultProp.clear(); + registerEnumClassesScoped = false; // Foreign type can have a default property or an attached types + // or RegisterEnumClassesUnscoped classinfo. const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray(); for (const QJsonValue classInfo : classInfos) { const QJsonObject obj = classInfo.toObject(); @@ -205,6 +216,9 @@ void QmlTypesClassDescription::collect( defaultProp = foreignValue; } else if (parentProp.isEmpty() && foreignName == QLatin1String("ParentProperty")) { parentProp = foreignValue; + } else if (foreignName == QLatin1String("RegisterEnumClassesUnscoped")) { + if (foreignValue == QLatin1String("false")) + registerEnumClassesScoped = true; } else if (foreignName == QLatin1String("QML.Attached")) { attachedType = foreignValue; collectRelated(foreignValue, types, foreign, defaultRevision); diff --git a/src/qmltyperegistrar/qmltypesclassdescription.h b/src/qmltyperegistrar/qmltypesclassdescription.h index 9ddf79886d..bca95cdd82 100644 --- a/src/qmltyperegistrar/qmltypesclassdescription.h +++ b/src/qmltyperegistrar/qmltypesclassdescription.h @@ -55,6 +55,7 @@ struct QmlTypesClassDescription bool isSingleton = false; bool isRootClass = false; bool hasCustomParser = false; + bool registerEnumClassesScoped = false; QStringList implementsInterfaces; enum CollectMode { diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp index f26693c293..ded8c61a18 100644 --- a/src/qmltyperegistrar/qmltypescreator.cpp +++ b/src/qmltyperegistrar/qmltypescreator.cpp @@ -244,7 +244,8 @@ void QmlTypesCreator::writeMethods(const QJsonArray &methods, const QString &typ } } -void QmlTypesCreator::writeEnums(const QJsonArray &enums) +void QmlTypesCreator::writeEnums( + const QJsonArray &enums, QmlTypesCreator::EnumClassesMode enumClassesMode) { for (const QJsonValue item : enums) { const QJsonObject obj = item.toObject(); @@ -263,6 +264,13 @@ void QmlTypesCreator::writeEnums(const QJsonArray &enums) auto isFlag = obj.find(QLatin1String("isFlag")); if (isFlag != obj.end() && isFlag->toBool()) m_qml.writeBooleanBinding(isFlag.key(), true); + + if (enumClassesMode == EnumClassesMode::Scoped) { + const auto isClass = obj.find(QLatin1String("isClass")); + if (isClass != obj.end() && isClass->toBool()) + m_qml.writeBooleanBinding(QLatin1String("isScoped"), true); + } + m_qml.writeArrayBinding(QLatin1String("values"), valueList); m_qml.writeEndObject(); } @@ -382,7 +390,11 @@ void QmlTypesCreator::writeComponents() writeClassProperties(collector); if (const QJsonObject *classDef = collector.resolvedClass) { - writeEnums(members(classDef, enumsKey, m_version)); + writeEnums( + members(classDef, enumsKey, m_version), + collector.registerEnumClassesScoped + ? EnumClassesMode::Scoped + : EnumClassesMode::Unscoped); writeProperties(members(classDef, propertiesKey, m_version)); @@ -411,7 +423,11 @@ void QmlTypesCreator::writeComponents() collector.collectLocalAnonymous(&component, m_ownTypes, m_foreignTypes, m_version); writeClassProperties(collector); - writeEnums(members(&component, enumsKey, m_version)); + writeEnums( + members(&component, enumsKey, m_version), + collector.registerEnumClassesScoped + ? EnumClassesMode::Scoped + : EnumClassesMode::Unscoped); writeProperties(members(&component, propertiesKey, m_version)); diff --git a/src/qmltyperegistrar/qmltypescreator.h b/src/qmltyperegistrar/qmltypescreator.h index bfb284eef5..7e9f6721c8 100644 --- a/src/qmltyperegistrar/qmltypescreator.h +++ b/src/qmltyperegistrar/qmltypescreator.h @@ -53,7 +53,10 @@ private: void writeType(const QJsonObject &property, const QString &key); void writeProperties(const QJsonArray &properties); void writeMethods(const QJsonArray &methods, const QString &type); - void writeEnums(const QJsonArray &enums); + + enum class EnumClassesMode { Scoped, Unscoped }; + void writeEnums(const QJsonArray &enums, EnumClassesMode enumClassesMode); + void writeComponents(); QByteArray m_output; |
