1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmljsliteralbindingcheck_p.h"
5
6#include <private/qqmljsimportvisitor_p.h>
7#include <private/qqmljstyperesolver_p.h>
8#include <private/qqmljsmetatypes_p.h>
9#include <private/qqmlsa_p.h>
10#include <private/qqmlsasourcelocation_p.h>
11#include <private/qqmlstringconverters_p.h>
12
13QT_BEGIN_NAMESPACE
14
15using namespace Qt::StringLiterals;
16
17// This makes no sense, but we want to warn about things QQmlPropertyResolver complains about.
18static bool canConvertForLiteralBinding(QQmlJSTypeResolver *resolver,
19 const QQmlSA::Element &fromElement,
20 const QQmlSA::Element &toElement)
21{
22 Q_ASSERT(resolver);
23 auto from = QQmlJSScope::scope(fromElement);
24 auto to = QQmlJSScope::scope(toElement);
25 if (resolver->equals(a: from, b: to))
26 return true;
27
28 if (!resolver->canConvertFromTo(from, to))
29 return false;
30
31 const bool fromIsString = resolver->equals(a: from, b: resolver->stringType());
32
33 if (resolver->equals(a: to, b: resolver->stringType())
34 || resolver->equals(a: to, b: resolver->stringListType())
35 || resolver->equals(a: to, b: resolver->byteArrayType())
36 || resolver->equals(a: to, b: resolver->urlType())) {
37 return fromIsString;
38 }
39
40 if (resolver->isNumeric(type: to))
41 return resolver->isNumeric(type: from);
42
43 if (resolver->equals(a: to, b: resolver->boolType()))
44 return resolver->equals(a: from, b: resolver->boolType());
45
46 return true;
47}
48
49QQmlJSLiteralBindingCheck::QQmlJSLiteralBindingCheck(QQmlSA::PassManager *passManager)
50 : LiteralBindingCheckBase(passManager),
51 m_resolver(QQmlSA::PassManagerPrivate::resolver(*passManager))
52{
53}
54
55QQmlJSStructuredTypeError QQmlJSLiteralBindingCheck::check(const QString &typeName,
56 const QString &value) const
57{
58 return QQmlJSValueTypeFromStringCheck::hasError(typeName, value);
59}
60
61static QString literalPrettyTypeName(QQmlSA::BindingType type)
62{
63 switch (type) {
64 case QQmlSA::BindingType::BoolLiteral:
65 return u"bool"_s;
66 case QQmlSA::BindingType::NumberLiteral:
67 return u"double"_s;
68 case QQmlSA::BindingType::StringLiteral:
69 return u"string"_s;
70 case QQmlSA::BindingType::RegExpLiteral:
71 return u"regexp"_s;
72 case QQmlSA::BindingType::Null:
73 return u"null"_s;
74 default:
75 return QString();
76 }
77 Q_UNREACHABLE_RETURN(QString());
78}
79
80QQmlSA::Property LiteralBindingCheckBase::getProperty(const QString &propertyName,
81 const QQmlSA::Binding &binding,
82 const QQmlSA::Element &bindingScope) const
83{
84 if (!QQmlSA::Binding::isLiteralBinding(binding.bindingType()))
85 return {};
86
87 const QString unqualifiedPropertyName = [&propertyName]() -> QString {
88 if (auto idx = propertyName.lastIndexOf(c: u'.'); idx != -1 && idx != propertyName.size() - 1)
89 return propertyName.sliced(pos: idx + 1);
90 return propertyName;
91 }();
92
93 return bindingScope.property(propertyName: unqualifiedPropertyName);
94}
95
96
97void LiteralBindingCheckBase::onBinding(const QQmlSA::Element &element, const QString &propertyName,
98 const QQmlSA::Binding &binding,
99 const QQmlSA::Element &bindingScope,
100 const QQmlSA::Element &value)
101{
102 Q_UNUSED(value);
103
104 const auto property = getProperty(propertyName, binding, bindingScope);
105 if (!property.isValid())
106 return;
107
108 // If the property is defined in the same scope where it is set,
109 // we are in fact allowed to set it, even if it's not writable.
110 if (property.isReadonly() && !element.hasOwnProperty(propertyName)) {
111 emitWarning(diagnostic: u"Cannot assign to read-only property %1"_s.arg(a: propertyName),
112 id: qmlReadOnlyProperty, srcLocation: binding.sourceLocation());
113 return;
114 }
115 if (auto propertyType = property.type(); propertyType) {
116 auto construction = check(typeName: propertyType.internalId(), value: binding.stringValue());
117 if (construction.isValid()) {
118 const QString warningMessage =
119 u"Construction from string is deprecated. Use structured value type "
120 u"construction instead for type \"%1\""_s.arg(a: propertyType.internalId());
121
122 if (!construction.code.isNull()) {
123 QQmlSA::FixSuggestion suggestion(
124 u"Replace string by structured value construction"_s,
125 binding.sourceLocation(), construction.code);
126 emitWarning(diagnostic: warningMessage, id: qmlIncompatibleType, srcLocation: binding.sourceLocation(), fix: suggestion);
127 return;
128 }
129 emitWarning(diagnostic: warningMessage, id: qmlIncompatibleType, srcLocation: binding.sourceLocation());
130 return;
131 }
132 }
133
134}
135
136void QQmlJSLiteralBindingCheck::onBinding(const QQmlSA::Element &element,
137 const QString &propertyName,
138 const QQmlSA::Binding &binding,
139 const QQmlSA::Element &bindingScope,
140 const QQmlSA::Element &value)
141{
142 LiteralBindingCheckBase::onBinding(element, propertyName, binding, bindingScope, value);
143
144 const auto property = getProperty(propertyName, binding, bindingScope);
145 if (!property.isValid())
146 return;
147
148 if (!canConvertForLiteralBinding(resolver: m_resolver, fromElement: resolveLiteralType(binding), toElement: property.type())) {
149 emitWarning(diagnostic: u"Cannot assign literal of type %1 to %2"_s.arg(
150 args: literalPrettyTypeName(type: binding.bindingType()),
151 args: QQmlJSScope::prettyName(name: property.typeName())),
152 id: qmlIncompatibleType, srcLocation: binding.sourceLocation());
153 return;
154 }
155}
156
157QT_END_NAMESPACE
158

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/qmlcompiler/qqmljsliteralbindingcheck.cpp