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