aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2021-11-15 16:16:16 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2021-11-17 20:59:23 +0100
commitc9380aa42805cf55736dc87c87149d912282c0ae (patch)
treea1b824fcdf3768ed4e6d4477ef180649e239bd0d
parentf9bddfa12d233d9ef01299869f4d491d62a0828e (diff)
Clean up property attributes (2/2)
This commit introduces a new AST node for property attributes (currently required, default and readonly). That node is not integrated into our visitors, as it is only meant as an internal implementaion detail of UiPublicMember. All information stored in it is meant to be accessed by UiPublicMember's methods (see also the note below). We use an union of SourceLocation and a node pointer to only pay the overhead of the new node for properties; signals simply store the signal token. The grammar is rewritten with a new UiPropertyAttributes rule, which avoids quite a bit of duplication in various rules, which had to deal with the combinatorial explosion of attributes. Some parse errors are now turned into semantic errors instead (readonly without initializer, required with initializer). By centralizing the handling of attributes in the grammar, we now can easily support e.g. default properties with a list initializer. As part of this restructuring, UiPublicMember's firstSourceLocation is fixed to ensure that we actually return the first source location, independent of the order in which the attributes are written. Note: In theory, we would not need to make UiPropertyAttributes an AST node. It could be a simple data class. However, the parser currently assumes that every pointer in its parser stack is an AST node, and touching that part would be a larger undertaking. As we use a pool allocator, the cost is not that high (though we use more memory from the pool then we would strictly need to). Change-Id: Ia1d9fd7a6553d443cc57bc3d773f5be0aebe0e0e Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/parser/qqmljs.g200
-rw-r--r--src/qml/parser/qqmljsast.cpp17
-rw-r--r--src/qml/parser/qqmljsast_p.h103
-rw-r--r--src/qmldom/qqmldomastcreator.cpp4
-rw-r--r--src/qmldom/qqmldomastdumper.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializer.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializerUser.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializer.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializerUser.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/property.4.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp28
11 files changed, 232 insertions, 149 deletions
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index c3ceeb24b3..9bff45faf3 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -315,6 +315,7 @@ public:
AST::UiPragma *UiPragma;
AST::UiImport *UiImport;
AST::UiParameterList *UiParameterList;
+ AST::UiPropertyAttributes *UiPropertyAttributes;
AST::UiPublicMember *UiPublicMember;
AST::UiObjectDefinition *UiObjectDefinition;
AST::UiObjectInitializer *UiObjectInitializer;
@@ -1273,7 +1274,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN Semic
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
+ node->setPropertyToken(loc(1));
node->typeToken = loc(2);
node->identifierToken = loc(2);
node->parameters = sym(4).UiParameterList;
@@ -1287,7 +1288,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER Semicolon;
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
+ node->setPropertyToken(loc(1));
node->typeToken = loc(2);
node->identifierToken = loc(2);
node->semicolonToken = loc(3);
@@ -1295,114 +1296,97 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER Semicolon;
} break;
./
-UiObjectMemberListPropertyNoInitialiser: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
- } break;
-./
-
-UiObjectMember: UiObjectMemberListPropertyNoInitialiser;
+-------------------------------------------------------------------------------
+-- There is some ambiguity in whether required default property should be parsed
+-- as required (default (property)) or as ((required (default)) property)
+-- by reducing after each attribute modifier, we ensure that T_PROPERTY (which
+-- is always available is used as the base case (so we only have to allocate the
+-- node in the T_PROPERY case, and all other rules can assume that the node is
+-- already available).
+--------------------------------------------------------------------------------
-UiObjectMember: T_READONLY UiObjectMemberListPropertyNoInitialiser;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_readonlyToken = loc(1);
- sym(1).Node = node;
- } break;
-./
+AttrRequired: T_REQUIRED %prec REDUCE_HERE;
+AttrReadonly: T_READONLY %prec REDUCE_HERE;
+AttrDefault: T_DEFAULT %prec REDUCE_HERE;
-UiObjectMemberPropertyNoInitialiser: T_PROPERTY UiPropertyType QmlIdentifier Semicolon;
+UiPropertyAttributes: AttrRequired UiPropertyAttributes;
/.
case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4);
- sym(1).Node = node;
+ AST::UiPropertyAttributes *node = sym(2).UiPropertyAttributes;
+ if (node->isRequired())
+ diagnostic_messages.append(compileError(node->requiredToken(), QLatin1String("Duplicated 'required' attribute is not allowed."), QtCriticalMsg));
+ node->m_requiredToken = loc(1);
+ sym(1).UiPropertyAttributes = node;
} break;
./
-
-UiObjectMember: UiObjectMemberPropertyNoInitialiser;
-
-UiObjectMember: T_DEFAULT UiObjectMemberPropertyNoInitialiser;
+UiPropertyAttributes: AttrDefault UiPropertyAttributes;
/.
case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
+ AST::UiPropertyAttributes *node = sym(2).UiPropertyAttributes;
+ if (node->isDefaultMember())
+ diagnostic_messages.append(compileError(node->requiredToken(), QLatin1String("Duplicated 'default' attribute is not allowed."), QtCriticalMsg));
node->m_defaultToken = loc(1);
- sym(1).Node = node;
+ sym(1).UiPropertyAttributes = node;
} break;
./
-
-UiObjectMember: T_REQUIRED UiObjectMemberListPropertyNoInitialiser;
+UiPropertyAttributes: AttrReadonly UiPropertyAttributes;
/.
case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_requiredToken = loc(1);
- sym(1).Node = node;
+ AST::UiPropertyAttributes *node = sym(2).UiPropertyAttributes;
+ if (node->isReadonly())
+ diagnostic_messages.append(compileError(node->requiredToken(), QLatin1String("Duplicated 'readonly' attribute is not allowed."), QtCriticalMsg));
+ node->m_readonlyToken = loc(1);
+ sym(1).UiPropertyAttributes = node;
} break;
./
-UiObjectMember: T_DEFAULT T_REQUIRED UiObjectMemberListPropertyNoInitialiser;
+UiPropertyAttributes: T_PROPERTY;
/.
case $rule_number: {
- AST::UiPublicMember *node = sym(3).UiPublicMember;
- node->m_requiredToken = loc(2);
- node->m_defaultToken = loc(1);
- sym(1).Node = node;
+ AST::UiPropertyAttributes *node = new (pool) AST::UiPropertyAttributes();
+ node->m_propertyToken = loc(1);
+ sym(1).UiPropertyAttributes = node;
} break;
./
-UiObjectMember: T_REQUIRED T_DEFAULT UiObjectMemberListPropertyNoInitialiser;
+UiObjectMemberListPropertyNoInitialiser: UiPropertyAttributes T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon;
/.
case $rule_number: {
- AST::UiPublicMember *node = sym(3).UiPublicMember;
- node->m_requiredToken = loc(1);
- node->m_defaultToken = loc(2);
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
+ auto attributes = sym(1).UiPropertyAttributes;
+ node->setAttributes(attributes);
+ if (attributes->isReadonly())
+ diagnostic_messages.append(compileError(attributes->readonlyToken(), QLatin1String("Read-only properties require an initializer."), QtWarningMsg));
+ node->typeModifier = stringRef(2);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7);
sym(1).Node = node;
} break;
./
-UiObjectMember: T_DEFAULT UiObjectMemberListPropertyNoInitialiser;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_defaultToken = loc(1);
- sym(1).Node = node;
- } break;
-./
+UiObjectMember: UiObjectMemberListPropertyNoInitialiser;
-UiObjectMember: T_DEFAULT T_REQUIRED UiObjectMemberPropertyNoInitialiser;
+UiObjectMemberPropertyNoInitialiser: UiPropertyAttributes UiPropertyType QmlIdentifier Semicolon;
/.
case $rule_number: {
- AST::UiPublicMember *node = sym(3).UiPublicMember;
- node->m_defaultToken = loc(1);
- node->m_requiredToken = loc(2);
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
+ auto attributes = sym(1).UiPropertyAttributes;
+ if (attributes->isReadonly())
+ diagnostic_messages.append(compileError(attributes->readonlyToken(), QLatin1String("Read-only properties require an initializer."), QtCriticalMsg));
+ node->setAttributes(attributes);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4);
sym(1).Node = node;
} break;
./
-UiObjectMember: T_REQUIRED T_DEFAULT UiObjectMemberPropertyNoInitialiser;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(3).UiPublicMember;
- node->m_defaultToken = loc(2);
- node->m_requiredToken = loc(1);
- sym(1).Node = node;
- } break;
-./
+UiObjectMember: UiObjectMemberPropertyNoInitialiser;
OptionalSemicolon: | Semicolon;
/.
@@ -1422,20 +1406,14 @@ UiRequired: T_REQUIRED QmlIdentifier Semicolon;
UiObjectMember: UiRequired;
-UiObjectMember: T_REQUIRED UiObjectMemberPropertyNoInitialiser;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_requiredToken = loc(1);
- sym(1).Node = node;
- } break;
-./
-
-UiObjectMemberWithScriptStatement: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon;
+UiObjectMemberWithScriptStatement: UiPropertyAttributes UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), sym(5).Statement);
- node->propertyToken = loc(1);
+ auto attributes = sym(1).UiPropertyAttributes;
+ if (attributes->isRequired())
+ diagnostic_messages.append(compileError(attributes->requiredToken(), QLatin1String("Required properties with initializer do not make sense."), QtCriticalMsg));
+ node->setAttributes(attributes);
node->typeToken = loc(2);
node->identifierToken = loc(3);
node->colonToken = loc(4);
@@ -1445,30 +1423,15 @@ UiObjectMemberWithScriptStatement: T_PROPERTY UiPropertyType QmlIdentifier T_COL
UiObjectMember: UiObjectMemberWithScriptStatement;
-UiObjectMember: T_READONLY UiObjectMemberWithScriptStatement;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_readonlyToken = loc(1);
- sym(1).Node = node;
- } break;
-./
-
-UiObjectMember: T_DEFAULT UiObjectMemberWithScriptStatement;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_defaultToken = loc(1);
- sym(1).Node = node;
- } break;
-./
-
-UiObjectMemberWithArray: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon;
+UiObjectMemberWithArray: UiPropertyAttributes T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
+ auto attributes = sym(1).UiPropertyAttributes;
+ if (attributes->isRequired())
+ diagnostic_messages.append(compileError(attributes->requiredToken(), QLatin1String("Required properties with initializer do not make sense."), QtCriticalMsg));
+ node->setAttributes(attributes);
node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
node->typeModifierToken = loc(2);
node->typeToken = loc(4);
node->identifierToken = loc(6);
@@ -1476,7 +1439,7 @@ UiObjectMemberWithArray: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIde
AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
propertyName->identifierToken = loc(6);
- propertyName->next = 0;
+ propertyName->next = nullptr;
AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(propertyName, sym(9).UiArrayMemberList->finish());
binding->colonToken = loc(7);
@@ -1491,27 +1454,21 @@ UiObjectMemberWithArray: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIde
UiObjectMember: UiObjectMemberWithArray;
-UiObjectMember: T_READONLY UiObjectMemberWithArray;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_readonlyToken = loc(1);
- sym(1).Node = node;
- } break;
-./
-
-UiObjectMemberExpressionStatementLookahead: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon;
+UiObjectMemberExpressionStatementLookahead: UiPropertyAttributes UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
+ auto attributes = sym(1).UiPropertyAttributes;
+ if (attributes->isRequired())
+ diagnostic_messages.append(compileError(attributes->requiredToken(), QLatin1String("Required properties with initializer do not make sense."), QtWarningMsg));
+ node->setAttributes(attributes);
node->typeToken = loc(2);
node->identifierToken = loc(3);
node->semicolonToken = loc(4); // insert a fake ';' before ':'
AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
propertyName->identifierToken = loc(3);
- propertyName->next = 0;
+ propertyName->next = nullptr;
AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer);
@@ -1525,15 +1482,6 @@ UiObjectMemberExpressionStatementLookahead: T_PROPERTY UiPropertyType QmlIdentif
UiObjectMember: UiObjectMemberExpressionStatementLookahead;
-UiObjectMember: T_READONLY UiObjectMemberExpressionStatementLookahead;
-/.
- case $rule_number: {
- AST::UiPublicMember *node = sym(2).UiPublicMember;
- node->m_readonlyToken = loc(1);
- sym(1).Node = node;
- } break;
-./
-
UiObjectMember: GeneratorDeclaration;
/.
case $rule_number: {
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index a068a412df..279ed62956 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -37,12 +37,15 @@
**
****************************************************************************/
+#include <QString>
#include <QLocale>
#include "qqmljsast_p.h"
#include "qqmljsastvisitor_p.h"
#include <qlocale.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
@@ -1648,6 +1651,20 @@ void UiAnnotation::accept0(BaseVisitor *visitor)
visitor->endVisit(this);
}
+SourceLocation UiPropertyAttributes::firstSourceLocation() const
+{
+ std::array<const SourceLocation *, 4> tokens {&m_propertyToken, &m_defaultToken, &m_readonlyToken, &m_requiredToken};
+ const auto it = std::min_element(tokens.begin(), tokens.end(), compareLocationsByBegin<true>);
+ return **it;
+}
+
+SourceLocation UiPropertyAttributes::lastSourceLocation() const
+{
+ std::array<const SourceLocation *, 4> tokens {&m_propertyToken, &m_defaultToken, &m_readonlyToken, &m_requiredToken};
+ const auto it = std::max_element(tokens.begin(), tokens.end(), compareLocationsByBegin<false>);
+ return **it;
+}
+
} } // namespace QQmlJS::AST
QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 6e51a906bb..f9e62c79fd 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -56,11 +56,12 @@
#include <private/qqmljsmemorypool_p.h>
-#include <QtCore/qstring.h>
#include <QtCore/qversionnumber.h>
QT_BEGIN_NAMESPACE
+class QString;
+
namespace QQmlJS {
class Parser;
}
@@ -257,6 +258,7 @@ public:
Kind_UiPragma,
Kind_UiProgram,
Kind_UiParameterList,
+ Kind_UiPropertyAttributes,
Kind_UiPublicMember,
Kind_UiQualifiedId,
Kind_UiScriptBinding,
@@ -3369,6 +3371,46 @@ public:
SourceLocation colonToken;
};
+class QML_PARSER_EXPORT UiPropertyAttributes : public Node
+{
+ QQMLJS_DECLARE_AST_NODE(UiPropertyAttributes)
+public:
+ UiPropertyAttributes() { kind = K; }
+
+ SourceLocation defaultToken() const { return m_defaultToken; }
+ bool isDefaultMember() const { return defaultToken().isValid(); }
+ SourceLocation requiredToken() const { return m_requiredToken; }
+ bool isRequired() const { return requiredToken().isValid(); }
+ SourceLocation readonlyToken() const { return m_readonlyToken; }
+ bool isReadonly() const { return readonlyToken().isValid(); }
+
+ SourceLocation propertyToken() const { return m_propertyToken; }
+
+ template <bool InvalidIsLargest = true>
+ static bool compareLocationsByBegin(const SourceLocation *& lhs, const SourceLocation *& rhs)
+ {
+ if (lhs->isValid() && rhs->isValid())
+ return lhs->begin() < rhs->begin();
+ else if (lhs->isValid())
+ return InvalidIsLargest;
+ else
+ return !InvalidIsLargest;
+ }
+
+ void accept0(BaseVisitor *) override {} // intentionally do nothing
+
+ SourceLocation firstSourceLocation() const override;
+
+ SourceLocation lastSourceLocation() const override;
+
+private:
+ friend class QQmlJS::Parser;
+ SourceLocation m_defaultToken;
+ SourceLocation m_readonlyToken;
+ SourceLocation m_requiredToken;
+ SourceLocation m_propertyToken;
+};
+
class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember
{
public:
@@ -3389,15 +3431,10 @@ public:
SourceLocation firstSourceLocation() const override
{
- // ### FIXME: return the first(!) modifier token
- if (defaultToken().isValid())
- return defaultToken();
- else if (readonlyToken().isValid())
- return readonlyToken();
- else if (requiredToken().isValid())
- return requiredToken();
-
- return propertyToken;
+ if (hasAttributes)
+ return m_attributes->firstSourceLocation();
+ else
+ return m_propertyToken;
}
SourceLocation lastSourceLocation() const override
@@ -3410,15 +3447,44 @@ public:
return semicolonToken;
}
- SourceLocation defaultToken() const { return m_defaultToken; }
+ SourceLocation defaultToken() const
+ {
+ return hasAttributes ? m_attributes->defaultToken() : SourceLocation {};
+ }
bool isDefaultMember() const { return defaultToken().isValid(); }
- SourceLocation requiredToken() const { return m_requiredToken; }
+
+ SourceLocation requiredToken() const
+ {
+ return hasAttributes ? m_attributes->requiredToken() : SourceLocation {};
+ }
bool isRequired() const { return requiredToken().isValid(); }
- SourceLocation readonlyToken() const { return m_readonlyToken; }
+
+ SourceLocation readonlyToken() const
+ {
+ return hasAttributes ? m_attributes->readonlyToken() : SourceLocation {};
+ }
bool isReadonly() const { return readonlyToken().isValid(); }
+ void setAttributes(UiPropertyAttributes *attributes)
+ {
+ m_attributes = attributes;
+ hasAttributes = true;
+ }
+
+ SourceLocation propertyToken() const
+ {
+ return hasAttributes ? m_attributes->propertyToken() : m_propertyToken;
+ }
+
+ void setPropertyToken(SourceLocation token)
+ {
+ m_propertyToken = token;
+ hasAttributes = false;
+ }
+
// attributes
- enum { Signal, Property } type;
+ enum : bool { Signal, Property } type;
+ bool hasAttributes = false;
QStringView typeModifier;
UiQualifiedId *memberType;
QStringView name;
@@ -3426,17 +3492,16 @@ public:
UiObjectMember *binding; // initialized with a QML object or array.
UiParameterList *parameters;
// TODO: merge source locations
- SourceLocation propertyToken;
SourceLocation typeModifierToken;
SourceLocation typeToken;
SourceLocation identifierToken;
SourceLocation colonToken;
SourceLocation semicolonToken;
private:
- friend class QQmlJS::Parser;
- SourceLocation m_defaultToken;
- SourceLocation m_readonlyToken;
- SourceLocation m_requiredToken;
+ union {
+ SourceLocation m_propertyToken = SourceLocation {};
+ UiPropertyAttributes *m_attributes;
+ };
};
class QML_PARSER_EXPORT UiObjectDefinition: public UiObjectMember
diff --git a/src/qmldom/qqmldomastcreator.cpp b/src/qmldom/qqmldomastcreator.cpp
index 6d89075739..3a6d104ae7 100644
--- a/src/qmldom/qqmldomastcreator.cpp
+++ b/src/qmldom/qqmldomastcreator.cpp
@@ -372,7 +372,7 @@ public:
MethodInfo *mPtr;
Path p = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
pushEl(p, *mPtr, el);
- FileLocations::addRegion(nodeStack.last().fileLocations, u"signal", el->propertyToken);
+ FileLocations::addRegion(nodeStack.last().fileLocations, u"signal", el->propertyToken());
MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
AST::UiParameterList *args = el->parameters;
while (args) {
@@ -404,7 +404,7 @@ public:
current<QmlObject>().addPropertyDef(p, AddOption::KeepExisting, &pPtr);
pushEl(pPathFromOwner, *pPtr, el);
FileLocations::addRegion(nodeStack.last().fileLocations, u"property",
- el->propertyToken);
+ el->propertyToken());
if (p.name == u"id")
qmlFile.addError(
myParseErrors()
diff --git a/src/qmldom/qqmldomastdumper.cpp b/src/qmldom/qqmldomastdumper.cpp
index 42f58a04ed..3c66e1c180 100644
--- a/src/qmldom/qqmldomastdumper.cpp
+++ b/src/qmldom/qqmldomastdumper.cpp
@@ -198,7 +198,7 @@ public:
.arg(quotedString(typeStr), quotedString(el->typeModifier),
quotedString(el->name), boolStr(el->isDefaultMember()),
boolStr(el->isReadonly()), boolStr(el->isRequired()),
- loc(el->defaultToken()), loc(el->readonlyToken()), loc(el->propertyToken),
+ loc(el->defaultToken()), loc(el->readonlyToken()), loc(el->propertyToken()),
loc(el->requiredToken()), loc(el->typeModifierToken), loc(el->typeToken),
loc(el->identifierToken), loc(el->colonToken),
semicolonToken(el->semicolonToken)));
diff --git a/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializer.qml b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializer.qml
new file mode 100644
index 0000000000..4fd1f45010
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializer.qml
@@ -0,0 +1,7 @@
+import QtQml
+
+QtObject {
+ id: root
+ default property QtObject entry
+ : QtObject { objectName: "default" }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializerUser.qml b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializerUser.qml
new file mode 100644
index 0000000000..7e212c4324
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithInitializerUser.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+DefaultPropertyWithInitializer {
+ QtObject { objectName: "changed" }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializer.qml b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializer.qml
new file mode 100644
index 0000000000..1ba3c7450b
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializer.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ id: root
+ default property list<QtObject> data: [ QtObject {objectName: "1" } ]
+ property QtObject entry: data[data.length - 1]
+
+}
diff --git a/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializerUser.qml b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializerUser.qml
new file mode 100644
index 0000000000..43d23f90a4
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/DefaultPropertyWithListInitializerUser.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+DefaultPropertyWithListInitializer {
+ QtObject {objectName: "2"}
+}
diff --git a/tests/auto/qml/qqmllanguage/data/property.4.errors.txt b/tests/auto/qml/qqmllanguage/data/property.4.errors.txt
index 2807384ec4..4be47f19c6 100644
--- a/tests/auto/qml/qqmllanguage/data/property.4.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/property.4.errors.txt
@@ -1 +1 @@
-5:1:Expected token `:'
+4:5:Read-only properties require an initializer.
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index db18a02185..14c407edfc 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -163,6 +163,8 @@ private slots:
void scriptStringWithoutSourceCode();
void scriptStringComparison();
void defaultPropertyListOrder();
+ void defaultPropertyWithInitializer_data();
+ void defaultPropertyWithInitializer();
void declaredPropertyValues();
void dontDoubleCallClassBegin();
void reservedWords_data();
@@ -2669,6 +2671,32 @@ void tst_qqmllanguage::defaultPropertyListOrder()
QCOMPARE(container->getChildren()->at(5)->property("index"), QVariant(5));
}
+void tst_qqmllanguage::defaultPropertyWithInitializer_data()
+{
+ QTest::addColumn<QUrl>("file");
+ QTest::addColumn<QString>("objectName");
+
+ QTest::newRow("base") << testFileUrl("DefaultPropertyWithInitializer.qml") << u"default"_qs;
+ QTest::newRow("user") << testFileUrl("DefaultPropertyWithInitializerUser.qml") << u"changed"_qs;
+ QTest::newRow("list base") << testFileUrl("DefaultPropertyWithListInitializer.qml") << u"1"_qs;
+ QTest::newRow("list user") << testFileUrl("DefaultPropertyWithListInitializerUser.qml") << u"2"_qs;
+}
+
+void tst_qqmllanguage::defaultPropertyWithInitializer()
+{
+ QFETCH(QUrl, file);
+ QFETCH(QString, objectName);
+
+ QQmlComponent component(&engine, file);
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ auto entry = root->property("entry").value<QObject *>();
+ QVERIFY(entry);
+ QCOMPARE(entry->objectName(), objectName);
+}
+
void tst_qqmllanguage::declaredPropertyValues()
{
QQmlComponent component(&engine, testFileUrl("declaredPropertyValues.qml"));