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);
}
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)
{
m_savedBindingOuterScope = m_currentScope;
@ -1589,9 +1607,17 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
if (name.isEmpty())
break;
enterEnvironmentNonUnique(name.front().isUpper() ? QQmlJSScope::AttachedPropertyScope
: QQmlJSScope::GroupedPropertyScope,
name, group->firstSourceLocation());
const bool isAttachedProperty = name.front().isUpper();
if (isAttachedProperty) {
// attached property
enterEnvironmentNonUnique(QQmlJSScope::AttachedPropertyScope,
name, group->firstSourceLocation());
} else {
// grouped property
enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope,
name, group->firstSourceLocation());
createGroupBinding(m_currentScope, name, group->firstSourceLocation());
}
}
auto name = group->name;
@ -1982,7 +2008,12 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope
: QQmlJSScope::GroupedPropertyScope;
bool exists = enterEnvironmentNonUnique(scopeKind, idName, group->firstSourceLocation());
if (scopeKind == QQmlJSScope::GroupedPropertyScope)
createGroupBinding(m_currentScope, idName, group->firstSourceLocation());
++scopesEnteredCounter;
needsResolution = needsResolution || !exists;
}

View File

@ -466,7 +466,23 @@ private:
friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); }
};
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); }
};
using type = std::variant<Invalid, BoolLiteral, NumberLiteral, StringLiteral,
@ -530,6 +546,12 @@ public:
m_bindingContent = Content::Script {};
}
void setGroupBinding(const QSharedPointer<const QQmlJSScope> &groupScope)
{
ensureSetBindingTypeOnce();
m_bindingContent = Content::GroupProperty { groupScope };
}
void setBoolLiteral(bool value)
{
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/qqmljsimportvisitor_p.h>
#include <private/qqmljstyperesolver_p.h>
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
class tst_qqmljsscope : public QQmlDataTest
{
@ -95,11 +97,13 @@ private Q_SLOTS:
void signalCreationDifferences();
void allTypesAvailable();
void shadowing();
#ifdef LABS_QML_MODELS_PRESENT
void componentWrappedObjects();
void labsQmlModelsSanity();
#endif
void unknownCppBase();
void groupedProperties();
public:
tst_qqmljsscope()
@ -262,5 +266,15 @@ void tst_qqmljsscope::unknownCppBase()
// 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)
#include "tst_qqmljsscope.moc"