122 const QQmlSA::Element &,
const QQmlSA::Element &,
123 QQmlSA::SourceLocation location)
125 static constexpr std::array forbiddenAssignments = {
"baseline"_L1,
132 "horizontalCenter"_L1,
133 "horizontalCenterOffset"_L1,
145 "verticalCenterOffset"_L1,
150 Q_ASSERT(std::is_sorted(forbiddenAssignments.cbegin(), forbiddenAssignments.cend()));
151 if (std::find(forbiddenAssignments.cbegin(), forbiddenAssignments.cend(), propertyName)
152 != forbiddenAssignments.cend()) {
153 emitWarning(
"Imperative JavaScript assignments can break the visual tooling in Qt Design "
155 WarnImperativeCodeNotEditableInVisualDesigner, location);
160 const QString &propertyName,
161 const QQmlSA::Element &readScope,
162 QQmlSA::SourceLocation location)
166 const QQmlSA::Element globalJSObject = resolveBuiltinType(u"GlobalObject");
167 if (element != globalJSObject)
170 constexpr std::array translationFunctions = {
172 "QT_TRANSLATE_NOOP"_L1,
177 constexpr std::array idTranslationFunctions = {
182 const bool isTranslation =
183 std::find(translationFunctions.cbegin(), translationFunctions.cend(), propertyName)
184 != translationFunctions.cend();
185 const bool isIdTranslation =
186 std::find(idTranslationFunctions.cbegin(), idTranslationFunctions.cend(), propertyName)
187 != idTranslationFunctions.cend();
189 if (!isTranslation && !isIdTranslation)
192 const TranslationType current = isTranslation ? Normal : IdBased;
194 if (m_lastTranslationFunction == None) {
195 m_lastTranslationFunction = current;
199 if (m_lastTranslationFunction != current) {
200 emitWarning(
"Do not mix translation functions", qmlTranslationFunctionMismatch, location);
206 if (!rootElement.filePath().endsWith(u".ui.qml"))
209 manager->registerPropertyPass(std::make_shared<FunctionCallValidator>(manager),
210 QAnyStringView(), QAnyStringView());
211 manager->registerPropertyPass(std::make_shared<QdsBindingValidator>(manager, rootElement),
212 QAnyStringView(), QAnyStringView());
213 manager->registerPropertyPass(std::make_unique<QQmlJSTranslationFunctionMismatchCheck>(manager),
214 QString(), QString(), QString());
215 manager->registerElementPass(std::make_unique<QdsElementValidator>(manager));
219 const Element &readScope, SourceLocation location)
221 auto currentQmlScope = QQmlJSScope::findCurrentQMLScope(QQmlJSScope::scope(readScope));
224 if (currentQmlScope && currentQmlScope->inherits(QQmlJSScope::scope(m_connectionsType)))
228 const Element globalJSObject = resolveBuiltinType(u"GlobalObject");
229 const Element mathObjectType = globalJSObject.property(u"Math"_s).type();
230 if (element.inherits(mathObjectType))
233 const Element qjsValue = resolveBuiltinType(u"QJSValue");
234 if (element.inherits(qjsValue)) {
239 const std::array<QStringView, 4> dateMethodmethods{ u"now", u"parse", u"prototype",
241 if (
auto it = std::find(dateMethodmethods.cbegin(), dateMethodmethods.cend(), propertyName);
242 it != dateMethodmethods.cend())
246 static const std::vector<std::pair<Element, std::unordered_set<QString>>>
247 whiteListedFunctions = {
257 u"isNaN"_s, u"isFinite"_s,
258 u"qsTr"_s, u"qsTrId"_s, u"qsTranslate"_s,
259 u"QT_TRANSLATE_NOOP"_s, u"QT_TRID_NOOP"_s, u"QT_TR_NOOP"_s,
262 { resolveBuiltinType(u"ArrayPrototype"_s), { u"indexOf"_s, u"lastIndexOf"_s } },
263 { resolveBuiltinType(u"NumberPrototype"_s),
272 { resolveBuiltinType(u"StringPrototype"_s),
276 u"toLocaleLowerCase"_s,
278 u"toLocaleUpperCase"_s,
288 { resolveType(u"QtQml"_s, u"Qt"_s),
289 { u"lighter"_s, u"darker"_s, u"rgba"_s, u"tint"_s, u"hsla"_s, u"hsva"_s,
290 u"point"_s, u"rect"_s, u"size"_s, u"vector2d"_s, u"vector3d"_s, u"vector4d"_s,
291 u"quaternion"_s, u"matrix4x4"_s, u"formatDate"_s, u"formatDateTime"_s,
292 u"formatTime"_s, u"resolvedUrl"_s } },
295 for (
const auto &[currentElement, methods] : whiteListedFunctions) {
296 if ((!currentElement || element.inherits(currentElement)) && methods.count(propertyName)) {
302 emitWarning(u"Arbitrary functions and function calls outside of a Connections object are not "
303 u"supported in a UI file (.ui.qml)",
304 ErrFunctionsNotSupportedInQmlUi, location);
309 auto loadTypes = [&manager,
this](QSpan<
const UnsupportedName> names, QSpan<Element> output) {
310 for (qsizetype i = 0; i < qsizetype(names.size()); ++i) {
311 if (!manager->hasImportedModule(names[i].first))
313 output[i] = resolveType(names[i].first, names[i].second);
316 loadTypes(s_unsupportedElementNames, m_unsupportedElements);
317 loadTypes(s_unsupportedRootNames, m_unsupportedRootElements);
318 m_qtObject = resolveType(
"QtQml"_L1,
"QtObject"_L1);
319 m_supportFunctions = { resolveType(
"QtQml"_L1,
"Connections"_L1),
320 resolveType(
"QtQuick"_L1,
"ScriptAction"_L1) };
347 enum WarningType { ForElements, ForRootElements };
348 auto warnIfElementIsUnsupported = [
this, &element](WarningType warningType) {
349 QSpan<
const Element> unsupportedComponents = warningType == ForElements
350 ? QSpan<
const Element>(m_unsupportedElements)
351 : QSpan<
const Element>(m_unsupportedRootElements);
352 const QStringView message = warningType == ForElements
353 ? u"This type (%1) is not supported in a UI file (.ui.qml)."_sv
354 : u"This type (%1) is not supported as a root element of a UI file (.ui.qml)."_sv;
355 const LoggerWarningId &id = warningType == ForElements ? ErrUnsupportedTypeInQmlUi
356 : ErrUnsupportedRootTypeInQmlUi;
358 for (
const auto &unsupportedElement : unsupportedComponents) {
359 if (!unsupportedElement || !element.inherits(unsupportedElement))
362 emitWarning(message.arg(element.baseTypeName()), id, element.sourceLocation());
368 if (warningType == ForRootElements && element.baseType() == m_qtObject)
369 emitWarning(message.arg(element.baseTypeName()), id, element.sourceLocation());
372 if (element.isFileRootComponent())
373 warnIfElementIsUnsupported(ForRootElements);
374 warnIfElementIsUnsupported(ForElements);
376 if (QString id = resolveElementToId(element, element); !id.isEmpty()) {
377 static constexpr std::array unsupportedNames = {
378 "action"_L1,
"alias"_L1,
"anchors"_L1,
"as"_L1,
"baseState"_L1,
379 "bool"_L1,
"border"_L1,
"bottom"_L1,
"break"_L1,
"case"_L1,
380 "catch"_L1,
"clip"_L1,
"color"_L1,
"continue"_L1,
"data"_L1,
381 "date"_L1,
"debugger"_L1,
"default"_L1,
"delete"_L1,
"do"_L1,
382 "double"_L1,
"else"_L1,
"enabled"_L1,
"enumeration"_L1,
"finally"_L1,
383 "flow"_L1,
"focus"_L1,
"font"_L1,
"for"_L1,
"function"_L1,
384 "height"_L1,
"id"_L1,
"if"_L1,
"import"_L1,
"in"_L1,
385 "instanceof"_L1,
"int"_L1,
"item"_L1,
"layer"_L1,
"left"_L1,
386 "list"_L1,
"margin"_L1,
"matrix4x4"_L1,
"new"_L1,
"opacity"_L1,
387 "padding"_L1,
"parent"_L1,
"point"_L1,
"print"_L1,
"quaternion"_L1,
388 "real"_L1,
"rect"_L1,
"return"_L1,
"right"_L1,
"scale"_L1,
389 "shaderInfo"_L1,
"size"_L1,
"source"_L1,
"sprite"_L1,
"spriteSequence"_L1,
390 "state"_L1,
"string"_L1,
"switch"_L1,
"text"_L1,
"texture"_L1,
391 "this"_L1,
"throw"_L1,
"time"_L1,
"top"_L1,
"try"_L1,
392 "typeof"_L1,
"url"_L1,
"var"_L1,
"variant"_L1,
"vector"_L1,
393 "vector2d"_L1,
"vector3d"_L1,
"vector4d"_L1,
"visible"_L1,
"void"_L1,
394 "while"_L1,
"width"_L1,
"with"_L1,
"x"_L1,
"y"_L1,
398 Q_ASSERT(std::is_sorted(unsupportedNames.begin(), unsupportedNames.end()));
399 if (std::binary_search(unsupportedNames.cbegin(), unsupportedNames.cend(), id)) {
401 u"This id (%1) might be ambiguous and is not supported in a UI file (.ui.qml)."_s
403 ErrInvalidIdeInVisualDesigner, element.idSourceLocation());
407 if (std::none_of(m_supportFunctions.cbegin(), m_supportFunctions.cend(),
408 [&element](
const Element &base) {
return base && element.inherits(base); })) {
409 complainAboutFunctions(element);
void onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, QQmlSA::SourceLocation location) override
Executes whenever a property is written to.