Compiler: Handle method list return types from qmltypes file

When parsing methods details from qmltypes files, the `isList` value
was ignored. It indicates that the return type of the method is a list
of the type specified in the `type` value.

Add the isList flag to QQmlJSMetaParameter and make the return value a
QQmlJSMetaParameter, too, so that we can pass the flag to the type
resolution.

Fixes: QTBUG-122106
Change-Id: I6ea07c02fbeb6cb07d9fe9184205ff7f3274fd73
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
(cherry picked from commit e303d12827)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 411f6cce09)
(cherry picked from commit 7d3204ff06)
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
This commit is contained in:
Olivier De Cannière 2024-02-09 14:50:51 +01:00 committed by Ulf Hermann
parent 76d22624ed
commit dd865c1a62
7 changed files with 91 additions and 31 deletions

View File

@ -120,7 +120,7 @@ public:
Const, Const,
}; };
QQmlJSMetaParameter(const QString &name, const QString &typeName, QQmlJSMetaParameter(const QString &name = QString(), const QString &typeName = QString(),
Constness typeQualifier = NonConst, Constness typeQualifier = NonConst,
QWeakPointer<const QQmlJSScope> type = {}) QWeakPointer<const QQmlJSScope> type = {})
: m_name(name), m_typeName(typeName), m_type(type), m_typeQualifier(typeQualifier) : m_name(name), m_typeName(typeName), m_type(type), m_typeQualifier(typeQualifier)
@ -135,9 +135,13 @@ public:
void setType(QWeakPointer<const QQmlJSScope> type) { m_type = type; } void setType(QWeakPointer<const QQmlJSScope> type) { m_type = type; }
Constness typeQualifier() const { return m_typeQualifier; } Constness typeQualifier() const { return m_typeQualifier; }
void setTypeQualifier(Constness typeQualifier) { m_typeQualifier = typeQualifier; } void setTypeQualifier(Constness typeQualifier) { m_typeQualifier = typeQualifier; }
bool isPointer() const { return m_isPointer; } bool isPointer() const { return m_isPointer; }
void setIsPointer(bool isPointer) { m_isPointer = isPointer; } void setIsPointer(bool isPointer) { m_isPointer = isPointer; }
bool isList() const { return m_isList; }
void setIsList(bool isList) { m_isList = isList; }
friend bool operator==(const QQmlJSMetaParameter &a, const QQmlJSMetaParameter &b) friend bool operator==(const QQmlJSMetaParameter &a, const QQmlJSMetaParameter &b)
{ {
return a.m_name == b.m_name && a.m_typeName == b.m_typeName return a.m_name == b.m_name && a.m_typeName == b.m_typeName
@ -162,8 +166,11 @@ private:
QWeakPointer<const QQmlJSScope> m_type; QWeakPointer<const QQmlJSScope> m_type;
Constness m_typeQualifier = NonConst; Constness m_typeQualifier = NonConst;
bool m_isPointer = false; bool m_isPointer = false;
bool m_isList = false;
}; };
using QQmlJSMetaReturnType = QQmlJSMetaParameter;
class QQmlJSMetaMethod class QQmlJSMetaMethod
{ {
public: public:
@ -191,20 +198,18 @@ public:
QQmlJSMetaMethod() = default; QQmlJSMetaMethod() = default;
explicit QQmlJSMetaMethod(QString name, QString returnType = QString()) explicit QQmlJSMetaMethod(QString name, QString returnType = QString())
: m_name(std::move(name)) : m_name(std::move(name))
, m_returnTypeName(std::move(returnType)) , m_returnType(QString(), std::move(returnType))
, m_methodType(Method) , m_methodType(Method)
{} {}
QString methodName() const { return m_name; } QString methodName() const { return m_name; }
void setMethodName(const QString &name) { m_name = name; } void setMethodName(const QString &name) { m_name = name; }
QString returnTypeName() const { return m_returnTypeName; } QQmlJSMetaReturnType returnValue() const { return m_returnType; }
QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.toStrongRef(); } void setReturnValue(const QQmlJSMetaReturnType returnValue) { m_returnType = returnValue; }
void setReturnTypeName(const QString &type) { m_returnTypeName = type; } QString returnTypeName() const { return m_returnType.typeName(); }
void setReturnType(const QSharedPointer<const QQmlJSScope> &type) QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.type(); }
{ void setReturnTypeName(const QString &type) { m_returnType.setTypeName(type); }
m_returnType = type;
}
QList<QQmlJSMetaParameter> parameters() const { return m_parameters; } QList<QQmlJSMetaParameter> parameters() const { return m_parameters; }
@ -257,7 +262,7 @@ public:
friend bool operator==(const QQmlJSMetaMethod &a, const QQmlJSMetaMethod &b) friend bool operator==(const QQmlJSMetaMethod &a, const QQmlJSMetaMethod &b)
{ {
return a.m_name == b.m_name && a.m_returnTypeName == b.m_returnTypeName return a.m_name == b.m_name && a.m_returnType == b.m_returnType
&& a.m_returnType == b.m_returnType && a.m_parameters == b.m_parameters && a.m_returnType == b.m_returnType && a.m_parameters == b.m_parameters
&& a.m_annotations == b.m_annotations && a.m_methodType == b.m_methodType && a.m_annotations == b.m_annotations && a.m_methodType == b.m_methodType
&& a.m_methodAccess == b.m_methodAccess && a.m_revision == b.m_revision && a.m_methodAccess == b.m_methodAccess && a.m_revision == b.m_revision
@ -274,8 +279,7 @@ public:
QtPrivate::QHashCombine combine; QtPrivate::QHashCombine combine;
seed = combine(seed, method.m_name); seed = combine(seed, method.m_name);
seed = combine(seed, method.m_returnTypeName); seed = combine(seed, method.m_returnType);
seed = combine(seed, method.m_returnType.toStrongRef().data());
seed = combine(seed, method.m_annotations); seed = combine(seed, method.m_annotations);
seed = combine(seed, method.m_methodType); seed = combine(seed, method.m_methodType);
seed = combine(seed, method.m_methodAccess); seed = combine(seed, method.m_methodAccess);
@ -291,8 +295,8 @@ public:
private: private:
QString m_name; QString m_name;
QString m_returnTypeName;
QWeakPointer<const QQmlJSScope> m_returnType; QQmlJSMetaReturnType m_returnType;
QList<QQmlJSMetaParameter> m_parameters; QList<QQmlJSMetaParameter> m_parameters;
QList<QQmlJSAnnotation> m_annotations; QList<QQmlJSAnnotation> m_annotations;

View File

@ -502,24 +502,26 @@ QTypeRevision QQmlJSScope::resolveType(
} }
} }
for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) { const auto resolveParameter = [&](QQmlJSMetaParameter &parameter) {
const QString returnTypeName = it->returnTypeName(); if (const QString typeName = parameter.typeName();
if (!it->returnType() && !returnTypeName.isEmpty()) { !parameter.type() && !typeName.isEmpty()) {
const auto returnType = findType(returnTypeName, context, usedTypes); const auto type = findType(typeName, context, usedTypes);
it->setReturnType(returnType.scope); if (type.scope && type.scope->isReferenceType())
parameter.setIsPointer(true);
parameter.setType({ (type.scope && parameter.isList())
? type.scope->listType()
: type.scope });
} }
};
for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) {
auto returnValue = it->returnValue();
resolveParameter(returnValue);
it->setReturnValue(returnValue);
auto parameters = it->parameters(); auto parameters = it->parameters();
for (int i = 0, length = parameters.size(); i < length; ++i) { for (int i = 0, length = parameters.size(); i < length; ++i)
auto &parameter = parameters[i]; resolveParameter(parameters[i]);
if (const QString typeName = parameter.typeName();
!parameter.type() && !typeName.isEmpty()) {
const auto type = findType(typeName, context, usedTypes);
if (type.scope && type.scope->isReferenceType())
parameter.setIsPointer(true);
parameter.setType({ type.scope });
}
}
it->setParameters(parameters); it->setParameters(parameters);
} }

