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