diff options
author | Sami Shalayel <sami.shalayel@qt.io> | 2024-02-14 17:34:58 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2024-03-01 12:28:58 +0000 |
commit | 2dfd04c529d80bd4c5a701fcadfbb0dc2050ba2f (patch) | |
tree | 108b4be31d9af0ce7e90cb4099abe5dd9d78a842 | |
parent | 8e6e019cc496e17055942e69913cdc87a8e3c3dd (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.cpp | 56 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor_p.h | 9 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsmetatypes_p.h | 5 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope_p.h | 8 | ||||
-rw-r--r-- | tests/auto/qml/qmltc_qprocess/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmltc_qprocess/data/invalidTypeAnnotation.qml | 20 | ||||
-rw-r--r-- | tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp | 14 |
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); |