View File

@ -299,7 +299,9 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bo
} else if (name == QLatin1String("isJavaScriptFunction")) { } else if (name == QLatin1String("isJavaScriptFunction")) {
metaMethod.setIsJavaScriptFunction(true); metaMethod.setIsJavaScriptFunction(true);
} else if (name == QLatin1String("isList")) { } else if (name == QLatin1String("isList")) {
// TODO: Theoretically this can happen. QQmlJSMetaMethod should store it. auto metaReturnType = metaMethod.returnValue();
metaReturnType.setIsList(true);
metaMethod.setReturnValue(metaReturnType);
} else if (name == QLatin1String("isPointer")) { } else if (name == QLatin1String("isPointer")) {
// TODO: We don't need this information. We can probably drop all isPointer members // TODO: We don't need this information. We can probably drop all isPointer members
// once we make sure that the type information is always complete. The // once we make sure that the type information is always complete. The
@ -429,6 +431,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
QString type; QString type;
bool isConstant = false; bool isConstant = false;
bool isPointer = false; bool isPointer = false;
bool isList = false;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
@ -450,7 +453,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
} else if (id == QLatin1String("isReadonly")) { } else if (id == QLatin1String("isReadonly")) {
// ### unhandled // ### unhandled
} else if (id == QLatin1String("isList")) { } else if (id == QLatin1String("isList")) {
// ### unhandled isList = readBoolBinding(script);
} else { } else {
addWarning(script->firstSourceLocation(), addWarning(script->firstSourceLocation(),
tr("Expected only name and type script bindings.")); tr("Expected only name and type script bindings."));
@ -460,6 +463,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
QQmlJSMetaParameter p(name, type); QQmlJSMetaParameter p(name, type);
p.setTypeQualifier(isConstant ? QQmlJSMetaParameter::Const : QQmlJSMetaParameter::NonConst); p.setTypeQualifier(isConstant ? QQmlJSMetaParameter::Const : QQmlJSMetaParameter::NonConst);
p.setIsPointer(isPointer); p.setIsPointer(isPointer);
p.setIsList(isList);
metaMethod->addParameter(std::move(p)); metaMethod->addParameter(std::move(p));
} }

