summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2022-01-12 15:41:24 +0100
committerMaximilian Goldstein <max.goldstein@qt.io>2022-01-17 19:53:06 +0100
commit0790bc85a121275a0fd48090706b72a28b51f1e0 (patch)
tree3b0a8846771fffb815da002f7388dfc436ef70d6
parentc2598d344dfeb339ef791380737c9ad660151896 (diff)
downloadqtdeclarative-0790bc85a121275a0fd48090706b72a28b51f1e0.tar.gz
qmlcompiler: Add proper null type
Previously we treated null as a generic primitive value (in functions) or as a var type (in literal bindings), this change ends this practice by introducing an uniform null type used everywhere and defined in builtins. Now we can also properly warn about setting properties to null if they aren't variants. Fixes: QTBUG-98409 Change-Id: If32420a59948696491f24521bbc0f251095a9699 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/imports/builtins/builtins.qmltypes5
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp2
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp2
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h6
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp2
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp9
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h2
-rw-r--r--tests/auto/qml/qmllint/data/nullBinding.qml5
-rw-r--r--tests/auto/qml/qmllint/data/nullBindingFunction.qml5
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp8
10 files changed, 40 insertions, 6 deletions
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
index 0bc89d8c9c..93f6019901 100644
--- a/src/imports/builtins/builtins.qmltypes
+++ b/src/imports/builtins/builtins.qmltypes
@@ -11,6 +11,11 @@ Module {
}
Component {
+ name: "std::nullptr_t"
+ accessSemantics: "none"
+ }
+
+ Component {
name: "QVariant"
accessSemantics: "value"
exports: ["QML/var 1.0", "QML/variant 1.0"]
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index e6efb9d649..1d52a2582b 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -473,7 +473,7 @@ void QQmlJSCodeGenerator::generate_LoadNull()
INJECT_TRACE_INFO(generate_LoadNull);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = QJSPrimitiveNull()"_qs;
+ m_body += u" = nullptr"_qs;
m_body += u";\n"_qs;
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 1530a6cc89..87e6b0d7e0 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -1412,7 +1412,7 @@ bool QQmlJSImportVisitor::parseLiteralBinding(const QString name,
// QQmlJSMetaPropertyBinding::hasLiteral()
value = QVariant::fromValue(nullptr);
Q_ASSERT(value.isNull());
- literalType = u"var"_qs; // QTBUG-98409
+ literalType = u"$anonymous$.std::nullptr_t"_qs;
bindingType = QQmlJSMetaPropertyBinding::Null;
break;
case Node::Kind_NumericLiteral:
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index 903ab1992d..c68010be30 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -503,7 +503,11 @@ public:
QString valueSourceTypeName() const { return m_valueSourceTypeName; }
QSharedPointer<const QQmlJSScope> valueSourceType() const { return m_valueSource; }
- bool hasLiteral() const { return isLiteralBinding() && !m_literalValue.isNull(); }
+ bool hasLiteral() const
+ {
+ return isLiteralBinding()
+ && (!m_literalValue.isNull() || m_bindingType == QQmlJSMetaPropertyBinding::Null);
+ }
bool hasObject() const { return m_bindingType == BindingType::Object && !m_value.isNull(); }
bool hasInterceptor() const
{
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 54cbf1e02b..900d642cff 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -139,7 +139,7 @@ void QQmlJSTypePropagator::generate_LoadFalse()
void QQmlJSTypePropagator::generate_LoadNull()
{
- m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->jsPrimitiveType());
+ m_state.accumulatorOut = m_typeResolver->globalType(m_typeResolver->nullType());
}
void QQmlJSTypePropagator::generate_LoadUndefined()
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 9c5264e68d..0cc4a83963 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -67,6 +67,7 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
{
const QHash<QString, QQmlJSScope::ConstPtr> builtinTypes = importer->builtinInternalNames();
m_voidType = builtinTypes[u"void"_qs];
+ m_nullType = builtinTypes[u"std::nullptr_t"_qs];
m_realType = builtinTypes[u"double"_qs];
m_floatType = builtinTypes[u"float"_qs];
m_intType = builtinTypes[u"int"_qs];
@@ -291,7 +292,8 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
{
return type == m_intType || type == m_realType || type == m_floatType || type == m_boolType
- || type == m_voidType || type == m_stringType || type == m_jsPrimitiveType;
+ || type == m_voidType || type == m_nullType || type == m_stringType
+ || type == m_jsPrimitiveType;
}
bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
@@ -386,8 +388,11 @@ bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
if (to.isNull())
return false;
+ if (from == m_nullType && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ return true;
+
if (from == m_jsPrimitiveType) {
- // You can cast any primitive (in particular null) to a nullptr
+ // You can cast any primitive to a nullptr
return isPrimitive(to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
}
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index a59ffd6e37..893d287170 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -68,6 +68,7 @@ public:
void init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program);
QQmlJSScope::ConstPtr voidType() const { return m_voidType; }
+ QQmlJSScope::ConstPtr nullType() const { return m_nullType; }
QQmlJSScope::ConstPtr realType() const { return m_realType; }
QQmlJSScope::ConstPtr floatType() const { return m_floatType; }
QQmlJSScope::ConstPtr intType() const { return m_intType; }
@@ -153,6 +154,7 @@ protected:
QQmlJSRegisterContent lengthProperty(bool isWritable, const QQmlJSScope::ConstPtr &scope) const;
QQmlJSScope::ConstPtr m_voidType;
+ QQmlJSScope::ConstPtr m_nullType;
QQmlJSScope::ConstPtr m_numberPrototype;
QQmlJSScope::ConstPtr m_realType;
QQmlJSScope::ConstPtr m_floatType;
diff --git a/tests/auto/qml/qmllint/data/nullBinding.qml b/tests/auto/qml/qmllint/data/nullBinding.qml
new file mode 100644
index 0000000000..f185ce9eda
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/nullBinding.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ x: null
+}
diff --git a/tests/auto/qml/qmllint/data/nullBindingFunction.qml b/tests/auto/qml/qmllint/data/nullBindingFunction.qml
new file mode 100644
index 0000000000..89142bd581
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/nullBindingFunction.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ x: { return null }
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index b1fe3fc103..1359264a10 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -829,6 +829,14 @@ void TestQmllint::dirtyQmlCode_data()
<< QStringLiteral("didYouMeanComponent.qml")
<< QStringLiteral("Itym was not found. Did you add all import paths?") << QString()
<< QStringLiteral("Item") << false;
+ QTest::newRow("nullBinding")
+ << QStringLiteral("nullBinding.qml")
+ << QStringLiteral("Cannot assign binding of type $anonymous$.std::nullptr_t to double")
+ << QString() << QString() << false;
+ QTest::newRow("nullBindingFunction")
+ << QStringLiteral("nullBindingFunction.qml")
+ << QStringLiteral("Cannot assign binding of type std::nullptr_t to double") << QString()
+ << QString() << false;
}
void TestQmllint::dirtyQmlCode()