Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
quicklintplugin.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
4#include "quicklintplugin.h"
5
7
8using namespace Qt::StringLiterals;
9
10static constexpr QQmlSA::LoggerWarningId quickLayoutPositioning { "Quick.layout-positioning" };
11static constexpr QQmlSA::LoggerWarningId quickAttachedPropertyType { "Quick.attached-property-type" };
12static constexpr QQmlSA::LoggerWarningId quickControlsNativeCustomize { "Quick.controls-native-customize" };
13static constexpr QQmlSA::LoggerWarningId quickAnchorCombinations { "Quick.anchor-combinations" };
14static constexpr QQmlSA::LoggerWarningId quickUnexpectedVarType { "Quick.unexpected-var-type" };
15static constexpr QQmlSA::LoggerWarningId quickPropertyChangesParsed { "Quick.property-changes-parsed" };
16static constexpr QQmlSA::LoggerWarningId quickControlsAttachedPropertyReuse { "Quick.controls-attached-property-reuse" };
17static constexpr QQmlSA::LoggerWarningId quickAttachedPropertyReuse { "Quick.attached-property-reuse" };
18
24
27 QAnyStringView propertyName,
29{
30 auto element = resolveType(moduleName, typeName);
31 if (!element.isNull())
32 m_types[element].append({ propertyName.toString(), warning.toString() });
33}
34
36{
37 if (!element.parentScope())
38 return false;
39
40 for (const auto &pair : std::as_const(m_types).asKeyValueRange()) {
41 if (element.parentScope().inherits(pair.first))
42 return true;
43 }
44
45 return false;
46}
47
49{
50 for (const auto &elementPair : std::as_const(m_types).asKeyValueRange()) {
51 const QQmlSA::Element &type = elementPair.first;
52 if (!element.parentScope().inherits(type))
53 continue;
54
55 for (const auto &warning : elementPair.second) {
56 if (!element.hasOwnPropertyBindings(warning.propertyName))
57 continue;
58
59 const auto bindings = element.ownPropertyBindings(warning.propertyName);
60 const auto firstBinding = bindings.constBegin().value();
61 emitWarning(warning.message, quickLayoutPositioning, firstBinding.sourceLocation());
62 }
63 break;
64 }
65}
66
71
73 QList<TypeDescription> allowedTypes,
74 bool allowInDelegate, QAnyStringView warning)
75{
76 QVarLengthArray<QQmlSA::Element, 4> elements;
77
78 const QQmlSA::Element baseType = resolveType(attachType.module, attachType.name);
79 const QQmlSA::Element attachedType = resolveAttached(attachType.module, attachType.name);
80
81 for (const TypeDescription &desc : allowedTypes) {
82 const QQmlSA::Element type = resolveType(desc.module, desc.name);
83 if (type.isNull())
84 continue;
85 elements.push_back(type);
86 }
87
88 m_attachedTypes.insert(
89 { std::make_pair<>(attachedType.internalId(),
90 Warning{ elements, allowInDelegate, warning.toString() }) });
91
92 return attachedType.internalId();
93}
94
95void AttachedPropertyTypeValidatorPass::checkWarnings(const QQmlSA::Element &element,
96 const QQmlSA::Element &scopeUsedIn,
98{
99 auto warning = m_attachedTypes.constFind(element.internalId());
100 if (warning == m_attachedTypes.cend())
101 return;
102 for (const QQmlSA::Element &type : warning->allowedTypes) {
103 if (scopeUsedIn.inherits(type))
104 return;
105 }
106
107 if (warning->allowInDelegate) {
108 if (scopeUsedIn.isPropertyRequired(u"index"_s)
109 || scopeUsedIn.isPropertyRequired(u"model"_s))
110 return;
111
112 // If the scope is at the root level, we cannot know whether it will be used
113 // as a delegate or not.
114 // ### TODO: add a method to check whether a scope is the global scope
115 // so that we do not need to use internalId
116 if (!scopeUsedIn.parentScope() || scopeUsedIn.parentScope().internalId() == u"global"_s)
117 return;
118
119 for (const QQmlSA::Binding &binding :
120 scopeUsedIn.parentScope().propertyBindings(u"delegate"_s)) {
121 if (!binding.hasObject())
122 continue;
123 if (binding.objectType() == scopeUsedIn)
124 return;
125 }
126 }
127
129}
130
132 const QString &propertyName,
133 const QQmlSA::Binding &binding,
134 const QQmlSA::Element &bindingScope,
135 const QQmlSA::Element &value)
136{
138
139 // We can only analyze simple attached bindings since we don't see
140 // the grouped and attached properties that lead up to this here.
141 //
142 // TODO: This is very crude.
143 // We should add API for grouped and attached properties.
144 if (propertyName.count(QLatin1Char('.')) > 1)
145 return;
146
147 checkWarnings(bindingScope.baseType(), element, binding.sourceLocation());
148}
149
151 const QString &propertyName,
152 const QQmlSA::Element &readScope,
154{
155 // If the attachment does not have such a property or method then
156 // it's either a more general error or an enum. Enums are fine.
157 if (element.hasProperty(propertyName) || element.hasMethod(propertyName))
158 checkWarnings(element, readScope, location);
159}
160
162 const QString &propertyName,
163 const QQmlSA::Element &value,
164 const QQmlSA::Element &writeScope,
166{
167 Q_UNUSED(propertyName)
169
170 checkWarnings(element, writeScope, location);
171}
172
174 : QQmlSA::ElementPass(manager)
175{
176 m_elements = {
177 ControlElement { "Control",
178 QStringList { "background", "contentItem", "leftPadding", "rightPadding",
179 "topPadding", "bottomPadding", "horizontalPadding",
180 "verticalPadding", "padding" },
181 false, true },
182 ControlElement { "Button", QStringList { "indicator" } },
183 ControlElement {
184 "ApplicationWindow",
185 QStringList { "background", "contentItem", "header", "footer", "menuBar" } },
186 ControlElement { "ComboBox", QStringList { "indicator" } },
187 ControlElement { "Dial", QStringList { "handle" } },
188 ControlElement { "GroupBox", QStringList { "label" } },
189 ControlElement { "$internal$.QQuickIndicatorButton", QStringList { "indicator" }, false },
190 ControlElement { "Label", QStringList { "background" } },
191 ControlElement { "MenuItem", QStringList { "arrow" } },
192 ControlElement { "Page", QStringList { "header", "footer" } },
193 ControlElement { "Popup", QStringList { "background", "contentItem" } },
194 ControlElement { "RangeSlider", QStringList { "handle" } },
195 ControlElement { "Slider", QStringList { "handle" } },
196 ControlElement { "$internal$.QQuickSwipe",
197 QStringList { "leftItem", "behindItem", "rightItem" }, false },
198 ControlElement { "TextArea", QStringList { "background" } },
199 ControlElement { "TextField", QStringList { "background" } },
200 };
201
202 for (const QString &module : { u"QtQuick.Controls.macOS"_s, u"QtQuick.Controls.Windows"_s }) {
203 if (!manager->hasImportedModule(module))
204 continue;
205
206 QQmlSA::Element control = resolveType(module, "Control");
207
208 for (ControlElement &element : m_elements) {
209 auto type = resolveType(element.isInModuleControls ? module : "QtQuick.Templates",
210 element.name);
211
212 if (type.isNull())
213 continue;
214
215 element.inheritsControl = !element.isControl && type.inherits(control);
216 element.element = type;
217 }
218
219 m_elements.removeIf([](const ControlElement &element) { return element.element.isNull(); });
220
221 break;
222 }
223}
224
226{
227 for (const ControlElement &controlElement : m_elements) {
228 // If our element inherits control, we don't have to individually check for them here.
229 if (controlElement.inheritsControl)
230 continue;
231 if (element.inherits(controlElement.element))
232 return true;
233 }
234 return false;
235}
236
238{
239 for (const ControlElement &controlElement : m_elements) {
240 if (element.inherits(controlElement.element)) {
241 for (const QString &propertyName : controlElement.restrictedProperties) {
242 if (element.hasOwnPropertyBindings(propertyName)) {
243 emitWarning(QStringLiteral("Not allowed to override \"%1\" because native "
244 "styles cannot be customized: See "
245 "https://doc-snapshots.qt.io/qt6-dev/"
246 "qtquickcontrols-customize.html#customization-"
247 "reference for more information.")
248 .arg(propertyName),
250 }
251 }
252 // Since all the different types we have rules for don't inherit from each other (except
253 // for Control) we don't have to keep checking whether other types match once we've
254 // found one that has been inherited from.
255 if (!controlElement.isControl)
256 break;
257 }
258 }
259}
260
262 : QQmlSA::ElementPass(manager)
263 , m_item(resolveType("QtQuick", "Item"))
264{
265}
266
268{
269 return !m_item.isNull() && element.inherits(m_item)
270 && element.hasOwnPropertyBindings(u"anchors"_s);
271}
272
274{
275 enum BindingLocation { Exists = 1, Own = (1 << 1) };
276 QHash<QString, qint8> bindings;
277
278 const QStringList properties = { u"left"_s, u"right"_s, u"horizontalCenter"_s,
279 u"top"_s, u"bottom"_s, u"verticalCenter"_s,
280 u"baseline"_s };
281
282 QList<QQmlSA::Binding> anchorBindings = element.propertyBindings(u"anchors"_s);
283
284 for (qsizetype i = anchorBindings.size() - 1; i >= 0; i--) {
285 auto groupType = anchorBindings[i].groupType();
286 if (groupType.isNull())
287 continue;
288
289 for (const QString &name : properties) {
290
291 const auto &propertyBindings = groupType.ownPropertyBindings(name);
292 if (propertyBindings.begin() == propertyBindings.end())
293 continue;
294
295 bool isUndefined = false;
296 for (const auto &propertyBinding : propertyBindings) {
297 if (propertyBinding.hasUndefinedScriptValue()) {
298 isUndefined = true;
299 break;
300 }
301 }
302
303 if (isUndefined)
304 bindings[name] = 0;
305 else
306 bindings[name] |= Exists | ((i == 0) ? Own : 0);
307 }
308 }
309
310 auto ownSourceLocation = [&](QStringList properties) -> QQmlSA::SourceLocation {
312
313 for (const QString &name : properties) {
314 if (bindings[name] & Own) {
315 QQmlSA::Element groupType = QQmlSA::Element{ anchorBindings[0].groupType() };
316 auto bindings = groupType.ownPropertyBindings(name);
317 Q_ASSERT(bindings.begin() != bindings.end());
318 warnLoc = bindings.begin().value().sourceLocation();
319 break;
320 }
321 }
322 return warnLoc;
323 };
324
325 if ((bindings[u"left"_s] & bindings[u"right"_s] & bindings[u"horizontalCenter"_s]) & Exists) {
326 QQmlSA::SourceLocation warnLoc =
327 ownSourceLocation({ u"left"_s, u"right"_s, u"horizontalCenter"_s });
328
329 if (warnLoc.isValid()) {
331 "Cannot specify left, right, and horizontalCenter anchors at the same time.",
332 quickAnchorCombinations, warnLoc);
333 }
334 }
335
336 if ((bindings[u"top"_s] & bindings[u"bottom"_s] & bindings[u"verticalCenter"_s]) & Exists) {
337 QQmlSA::SourceLocation warnLoc =
338 ownSourceLocation({ u"top"_s, u"bottom"_s, u"verticalCenter"_s });
339 if (warnLoc.isValid()) {
340 emitWarning("Cannot specify top, bottom, and verticalCenter anchors at the same time.",
341 quickAnchorCombinations, warnLoc);
342 }
343 }
344
345 if ((bindings[u"baseline"_s] & (bindings[u"bottom"_s] | bindings[u"verticalCenter"_s]))
346 & Exists) {
347 QQmlSA::SourceLocation warnLoc =
348 ownSourceLocation({ u"baseline"_s, u"bottom"_s, u"verticalCenter"_s });
349 if (warnLoc.isValid()) {
350 emitWarning("Baseline anchor cannot be used in conjunction with top, bottom, or "
351 "verticalCenter anchors.",
352 quickAnchorCombinations, warnLoc);
353 }
354 }
355}
356
358 : QQmlSA::ElementPass(manager)
359 , m_swipeDelegate(resolveType("QtQuick.Controls", "SwipeDelegate"))
360{
361}
362
364{
365 return !m_swipeDelegate.isNull() && element.inherits(m_swipeDelegate);
366}
367
369{
370 for (const auto &property : { u"background"_s, u"contentItem"_s }) {
371 for (const auto &binding : element.ownPropertyBindings(property)) {
372 if (!binding.hasObject())
373 continue;
374 const QQmlSA::Element element = QQmlSA::Element{ binding.objectType() };
375 const auto &bindings = element.propertyBindings(u"anchors"_s);
376 if (bindings.isEmpty())
377 continue;
378
379 if (bindings.first().bindingType() != QQmlSA::BindingType::GroupProperty)
380 continue;
381
382 auto anchors = bindings.first().groupType();
383 for (const auto &disallowed : { u"fill"_s, u"centerIn"_s, u"left"_s, u"right"_s }) {
384 if (anchors.hasPropertyBindings(disallowed)) {
386 const auto &ownBindings = anchors.ownPropertyBindings(disallowed);
387 if (ownBindings.begin() != ownBindings.end()) {
388 location = ownBindings.begin().value().sourceLocation();
389 }
390
392 u"SwipeDelegate: Cannot use horizontal anchors with %1; unable to layout the item."_s
393 .arg(property),
395 break;
396 }
397 }
398 break;
399 }
400 }
401
402 const auto &swipe = element.ownPropertyBindings(u"swipe"_s);
403 if (swipe.begin() == swipe.end())
404 return;
405
406 const auto firstSwipe = swipe.begin().value();
407 if (firstSwipe.bindingType() != QQmlSA::BindingType::GroupProperty)
408 return;
409
410 auto group = firstSwipe.groupType();
411
412 const std::array ownDirBindings = { group.ownPropertyBindings(u"right"_s),
413 group.ownPropertyBindings(u"left"_s),
414 group.ownPropertyBindings(u"behind"_s) };
415
416 auto ownBindingIterator =
417 std::find_if(ownDirBindings.begin(), ownDirBindings.end(),
418 [](const auto &bindings) { return bindings.begin() != bindings.end(); });
419
420 if (ownBindingIterator == ownDirBindings.end())
421 return;
422
423 if (group.hasPropertyBindings(u"behind"_s)
424 && (group.hasPropertyBindings(u"right"_s) || group.hasPropertyBindings(u"left"_s))) {
425 emitWarning("SwipeDelegate: Cannot set both behind and left/right properties",
426 quickAnchorCombinations, ownBindingIterator->begin().value().sourceLocation());
427 }
428}
429
432 const QMultiHash<QString, TypeDescription> &expectedPropertyTypes)
433 : QQmlSA::PropertyPass(manager)
434{
435 QMultiHash<QString, QQmlSA::Element> propertyTypes;
436
437 for (const auto &pair : expectedPropertyTypes.asKeyValueRange()) {
438 const QQmlSA::Element propType = pair.second.module.isEmpty()
439 ? resolveBuiltinType(pair.second.name)
440 : resolveType(pair.second.module, pair.second.name);
441 if (!propType.isNull())
442 propertyTypes.insert(pair.first, propType);
443 }
444
445 m_expectedPropertyTypes = propertyTypes;
446}
447
449 const QString &propertyName,
450 const QQmlSA::Binding &binding,
451 const QQmlSA::Element &bindingScope,
452 const QQmlSA::Element &value)
453{
454 Q_UNUSED(element);
455 Q_UNUSED(bindingScope);
456
457 const auto range = m_expectedPropertyTypes.equal_range(propertyName);
458
459 if (range.first == range.second)
460 return;
461
462 QQmlSA::Element bindingType;
463
464 if (!value.isNull()) {
465 bindingType = value;
466 } else {
468 bindingType = resolveLiteralType(binding);
469 } else {
470 switch (binding.bindingType()) {
472 bindingType = QQmlSA::Element{ binding.objectType() };
473 break;
475 break;
476 default:
477 return;
478 }
479 }
480 }
481
482 if (std::find_if(range.first, range.second,
483 [&](const QQmlSA::Element &scope) { return bindingType.inherits(scope); })
484 == range.second) {
485
486 const bool bindingTypeIsComposite = bindingType.isComposite();
487 if (bindingTypeIsComposite && !bindingType.baseType()) {
488 /* broken module or missing import, there is nothing we
489 can really check here, as something is amiss. We
490 simply skip this binding, and assume that whatever
491 caused the breakage here will already cause another
492 warning somewhere else.
493 */
494 return;
495 }
496 const QString bindingTypeName =
497 bindingTypeIsComposite ? bindingType.baseType().name()
498 : bindingType.name();
499 QStringList expectedTypeNames;
500
501 for (auto it = range.first; it != range.second; it++)
502 expectedTypeNames << it.value().name();
503
504 emitWarning(u"Unexpected type for property \"%1\" expected %2 got %3"_s.arg(
505 propertyName, expectedTypeNames.join(u", "_s), bindingTypeName),
507 }
508}
509
510void AttachedPropertyReuse::onRead(const QQmlSA::Element &element, const QString &propertyName,
511 const QQmlSA::Element &readScope,
513{
514 const auto range = usedAttachedTypes.equal_range(readScope);
515 const auto attachedTypeAndLocation = std::find_if(
516 range.first, range.second, [&](const ElementAndLocation &elementAndLocation) {
517 return elementAndLocation.element == element;
518 });
519 if (attachedTypeAndLocation != range.second) {
520 const QQmlSA::SourceLocation attachedLocation = attachedTypeAndLocation->location;
521
522 // Ignore enum accesses, as these will not cause the attached object to be created.
523 // Also ignore anything we cannot determine.
524 if (!element.hasProperty(propertyName) && !element.hasMethod(propertyName))
525 return;
526
527 for (QQmlSA::Element scope = readScope.parentScope(); !scope.isNull();
528 scope = scope.parentScope()) {
529 const auto range = usedAttachedTypes.equal_range(scope);
530 bool found = false;
531 for (auto it = range.first; it != range.second; ++it) {
532 if (it->element == element) {
533 found = true;
534 break;
535 }
536 }
537 if (!found)
538 continue;
539
540 const QString id = resolveElementToId(scope, readScope);
541 const QQmlSA::SourceLocation idInsertLocation{ attachedLocation.offset(), 0,
542 attachedLocation.startLine(),
543 attachedLocation.startColumn() };
544 QQmlSA::FixSuggestion suggestion{ "Reference it by id instead:"_L1, idInsertLocation,
545 id.isEmpty() ? u"<id>."_s : (id + '.'_L1) };
546
547 if (id.isEmpty())
548 suggestion.setHint("You first have to give the element an id"_L1);
549 else
550 suggestion.setAutoApplicable();
551
552 emitWarning("Using attached type %1 already initialized in a parent scope."_L1.arg(
553 element.name()),
554 category, attachedLocation, suggestion);
555 }
556
557 return;
558 }
559
560 if (element.hasProperty(propertyName))
561 return; // an actual property
562
564 QQmlSA::Element attached = resolveAttachedInFileScope(propertyName);
565 if (!type || !attached)
566 return;
567
568 if (category == quickControlsAttachedPropertyReuse) {
569 for (QQmlSA::Element parent = attached; parent; parent = parent.baseType()) {
570 // ### TODO: Make it possible to resolve QQuickAttachedPropertyPropagator
571 // so that we don't have to compare the internal id
572 if (parent.internalId() == "QQuickAttachedPropertyPropagator"_L1) {
573 usedAttachedTypes.insert(readScope, {attached, location});
574 break;
575 }
576 }
577
578 } else {
579 usedAttachedTypes.insert(readScope, {attached, location});
580 }
581}
582
583void AttachedPropertyReuse::onWrite(const QQmlSA::Element &element, const QString &propertyName,
584 const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
586{
588 onRead(element, propertyName, writeScope, location);
589}
590
592 const QQmlSA::Element &rootElement)
593{
594 const QQmlSA::LoggerWarningId attachedReuseCategory = [manager]() {
595 if (manager->isCategoryEnabled(quickAttachedPropertyReuse))
597 if (manager->isCategoryEnabled(qmlAttachedPropertyReuse))
600 }();
601
602 const bool hasQuick = manager->hasImportedModule("QtQuick");
603 const bool hasQuickLayouts = manager->hasImportedModule("QtQuick.Layouts");
604 const bool hasQuickControls = manager->hasImportedModule("QtQuick.Templates")
605 || manager->hasImportedModule("QtQuick.Controls")
606 || manager->hasImportedModule("QtQuick.Controls.Basic");
607
608 Q_UNUSED(rootElement);
609
610 if (hasQuick) {
611 manager->registerElementPass(std::make_unique<AnchorsValidatorPass>(manager));
612 manager->registerElementPass(std::make_unique<PropertyChangesValidatorPass>(manager));
613
614 auto forbiddenChildProperty =
615 std::make_unique<ForbiddenChildrenPropertyValidatorPass>(manager);
616
617 for (const QString &element : { u"Grid"_s, u"Flow"_s }) {
618 for (const QString &property : { u"anchors"_s, u"x"_s, u"y"_s }) {
619 forbiddenChildProperty->addWarning(
620 "QtQuick", element, property,
621 u"Cannot specify %1 for items inside %2. %2 will not function."_s.arg(
622 property, element));
623 }
624 }
625
626 if (hasQuickLayouts) {
627 forbiddenChildProperty->addWarning(
628 "QtQuick.Layouts", "Layout", "anchors",
629 "Detected anchors on an item that is managed by a layout. This is undefined "
630 u"behavior; use Layout.alignment instead.");
631 forbiddenChildProperty->addWarning(
632 "QtQuick.Layouts", "Layout", "x",
633 "Detected x on an item that is managed by a layout. This is undefined "
634 u"behavior; use Layout.leftMargin or Layout.rightMargin instead.");
635 forbiddenChildProperty->addWarning(
636 "QtQuick.Layouts", "Layout", "y",
637 "Detected y on an item that is managed by a layout. This is undefined "
638 u"behavior; use Layout.topMargin or Layout.bottomMargin instead.");
639 forbiddenChildProperty->addWarning(
640 "QtQuick.Layouts", "Layout", "width",
641 "Detected width on an item that is managed by a layout. This is undefined "
642 u"behavior; use implicitWidth or Layout.preferredWidth instead.");
643 forbiddenChildProperty->addWarning(
644 "QtQuick.Layouts", "Layout", "height",
645 "Detected height on an item that is managed by a layout. This is undefined "
646 u"behavior; use implictHeight or Layout.preferredHeight instead.");
647 }
648
649 manager->registerElementPass(std::move(forbiddenChildProperty));
650 }
651
652 auto attachedPropertyType = std::make_shared<AttachedPropertyTypeValidatorPass>(manager);
653
654 auto addAttachedWarning = [&](TypeDescription attachedType, QList<TypeDescription> allowedTypes,
655 QAnyStringView warning, bool allowInDelegate = false) {
656 QString attachedTypeName = attachedPropertyType->addWarning(attachedType, allowedTypes,
657 allowInDelegate, warning);
658 manager->registerPropertyPass(attachedPropertyType, attachedType.module,
659 u"$internal$."_s + attachedTypeName, {}, false);
660 };
661
662 auto addVarBindingWarning =
663 [&](QAnyStringView moduleName, QAnyStringView typeName,
664 const QMultiHash<QString, TypeDescription> &expectedPropertyTypes) {
665 auto varBindingType = std::make_shared<VarBindingTypeValidatorPass>(
666 manager, expectedPropertyTypes);
667 for (const auto &propertyName : expectedPropertyTypes.uniqueKeys()) {
668 manager->registerPropertyPass(varBindingType, moduleName, typeName,
669 propertyName);
670 }
671 };
672
673 if (hasQuick) {
674 addVarBindingWarning("QtQuick", "TableView",
675 { { "columnWidthProvider", { "", "function" } },
676 { "rowHeightProvider", { "", "function" } } });
677 addAttachedWarning({ "QtQuick", "Accessible" }, { { "QtQuick", "Item" } },
678 "Accessible must be attached to an Item");
679 addAttachedWarning({ "QtQuick", "LayoutMirroring" },
680 { { "QtQuick", "Item" }, { "QtQuick", "Window" } },
681 "LayoutDirection attached property only works with Items and Windows");
682 addAttachedWarning({ "QtQuick", "EnterKey" }, { { "QtQuick", "Item" } },
683 "EnterKey attached property only works with Items");
684 }
685 if (hasQuickLayouts) {
686 addAttachedWarning({ "QtQuick.Layouts", "Layout" }, { { "QtQuick", "Item" } },
687 "Layout must be attached to Item elements");
688 addAttachedWarning({ "QtQuick.Layouts", "StackLayout" }, { { "QtQuick", "Item" } },
689 "StackLayout must be attached to an Item");
690 }
691
692
693 if (hasQuickControls) {
694 manager->registerElementPass(std::make_unique<ControlsSwipeDelegateValidatorPass>(manager));
695 manager->registerPropertyPass(std::make_unique<AttachedPropertyReuse>(
696 manager, attachedReuseCategory), "", "");
697
698 addAttachedWarning({ "QtQuick.Templates", "ScrollBar" },
699 { { "QtQuick", "Flickable" }, { "QtQuick.Templates", "ScrollView" } },
700 "ScrollBar must be attached to a Flickable or ScrollView");
701 addAttachedWarning({ "QtQuick.Templates", "ScrollIndicator" },
702 { { "QtQuick", "Flickable" } },
703 "ScrollIndicator must be attached to a Flickable");
704 addAttachedWarning({ "QtQuick.Templates", "TextArea" }, { { "QtQuick", "Flickable" } },
705 "TextArea must be attached to a Flickable");
706 addAttachedWarning({ "QtQuick.Templates", "SplitView" }, { { "QtQuick", "Item" } },
707 "SplitView attached property only works with Items");
708 addAttachedWarning({ "QtQuick.Templates", "StackView" }, { { "QtQuick", "Item" } },
709 "StackView attached property only works with Items");
710 addAttachedWarning({ "QtQuick.Templates", "ToolTip" }, { { "QtQuick", "Item" } },
711 "ToolTip must be attached to an Item");
712 addAttachedWarning({ "QtQuick.Templates", "SwipeDelegate" }, { { "QtQuick", "Item" } },
713 "Attached properties of SwipeDelegate must be accessed through an Item");
714 addAttachedWarning({ "QtQuick.Templates", "SwipeView" }, { { "QtQuick", "Item" } },
715 "SwipeView must be attached to an Item");
716 addAttachedWarning(
717 { "QtQuick.Templates", "Tumbler" }, { { "QtQuick", "Tumbler" } },
718 "Tumbler: attached properties of Tumbler must be accessed through a delegate item",
719 true);
720 addVarBindingWarning("QtQuick.Templates", "Tumbler",
721 { { "contentItem", { "QtQuick", "PathView" } },
722 { "contentItem", { "QtQuick", "ListView" } } });
723 addVarBindingWarning("QtQuick.Templates", "SpinBox",
724 { { "textFromValue", { "", "function" } },
725 { "valueFromText", { "", "function" } } });
726 } else if (attachedReuseCategory != quickControlsAttachedPropertyReuse) {
727 manager->registerPropertyPass(std::make_unique<AttachedPropertyReuse>(
728 manager, attachedReuseCategory), "", "");
729 }
730
731 if (manager->hasImportedModule(u"QtQuick.Controls.macOS"_s)
732 || manager->hasImportedModule(u"QtQuick.Controls.Windows"_s))
733 manager->registerElementPass(std::make_unique<ControlsNativeValidatorPass>(manager));
734}
735
737 : QQmlSA::ElementPass(manager)
738 , m_propertyChanges(resolveType("QtQuick", "PropertyChanges"))
739{
740}
741
743{
744 return !m_propertyChanges.isNull() && element.inherits(m_propertyChanges);
745}
746
748{
749 const QQmlSA::Binding::Bindings bindings = element.ownPropertyBindings();
750
751 const auto target =
752 std::find_if(bindings.constBegin(), bindings.constEnd(),
753 [](const auto binding) { return binding.propertyName() == u"target"_s; });
754 if (target == bindings.constEnd())
755 return;
756
757 QString targetId = u"<id>"_s;
758 const auto targetLocation = target.value().sourceLocation();
759 const QString targetBinding = sourceCode(targetLocation);
760 const QQmlSA::Element targetElement = resolveIdToElement(targetBinding, element);
761 if (!targetElement.isNull())
762 targetId = targetBinding;
763
764 bool hadCustomParsedBindings = false;
765 for (auto it = bindings.constBegin(); it != bindings.constEnd(); ++it) {
766 const auto &propertyName = it.key();
767 const auto &propertyBinding = it.value();
768 if (element.hasProperty(propertyName))
769 continue;
770
771 const QQmlSA::SourceLocation bindingLocation = propertyBinding.sourceLocation();
772 if (!targetElement.isNull() && !targetElement.hasProperty(propertyName)) {
774 "Unknown property \"%1\" in PropertyChanges."_L1.arg(propertyName),
775 quickPropertyChangesParsed, bindingLocation);
776 continue;
777 }
778
779 QString binding = sourceCode(bindingLocation);
780 if (binding.length() > 16)
781 binding = binding.left(13) + "..."_L1;
782
783 hadCustomParsedBindings = true;
784 emitWarning("Property \"%1\" is custom-parsed in PropertyChanges. "
785 "You should phrase this binding as \"%2.%1: %3\""_L1.arg(propertyName, targetId,
786 binding),
787 quickPropertyChangesParsed, bindingLocation);
788 }
789
790 if (hadCustomParsedBindings && !targetElement.isNull()) {
791 emitWarning("You should remove any bindings on the \"target\" property and avoid "
792 "custom-parsed bindings in PropertyChanges.",
793 quickPropertyChangesParsed, targetLocation);
794 }
795}
796
798
799#include "moc_quicklintplugin.cpp"
bool shouldRun(const QQmlSA::Element &element) override
Controls whether the run() function should be executed on the given element.
AnchorsValidatorPass(QQmlSA::PassManager *manager)
void run(const QQmlSA::Element &element) override
Executes if shouldRun() returns true.
void onRead(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override
Executes whenever a property is read.
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.
void onRead(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override
Executes whenever a property is read.
AttachedPropertyTypeValidatorPass(QQmlSA::PassManager *manager)
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.
void onBinding(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) override
Executes whenever a property gets bound to a value.
QString addWarning(TypeDescription attachType, QList< TypeDescription > allowedTypes, bool allowInDelegate, QAnyStringView warning)
ControlsNativeValidatorPass(QQmlSA::PassManager *manager)
void run(const QQmlSA::Element &element) override
Executes if shouldRun() returns true.
bool shouldRun(const QQmlSA::Element &element) override
Controls whether the run() function should be executed on the given element.
bool shouldRun(const QQmlSA::Element &element) override
Controls whether the run() function should be executed on the given element.
ControlsSwipeDelegateValidatorPass(QQmlSA::PassManager *manager)
void run(const QQmlSA::Element &element) override
Executes if shouldRun() returns true.
void addWarning(QAnyStringView moduleName, QAnyStringView typeName, QAnyStringView propertyName, QAnyStringView warning)
ForbiddenChildrenPropertyValidatorPass(QQmlSA::PassManager *manager)
void run(const QQmlSA::Element &element) override
Executes if shouldRun() returns true.
bool shouldRun(const QQmlSA::Element &element) override
Controls whether the run() function should be executed on the given element.
void run(const QQmlSA::Element &element) override
Executes if shouldRun() returns true.
PropertyChangesValidatorPass(QQmlSA::PassManager *manager)
bool shouldRun(const QQmlSA::Element &element) override
Controls whether the run() function should be executed on the given element.
\inmodule QtCore
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1218
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
const_iterator cend() const noexcept
Definition qhash.h:1218
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
qsizetype removeIf(Predicate pred)
Definition qlist.h:604
std::pair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:2266
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2034
\inmodule QtQmlCompiler
Definition qqmlsa.h:57
QMultiHash< QString, Binding >::const_iterator constBegin() const
Returns an iterator to the beginning of the bindings.
Definition qqmlsa.cpp:66
QMultiHash< QString, Binding >::const_iterator begin() const
Definition qqmlsa.h:65
QMultiHash< QString, Binding >::const_iterator constEnd() const
Returns an iterator to the end of the bindings.
Definition qqmlsa.cpp:80
\inmodule QtQmlCompiler
Definition qqmlsa.h:52
BindingType bindingType() const
Definition qqmlsa.cpp:165
static bool isLiteralBinding(BindingType)
Returns true if bindingType is a literal type, and false otherwise.
Definition qqmlsa.cpp:252
Element objectType() const
Returns the type of the associated object if the content type of this binding is Object,...
Definition qqmlsa.cpp:235
QQmlSA::SourceLocation sourceLocation() const
Returns the location in the QML code where this binding is defined.
Definition qqmlsa.cpp:199
\inmodule QtQmlCompiler
Definition qqmlsa.h:193
QString internalId() const
Definition qqmlsa.cpp:628
Element baseType() const
Returns the Element this Element derives from.
Definition qqmlsa.cpp:591
bool hasMethod(const QString &methodName) const
Returns whether this Element has a method with the name methodName.
Definition qqmlsa.cpp:697
QString name() const
Returns the name of this Element.
Definition qqmlsa.cpp:825
QQmlSA::SourceLocation sourceLocation() const
Returns the location in the QML code where this Element is defined.
Definition qqmlsa.cpp:721
bool isNull() const
Definition qqmlsa.cpp:620
Binding::Bindings ownPropertyBindings() const
Returns this Element's property bindings which are not defined on its base or extension objects.
Definition qqmlsa.cpp:756
QList< Binding > propertyBindings(const QString &propertyName) const
Returns this Element's property bindings that have the name propertyName.
Definition qqmlsa.cpp:774
bool isComposite() const
Returns true for objects defined from Qml, and false for objects declared from C++.
Definition qqmlsa.cpp:645
bool hasProperty(const QString &propertyName) const
Returns whether this Element has a property with the name propertyName.
Definition qqmlsa.cpp:653
Element parentScope() const
Returns the Element that encloses this Element.
Definition qqmlsa.cpp:607
bool inherits(const Element &) const
Returns whether this Element inherits from element.
Definition qqmlsa.cpp:615
bool hasOwnPropertyBindings(const QString &propertyName) const
Returns whether this Element has property bindings which are not defined in its base or extension obj...
Definition qqmlsa.cpp:747
\inmodule QtQmlCompiler
Definition qqmlsa.h:386
void setHint(const QString &)
Sets hint as the hint for this fix suggestion.
Definition qqmlsa.cpp:1730
void emitWarning(QAnyStringView diagnostic, LoggerWarningId id)
Emits a warning message diagnostic about an issue of type id.
Definition qqmlsa.cpp:881
Element resolveLiteralType(const Binding &binding)
Returns the element representing the type of literal in binding.
Definition qqmlsa.cpp:993
Element resolveBuiltinType(QAnyStringView typeName) const
Returns the type of the built-in type identified by typeName.
Definition qqmlsa.cpp:965
Element resolveAttached(QAnyStringView moduleName, QAnyStringView typeName)
Returns the attached type of typeName defined in module moduleName.
Definition qqmlsa.cpp:983
QString resolveElementToId(const Element &element, const Element &context)
Returns the id of element in a given context.
Definition qqmlsa.cpp:1016
Element resolveTypeInFileScope(QAnyStringView typeName)
Returns the type corresponding to typeName inside the currently analysed file.
Definition qqmlsa.cpp:919
QString sourceCode(QQmlSA::SourceLocation location)
Returns the source code located within location.
Definition qqmlsa.cpp:1027
Element resolveAttachedInFileScope(QAnyStringView typeName)
Returns the attached type corresponding to typeName used inside the currently analysed file.
Definition qqmlsa.cpp:931
Element resolveIdToElement(QAnyStringView id, const Element &context)
Returns the element in context that has id id.
Definition qqmlsa.cpp:1004
Element resolveType(QAnyStringView moduleName, QAnyStringView typeName)
Returns the type of typeName defined in module moduleName.
Definition qqmlsa.cpp:949
\inmodule QtQmlCompiler
\inmodule QtQmlCompiler
Definition qqmlsa.h:303
\inmodule QtQmlCompiler
quint32 offset() const
Returns the offset of the beginning of this source location.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
qsizetype count(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4833
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
void registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) override
Adds a pass manager that will be executed on rootElement.
VarBindingTypeValidatorPass(QQmlSA::PassManager *manager, const QMultiHash< QString, TypeDescription > &expectedPropertyTypes)
void onBinding(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) override
Executes whenever a property gets bound to a value.
QSet< QString >::iterator it
\inmodule QtQmlCompiler
Combined button and popup list for selecting options.
static const QCssKnownValue properties[NumProperties - 1]
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * typeName
GLint location
GLsizei range
GLenum type
GLboolean GLuint group
GLenum target
GLuint name
const QQmlSA::LoggerWarningId qmlAttachedPropertyReuse
QDebug warning(QAnyStringView fileName, int lineNumber)
QQuickAnchors * anchors(QQuickItem *item)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
#define QStringLiteral(str)
static const QTextHtmlElement elements[Html_NumElements]
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
static constexpr QQmlSA::LoggerWarningId quickUnexpectedVarType
static constexpr QQmlSA::LoggerWarningId quickControlsNativeCustomize
static constexpr QQmlSA::LoggerWarningId quickControlsAttachedPropertyReuse
static constexpr QQmlSA::LoggerWarningId quickAttachedPropertyType
static constexpr QQmlSA::LoggerWarningId quickAnchorCombinations
static constexpr QQmlSA::LoggerWarningId quickAttachedPropertyReuse
static constexpr QQmlSA::LoggerWarningId quickPropertyChangesParsed
static constexpr QQmlSA::LoggerWarningId quickLayoutPositioning
const char property[13]
Definition qwizard.cpp:101
QNetworkAccessManager manager
\inmodule QtCore \reentrant
Definition qchar.h:18