aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2020-01-30 10:51:34 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2020-01-31 14:46:03 +0100
commit85f15e2af40be1e9500fe600daba31839b904ef4 (patch)
treed37e14b6940ef04cc04cc2c5e4fd37457ff9ae3e
parenta87ad432a002614a5cca4d635ead5aedb97ba757 (diff)
Required properties: Allow retroactive require specification
It is now possible to mark a property of a parent class as required in the child by writing required <propertyName> Change-Id: I9e9d58c7b5c00577b056e905b39744b2fa359ea0 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/common/qv4compileddata_p.h30
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp59
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h13
-rw-r--r--src/qml/parser/qqmljs.g13
-rw-r--r--src/qml/parser/qqmljsast.cpp8
-rw-r--r--src/qml/parser/qqmljsast_p.h25
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h1
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h3
-rw-r--r--src/qml/qml/qqmlincubator_p.h4
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp40
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h2
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp1
-rw-r--r--tests/auto/qml/qmlmin/tst_qmlmin.cpp1
-rw-r--r--tests/auto/qml/qqmllanguage/data/NonRequiredBase.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/RequiredBase.qml3
-rw-r--r--tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/requiredProperties.4.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/requiredProperties.5.qml1
-rw-r--r--tests/auto/qml/qqmllanguage/data/requiredProperties.6.qml3
-rw-r--r--tests/auto/qml/qqmllanguage/data/requiredProperties.7.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp30
21 files changed, 237 insertions, 21 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 42c476763e..a5a1cf8969 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -75,7 +75,7 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
-#define QV4_DATA_STRUCTURE_VERSION 0x28// support inline components
+#define QV4_DATA_STRUCTURE_VERSION 0x29// support additional required property features
class QIODevice;
class QQmlTypeNameCache;
@@ -695,6 +695,12 @@ struct Property
};
static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+struct RequiredPropertyExtraData {
+ quint32_le nameIndex;
+};
+
+static_assert (sizeof(RequiredPropertyExtraData) == 4, "RequiredPropertyExtraData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
struct Alias {
enum Flags : unsigned int {
IsReadOnly = 0x1,
@@ -765,13 +771,16 @@ struct Object
Location locationOfIdProperty;
quint32_le offsetToInlineComponents;
quint16_le nInlineComponents;
+ quint32_le offsetToRequiredPropertyExtraData;
+ quint16_le nRequiredPropertyExtraData;
// Function[]
// Property[]
// Signal[]
// Binding[]
// InlineComponent[]
+// RequiredPropertyExtraData[]
- static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents)
+ static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData)
{
return ( sizeof(Object)
+ nFunctions * sizeof(quint32)
@@ -782,6 +791,7 @@ struct Object
+ nBindings * sizeof(Binding)
+ nNamedObjectsInComponent * sizeof(int)
+ nInlineComponents * sizeof(InlineComponent)
+ + nRequiredPropertyExtraData * sizeof(RequiredPropertyExtraData)
+ 0x7
) & ~0x7;
}
@@ -835,6 +845,16 @@ struct Object
return reinterpret_cast<const InlineComponent*>(reinterpret_cast<const char *>(this) + offsetToInlineComponents);
}
+ const RequiredPropertyExtraData *requiredPropertyExtraDataAt(int idx) const
+ {
+ return requiredPropertyExtraDataTable() + idx;
+ }
+
+ const RequiredPropertyExtraData *requiredPropertyExtraDataTable() const
+ {
+ return reinterpret_cast<const RequiredPropertyExtraData*>(reinterpret_cast<const char *>(this) + offsetToRequiredPropertyExtraData);
+ }
+
// --- QQmlPropertyCacheCreator interface
int propertyCount() const { return nProperties; }
int aliasCount() const { return nAliases; }
@@ -863,10 +883,14 @@ struct Object
InlineComponentIterator inlineComponentsBegin() const {return InlineComponentIterator(this, 0);}
InlineComponentIterator inlineComponentsEnd() const {return InlineComponentIterator(this, nInlineComponents);}
+ typedef TableIterator<RequiredPropertyExtraData, Object, &Object::requiredPropertyExtraDataAt> RequiredPropertyExtraDataIterator;
+ RequiredPropertyExtraDataIterator requiredPropertyExtraDataBegin() const {return RequiredPropertyExtraDataIterator(this, 0); };
+ RequiredPropertyExtraDataIterator requiredPropertyExtraDataEnd() const {return RequiredPropertyExtraDataIterator(this, nRequiredPropertyExtraData); };
+
int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
// ---
};
-static_assert(sizeof(Object) == 76, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Object) == 84, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Import
{
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 811f88cb73..a3ee431e90 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -64,6 +64,33 @@ using namespace QQmlJS;
return false; \
}
+void Object::simplifyRequiredProperties() {
+ // if a property of the current object was marked as required
+ // do not store that information in the ExtraData
+ // but rather mark the property as required
+ QSet<int> required;
+ for (auto it = this->requiredPropertyExtraDataBegin(); it != this->requiredPropertyExtraDataEnd(); ++it)
+ required.insert(it->nameIndex);
+ if (required.isEmpty())
+ return;
+ for (auto it = this->propertiesBegin(); it != this->propertiesEnd(); ++it) {
+ auto requiredIt = required.find(it->nameIndex);
+ if (requiredIt != required.end()) {
+ it->isRequired = true;
+ required.erase(requiredIt);
+ }
+ }
+ QmlIR::RequiredPropertyExtraData *prev = nullptr;
+ auto current = this->requiredPropertyExtraDatas->first;
+ while (current) {
+ if (required.contains(current->nameIndex))
+ prev = current;
+ else
+ requiredPropertyExtraDatas->unlink(prev, current);
+ current = current->next;
+ }
+}
+
bool Parameter::init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString &parameterName,
const QString &typeName)
{
@@ -162,6 +189,7 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons
functions = pool->New<PoolList<Function> >();
functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >();
inlineComponents = pool->New<PoolList<InlineComponent>>();
+ requiredPropertyExtraDatas = pool->New<PoolList<RequiredPropertyExtraData>>();
declarationsOverride = nullptr;
}
@@ -287,6 +315,11 @@ void Object::appendInlineComponent(InlineComponent *ic)
inlineComponents->append(ic);
}
+void Object::appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraData)
+{
+ requiredPropertyExtraDatas->append(extraData);
+}
+
QString Object::appendBinding(Binding *b, bool isListBinding)
{
const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0));
@@ -452,6 +485,10 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen
qSwap(_imports, output->imports);
qSwap(_pragmas, output->pragmas);
qSwap(_objects, output->objects);
+
+ for (auto object: output->objects)
+ object->simplifyRequiredProperties();
+
return errors.isEmpty();
}
@@ -988,6 +1025,14 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
return false;
}
+bool IRBuilder::visit(AST::UiRequired *ast)
+{
+ auto extraData = New<RequiredPropertyExtraData>();
+ extraData->nameIndex = registerString(ast->name.toString());
+ _object->appendRequiredPropertyExtraData(extraData);
+ return false;
+}
+
QString IRBuilder::asString(QQmlJS::AST::UiQualifiedId *node)
{
QString s;
@@ -1590,7 +1635,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
uint nextOffset = objectOffset + objectOffsetTableSize;
for (Object *o : qAsConst(output.objects)) {
objectOffsets.insert(o, nextOffset);
- nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size(), o->inlineComponentCount());
+ nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size(), o->inlineComponentCount(), o->requiredPropertyExtraDataCount());
int signalTableSize = 0;
for (const Signal *s = o->firstSignal(); s; s = s->next)
@@ -1673,6 +1718,10 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
objectToWrite->offsetToInlineComponents = nextOffset;
nextOffset += objectToWrite->nInlineComponents * sizeof (QV4::CompiledData::InlineComponent);
+ objectToWrite->nRequiredPropertyExtraData = o->requiredPropertyExtraDataCount();
+ objectToWrite->offsetToRequiredPropertyExtraData = nextOffset;
+ nextOffset += objectToWrite->nRequiredPropertyExtraData * sizeof(QV4::CompiledData::RequiredPropertyExtraData);
+
quint32_le *functionsTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToFunctions);
for (const Function *f = o->firstFunction(); f; f = f->next)
*functionsTable++ = o->runtimeFunctionIndices.at(f->index);
@@ -1752,6 +1801,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
*icToWrite = *ic;
inlineComponentPtr += sizeof(QV4::CompiledData::InlineComponent);
}
+
+ char *requiredPropertyExtraDataPtr = objectPtr + objectToWrite->offsetToRequiredPropertyExtraData;
+ for (auto it = o->requiredPropertyExtraDataBegin(); it != o->requiredPropertyExtraDataEnd(); ++it) {
+ const RequiredPropertyExtraData *extraData = it.ptr;
+ QV4::CompiledData::RequiredPropertyExtraData *extraDataToWrite = reinterpret_cast<QV4::CompiledData::RequiredPropertyExtraData*>(requiredPropertyExtraDataPtr);
+ *extraDataToWrite = *extraData;
+ requiredPropertyExtraDataPtr += sizeof(QV4::CompiledData::RequiredPropertyExtraData);
+ }
}
if (!output.javaScriptCompilationUnit.data) {
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index d4f2eb8dd4..32921638af 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -285,6 +285,11 @@ struct Alias : public QV4::CompiledData::Alias
Alias *next;
};
+struct RequiredPropertyExtraData : public QV4::CompiledData::RequiredPropertyExtraData
+{
+ RequiredPropertyExtraData *next;
+};
+
struct Function
{
QV4::CompiledData::Location location;
@@ -341,6 +346,9 @@ public:
int functionCount() const { return functions->count; }
const InlineComponent *inlineComponent() const { return inlineComponents->first; }
int inlineComponentCount() const { return inlineComponents->count; }
+ const RequiredPropertyExtraData *requiredPropertyExtraData() const {return requiredPropertyExtraDatas->first; }
+ int requiredPropertyExtraDataCount() const { return requiredPropertyExtraDatas->count; }
+ void simplifyRequiredProperties();
PoolList<Binding>::Iterator bindingsBegin() const { return bindings->begin(); }
PoolList<Binding>::Iterator bindingsEnd() const { return bindings->end(); }
@@ -356,6 +364,8 @@ public:
PoolList<Function>::Iterator functionsEnd() const { return functions->end(); }
PoolList<InlineComponent>::Iterator inlineComponentsBegin() const { return inlineComponents->begin(); }
PoolList<InlineComponent>::Iterator inlineComponentsEnd() const { return inlineComponents->end(); }
+ PoolList<RequiredPropertyExtraData>::Iterator requiredPropertyExtraDataBegin() const {return requiredPropertyExtraDatas->begin(); }
+ PoolList<RequiredPropertyExtraData>::Iterator requiredPropertyExtraDataEnd() const {return requiredPropertyExtraDatas->end(); }
// If set, then declarations for this object (and init bindings for these) should go into the
// specified object. Used for declarations inside group properties.
@@ -369,6 +379,7 @@ public:
QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation);
void appendFunction(QmlIR::Function *f);
void appendInlineComponent(InlineComponent *ic);
+ void appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraData);
QString appendBinding(Binding *b, bool isListBinding);
Binding *findBinding(quint32 nameIndex) const;
@@ -393,6 +404,7 @@ private:
PoolList<Binding> *bindings;
PoolList<Function> *functions;
PoolList<InlineComponent> *inlineComponents;
+ PoolList<RequiredPropertyExtraData> *requiredPropertyExtraDatas;
};
struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma
@@ -469,6 +481,7 @@ public:
bool visit(QQmlJS::AST::UiPublicMember *ast) override;
bool visit(QQmlJS::AST::UiScriptBinding *ast) override;
bool visit(QQmlJS::AST::UiSourceElement *ast) override;
+ bool visit(QQmlJS::AST::UiRequired *ast) override;
void throwRecursionDepthError() override
{
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index b0f0f6a809..879de92e5f 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -1350,6 +1350,18 @@ OptionalSemicolon: | Semicolon;
and then we would miss a semicolon (see tests/auto/quick/qquickvisualdatamodel/data/objectlist.qml)*/
./
+UiRequired: T_REQUIRED QmlIdentifier Semicolon;
+/.
+ case $rule_number: {
+ AST::UiRequired *node = new (pool) AST::UiRequired(stringRef(2));
+ node->requiredToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: UiRequired;
+
UiObjectMember: T_REQUIRED UiObjectMemberPropertyNoInitialiser;
/.
case $rule_number: {
@@ -1360,7 +1372,6 @@ UiObjectMember: T_REQUIRED UiObjectMemberPropertyNoInitialiser;
} break;
./
-
UiObjectMemberWithScriptStatement: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon;
/.
case $rule_number: {
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index aa3e8ab5e3..416916c547 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -1554,6 +1554,14 @@ void UiInlineComponent::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+void UiRequired::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
} } // namespace QQmlJS::AST
QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 48a994cd33..f3369676e9 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -258,7 +258,8 @@ public:
Kind_UiHeaderItemList,
Kind_UiEnumDeclaration,
Kind_UiEnumMemberList,
- Kind_UiVersionSpecifier
+ Kind_UiVersionSpecifier,
+ Kind_UiRequired
};
inline Node() {}
@@ -3103,6 +3104,28 @@ public:
SourceLocation semicolonToken;
};
+class QML_PARSER_EXPORT UiRequired: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(UiRequired)
+
+ UiRequired(QStringRef name)
+ :name(name)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return requiredToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return semicolonToken; }
+
+ QStringRef name;
+ SourceLocation requiredToken;
+ SourceLocation semicolonToken;
+};
+
class QML_PARSER_EXPORT UiHeaderItemList: public Node
{
public:
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index 8a8ee2dfae..fe260e2bb5 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -183,6 +183,7 @@ class UiHeaderItemList;
class UiEnumDeclaration;
class UiEnumMemberList;
class UiVersionSpecifier;
+class UiRequired;
} // namespace AST
} // namespace QQmlJS
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index d6b92990ad..fcc48da1d3 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -414,6 +414,9 @@ public:
virtual bool visit(TypeAnnotation *) { return true; }
virtual void endVisit(TypeAnnotation *) {}
+ virtual bool visit(UiRequired *) { return true; }
+ virtual void endVisit(UiRequired *) {}
+
virtual void throwRecursionDepthError() = 0;
quint16 recursionDepth() const { return m_recursionDepth; }
diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h
index aadb147bd5..a674ff274f 100644
--- a/src/qml/qml/qqmlincubator_p.h
+++ b/src/qml/qml/qqmlincubator_p.h
@@ -61,9 +61,7 @@
QT_BEGIN_NAMESPACE
-class QQmlPropertyData;
-struct RequiredPropertyInfo;
-using RequiredProperties = QHash<QQmlPropertyData*, RequiredPropertyInfo>;
+class RequiredProperties;
class QQmlIncubator;
class Q_QML_PRIVATE_EXPORT QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index a26cdba53a..e990376fbc 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -1505,23 +1505,45 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings)
_ddata->deferData(_compiledObjectIndex, compilationUnit, context);
+ QSet<QString> postHocRequired;
+ for (auto it = _compiledObject->requiredPropertyExtraDataBegin(); it != _compiledObject->requiredPropertyExtraDataEnd(); ++it)
+ postHocRequired.insert(stringAt(it->nameIndex));
+ bool hadInheritedRequiredProperties = !postHocRequired.empty();
+
for (int propertyIndex = 0; propertyIndex != _compiledObject->propertyCount(); ++propertyIndex) {
const QV4::CompiledData::Property* property = _compiledObject->propertiesBegin() + propertyIndex;
QQmlPropertyData *propertyData = _propertyCache->property(_propertyCache->propertyOffset() + propertyIndex);
- if (property->isRequired) {
- sharedState->hadRequiredProperties = true;
- sharedState->requiredProperties.insert(propertyData,
- RequiredPropertyInfo {compilationUnit->stringAt(property->nameIndex), compilationUnit->finalUrl(), property->location, {}});
- }
+ // only compute stringAt if there's a chance for the lookup to succeed
+ auto postHocIt = postHocRequired.isEmpty() ? postHocRequired.end() : postHocRequired.find(stringAt(property->nameIndex));
+ if (!property->isRequired && postHocRequired.end() == postHocIt)
+ continue;
+ if (postHocIt != postHocRequired.end())
+ postHocRequired.erase(postHocIt);
+ sharedState->hadRequiredProperties = true;
+ sharedState->requiredProperties.insert(propertyData,
+ RequiredPropertyInfo {compilationUnit->stringAt(property->nameIndex), compilationUnit->finalUrl(), property->location, {}});
+
}
for (int i = 0; i <= _propertyCache->propertyOffset(); ++i) {
QQmlPropertyData *propertyData = _propertyCache->property(i);
- if (propertyData && propertyData->isRequired()) {
- sharedState->hadRequiredProperties = true;
- sharedState->requiredProperties.insert(propertyData, RequiredPropertyInfo {propertyData->name(_qobject), compilationUnit->finalUrl(), _compiledObject->location, {}});
- }
+ if (!propertyData)
+ continue;
+ if (!propertyData->isRequired() && postHocRequired.isEmpty())
+ continue;
+ QString name = propertyData->name(_qobject);
+ auto postHocIt = postHocRequired.find(name);
+ if (!propertyData->isRequired() && postHocRequired.end() == postHocIt )
+ continue;
+
+ if (postHocIt != postHocRequired.end())
+ postHocRequired.erase(postHocIt);
+
+ sharedState->hadRequiredProperties = true;
+ sharedState->requiredProperties.insert(propertyData, RequiredPropertyInfo {name, compilationUnit->finalUrl(), _compiledObject->location, {}});
}
+ if (!postHocRequired.isEmpty() && hadInheritedRequiredProperties)
+ recordError({}, QLatin1String("Property %1 was marked as required but does not exist").arg(*postHocRequired.begin()));
if (_compiledObject->nFunctions > 0)
setupFunctions();
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index f8ad90be15..bca450addb 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -86,7 +86,7 @@ struct RequiredPropertyInfo
QVector<AliasToRequiredInfo> aliasesToRequired;
};
-using RequiredProperties = QHash<QQmlPropertyData*, RequiredPropertyInfo>;
+class RequiredProperties : public QHash<QQmlPropertyData*, RequiredPropertyInfo> {};
struct QQmlObjectCreatorSharedState : public QSharedData
{
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 3201fa5ace..b10eaf26f1 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -113,7 +113,6 @@ void TestQmlformat::initTestCase()
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml";
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml";
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml";
- m_invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml";
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml";
diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
index e7498a8583..0501a8112a 100644
--- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp
+++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
@@ -132,7 +132,6 @@ void tst_qmlmin::initTestCase()
invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml";
invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml";
invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml";
- invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml";
// generatorFunction.qml is not invalid per se, but the minifier cannot handle yield statements
invalidFiles << "tests/auto/qml/qqmlecmascript/data/generatorFunction.qml";
#endif
diff --git a/tests/auto/qml/qqmllanguage/data/NonRequiredBase.qml b/tests/auto/qml/qqmllanguage/data/NonRequiredBase.qml
new file mode 100644
index 0000000000..60d45c2b1e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/NonRequiredBase.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+ property int i
+}
diff --git a/tests/auto/qml/qqmllanguage/data/RequiredBase.qml b/tests/auto/qml/qqmllanguage/data/RequiredBase.qml
new file mode 100644
index 0000000000..4effdbf1c7
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/RequiredBase.qml
@@ -0,0 +1,3 @@
+NonRequiredBase {
+ required i
+}
diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml
new file mode 100644
index 0000000000..2585cf361e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.15
+
+Item {
+ property int i;
+ required i;
+}
diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.4.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.4.qml
new file mode 100644
index 0000000000..1126f845c9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.4.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+ required objectName
+}
diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.5.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.5.qml
new file mode 100644
index 0000000000..c2d155b123
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.5.qml
@@ -0,0 +1 @@
+RequiredBase {}
diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.6.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.6.qml
new file mode 100644
index 0000000000..e8802aef20
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.6.qml
@@ -0,0 +1,3 @@
+RequiredBase {
+ i: 42
+}
diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.7.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.7.qml
new file mode 100644
index 0000000000..40987f5c56
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.7.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+ required blub
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 5027c0825f..b4ad9bd7b3 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -1675,6 +1675,36 @@ void tst_qqmllanguage::requiredProperty()
QQmlComponent component(&engine, testFileUrl("requiredProperties.2.qml"));
QVERIFY(!component.errors().empty());
}
+ {
+ QQmlComponent component(&engine, testFileUrl("requiredProperties.4.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!component.errors().empty());
+ QVERIFY(component.errorString().contains("Required property objectName was not initialized"));
+ }
+ {
+ QQmlComponent component(&engine, testFileUrl("requiredProperties.3.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!component.errors().empty());
+ QVERIFY(component.errorString().contains("Required property i was not initialized"));
+ }
+ {
+ QQmlComponent component(&engine, testFileUrl("requiredProperties.5.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!component.errors().empty());
+ QVERIFY(component.errorString().contains("Required property i was not initialized"));
+ }
+ {
+ QQmlComponent component(&engine, testFileUrl("requiredProperties.6.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ }
+ {
+ QQmlComponent component(&engine, testFileUrl("requiredProperties.7.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!component.errors().empty());
+ QVERIFY(component.errorString().contains("Property blub was marked as required but does not exist"));
+ }
}
class MyClassWithRequiredProperty : public QObject