diff options
author | Evgeniy A. Dushistov <dushistov@mail.ru> | 2020-06-18 21:52:58 +0300 |
---|---|---|
committer | Evgeniy A. Dushistov <dushistov@mail.ru> | 2020-06-19 16:35:16 +0300 |
commit | aa8d5a45930dcfce7f25878fa36c1595b1dfc023 (patch) | |
tree | c456df207790ca5af31d9367ab184037d6696c3e | |
parent | 9139373c9b3a8cd8541e0253bdf72cf939726e92 (diff) |
qmllint: search methods and properties also in attached type
I changed the strategy of traverse via related types.
Before:
traversal happens only via linked list type -> type->superClass
After:
traversal via binary tree formed by attachedType and superClass
I use pre-order strategy, current type, then all attached types,
then all super classes
Fixes: QTBUG-84861
Change-Id: I4a8fce869bc541de6900514d29575174d2753f02
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | tests/auto/qml/qmllint/data/AttachedType.qml | 5 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/Things/plugins.qmltypes | 25 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 1 | ||||
-rw-r--r-- | tools/qmllint/checkidentifiers.cpp | 67 |
4 files changed, 76 insertions, 22 deletions
diff --git a/tests/auto/qml/qmllint/data/AttachedType.qml b/tests/auto/qml/qmllint/data/AttachedType.qml new file mode 100644 index 0000000000..02fa514e83 --- /dev/null +++ b/tests/auto/qml/qmllint/data/AttachedType.qml @@ -0,0 +1,5 @@ +import Things 1.0 + +Item { + property real test: Screen.pixelDensity * 0.5 +} diff --git a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes index 95ff95eb7d..448a966145 100644 --- a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes +++ b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes @@ -30,4 +30,29 @@ Module { Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } Property { name: "contentChildren"; type: "QQuickItem"; isList: true; isReadonly: true } } + Component { + name: "MyScreen" + prototype: "QObject" + exports: [ + "Things.Window/Screen 1.0", + ] + isCreatable: false + attachedType: "MyScreenAttached" + } + Component { + name: "MyScreenAttached" + prototype: "MyScreenInfo" + exports: [ + "Things.Window/ScreenAttached 1.0", + ] + } + Component { + name: "MyScreenInfo" + prototype: "QObject" + exports: [ + "Things.Window/ScreenInfo 1.0", + ] + isCreatable: false + Property { name: "pixelDensity"; type: "double"; isReadonly: true } + } } diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 7459a813be..d5912def6b 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -223,6 +223,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("EnumAccess1") << QStringLiteral("EnumAccess1.qml"); QTest::newRow("EnumAccess2") << QStringLiteral("EnumAccess2.qml"); QTest::newRow("ListProperty") << QStringLiteral("ListProperty.qml"); + QTest::newRow("AttachedType") << QStringLiteral("AttachedType.qml"); } void TestQmllint::cleanQmlCode() diff --git a/tools/qmllint/checkidentifiers.cpp b/tools/qmllint/checkidentifiers.cpp index 32eb4ce025..2771e1cfb2 100644 --- a/tools/qmllint/checkidentifiers.cpp +++ b/tools/qmllint/checkidentifiers.cpp @@ -31,6 +31,7 @@ #include <QtCore/qqueue.h> #include <QtCore/qsharedpointer.h> +#include <stack> class IssueLocationWithContext { @@ -82,6 +83,30 @@ void CheckIdentifiers::printContext(const QQmlJS::SourceLocation &location) cons + QLatin1Char('\n'), Normal); } +static bool walkViaParentAndAttachedScopes(ScopeTree::ConstPtr rootType, + const QHash<QString, ScopeTree::ConstPtr> &allTypes, + std::function<bool(ScopeTree::ConstPtr)> visit) +{ + if (rootType == nullptr) + return false; + std::stack<ScopeTree::ConstPtr> stack; + stack.push(rootType); + while (!stack.empty()) { + const auto type = stack.top(); + stack.pop(); + + if (visit(type)) + return true; + + if (auto superType = allTypes.value(type->superclassName())) + stack.push(superType); + + if (auto attachedType = allTypes.value(type->attachedTypeName())) + stack.push(attachedType); + } + return false; +} + bool CheckIdentifiers::checkMemberAccess(const QVector<ScopeTree::FieldMember> &members, const ScopeTree::ConstPtr &outerScope, const MetaProperty *prop) const @@ -199,29 +224,27 @@ bool CheckIdentifiers::checkMemberAccess(const QVector<ScopeTree::FieldMember> & if (!detectedRestrictiveName.isEmpty()) continue; - auto type = m_types.value(access.m_parentType.isEmpty() ? scopeName : access.m_parentType); - bool typeFound = false; - while (type) { - const auto typeProperties = type->properties(); - const auto typeIt = typeProperties.find(access.m_name); - if (typeIt != typeProperties.end()) { - const ScopeTree::ConstPtr propType = typeIt->type(); - scope = propType ? propType : m_types.value(typeIt->typeName()); - typeFound = true; - break; - } - - const auto typeMethods = type->methods(); - const auto typeMethodIt = typeMethods.find(access.m_name); - if (typeMethodIt != typeMethods.end()) { - detectedRestrictiveName = access.m_name; - detectedRestrictiveKind = QLatin1String("method"); - typeFound = true; - break; - } + auto rootType = + m_types.value(access.m_parentType.isEmpty() ? scopeName : access.m_parentType); + bool typeFound = + walkViaParentAndAttachedScopes(rootType, m_types, [&](ScopeTree::ConstPtr type) { + const auto typeProperties = type->properties(); + const auto typeIt = typeProperties.find(access.m_name); + if (typeIt != typeProperties.end()) { + const ScopeTree::ConstPtr propType = typeIt->type(); + scope = propType ? propType : m_types.value(typeIt->typeName()); + return true; + } - type = m_types.value(type->superclassName()); - } + const auto typeMethods = type->methods(); + const auto typeMethodIt = typeMethods.find(access.m_name); + if (typeMethodIt != typeMethods.end()) { + detectedRestrictiveName = access.m_name; + detectedRestrictiveKind = QLatin1String("method"); + return true; + } + return false; + }); if (typeFound) continue; |