diff options
| -rw-r--r-- | tests/auto/qml/qmllint/data/badTypeAssertion.qml | 6 | ||||
| -rw-r--r-- | tests/auto/qml/qmllint/data/goodTypeAssertion.qml | 6 | ||||
| -rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 5 | ||||
| -rw-r--r-- | tools/qmllint/findunqualified.cpp | 28 | ||||
| -rw-r--r-- | tools/qmllint/findunqualified.h | 3 | ||||
| -rw-r--r-- | tools/qmllint/scopetree.cpp | 20 | ||||
| -rw-r--r-- | tools/qmllint/scopetree.h | 4 |
7 files changed, 63 insertions, 9 deletions
diff --git a/tests/auto/qml/qmllint/data/badTypeAssertion.qml b/tests/auto/qml/qmllint/data/badTypeAssertion.qml new file mode 100644 index 0000000000..717fc1b1bb --- /dev/null +++ b/tests/auto/qml/qmllint/data/badTypeAssertion.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Item { + property QtObject foo: Item { x: 4 } + property real foox: (foo as Item).rrr +} diff --git a/tests/auto/qml/qmllint/data/goodTypeAssertion.qml b/tests/auto/qml/qmllint/data/goodTypeAssertion.qml new file mode 100644 index 0000000000..6f5f52eb6b --- /dev/null +++ b/tests/auto/qml/qmllint/data/goodTypeAssertion.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Item { + property QtObject foo: Item { x: 4 } + property real foox: (foo as Item).x +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index f906c3ac45..655e6a1d65 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -156,6 +156,10 @@ void TestQmllint::dirtyQmlCode_data() << QStringLiteral("parentIsComponent.qml") << QString("Warning: Property \"progress\" not found on type \"QQuickItem\" at 7:39") << QString(); + QTest::newRow("badTypeAssertion") + << QStringLiteral("badTypeAssertion.qml") + << QString("Warning: Property \"rrr\" not found on type \"Item\" at 5:39") + << QString(); } void TestQmllint::dirtyQmlCode() @@ -186,6 +190,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("methodsInJavascript") << QStringLiteral("javascriptMethods.qml"); QTest::newRow("goodAlias") << QStringLiteral("goodAlias.qml"); QTest::newRow("goodParent") << QStringLiteral("goodParent.qml"); + QTest::newRow("goodTypeAssertion") << QStringLiteral("goodTypeAssertion.qml"); } void TestQmllint::cleanQmlCode() diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index d0dc3a3711..dc1bb29567 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -900,11 +900,37 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FieldMemberExpression *) void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember) { - if (m_fieldMemberBase == fieldMember->base) { + using namespace QQmlJS::AST; + ExpressionNode *base = fieldMember->base; + while (auto *nested = cast<NestedExpression *>(base)) + base = nested->expression; + + if (m_fieldMemberBase == base) { + QString type; + if (auto *binary = cast<BinaryExpression *>(base)) { + if (binary->op == QSOperator::As) { + if (auto *right = cast<IdentifierExpression *>(binary->right)) + type = right->name.toString(); + } + } m_currentScope->accessMember(fieldMember->name.toString(), + type, fieldMember->identifierToken); m_fieldMemberBase = fieldMember; } else { m_fieldMemberBase = nullptr; } } + +bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::BinaryExpression *) +{ + return true; +} + +void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::BinaryExpression *binExp) +{ + if (binExp->op == QSOperator::As && m_fieldMemberBase == binExp->left) + m_fieldMemberBase = binExp; + else + m_fieldMemberBase = nullptr; +} diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h index f5955a3a74..6668b53b08 100644 --- a/tools/qmllint/findunqualified.h +++ b/tools/qmllint/findunqualified.h @@ -161,6 +161,9 @@ private: bool visit(QQmlJS::AST::PatternElement *) override; bool visit(QQmlJS::AST::FieldMemberExpression *idprop) override; void endVisit(QQmlJS::AST::FieldMemberExpression *) override; + + bool visit(QQmlJS::AST::BinaryExpression *) override; + void endVisit(QQmlJS::AST::BinaryExpression *) override; }; #endif // FINDUNQUALIFIED_H diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp index e5b0eecc5f..6c122f0368 100644 --- a/tools/qmllint/scopetree.cpp +++ b/tools/qmllint/scopetree.cpp @@ -88,14 +88,15 @@ bool ScopeTree::isIdInCurrentScope(const QString &id) const } void ScopeTree::addIdToAccessed(const QString &id, const QQmlJS::AST::SourceLocation &location) { - m_currentFieldMember = new FieldMemberList {id, location, {}}; + m_currentFieldMember = new FieldMemberList {id, QString(), location, {}}; m_accessedIdentifiers.push_back(std::unique_ptr<FieldMemberList>(m_currentFieldMember)); } -void ScopeTree::accessMember(const QString &name, const QQmlJS::AST::SourceLocation &location) +void ScopeTree::accessMember(const QString &name, const QString &parentType, + const QQmlJS::AST::SourceLocation &location) { Q_ASSERT(m_currentFieldMember); - auto *fieldMember = new FieldMemberList {name, location, {}}; + auto *fieldMember = new FieldMemberList {name, parentType, location, {}}; m_currentFieldMember->m_child.reset(fieldMember); m_currentFieldMember = fieldMember; } @@ -150,7 +151,9 @@ bool ScopeTree::checkMemberAccess( const auto scopeIt = scope->m_properties.find(access->m_name); if (scopeIt != scope->m_properties.end()) { - if (scopeIt->isList() || scopeIt->typeName() == QLatin1String("string")) { + const QString typeName = access->m_parentType.isEmpty() ? scopeIt->typeName() + : access->m_parentType; + if (scopeIt->isList() || typeName == QLatin1String("string")) { if (access->m_child && access->m_child->m_name != QLatin1String("length")) { colorOut.write("Warning: ", Warning); colorOut.write( @@ -166,8 +169,9 @@ bool ScopeTree::checkMemberAccess( } return true; } - const ScopeTree *type = scopeIt->type() ? scopeIt->type() - : types.value(scopeIt->typeName()).get(); + const ScopeTree *type = (scopeIt->type() && access->m_parentType.isEmpty()) + ? scopeIt->type() + : types.value(typeName).get(); return checkMemberAccess(code, access.get(), type, types, colorOut); } @@ -199,7 +203,9 @@ bool ScopeTree::checkMemberAccess( while (type) { const auto typeIt = type->m_properties.find(access->m_name); if (typeIt != type->m_properties.end()) { - const auto propType = typeIt->type(); + const ScopeTree *propType = access->m_parentType.isEmpty() + ? typeIt->type() + : types.value(access->m_parentType).get(); return checkMemberAccess(code, access.get(), propType ? propType : types.value(typeIt->typeName()).get(), types, colorOut); diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h index eb5f384477..f5d1155a49 100644 --- a/tools/qmllint/scopetree.h +++ b/tools/qmllint/scopetree.h @@ -120,7 +120,8 @@ public: bool isIdInCurrentScope(const QString &id) const; void addIdToAccessed(const QString &id, const QQmlJS::AST::SourceLocation &location); - void accessMember(const QString &name, const QQmlJS::AST::SourceLocation &location); + void accessMember(const QString &name, const QString &parentType, + const QQmlJS::AST::SourceLocation &location); void resetMemberScope(); bool isVisualRootScope() const; @@ -172,6 +173,7 @@ private: struct FieldMemberList { QString m_name; + QString m_parentType; QQmlJS::AST::SourceLocation m_location; std::unique_ptr<FieldMemberList> m_child; }; |
