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,
};
QQmlJSMetaParameter(const QString &name, const QString &typeName,
QQmlJSMetaParameter(const QString &name = QString(), const QString &typeName = QString(),
Constness typeQualifier = NonConst,
QWeakPointer<const QQmlJSScope> type = {})
: 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; }
Constness typeQualifier() const { return m_typeQualifier; }
void setTypeQualifier(Constness typeQualifier) { m_typeQualifier = typeQualifier; }
bool isPointer() const { return m_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)
{
return a.m_name == b.m_name && a.m_typeName == b.m_typeName
@ -162,8 +166,11 @@ private:
QWeakPointer<const QQmlJSScope> m_type;
Constness m_typeQualifier = NonConst;
bool m_isPointer = false;
bool m_isList = false;
};
using QQmlJSMetaReturnType = QQmlJSMetaParameter;
class QQmlJSMetaMethod
{
public:
@ -191,20 +198,18 @@ public:
QQmlJSMetaMethod() = default;
explicit QQmlJSMetaMethod(QString name, QString returnType = QString())
: m_name(std::move(name))
, m_returnTypeName(std::move(returnType))
, m_returnType(QString(), std::move(returnType))
, m_methodType(Method)
{}
QString methodName() const { return m_name; }
void setMethodName(const QString &name) { m_name = name; }
QString returnTypeName() const { return m_returnTypeName; }
QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.toStrongRef(); }
void setReturnTypeName(const QString &type) { m_returnTypeName = type; }
void setReturnType(const QSharedPointer<const QQmlJSScope> &type)
{
m_returnType = type;
}
QQmlJSMetaReturnType returnValue() const { return m_returnType; }
void setReturnValue(const QQmlJSMetaReturnType returnValue) { m_returnType = returnValue; }
QString returnTypeName() const { return m_returnType.typeName(); }
QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.type(); }
void setReturnTypeName(const QString &type) { m_returnType.setTypeName(type); }
QList<QQmlJSMetaParameter> parameters() const { return m_parameters; }
@ -257,7 +262,7 @@ public:
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_annotations == b.m_annotations && a.m_methodType == b.m_methodType
&& a.m_methodAccess == b.m_methodAccess && a.m_revision == b.m_revision
@ -274,8 +279,7 @@ public:
QtPrivate::QHashCombine combine;
seed = combine(seed, method.m_name);
seed = combine(seed, method.m_returnTypeName);
seed = combine(seed, method.m_returnType.toStrongRef().data());
seed = combine(seed, method.m_returnType);
seed = combine(seed, method.m_annotations);
seed = combine(seed, method.m_methodType);
seed = combine(seed, method.m_methodAccess);
@ -291,8 +295,8 @@ public:
private:
QString m_name;
QString m_returnTypeName;
QWeakPointer<const QQmlJSScope> m_returnType;
QQmlJSMetaReturnType m_returnType;
QList<QQmlJSMetaParameter> m_parameters;
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 QString returnTypeName = it->returnTypeName();
if (!it->returnType() && !returnTypeName.isEmpty()) {
const auto returnType = findType(returnTypeName, context, usedTypes);
it->setReturnType(returnType.scope);
}
auto parameters = it->parameters();
for (int i = 0, length = parameters.size(); i < length; ++i) {
auto &parameter = parameters[i];
const auto resolveParameter = [&](QQmlJSMetaParameter &parameter) {
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 });
}
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();
for (int i = 0, length = parameters.size(); i < length; ++i)
resolveParameter(parameters[i]);
it->setParameters(parameters);
}

View File

@ -299,7 +299,9 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bo
} else if (name == QLatin1String("isJavaScriptFunction")) {
metaMethod.setIsJavaScriptFunction(true);
} 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")) {
// 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
@ -429,6 +431,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
QString type;
bool isConstant = false;
bool isPointer = false;
bool isList = false;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
@ -450,7 +453,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
} else if (id == QLatin1String("isReadonly")) {
// ### unhandled
} else if (id == QLatin1String("isList")) {
// ### unhandled
isList = readBoolBinding(script);
} else {
addWarning(script->firstSourceLocation(),
tr("Expected only name and type script bindings."));
@ -460,6 +463,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM
QQmlJSMetaParameter p(name, type);
p.setTypeQualifier(isConstant ? QQmlJSMetaParameter::Const : QQmlJSMetaParameter::NonConst);
p.setIsPointer(isPointer);
p.setIsList(isList);
metaMethod->addParameter(std::move(p));
}

View File

@ -12,6 +12,7 @@ set(cpp_sources
enumProperty.h
gadgetwithenum.h
invisible.h
listprovider.h
multiforeign.h
objectwithmethod.h
person.cpp person.h
@ -37,6 +38,7 @@ set(qml_files
Cycle1.qml
Cycle2.qml
Cycle3.qml
CppMethodListReturnType.qml
CxxTypeFromDir.qml
CxxTypeFromImplicit.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 simpleBinding();
void cppMethodListReturnType();
void cppValueTypeList();
void anchorsFill();
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()
{
QQmlEngine engine;