aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2021-04-14 13:18:02 +0200
committerMaximilian Goldstein <max.goldstein@qt.io>2021-04-19 10:15:13 +0200
commit63c906ddc861d9c2116140c43c506823fc115817 (patch)
tree77a8845c0c9d8542eb70bc04418d9ced0ab9267c
parent9c6b1d7fb6eb627655f891fd406e8381de25281d (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.cpp12
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h4
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h6
-rw-r--r--tests/auto/qml/qmllint/data/DeprecatedFunctions.qml9
-rw-r--r--tests/auto/qml/qmllint/data/deprecatedFunction.qml10
-rw-r--r--tests/auto/qml/qmllint/data/deprecatedFunctionInherited.qml7
-rw-r--r--tests/auto/qml/qmllint/data/deprecatedFunctionOverride.qml8
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp11
-rw-r--r--tools/qmllint/checkidentifiers.cpp21
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 &parameter : 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;
}