View File

@ -12,6 +12,7 @@ set(cpp_sources
enumProperty.h enumProperty.h
gadgetwithenum.h gadgetwithenum.h
invisible.h invisible.h
listprovider.h
multiforeign.h multiforeign.h
objectwithmethod.h objectwithmethod.h
person.cpp person.h person.cpp person.h
@ -37,6 +38,7 @@ set(qml_files
Cycle1.qml Cycle1.qml
Cycle2.qml Cycle2.qml
Cycle3.qml Cycle3.qml
CppMethodListReturnType.qml
CxxTypeFromDir.qml CxxTypeFromDir.qml
CxxTypeFromImplicit.qml CxxTypeFromImplicit.qml
Dummy.qml Dummy.qml

View File

@ -0,0 +1,12 @@
pragma Strict
import QtQuick
import TestTypes
Item {
ListProvider {
id: listProvider
}
property var list: listProvider.intList()
}

View File

@ -0,0 +1,24 @@
#ifndef QLISTPROVIDER_H
#define QLISTPROVIDER_H
#include <QObject>
#include <QQmlEngine>
class QListProvider : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(ListProvider)
public:
explicit QListProvider(QObject *parent = nullptr) : QObject(parent) { }
Q_INVOKABLE QList<int> intList() const
{
QList<int> list;
for (int i = 0; i < 3; ++i)
list.append(i);
return list;
}
};
#endif // QLISTPROVIDER_H

View File

@ -29,6 +29,7 @@ private slots:
void initTestCase(); void initTestCase();
void simpleBinding(); void simpleBinding();
void cppMethodListReturnType();
void cppValueTypeList(); void cppValueTypeList();
void anchorsFill(); void anchorsFill();
void signalHandler(); void signalHandler();
@ -221,6 +222,17 @@ void tst_QmlCppCodegen::simpleBinding()
} }
} }
void tst_QmlCppCodegen::cppMethodListReturnType()
{
QQmlEngine engine;
QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/CppMethodListReturnType.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
QCOMPARE(o->property("list").toList()[2].toInt(), 2);
}
void tst_QmlCppCodegen::cppValueTypeList() void tst_QmlCppCodegen::cppValueTypeList()
{ {
QQmlEngine engine; QQmlEngine engine;