diff options
author | Maximilian Goldstein <max.goldstein@qt.io> | 2021-04-14 13:18:02 +0200 |
---|---|---|
committer | Maximilian Goldstein <max.goldstein@qt.io> | 2021-04-19 10:15:13 +0200 |
commit | 63c906ddc861d9c2116140c43c506823fc115817 (patch) | |
tree | 77a8845c0c9d8542eb70bc04418d9ced0ab9267c | |
parent | 9c6b1d7fb6eb627655f891fd406e8381de25281d (diff) |
qmllint: Warn about deprecated functions
Now qmllint will also warn when functions have a Deprecated annotation which previously only applied to properties and elements.
Task-number: QTBUG-84895
Fixes: QTBUG-79857
Change-Id: Ie1436822dc06bfd1ee4305a8468900409c3f8b0a
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor.cpp | 12 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor_p.h | 4 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsmetatypes_p.h | 6 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/DeprecatedFunctions.qml | 9 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/deprecatedFunction.qml | 10 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/deprecatedFunctionInherited.qml | 7 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/deprecatedFunctionOverride.qml | 8 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 11 | ||||
-rw-r--r-- | tools/qmllint/checkidentifiers.cpp | 21 |
9 files changed, 88 insertions, 0 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index f950b3ad50..c63ff89cc2 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -451,6 +451,12 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp if (!name.isEmpty()) { QQmlJSMetaMethod method(name); method.setMethodType(QQmlJSMetaMethod::Method); + + if (!m_pendingMethodAnnotations.isEmpty()) { + method.setAnnotations(m_pendingMethodAnnotations); + m_pendingMethodAnnotations.clear(); + } + if (const auto *formals = fexpr->formals) { const auto parameters = formals->formals(); for (const auto ¶meter : parameters) { @@ -489,6 +495,12 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *) leaveEnvironment(); } +bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement) +{ + m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations); + return true; +} + bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl) { visitFunctionExpressionHelper(fdecl); diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h index f30d92698e..c79e756ac6 100644 --- a/src/qmlcompiler/qqmljsimportvisitor_p.h +++ b/src/qmlcompiler/qqmljsimportvisitor_p.h @@ -82,6 +82,7 @@ protected: bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override; bool visit(QQmlJS::AST::FunctionExpression *fexpr) override; void endVisit(QQmlJS::AST::FunctionExpression *) override; + bool visit(QQmlJS::AST::UiSourceElement *) override; bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override; void endVisit(QQmlJS::AST::FunctionDeclaration *) override; bool visit(QQmlJS::AST::ClassExpression *ast) override; @@ -147,6 +148,9 @@ protected: QVector<QQmlJSAnnotation> parseAnnotations(QQmlJS::AST::UiAnnotationList *list); QQmlJSLogger m_logger; + + // Used to temporarily store annotations for functions and generators wrapped in UiSourceElements + QVector<QQmlJSAnnotation> m_pendingMethodAnnotations; private: void importBaseModules(); void resolveAliases(); diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h index 3130ab6903..4e914d37cc 100644 --- a/src/qmlcompiler/qqmljsmetatypes_p.h +++ b/src/qmlcompiler/qqmljsmetatypes_p.h @@ -187,6 +187,9 @@ public: bool isValid() const { return !m_name.isEmpty(); } + const QVector<QQmlJSAnnotation>& annotations() const { return m_annotations; } + void setAnnotations(QVector<QQmlJSAnnotation> annotations) { m_annotations = annotations; } + friend bool operator==(const QQmlJSMetaMethod &a, const QQmlJSMetaMethod &b) { return a.m_name == b.m_name @@ -195,6 +198,7 @@ public: && a.m_paramNames == b.m_paramNames && a.m_paramTypeNames == b.m_paramTypeNames && a.m_paramTypes == b.m_paramTypes + && a.m_annotations == b.m_annotations && a.m_methodType == b.m_methodType && a.m_methodAccess == b.m_methodAccess && a.m_revision == b.m_revision @@ -215,6 +219,7 @@ public: seed = combine(seed, method.m_returnType.toStrongRef().data()); seed = combine(seed, method.m_paramNames); seed = combine(seed, method.m_paramTypeNames); + seed = combine(seed, method.m_annotations); seed = combine(seed, method.m_methodType); seed = combine(seed, method.m_methodAccess); seed = combine(seed, method.m_revision); @@ -234,6 +239,7 @@ private: QStringList m_paramNames; QStringList m_paramTypeNames; QList<QWeakPointer<const QQmlJSScope>> m_paramTypes; + QList<QQmlJSAnnotation> m_annotations; Type m_methodType = Signal; Access m_methodAccess = Private; diff --git a/tests/auto/qml/qmllint/data/DeprecatedFunctions.qml b/tests/auto/qml/qmllint/data/DeprecatedFunctions.qml new file mode 100644 index 0000000000..139e8a6230 --- /dev/null +++ b/tests/auto/qml/qmllint/data/DeprecatedFunctions.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + @Deprecated { reason: "This deprecation should be overridden!" } + function deprecatedOverride(a, b) {} + + @Deprecated { reason: "This deprecation should be visible!" } + function deprecatedInherited(c, d) {} +} diff --git a/tests/auto/qml/qmllint/data/deprecatedFunction.qml b/tests/auto/qml/qmllint/data/deprecatedFunction.qml new file mode 100644 index 0000000000..9face2edf7 --- /dev/null +++ b/tests/auto/qml/qmllint/data/deprecatedFunction.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +DeprecatedFunctions { + @Deprecated { reason: "No particular reason." } + function deprecated(foobar) {} + + Component.onCompleted: { + deprecated(); + } +} diff --git a/tests/auto/qml/qmllint/data/deprecatedFunctionInherited.qml b/tests/auto/qml/qmllint/data/deprecatedFunctionInherited.qml new file mode 100644 index 0000000000..0164530720 --- /dev/null +++ b/tests/auto/qml/qmllint/data/deprecatedFunctionInherited.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +DeprecatedFunctions { + Component.onCompleted: { + deprecatedInherited(); + } +} diff --git a/tests/auto/qml/qmllint/data/deprecatedFunctionOverride.qml b/tests/auto/qml/qmllint/data/deprecatedFunctionOverride.qml new file mode 100644 index 0000000000..f5c88b763e --- /dev/null +++ b/tests/auto/qml/qmllint/data/deprecatedFunctionOverride.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +DeprecatedFunctions { + function deprecatedOverride(x, y, z) {} + Component.onCompleted: { + deprecatedOverride(); + } +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 487321cffa..28f0974510 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -577,6 +577,16 @@ void TestQmllint::dirtyQmlCode_data() << QString() << QString() << true; + QTest::newRow("deprecatedFunction") + << QStringLiteral("deprecatedFunction.qml") + << QStringLiteral("Method \"deprecated(foobar)\" is deprecated (Reason: No particular reason.)") + << QString() + << false; + QTest::newRow("deprecatedFunctionInherited") + << QStringLiteral("deprecatedFunctionInherited.qml") + << QStringLiteral("Method \"deprecatedInherited(c, d)\" is deprecated (Reason: This deprecation should be visible!)") + << QString() + << false; } void TestQmllint::dirtyQmlCode() @@ -688,6 +698,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("goodAttachedProperty") << QStringLiteral("goodAttachedProperty.qml"); QTest::newRow("objectBindingOnVarProperty") << QStringLiteral("objectBoundToVar.qml"); QTest::newRow("Unversioned change signal without arguments") << QStringLiteral("unversionChangedSignalSansArguments.qml"); + QTest::newRow("deprecatedFunctionOverride") << QStringLiteral("deprecatedFunctionOverride.qml"); } void TestQmllint::cleanQmlCode() diff --git a/tools/qmllint/checkidentifiers.cpp b/tools/qmllint/checkidentifiers.cpp index e2ac8981f5..1e889926a1 100644 --- a/tools/qmllint/checkidentifiers.cpp +++ b/tools/qmllint/checkidentifiers.cpp @@ -299,6 +299,27 @@ void CheckIdentifiers::operator()( auto qmlScope = QQmlJSScope::findCurrentQMLScope(currentScope); if (qmlScope->hasMethod(memberAccessBase.m_name)) { // a property of a JavaScript function, or a method + auto methods = qmlScope->methods(memberAccessBase.m_name); + const QQmlJSMetaMethod &method = methods.constFirst(); + const auto &annotations = method.annotations(); + auto deprecationAnn = std::find_if(annotations.constBegin(), annotations.constEnd(), [](const QQmlJSAnnotation& annotation) { + return annotation.isDeprecation(); + }); + + // Once we encountered one possible method that is not deprecated, + // we can assume that the one beyond that is not what was being referenced + if (deprecationAnn == annotations.constEnd()) + continue; + + QQQmlJSDeprecation deprecation = deprecationAnn->deprecation(); + + QString message = QStringLiteral("Method \"%1(%2)\" is deprecated") + .arg(memberAccessBase.m_name, method.parameterNames().join(QStringLiteral(", "))); + + if (!deprecation.reason.isEmpty()) + message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason)); + + m_logger->log(message, Log_Deprecation, memberAccessBase.m_location); continue; } |