qmllint: Warn about assigning numeric types to strings

While it is generally possible to assign a numeric to a value of string
type, this is not the case for literal bindings: There, the engine's
QQmlTypeValidator notices the mismatch, and rejects the program.
Bring qmllint in line with that behavior.
Additionally, teach parseLiteralBinding to treat constant
UnaryMinusExpressions as NumberLiteral bindings.

Change-Id: Iad221e90eb1de81cabdddf5d601cc30f53ef20d3
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
Fabian Kosmale 2021-12-06 16:48:41 +01:00
parent 30251c0781
commit 7917b7ad64
5 changed files with 37 additions and 6 deletions

View File

@ -1335,7 +1335,8 @@ void QQmlJSImportVisitor::parseLiteralBinding(const QString name,
QString literalType; QString literalType;
QQmlJSMetaPropertyBinding::BindingType bindingType = QQmlJSMetaPropertyBinding::Invalid; QQmlJSMetaPropertyBinding::BindingType bindingType = QQmlJSMetaPropertyBinding::Invalid;
switch (exprStatement->expression->kind) { auto expr = exprStatement->expression;
switch (expr->kind) {
case Node::Kind_TrueLiteral: case Node::Kind_TrueLiteral:
value = true; value = true;
literalType = u"bool"_qs; literalType = u"bool"_qs;
@ -1356,21 +1357,21 @@ void QQmlJSImportVisitor::parseLiteralBinding(const QString name,
break; break;
case Node::Kind_NumericLiteral: case Node::Kind_NumericLiteral:
literalType = u"double"_qs; literalType = u"double"_qs;
value = cast<NumericLiteral *>(exprStatement->expression)->value; value = cast<NumericLiteral *>(expr)->value;
bindingType = QQmlJSMetaPropertyBinding::NumberLiteral; bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
break; break;
case Node::Kind_StringLiteral: case Node::Kind_StringLiteral:
literalType = u"string"_qs; literalType = u"string"_qs;
value = cast<StringLiteral *>(exprStatement->expression)->value.toString(); value = cast<StringLiteral *>(expr)->value.toString();
bindingType = QQmlJSMetaPropertyBinding::StringLiteral; bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
break; break;
case Node::Kind_RegExpLiteral: case Node::Kind_RegExpLiteral:
literalType = u"regexp"_qs; literalType = u"regexp"_qs;
value = cast<RegExpLiteral *>(exprStatement->expression)->pattern.toString(); value = cast<RegExpLiteral *>(expr)->pattern.toString();
bindingType = QQmlJSMetaPropertyBinding::RegExpLiteral; bindingType = QQmlJSMetaPropertyBinding::RegExpLiteral;
break; break;
case Node::Kind_TemplateLiteral: { case Node::Kind_TemplateLiteral: {
auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(exprStatement->expression); auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
Q_ASSERT(templateLit); Q_ASSERT(templateLit);
value = templateLit->value.toString(); value = templateLit->value.toString();
if (templateLit->hasNoSubstitution) { if (templateLit->hasNoSubstitution) {
@ -1382,7 +1383,14 @@ void QQmlJSImportVisitor::parseLiteralBinding(const QString name,
break; break;
} }
default: default:
return; if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
literalType = u"double"_qs;
bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
value = -lit->value;
}
}
break;
} }
if (!QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType)) if (!QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType))

View File

@ -146,6 +146,9 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
.arg(binding.literalTypeName()) .arg(binding.literalTypeName())
.arg(property.typeName()), .arg(property.typeName()),
Log_Type, binding.sourceLocation()); Log_Type, binding.sourceLocation());
} else if (property.type() == m_stringType && isNumeric(binding.literalType())) {
m_logger->logWarning(u"Cannot assign a numeric constant to a string property"_qs,
Log_Type, binding.sourceLocation());
} }
} }
} }

View File

@ -0,0 +1,5 @@
import QtQml
QtObject {
property string i: 1
}

View File

@ -0,0 +1,5 @@
import QtQml
QtObject {
property string i: -1
}

View File

@ -515,6 +515,16 @@ void TestQmllint::dirtyQmlCode_data()
<< QStringLiteral("Cannot assign binding of type QString to int") << QStringLiteral("Cannot assign binding of type QString to int")
<< QString() << QString()
<< false; << false;
QTest::newRow("bad constant number to string")
<< QStringLiteral("numberToStringProperty.qml")
<< QStringLiteral("Cannot assign a numeric constant to a string property")
<< QString()
<< false;
QTest::newRow("bad unary minus to string")
<< QStringLiteral("unaryMinusToStringProperty.qml")
<< QStringLiteral("Cannot assign a numeric constant to a string property")
<< QString()
<< false;
QTest::newRow("BadBinding") QTest::newRow("BadBinding")
<< QStringLiteral("badBinding.qml") << QStringLiteral("badBinding.qml")
<< QStringLiteral("Binding assigned to \"doesNotExist\", but no property " << QStringLiteral("Binding assigned to \"doesNotExist\", but no property "