qmlcompiler: Fully support required properties
Previously support of required properties was limited to detecting whether a property that was required actually exists. Now it also enables us to determine whether or not the required property was ever bound to. Still limited by the fact we do not fully support script bindings yet. Fixes: QTBUG-86755 Pick-to: 6.2 Change-Id: I1abb921d3b4f86a7929f0f829b541088e0c2bf60 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
906ccb4694
commit
803a5fda05
|
@ -40,7 +40,7 @@
|
|||
import QML
|
||||
|
||||
QtObject {
|
||||
required property string name
|
||||
required property string type
|
||||
property string name
|
||||
property string type
|
||||
property bool isPointer: false
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
import QML
|
||||
|
||||
Member {
|
||||
required property string type
|
||||
property string type
|
||||
property bool isPointer: false
|
||||
property bool isReadonly: false
|
||||
property bool isRequired: false
|
||||
|
|
|
@ -300,7 +300,7 @@ void QQmlJSImportVisitor::endVisit(UiProgram *)
|
|||
resolveAliases();
|
||||
processDefaultProperties();
|
||||
processPropertyTypes();
|
||||
checkPropertyBindings();
|
||||
processPropertyBindings();
|
||||
checkSignals();
|
||||
processPropertyBindingObjects();
|
||||
checkRequiredProperties();
|
||||
|
@ -589,9 +589,58 @@ void QQmlJSImportVisitor::checkRequiredProperties()
|
|||
Log_Required, required.location);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &defScope : m_objectDefinitionScopes) {
|
||||
if (defScope->parentScope() == m_globalScope || defScope->isInlineComponent())
|
||||
continue;
|
||||
|
||||
QVector<QQmlJSScope::ConstPtr> scopesToSearch;
|
||||
for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
|
||||
scopesToSearch << scope;
|
||||
const auto ownProperties = scope->ownProperties();
|
||||
for (auto propertyIt = ownProperties.constBegin();
|
||||
propertyIt != ownProperties.constEnd(); ++propertyIt) {
|
||||
const QString propName = propertyIt.key();
|
||||
|
||||
QQmlJSScope::ConstPtr prevRequiredScope;
|
||||
for (QQmlJSScope::ConstPtr requiredScope : scopesToSearch) {
|
||||
if (requiredScope->isPropertyLocallyRequired(propName)) {
|
||||
bool found =
|
||||
std::find_if(scopesToSearch.constBegin(), scopesToSearch.constEnd(),
|
||||
[&](QQmlJSScope::ConstPtr scope) {
|
||||
return scope->hasPropertyBinding(propName);
|
||||
})
|
||||
!= scopesToSearch.constEnd();
|
||||
|
||||
if (!found) {
|
||||
const QString propertyScopeName = scopesToSearch.length() > 1
|
||||
? getScopeName(scopesToSearch.at(scopesToSearch.length() - 2),
|
||||
QQmlJSScope::QMLScope)
|
||||
: u"here"_qs;
|
||||
const QString requiredScopeName = prevRequiredScope
|
||||
? getScopeName(prevRequiredScope, QQmlJSScope::QMLScope)
|
||||
: u"here"_qs;
|
||||
|
||||
QString message =
|
||||
QStringLiteral(
|
||||
"Component is missing required property %1 from %2")
|
||||
.arg(propName)
|
||||
.arg(propertyScopeName);
|
||||
if (requiredScope != scope)
|
||||
message += QStringLiteral(" (marked as required by %3)")
|
||||
.arg(requiredScopeName);
|
||||
|
||||
m_logger.log(message, Log_Required, defScope->sourceLocation());
|
||||
}
|
||||
}
|
||||
prevRequiredScope = requiredScope;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::checkPropertyBindings()
|
||||
void QQmlJSImportVisitor::processPropertyBindings()
|
||||
{
|
||||
for (auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
|
||||
QQmlJSScope::Ptr propertyScope = it.key();
|
||||
|
@ -639,6 +688,12 @@ void QQmlJSImportVisitor::checkPropertyBindings()
|
|||
|
||||
m_logger.log(message, Log_Deprecation, propertyScope->sourceLocation());
|
||||
}
|
||||
|
||||
QQmlJSMetaPropertyBinding binding(property);
|
||||
|
||||
// TODO: Actually store the value
|
||||
|
||||
scope->addOwnPropertyBinding(binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ protected:
|
|||
QVector<QQmlJSAnnotation> parseAnnotations(QQmlJS::AST::UiAnnotationList *list);
|
||||
void addDefaultProperties();
|
||||
void processDefaultProperties();
|
||||
void checkPropertyBindings();
|
||||
void processPropertyBindings();
|
||||
void checkRequiredProperties();
|
||||
void processPropertyTypes();
|
||||
void processPropertyBindingObjects();
|
||||
|
|
|
@ -3,4 +3,5 @@ import QtQml 2.15
|
|||
QtObject {
|
||||
property int x
|
||||
required x
|
||||
x: 5
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
component Base : Item {
|
||||
required property string required_now_string
|
||||
property string required_later_string
|
||||
|
||||
required property QtObject required_now_object
|
||||
property QtObject required_later_object
|
||||
}
|
||||
|
||||
component Derived : Base {
|
||||
required required_later_string
|
||||
required required_later_object
|
||||
}
|
||||
|
||||
Derived {
|
||||
required_now_string: ""
|
||||
required_later_string: ""
|
||||
|
||||
required_now_object: QtObject {}
|
||||
required_later_object: QtObject {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
component Base : Item {
|
||||
required property string required_now_string
|
||||
property string required_later_string
|
||||
property string required_even_later_string
|
||||
}
|
||||
|
||||
component Derived : Base {
|
||||
required required_later_string
|
||||
}
|
||||
|
||||
Derived {
|
||||
required required_even_later_string
|
||||
required_now_string: ""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
component Base : Item {
|
||||
required property string required_now_string
|
||||
property string required_later_string
|
||||
}
|
||||
|
||||
component Derived : Base {
|
||||
required required_later_string
|
||||
}
|
||||
|
||||
Derived {
|
||||
required property string required_defined_here_string
|
||||
required_later_string: ""
|
||||
}
|
||||
}
|
|
@ -828,8 +828,31 @@ void TestQmllint::requiredProperty()
|
|||
{
|
||||
QVERIFY(runQmllint("requiredProperty.qml", true).isEmpty());
|
||||
|
||||
{
|
||||
const QString errors = runQmllint("requiredMissingProperty.qml", false);
|
||||
QVERIFY(errors.contains(QStringLiteral("Property \"foo\" was marked as required but does not exist.")));
|
||||
QVERIFY(errors.contains(
|
||||
QStringLiteral("Property \"foo\" was marked as required but does not exist.")));
|
||||
}
|
||||
|
||||
QVERIFY(runQmllint("requiredPropertyBindings.qml", true).isEmpty());
|
||||
|
||||
{
|
||||
const QString errors = runQmllint("requiredPropertyBindingsNow.qml", false);
|
||||
QVERIFY(errors.contains(QStringLiteral(
|
||||
"Component is missing required property required_now_string from Base")));
|
||||
QVERIFY(errors.contains(QStringLiteral(
|
||||
"Component is missing required property required_defined_here_string from here")));
|
||||
}
|
||||
|
||||
{
|
||||
const QString errors = runQmllint("requiredPropertyBindingsLater.qml", false);
|
||||
QVERIFY(errors.contains(
|
||||
QStringLiteral("Component is missing required property required_later_string from "
|
||||
"Base (marked as required by Derived)")));
|
||||
QVERIFY(errors.contains(
|
||||
QStringLiteral("Component is missing required property required_even_later_string "
|
||||
"from Base (marked as required by here)")));
|
||||
}
|
||||
}
|
||||
|
||||
void TestQmllint::settingsFile()
|
||||
|
|
Loading…
Reference in New Issue