aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2024-02-20 15:04:21 +0100
committerUlf Hermann <ulf.hermann@qt.io>2024-03-26 12:32:36 +0100
commit38e35c2dd7711d9f63ec19fad72b102f7932207e (patch)
tree3caf188df3d08d8a4cdf70d69f2d87cbd366251e
parent24498d711a8bb1f6c2c3ca77004329d41bff3308 (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.qml1
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp1
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h14
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp4
-rw-r--r--src/qmltyperegistrar/qmltypesclassdescription.cpp24
-rw-r--r--src/qmltyperegistrar/qmltypesclassdescription.h1
-rw-r--r--src/qmltyperegistrar/qmltypescreator.cpp22
-rw-r--r--src/qmltyperegistrar/qmltypescreator.h5
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;