aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2024-02-14 17:34:58 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2024-03-01 12:28:58 +0000
commit2dfd04c529d80bd4c5a701fcadfbb0dc2050ba2f (patch)
tree108b4be31d9af0ce7e90cb4099abe5dd9d78a842
parent8e6e019cc496e17055942e69913cdc87a8e3c3dd (diff)
qmltc/importvisitor: warn about type annotations on methods
Warn when the types in method type annotations can't be resolved. This hinders qmltc from crashing when trying to compile a QML file with an invalid type annotation like `Qt.point`. To avoid copying and replacing lists, add helpers like mutableOwnMethodsRange() or mutableParametersRange() to obtain mutable iterators to the ownMethods of QQmlJSScope or the parameters of QQmlJSMetaMethod. Fixes: QTBUG-122251 Change-Id: Iffc6ff712fbeaa2fe8b83f94b0bc5a8c278d186c Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 4aa1deee1be966a6491ab5a1c1de09707a5e8215) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit a992badd6fe93812bbee206cc16f62f98ecc0f8c)
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp56
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h9
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h5
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h8
-rw-r--r--tests/auto/qml/qmltc_qprocess/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml20
-rw-r--r--tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp14
7 files changed, 111 insertions, 2 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 8b7ee573ff..70ade8c390 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -422,6 +422,7 @@ void QQmlJSImportVisitor::endVisit(UiProgram *)
setAllBindings();
processDefaultProperties();
processPropertyTypes();
+ processMethodTypes();
processPropertyBindings();
processPropertyBindingObjects();
checkRequiredProperties();
@@ -623,6 +624,39 @@ void QQmlJSImportVisitor::processPropertyTypes()
}
}
+void QQmlJSImportVisitor::processMethodTypes()
+{
+ for (const auto &type : m_pendingMethodTypes) {
+
+ for (auto [it, end] = type.scope->mutableOwnMethodsRange(type.methodName); it != end;
+ ++it) {
+ if (const auto returnType =
+ QQmlJSScope::findType(it->returnTypeName(), m_rootScopeImports).scope) {
+ it->setReturnType({ returnType });
+ } else {
+ m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
+ it->returnTypeName(), it->methodName()),
+ qmlUnresolvedType, type.location);
+ }
+
+ for (auto [parameter, parameterEnd] = it->mutableParametersRange();
+ parameter != parameterEnd; ++parameter) {
+ if (const auto parameterType =
+ QQmlJSScope::findType(parameter->typeName(), m_rootScopeImports)
+ .scope) {
+ parameter->setType({ parameterType });
+ } else {
+ m_logger->log(
+ u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
+ .arg(parameter->typeName(), parameter->name(),
+ it->methodName()),
+ qmlUnresolvedType, type.location);
+ }
+ }
+ }
+ }
+}
+
void QQmlJSImportVisitor::processPropertyBindingObjects()
{
QSet<QPair<QQmlJSScope::Ptr, QString>> foundLiterals;
@@ -1645,6 +1679,7 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
{
using namespace QQmlJS::AST;
auto name = fexpr->name.toString();
+ bool pending = false;
if (!name.isEmpty()) {
QQmlJSMetaMethod method(name);
method.setMethodType(QQmlJSMetaMethodType::Method);
@@ -1671,6 +1706,15 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
} else {
anyFormalTyped = true;
method.addParameter(QQmlJSMetaParameter(parameter.id, type));
+ if (!pending) {
+ m_pendingMethodTypes << PendingMethodType{
+ m_currentScope,
+ name,
+ combine(parameter.typeAnnotation->firstSourceLocation(),
+ parameter.typeAnnotation->lastSourceLocation())
+ };
+ pending = true;
+ }
}
}
}
@@ -1682,9 +1726,17 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
// Methods with only untyped arguments return an untyped value.
// Methods with at least one typed argument but no explicit return type return void.
// In order to make a function without arguments return void, you have to specify that.
- if (parseTypes && fexpr->typeAnnotation)
+ if (parseTypes && fexpr->typeAnnotation) {
method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
- else if (anyFormalTyped)
+ if (!pending) {
+ m_pendingMethodTypes << PendingMethodType{
+ m_currentScope, name,
+ combine(fexpr->typeAnnotation->firstSourceLocation(),
+ fexpr->typeAnnotation->lastSourceLocation())
+ };
+ pending = true;
+ }
+ } else if (anyFormalTyped)
method.setReturnTypeName(QStringLiteral("void"));
else
method.setReturnTypeName(QStringLiteral("var"));
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index 53715bced9..398b68dc19 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -259,6 +259,7 @@ protected:
void processPropertyBindings();
void checkRequiredProperties();
void processPropertyTypes();
+ void processMethodTypes();
void processPropertyBindingObjects();
void flushPendingSignalParameters();
@@ -284,6 +285,13 @@ protected:
QQmlJS::SourceLocation location;
};
+ struct PendingMethodType
+ {
+ QQmlJSScope::Ptr scope;
+ QString methodName;
+ QQmlJS::SourceLocation location;
+ };
+
struct PendingPropertyObjectBinding
{
QQmlJSScope::Ptr scope;
@@ -321,6 +329,7 @@ protected:
QHash<QQmlJSScope::Ptr, QVector<QQmlJSScope::Ptr>> m_pendingDefaultProperties;
QVector<PendingPropertyType> m_pendingPropertyTypes;
+ QVector<PendingMethodType> m_pendingMethodTypes;
QVector<PendingPropertyObjectBinding> m_pendingPropertyObjectBindings;
QVector<RequiredProperty> m_requiredProperties;
QVector<QQmlJSScope::Ptr> m_objectBindingScopes;
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index 4d01a08dd1..59ed27c467 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -220,6 +220,11 @@ public:
void setReturnType(QWeakPointer<const QQmlJSScope> type) { m_returnType.setType(type); }
QList<QQmlJSMetaParameter> parameters() const { return m_parameters; }
+ QPair<QList<QQmlJSMetaParameter>::iterator, QList<QQmlJSMetaParameter>::iterator>
+ mutableParametersRange()
+ {
+ return { m_parameters.begin(), m_parameters.end() };
+ }
QStringList parameterNames() const
{
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 053ba4127f..c7abe8dac0 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -286,6 +286,10 @@ public:
UnnamedPropertyTarget // default property bindings, where property name is unspecified
};
+ template <typename Key, typename Value>
+ using QMultiHashRange = QPair<typename QMultiHash<Key, Value>::iterator,
+ typename QMultiHash<Key, Value>::iterator>;
+
static QQmlJSScope::Ptr create() { return QSharedPointer<QQmlJSScope>(new QQmlJSScope); }
static QQmlJSScope::Ptr create(const QString &internalName)
{
@@ -322,6 +326,10 @@ QT_WARNING_POP
void setScopeType(ScopeType type) { m_scopeType = type; }
void addOwnMethod(const QQmlJSMetaMethod &method) { m_methods.insert(method.methodName(), method); }
+ QMultiHashRange<QString, QQmlJSMetaMethod> mutableOwnMethodsRange(const QString &name)
+ {
+ return m_methods.equal_range(name);
+ }
QMultiHash<QString, QQmlJSMetaMethod> ownMethods() const { return m_methods; }
QList<QQmlJSMetaMethod> ownMethods(const QString &name) const { return m_methods.values(name); }
bool hasOwnMethod(const QString &name) const { return m_methods.contains(name); }
diff --git a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
index 311ad71fde..387063fb28 100644
--- a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
+++ b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
@@ -35,6 +35,7 @@ qt6_add_qml_module(tst_qmltc_qprocess
data/uncreatable.qml
data/invalidSignalHandlers.qml
data/QmlBaseFromAnotherModule.qml
+ data/invalidTypeAnnotation.qml
)
set(common_libraries
diff --git a/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml b/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml
new file mode 100644
index 0000000000..fb8c8eb198
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+
+Item {
+ function f(): Qt.point {
+
+ }
+
+ function g() {
+
+ }
+
+ function h(a: int, b: Item) {
+
+ }
+
+ function alpha(a: int, b) {}
+ function beta(a: int, b): Item {}
+ function gamma(a: Qt.point, b) {}
+}
diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
index 9331fca89c..247a5e81ce 100644
--- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
+++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
@@ -53,6 +53,7 @@ private slots:
void invalidSignalHandlers();
void exports();
void qmlBaseFromAnotherModule();
+ void invalidTypeAnnotation();
};
#ifndef TST_QMLTC_QPROCESS_RESOURCES
@@ -280,6 +281,19 @@ void tst_qmltc_qprocess::qmlBaseFromAnotherModule()
}
}
+void tst_qmltc_qprocess::invalidTypeAnnotation()
+{
+ {
+ const auto errors = runQmltc(u"invalidTypeAnnotation.qml"_s, false);
+ QVERIFY(errors.contains(
+ u"invalidTypeAnnotation.qml:5:17: \"Qt.point\" was not found for the return type of method \"f\"."_s));
+ QVERIFY(errors.contains(
+ u"invalidTypeAnnotation.qml:19:21: \"Qt.point\" was not found for the type of parameter \"a\" in method \"gamma\"."_s));
+ QVERIFY(!errors.contains(u"\"var\""_s));
+ QVERIFY(!errors.contains(u"\"void\""_s));
+ }
+}
+
static QString fileToString(const QString &path)
{
QFile f(path);