qmlcompiler: Improve grouped property support

Still lacks support for merging and resolving grouped bindings, but
this will be handled in another patch.

Task-number: QTBUG-100168
Original-patch-by: Fabian Kosmale <fabian.kosmale@qt.io>
Change-Id: I1bb73f383acc3c8512676db47c5944f369b904b7
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
Maximilian Goldstein 2022-03-18 14:16:09 +01:00
parent 5fc74d216f
commit bc63196948
5 changed files with 81 additions and 4 deletions

View File

@ -1574,6 +1574,24 @@ void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scri
m_scopesById.insert(name, m_currentScope); m_scopesById.insert(name, m_currentScope);
} }
inline void createGroupBinding(QQmlJSScope::Ptr &scope, const QString &name,
const QQmlJS::SourceLocation &srcLocation)
{
auto parentScope = scope->parentScope();
const auto propertyBindings = parentScope->ownPropertyBindings(name);
const bool alreadyHasGroupedBinding = std::any_of(
propertyBindings.first, propertyBindings.second,
[](const QQmlJSMetaPropertyBinding &binding) {
return binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty;
});
if (alreadyHasGroupedBinding)
return;
QQmlJSMetaPropertyBinding groupBinding(srcLocation, name);
groupBinding.setGroupBinding(static_cast<QSharedPointer<QQmlJSScope>>(scope));
parentScope->addOwnPropertyBinding(std::move(groupBinding));
}
bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
{ {
m_savedBindingOuterScope = m_currentScope; m_savedBindingOuterScope = m_currentScope;
@ -1589,9 +1607,17 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
if (name.isEmpty()) if (name.isEmpty())
break; break;
enterEnvironmentNonUnique(name.front().isUpper() ? QQmlJSScope::AttachedPropertyScope const bool isAttachedProperty = name.front().isUpper();
: QQmlJSScope::GroupedPropertyScope, if (isAttachedProperty) {
// attached property
enterEnvironmentNonUnique(QQmlJSScope::AttachedPropertyScope,
name, group->firstSourceLocation()); name, group->firstSourceLocation());
} else {
// grouped property
enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope,
name, group->firstSourceLocation());
createGroupBinding(m_currentScope, name, group->firstSourceLocation());
}
} }
auto name = group->name; auto name = group->name;
@ -1982,7 +2008,12 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope
: QQmlJSScope::GroupedPropertyScope; : QQmlJSScope::GroupedPropertyScope;
bool exists = enterEnvironmentNonUnique(scopeKind, idName, group->firstSourceLocation()); bool exists = enterEnvironmentNonUnique(scopeKind, idName, group->firstSourceLocation());
if (scopeKind == QQmlJSScope::GroupedPropertyScope)
createGroupBinding(m_currentScope, idName, group->firstSourceLocation());
++scopesEnteredCounter; ++scopesEnteredCounter;
needsResolution = needsResolution || !exists; needsResolution = needsResolution || !exists;
} }

View File

@ -466,7 +466,23 @@ private:
friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); } friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); }
}; };
struct GroupProperty { struct GroupProperty {
friend bool operator==(GroupProperty , GroupProperty ) { return true; } /* Given a group property declaration like
anchors.left: root.left
the QQmlJSMetaPropertyBinding will have name "anchors", and a m_bindingContent
of type GroupProperty, with groupScope pointing to the scope introudced by anchors
In that scope, there will be another QQmlJSMetaPropertyBinding, with name "left" and
m_bindingContent Script (for root.left).
There should never be more than one GroupProperty for the same name in the same
scope, though: If the scope also contains anchors.top: root.top that should reuse the
GroupProperty content (and add a top: root.top binding in it). There might however
still be an additional object or script binding ( anchors: {left: foo, right: bar };
anchors: root.someFunction() ) or another binding to the property in a "derived"
type.
### TODO: Obtaining the effective binding result requires some resolving function
*/
QWeakPointer<const QQmlJSScope> groupScope;
friend bool operator==(GroupProperty a, GroupProperty b) { return a.groupScope == b.groupScope; }
friend bool operator!=(GroupProperty a, GroupProperty b) { return !(a == b); } friend bool operator!=(GroupProperty a, GroupProperty b) { return !(a == b); }
}; };
using type = std::variant<Invalid, BoolLiteral, NumberLiteral, StringLiteral, using type = std::variant<Invalid, BoolLiteral, NumberLiteral, StringLiteral,
@ -530,6 +546,12 @@ public:
m_bindingContent = Content::Script {}; m_bindingContent = Content::Script {};
} }
void setGroupBinding(const QSharedPointer<const QQmlJSScope> &groupScope)
{
ensureSetBindingTypeOnce();
m_bindingContent = Content::GroupProperty { groupScope };
}
void setBoolLiteral(bool value) void setBoolLiteral(bool value)
{ {
ensureSetBindingTypeOnce(); ensureSetBindingTypeOnce();

View File

@ -0,0 +1,5 @@
import QtQuick
Item {
anchors.top: parent.top
}

View File

@ -0,0 +1,5 @@
import QtQuick
GroupBase {
anchors.left: parent.right
}

View File

@ -44,6 +44,8 @@
#include <private/qqmljslogger_p.h> #include <private/qqmljslogger_p.h>
#include <private/qqmljsimportvisitor_p.h> #include <private/qqmljsimportvisitor_p.h>
#include <private/qqmljstyperesolver_p.h> #include <private/qqmljstyperesolver_p.h>
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
class tst_qqmljsscope : public QQmlDataTest class tst_qqmljsscope : public QQmlDataTest
{ {
@ -95,11 +97,13 @@ private Q_SLOTS:
void signalCreationDifferences(); void signalCreationDifferences();
void allTypesAvailable(); void allTypesAvailable();
void shadowing(); void shadowing();
#ifdef LABS_QML_MODELS_PRESENT #ifdef LABS_QML_MODELS_PRESENT
void componentWrappedObjects(); void componentWrappedObjects();
void labsQmlModelsSanity(); void labsQmlModelsSanity();
#endif #endif
void unknownCppBase(); void unknownCppBase();
void groupedProperties();
public: public:
tst_qqmljsscope() tst_qqmljsscope()
@ -262,5 +266,15 @@ void tst_qqmljsscope::unknownCppBase()
// we should not crash here, then it is a success // we should not crash here, then it is a success
} }
void tst_qqmljsscope::groupedProperties()
{
QQmlJSScope::ConstPtr root = run(u"groupProperties.qml"_qs);
QVERIFY(root);
QVERIFY(root->hasProperty(u"anchors"_qs));
auto anchorBindings = root->propertyBindings(u"anchors"_qs);
QVERIFY(!anchorBindings.isEmpty());
}
QTEST_MAIN(tst_qqmljsscope) QTEST_MAIN(tst_qqmljsscope)
#include "tst_qqmljsscope.moc" #include "tst_qqmljsscope.moc"