diff options
author | Sami Shalayel <sami.shalayel@qt.io> | 2024-04-15 17:40:14 +0200 |
---|---|---|
committer | Sami Shalayel <sami.shalayel@qt.io> | 2024-04-18 11:46:36 +0200 |
commit | 6fd68ab3f4a629cc36be83e6378db85e797acd74 (patch) | |
tree | ba1a5eeac73f3da1cfdb3228331e9acb4776d67f | |
parent | 5dd4720bd2a5d84127edd692209267cafb4b68a8 (diff) |
qmlls: assume bound identifiers in completions
Add a new option flag to QmlTypeResolver to allow resolving id's while
assuming that Components are bounds (even if they are currently not
bound). This allows qmlls to provide completions even if the user forgot
to set the pragma, instead of not suggesting anything at all after
"someIdThatIsOnlyVisibleWhenComponentsAreBound.".
Fixes: QTBUG-124459
Change-Id: I658c2f5d678f94f99a94a9e06aaf6d19db9b1147
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 1bcb8587ab6b7068d1af8897804820853e7bc9d0)
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/qmlcompiler/qqmljsscopesbyid_p.h | 23 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 7 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 6 | ||||
-rw-r--r-- | src/qmlls/qqmllsutils.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qmlls/utils/data/completions/boundComponents.qml | 21 | ||||
-rw-r--r-- | tests/auto/qmlls/utils/tst_qmlls_utils.cpp | 10 |
6 files changed, 57 insertions, 14 deletions
diff --git a/src/qmlcompiler/qqmljsscopesbyid_p.h b/src/qmlcompiler/qqmljsscopesbyid_p.h index ab97aeaccf..c9dca9ae34 100644 --- a/src/qmlcompiler/qqmljsscopesbyid_p.h +++ b/src/qmlcompiler/qqmljsscopesbyid_p.h @@ -22,6 +22,12 @@ QT_BEGIN_NAMESPACE +enum QQmlJSScopesByIdOption: char { + Default = 0, + AssumeComponentsAreBound = 1, +}; +Q_DECLARE_FLAGS(QQmlJSScopesByIdOptions, QQmlJSScopesByIdOption); + class QQmlJSScopesById { public: @@ -34,11 +40,12 @@ public: void setValueTypesAreAddressable(bool addressable) { m_valueTypesAreAddressable = addressable; } bool valueTypesAreAddressable() const { return m_valueTypesAreAddressable; } - QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer) const + QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer, + QQmlJSScopesByIdOptions options = Default) const { const QQmlJSScope::ConstPtr referrerRoot = componentRoot(referrer); for (auto it = m_scopesById.begin(), end = m_scopesById.end(); it != end; ++it) { - if (*it == scope && isComponentVisible(componentRoot(*it), referrerRoot)) + if (*it == scope && isComponentVisible(componentRoot(*it), referrerRoot, options)) return it.key(); } return QString(); @@ -49,7 +56,8 @@ public: Returns the scope that has id \a id in the component to which \a referrer belongs to. If no such scope exists, a null scope is returned. */ - QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer) const + QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer, + QQmlJSScopesByIdOptions options = Default) const { Q_ASSERT(!id.isEmpty()); const auto range = m_scopesById.equal_range(id); @@ -58,7 +66,7 @@ public: const QQmlJSScope::ConstPtr referrerRoot = componentRoot(referrer); for (auto it = range.first; it != range.second; ++it) { - if (isComponentVisible(componentRoot(*it), referrerRoot)) + if (isComponentVisible(componentRoot(*it), referrerRoot, options)) return *it; } @@ -95,10 +103,11 @@ private: return scope; } - bool isComponentVisible( - const QQmlJSScope::ConstPtr &observed, const QQmlJSScope::ConstPtr &observer) const + bool isComponentVisible(const QQmlJSScope::ConstPtr &observed, + const QQmlJSScope::ConstPtr &observer, + QQmlJSScopesByIdOptions options) const { - if (!m_componentsAreBound) + if (!m_componentsAreBound && !options.testAnyFlag(AssumeComponentsAreBound)) return observed == observer; for (QQmlJSScope::ConstPtr scope = observer; scope; scope = scope->parentScope()) { diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 14f4436eac..7bd8138421 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -997,8 +997,9 @@ static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &s return typeRevision.isValid() && typeRevision >= revision; } -QQmlJSRegisterContent QQmlJSTypeResolver::scopedType( - const QQmlJSScope::ConstPtr &scope, const QString &name, int lookupIndex) const +QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr &scope, + const QString &name, int lookupIndex, + QQmlJSScopesByIdOptions options) const { const auto isAssignedToDefaultProperty = [this](const QQmlJSScope::ConstPtr &parent, const QQmlJSScope::ConstPtr &child) { @@ -1017,7 +1018,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType( return false; }; - if (QQmlJSScope::ConstPtr identified = m_objectsById.scope(name, scope)) { + if (QQmlJSScope::ConstPtr identified = m_objectsById.scope(name, scope, options)) { return QQmlJSRegisterContent::create(storedType(identified), identified, lookupIndex, QQmlJSRegisterContent::ObjectById, scope); } diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index 47f82afe7e..5d740ded13 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -120,9 +120,9 @@ public: QQmlJSRegisterContent builtinType(const QQmlJSScope::ConstPtr &type) const; QQmlJSRegisterContent globalType(const QQmlJSScope::ConstPtr &type) const; - QQmlJSRegisterContent scopedType( - const QQmlJSScope::ConstPtr &scope, const QString &name, - int lookupIndex = QQmlJSRegisterContent::InvalidLookupIndex) const; + QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name, + int lookupIndex = QQmlJSRegisterContent::InvalidLookupIndex, + QQmlJSScopesByIdOptions options = Default) const; QQmlJSRegisterContent memberType( const QQmlJSRegisterContent &type, const QString &name, int lookupIndex = QQmlJSRegisterContent::InvalidLookupIndex) const; diff --git a/src/qmlls/qqmllsutils.cpp b/src/qmlls/qqmllsutils.cpp index 1fcbfc9f25..fa5d02447a 100644 --- a/src/qmlls/qqmllsutils.cpp +++ b/src/qmlls/qqmllsutils.cpp @@ -1273,7 +1273,9 @@ resolveIdentifierExpressionType(const DomItem &item, QQmlLSUtilsResolveOptions o } // check if its an id - QQmlJSRegisterContent fromId = resolver->scopedType(referrerScope, name); + QQmlJSRegisterContent fromId = + resolver->scopedType(referrerScope, name, QQmlJSRegisterContent::InvalidLookupIndex, + AssumeComponentsAreBound); if (fromId.variant() == QQmlJSRegisterContent::ObjectById) return QQmlLSUtilsExpressionType{ name, fromId.type(), QmlObjectIdIdentifier }; diff --git a/tests/auto/qmlls/utils/data/completions/boundComponents.qml b/tests/auto/qmlls/utils/data/completions/boundComponents.qml new file mode 100644 index 0000000000..343e7a3c7b --- /dev/null +++ b/tests/auto/qmlls/utils/data/completions/boundComponents.qml @@ -0,0 +1,21 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Item { + id: rootId + property int inRoot + + DelegateModel { + delegate: Item { + id: childId + + property int myInt: rootId.inRoot + property int inChild + + } + } + + property int myInt: childId.inChild +} diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp index 868742b5e5..22e13302b0 100644 --- a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp +++ b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp @@ -3280,6 +3280,16 @@ void tst_qmlls_utils::completions_data() << ExpectedCompletions({}) << QStringList{ u"QtQuick"_s, attachedTypeName, u"Rectangle"_s, forStatementCompletion, u"x"_s }; + + QTest::newRow("assumeBoundComponentsIdFromParent") + << testFile("completions/boundComponents.qml") << 14 << 33 + << ExpectedCompletions{ { u"rootId"_s, CompletionItemKind::Value } } + << QStringList{ u"inRoot"_s }; + + QTest::newRow("assumeBoundComponentsPropertyFromParent") + << testFile("completions/boundComponents.qml") << 14 << 40 + << ExpectedCompletions{ { u"inRoot"_s, CompletionItemKind::Property } } + << QStringList{ u"root"_s }; } void tst_qmlls_utils::completions() |