aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2024-04-15 17:40:14 +0200
committerSami Shalayel <sami.shalayel@qt.io>2024-04-18 11:46:36 +0200
commit6fd68ab3f4a629cc36be83e6378db85e797acd74 (patch)
treeba1a5eeac73f3da1cfdb3228331e9acb4776d67f
parent5dd4720bd2a5d84127edd692209267cafb4b68a8 (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.h23
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp7
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h6
-rw-r--r--src/qmlls/qqmllsutils.cpp4
-rw-r--r--tests/auto/qmlls/utils/data/completions/boundComponents.qml21
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.cpp10
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()