Qt 6.x
The Qt SDK
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 QQmlJS::LoggerWarningId quickLayoutPositioning { "Quick.layout-positioning" };
11static constexpr QQmlJS::LoggerWarningId quickAttachedPropertyType { "Quick.attached-property-type" };
12static constexpr QQmlJS::LoggerWarningId quickControlsNativeCustomize { "Quick.controls-native-customize" };
13static constexpr QQmlJS::LoggerWarningId quickAnchorCombinations { "Quick.anchor-combinations" };
14static constexpr QQmlJS::LoggerWarningId quickUnexpectedVarType { "Quick.unexpected-var-type" };
15static constexpr QQmlJS::LoggerWarningId quickPropertyChangesParsed { "Quick.property-changes-parsed" };
16static constexpr QQmlJS::LoggerWarningId quickControlsAttachedPropertyReuse { "Quick.controls-attached-property-reuse" };
17static constexpr QQmlJS::LoggerWarningId quickAttachedPropertyReuse { "Quick.attached-property-reuse" };
18
21 : QQmlSA::ElementPass(manager)
22{
23}
24
27 QAnyStringView propertyName,
28 QAnyStringView warning)
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 : 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 : 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
68 : QQmlSA::PropertyPass(manager)
69{
70}
71
73 QList<TypeDescription> allowedTypes,
74 bool allowInDelegate, QAnyStringView warning)
75{
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{
137 Q_UNUSED(element)
138 Q_UNUSED(propertyName)
139 Q_UNUSED(bindingScope)
141
142 checkWarnings(bindingScope.baseType(), element, binding.sourceLocation());
143}
144
146 const QString &propertyName,
147 const QQmlSA::Element &readScope,
149{
150 // If the attachment does not have such a property or method then
151 // it's either a more general error or an enum. Enums are fine.
152 if (element.hasProperty(propertyName) || element.hasMethod(propertyName))
153 checkWarnings(element, readScope, location);
154}
155
157 const QString &propertyName,
158 const QQmlSA::Element &value,
159 const QQmlSA::Element &writeScope,
161{
162 Q_UNUSED(propertyName)
164
165 checkWarnings(element, writeScope, location);
166}
167
169 : QQmlSA::ElementPass(manager)
170{
171 m_elements = {
172 ControlElement { "Control",
173 QStringList { "background", "contentItem", "leftPadding", "rightPadding",
174 "topPadding", "bottomPadding", "horizontalPadding",
175 "verticalPadding", "padding" },
176 false, true },
177 ControlElement { "Button", QStringList { "indicator" } },
178 ControlElement {
179 "ApplicationWindow",
180 QStringList { "background", "contentItem", "header", "footer", "menuBar" } },
181 ControlElement { "ComboBox", QStringList { "indicator" } },
182 ControlElement { "Dial", QStringList { "handle" } },
183 ControlElement { "Dialog", QStringList { "header", "footer" } },
184 ControlElement { "GroupBox", QStringList { "label" } },
185 ControlElement { "$internal$.QQuickIndicatorButton", QStringList { "indicator" }, false },
186 ControlElement { "Label", QStringList { "background" } },
187 ControlElement { "MenuItem", QStringList { "arrow" } },
188 ControlElement { "Page", QStringList { "header", "footer" } },
189 ControlElement { "Popup", QStringList { "background", "contentItem" } },
190 ControlElement { "RangeSlider", QStringList { "handle" } },
191 ControlElement { "Slider", QStringList { "handle" } },
192 ControlElement { "$internal$.QQuickSwipe",
193 QStringList { "leftItem", "behindItem", "rightItem" }, false },
194 ControlElement { "TextArea", QStringList { "background" } },
195 ControlElement { "TextField", QStringList { "background" } },
196 };
197
198 for (const QString &module : { u"QtQuick.Controls.macOS"_s, u"QtQuick.Controls.Windows"_s }) {
199 if (!manager->hasImportedModule(module))
200 continue;
201
202 QQmlSA::Element control = resolveType(module, "Control");
203
204 for (ControlElement &element : m_elements) {
205 auto type = resolveType(element.isInModuleControls ? module : "QtQuick.Templates",
206 element.name);
207
208 if (type.isNull())
209 continue;
210
211 element.inheritsControl = !element.isControl && type.inherits(control);
212 element.element = type;
213 }
214
215 m_elements.removeIf([](const ControlElement &element) { return element.element.isNull(); });
216
217 break;
218 }
219}
220
222{
223 for (const ControlElement &controlElement : m_elements) {
224 // If our element inherits control, we don't have to individually check for them here.
225 if (controlElement.inheritsControl)
226 continue;
227 if (element.inherits(controlElement.element))
228 return true;
229 }
230 return false;
231}
232
234{
235 for (const ControlElement &controlElement : m_elements) {
236 if (element.inherits(controlElement.element)) {
237 for (const QString &propertyName : controlElement.restrictedProperties) {
238 if (element.hasOwnPropertyBindings(propertyName)) {
239 emitWarning(QStringLiteral("Not allowed to override \"%1\" because native "
240 "styles cannot be customized: See "
241 "https://doc-snapshots.qt.io/qt6-dev/"
242 "qtquickcontrols-customize.html#customization-"
243 "reference for more information.")
244 .arg(propertyName),
246 }
247 }
248 // Since all the different types we have rules for don't inherit from each other (except
249 // for Control) we don't have to keep checking whether other types match once we've
250 // found one that has been inherited from.
251 if (!controlElement.isControl)
252 break;
253 }
254 }
255}
256
258 : QQmlSA::ElementPass(manager)
259 , m_item(resolveType("QtQuick", "Item"))
260{
261}
262
264{
265 return !m_item.isNull() && element.inherits(m_item)
266 && element.hasOwnPropertyBindings(u"anchors"_s);
267}
268
270{
271 enum BindingLocation { Exists = 1, Own = (1 << 1) };
272 QHash<QString, qint8> bindings;
273
274 const QStringList properties = { u"left"_s, u"right"_s, u"horizontalCenter"_s,
275 u"top"_s, u"bottom"_s, u"verticalCenter"_s,
276 u"baseline"_s };
277
278 QList<QQmlSA::Binding> anchorBindings = element.propertyBindings(u"anchors"_s);
279
280 for (qsizetype i = anchorBindings.size() - 1; i >= 0; i--) {
281 auto groupType = anchorBindings[i].groupType();
282 if (groupType.isNull())
283 continue;
284
285 for (const QString &name : properties) {
286
287 const auto &propertyBindings = groupType.ownPropertyBindings(name);
288 if (propertyBindings.begin() == propertyBindings.end())
289 continue;
290
291 bool isUndefined = false;
292 for (const auto &propertyBinding : propertyBindings) {
293 if (propertyBinding.hasUndefinedScriptValue()) {
294 isUndefined = true;
295 break;
296 }
297 }
298
299 if (isUndefined)
300 bindings[name] = 0;
301 else
302 bindings[name] |= Exists | ((i == 0) ? Own : 0);
303 }
304 }
305
306 auto ownSourceLocation = [&](QStringList properties) -> QQmlSA::SourceLocation {
308
309 for (const QString &name : properties) {
310 if (bindings[name] & Own) {
311 QQmlSA::Element groupType = QQmlSA::Element{ anchorBindings[0].groupType() };
312 auto bindings = groupType.ownPropertyBindings(name);
313 Q_ASSERT(bindings.begin() != bindings.end());
314 warnLoc = bindings.begin().value().sourceLocation();
315 break;
316 }
317 }
318 return warnLoc;
319 };
320
321 if ((bindings[u"left"_s] & bindings[u"right"_s] & bindings[u"horizontalCenter"_s]) & Exists) {
322 QQmlSA::SourceLocation warnLoc =
323 ownSourceLocation({ u"left"_s, u"right"_s, u"horizontalCenter"_s });
324
325 if (warnLoc.isValid()) {
327 "Cannot specify left, right, and horizontalCenter anchors at the same time.",
328 quickAnchorCombinations, warnLoc);
329 }
330 }
331
332 if ((bindings[u"top"_s] & bindings[u"bottom"_s] & bindings[u"verticalCenter"_s]) & Exists) {
333 QQmlSA::SourceLocation warnLoc =
334 ownSourceLocation({ u"top"_s, u"bottom"_s, u"verticalCenter"_s });
335 if (warnLoc.isValid()) {
336 emitWarning("Cannot specify top, bottom, and verticalCenter anchors at the same time.",
337 quickAnchorCombinations, warnLoc);
338 }
339 }
340
341 if ((bindings[u"baseline"_s] & (bindings[u"bottom"_s] | bindings[u"verticalCenter"_s]))
342 & Exists) {
343 QQmlSA::SourceLocation warnLoc =
344 ownSourceLocation({ u"baseline"_s, u"bottom"_s, u"verticalCenter"_s });
345 if (warnLoc.isValid()) {
346 emitWarning("Baseline anchor cannot be used in conjunction with top, bottom, or "
347 "verticalCenter anchors.",
348 quickAnchorCombinations, warnLoc);
349 }
350 }
351}
352
354 : QQmlSA::ElementPass(manager)
355 , m_swipeDelegate(resolveType("QtQuick.Controls", "SwipeDelegate"))
356{
357}
358
360{
361 return !m_swipeDelegate.isNull() && element.inherits(m_swipeDelegate);
362}
363
365{
366 for (const auto &property : { u"background"_s, u"contentItem"_s }) {
367 for (const auto &binding : element.ownPropertyBindings(property)) {
368 if (!binding.hasObject())
369 continue;
370 const QQmlSA::Element element = QQmlSA::Element{ binding.objectType() };
371 const auto &bindings = element.propertyBindings(u"anchors"_s);
372 if (bindings.isEmpty())
373 continue;
374
375 if (bindings.first().bindingType() != QQmlSA::BindingType::GroupProperty)
376 continue;
377
378 auto anchors = bindings.first().groupType();
379 for (const auto &disallowed : { u"fill"_s, u"centerIn"_s, u"left"_s, u"right"_s }) {
380 if (anchors.hasPropertyBindings(disallowed)) {
382 const auto &ownBindings = anchors.ownPropertyBindings(disallowed);
383 if (ownBindings.begin() != ownBindings.end()) {
384 location = ownBindings.begin().value().sourceLocation();
385 }
386
388 u"SwipeDelegate: Cannot use horizontal anchors with %1; unable to layout the item."_s
389 .arg(property),
391 break;
392 }
393 }
394 break;
395 }
396 }
397
398 const auto &swipe = element.ownPropertyBindings(u"swipe"_s);
399 if (swipe.begin() == swipe.end())
400 return;
401
402 const auto firstSwipe = swipe.begin().value();
403 if (firstSwipe.bindingType() != QQmlSA::BindingType::GroupProperty)
404 return;
405
406 auto group = firstSwipe.groupType();
407
408 const std::array ownDirBindings = { group.ownPropertyBindings(u"right"_s),
409 group.ownPropertyBindings(u"left"_s),
410 group.ownPropertyBindings(u"behind"_s) };
411
412 auto ownBindingIterator =
413 std::find_if(ownDirBindings.begin(), ownDirBindings.end(),
414 [](const auto &bindings) { return bindings.begin() != bindings.end(); });
415
416 if (ownBindingIterator == ownDirBindings.end())
417 return;
418
419 if (group.hasPropertyBindings(u"behind"_s)
420 && (group.hasPropertyBindings(u"right"_s) || group.hasPropertyBindings(u"left"_s))) {
421 emitWarning("SwipeDelegate: Cannot set both behind and left/right properties",
422 quickAnchorCombinations, ownBindingIterator->begin().value().sourceLocation());
423 }
424}
425
428 const QMultiHash<QString, TypeDescription> &expectedPropertyTypes)
429 : QQmlSA::PropertyPass(manager)
430{
432
433 for (const auto pair : expectedPropertyTypes.asKeyValueRange()) {
434 const QQmlSA::Element propType = pair.second.module.isEmpty()
435 ? resolveBuiltinType(pair.second.name)
436 : resolveType(pair.second.module, pair.second.name);
437 if (!propType.isNull())
438 propertyTypes.insert(pair.first, propType);
439 }
440
441 m_expectedPropertyTypes = propertyTypes;
442}
443
445 const QString &propertyName,
446 const QQmlSA::Binding &binding,
447 const QQmlSA::Element &bindingScope,
448 const QQmlSA::Element &value)
449{
450 Q_UNUSED(element);
451 Q_UNUSED(bindingScope);
452
453 const auto range = m_expectedPropertyTypes.equal_range(propertyName);
454
455 if (range.first == range.second)
456 return;
457
458 QQmlSA::Element bindingType;
459
460 if (!value.isNull()) {
461 bindingType = value;
462 } else {
464 bindingType = resolveLiteralType(binding);
465 } else {
466 switch (binding.bindingType()) {
468 bindingType = QQmlSA::Element{ binding.objectType() };
469 break;
471 break;
472 default:
473 return;
474 }
475 }
476 }
477
478 if (std::find_if(range.first, range.second,
479 [&](const QQmlSA::Element &scope) { return bindingType.inherits(scope); })
480 == range.second) {
481
482 const bool bindingTypeIsComposite = bindingType.isComposite();
483 if (bindingTypeIsComposite && !bindingType.baseType()) {
484 /* broken module or missing import, there is nothing we
485 can really check here, as something is amiss. We
486 simply skip this binding, and assume that whatever
487 caused the breakage here will already cause another
488 warning somewhere else.
489 */
490 return;
491 }
492 const QString bindingTypeName =
493 bindingTypeIsComposite ? bindingType.baseType().name()
494 : bindingType.name();
495 QStringList expectedTypeNames;
496
497 for (auto it = range.first; it != range.second; it++)
498 expectedTypeNames << it.value().name();
499
500 emitWarning(u"Unexpected type for property \"%1\" expected %2 got %3"_s.arg(
501 propertyName, expectedTypeNames.join(u", "_s), bindingTypeName),
503 }
504}
505
506void AttachedPropertyReuse::onRead(const QQmlSA::Element &element, const QString &propertyName,
507 const QQmlSA::Element &readScope,
509{
510 const auto range = usedAttachedTypes.equal_range(readScope);
511 const auto attachedTypeAndLocation = std::find_if(
512 range.first, range.second, [&](const ElementAndLocation &elementAndLocation) {
513 return elementAndLocation.element == element;
514 });
515 if (attachedTypeAndLocation != range.second) {
516 const QQmlSA::SourceLocation attachedLocation = attachedTypeAndLocation->location;
517
518 // Ignore enum accesses, as these will not cause the attached object to be created.
519 // Also ignore anything we cannot determine.
520 if (!element.hasProperty(propertyName) && !element.hasMethod(propertyName))
521 return;
522
523 for (QQmlSA::Element scope = readScope.parentScope(); !scope.isNull();
524 scope = scope.parentScope()) {
525 const auto range = usedAttachedTypes.equal_range(scope);
526 bool found = false;
527 for (auto it = range.first; it != range.second; ++it) {
528 if (it->element == element) {
529 found = true;
530 break;
531 }
532 }
533 if (!found)
534 continue;
535
536 const QString id = resolveElementToId(scope, readScope);
537 const QQmlSA::SourceLocation idInsertLocation{ attachedLocation.offset(), 0,
538 attachedLocation.startLine(),
539 attachedLocation.startColumn() };
540 QQmlSA::FixSuggestion suggestion{ "Reference it by id instead:"_L1, idInsertLocation,
541 id.isEmpty() ? u"<id>."_s : (id + '.'_L1) };
542
543 if (id.isEmpty())
544 suggestion.setHint("You first have to give the element an id"_L1);
545 else
546 suggestion.setAutoApplicable();
547
548 emitWarning("Using attached type %1 already initialized in a parent scope."_L1.arg(
549 element.name()),
550 category, attachedLocation, suggestion);
551 }
552
553 return;
554 }
555
556 if (element.hasProperty(propertyName))
557 return; // an actual property
558
560 QQmlSA::Element attached = resolveAttachedInFileScope(propertyName);
561 if (!type || !attached)
562 return;
563
564 if (category == quickControlsAttachedPropertyReuse) {
565 for (QQmlSA::Element parent = attached; parent; parent = parent.baseType()) {
566 // ### TODO: Make it possible to resolve QQuickAttachedPropertyPropagator
567 // so that we don't have to compare the internal id
568 if (parent.internalId() == "QQuickAttachedPropertyPropagator"_L1) {
569 usedAttachedTypes.insert(readScope, {attached, location});
570 break;
571 }
572 }
573
574 } else {
575 usedAttachedTypes.insert(readScope, {attached, location});
576 }
577}
578
579void AttachedPropertyReuse::onWrite(const QQmlSA::Element &element, const QString &propertyName,
580 const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
582{
584 onRead(element, propertyName, writeScope, location);
585}
586
588 const QQmlSA::Element &rootElement)
589{
590 const QQmlJS::LoggerWarningId attachedReuseCategory = [manager]() {
591 if (manager->isCategoryEnabled(quickAttachedPropertyReuse))
593 if (manager->isCategoryEnabled(qmlAttachedPropertyReuse))
596 }();
597
598 const bool hasQuick = manager->hasImportedModule("QtQuick");
599 const bool hasQuickLayouts = manager->hasImportedModule("QtQuick.Layouts");
600 const bool hasQuickControls = manager->hasImportedModule("QtQuick.Templates")
601 || manager->hasImportedModule("QtQuick.Controls")
602 || manager->hasImportedModule("QtQuick.Controls.Basic");
603
604 Q_UNUSED(rootElement);
605
606 if (hasQuick) {
607 manager->registerElementPass(std::make_unique<AnchorsValidatorPass>(manager));
608 manager->registerElementPass(std::make_unique<PropertyChangesValidatorPass>(manager));
609
610 auto forbiddenChildProperty =
611 std::make_unique<ForbiddenChildrenPropertyValidatorPass>(manager);
612
613 for (const QString &element : { u"Grid"_s, u"Flow"_s }) {
614 for (const QString &property : { u"anchors"_s, u"x"_s, u"y"_s }) {
615 forbiddenChildProperty->addWarning(
616 "QtQuick", element, property,
617 u"Cannot specify %1 for items inside %2. %2 will not function."_s.arg(
618 property, element));
619 }
620 }
621
622 if (hasQuickLayouts) {
623 forbiddenChildProperty->addWarning(
624 "QtQuick.Layouts", "Layout", "anchors",
625 "Detected anchors on an item that is managed by a layout. This is undefined "
626 u"behavior; use Layout.alignment instead.");
627 forbiddenChildProperty->addWarning(
628 "QtQuick.Layouts", "Layout", "x",
629 "Detected x on an item that is managed by a layout. This is undefined "
630 u"behavior; use Layout.leftMargin or Layout.rightMargin instead.");
631 forbiddenChildProperty->addWarning(
632 "QtQuick.Layouts", "Layout", "y",
633 "Detected y on an item that is managed by a layout. This is undefined "
634 u"behavior; use Layout.topMargin or Layout.bottomMargin instead.");
635 forbiddenChildProperty->addWarning(
636 "QtQuick.Layouts", "Layout", "width",
637 "Detected width on an item that is managed by a layout. This is undefined "
638 u"behavior; use implicitWidth or Layout.preferredWidth instead.");
639 forbiddenChildProperty->addWarning(
640 "QtQuick.Layouts", "Layout", "height",
641 "Detected height on an item that is managed by a layout. This is undefined "
642 u"behavior; use implictHeight or Layout.preferredHeight instead.");
643 }
644
645 manager->registerElementPass(std::move(forbiddenChildProperty));
646 }
647
648 auto attachedPropertyType = std::make_shared<AttachedPropertyTypeValidatorPass>(manager);
649
650 auto addAttachedWarning = [&](TypeDescription attachedType, QList<TypeDescription> allowedTypes,
651 QAnyStringView warning, bool allowInDelegate = false) {
652 QString attachedTypeName = attachedPropertyType->addWarning(attachedType, allowedTypes,
653 allowInDelegate, warning);
654 manager->registerPropertyPass(attachedPropertyType, attachedType.module,
655 u"$internal$."_s + attachedTypeName, {}, false);
656 };
657
658 auto addVarBindingWarning =
659 [&](QAnyStringView moduleName, QAnyStringView typeName,
660 const QMultiHash<QString, TypeDescription> &expectedPropertyTypes) {
661 auto varBindingType = std::make_shared<VarBindingTypeValidatorPass>(
662 manager, expectedPropertyTypes);
663 for (const auto &propertyName : expectedPropertyTypes.uniqueKeys()) {
664 manager->registerPropertyPass(varBindingType, moduleName, typeName,
665 propertyName);
666 }
667 };
668
669 if (hasQuick) {
670 addVarBindingWarning("QtQuick", "TableView",
671 { { "columnWidthProvider", { "", "function" } },
672 { "rowHeightProvider", { "", "function" } } });
673 addAttachedWarning({ "QtQuick", "Accessible" }, { { "QtQuick", "Item" } },
674 "Accessible must be attached to an Item");
675 addAttachedWarning({ "QtQuick", "LayoutMirroring" },
676 { { "QtQuick", "Item" }, { "QtQuick", "Window" } },
677 "LayoutDirection attached property only works with Items and Windows");
678 addAttachedWarning({ "QtQuick", "EnterKey" }, { { "QtQuick", "Item" } },
679 "EnterKey attached property only works with Items");
680 }
681 if (hasQuickLayouts) {
682 addAttachedWarning({ "QtQuick.Layouts", "Layout" }, { { "QtQuick", "Item" } },
683 "Layout must be attached to Item elements");
684 addAttachedWarning({ "QtQuick.Layouts", "StackLayout" }, { { "QtQuick", "Item" } },
685 "StackLayout must be attached to an Item");
686 }
687
688
689 if (hasQuickControls) {
690 manager->registerElementPass(std::make_unique<ControlsSwipeDelegateValidatorPass>(manager));
691 manager->registerPropertyPass(std::make_unique<AttachedPropertyReuse>(
692 manager, attachedReuseCategory), "", "");
693
694 addAttachedWarning({ "QtQuick.Templates", "ScrollBar" },
695 { { "QtQuick", "Flickable" }, { "QtQuick.Templates", "ScrollView" } },
696 "ScrollBar must be attached to a Flickable or ScrollView");
697 addAttachedWarning({ "QtQuick.Templates", "ScrollIndicator" },
698 { { "QtQuick", "Flickable" } },
699 "ScrollIndicator must be attached to a Flickable");
700 addAttachedWarning({ "QtQuick.Templates", "TextArea" }, { { "QtQuick", "Flickable" } },
701 "TextArea must be attached to a Flickable");
702 addAttachedWarning({ "QtQuick.Templates", "SplitView" }, { { "QtQuick", "Item" } },
703 "SplitView attached property only works with Items");
704 addAttachedWarning({ "QtQuick.Templates", "StackView" }, { { "QtQuick", "Item" } },
705 "StackView attached property only works with Items");
706 addAttachedWarning({ "QtQuick.Templates", "ToolTip" }, { { "QtQuick", "Item" } },
707 "ToolTip must be attached to an Item");
708 addAttachedWarning({ "QtQuick.Templates", "SwipeDelegate" }, { { "QtQuick", "Item" } },
709 "Attached properties of SwipeDelegate must be accessed through an Item");
710 addAttachedWarning({ "QtQuick.Templates", "SwipeView" }, { { "QtQuick", "Item" } },
711 "SwipeView must be attached to an Item");
712 addAttachedWarning(
713 { "QtQuick.Templates", "Tumbler" }, { { "QtQuick", "Tumbler" } },
714 "Tumbler: attached properties of Tumbler must be accessed through a delegate item",
715 true);
716 addVarBindingWarning("QtQuick.Templates", "Tumbler",
717 { { "contentItem", { "QtQuick", "PathView" } },
718 { "contentItem", { "QtQuick", "ListView" } } });
719 addVarBindingWarning("QtQuick.Templates", "SpinBox",
720 { { "textFromValue", { "", "function" } },
721 { "valueFromText", { "", "function" } } });
722 } else if (attachedReuseCategory != quickControlsAttachedPropertyReuse) {
723 manager->registerPropertyPass(std::make_unique<AttachedPropertyReuse>(
724 manager, attachedReuseCategory), "", "");
725 }
726
727 if (manager->hasImportedModule(u"QtQuick.Controls.macOS"_s)
728 || manager->hasImportedModule(u"QtQuick.Controls.Windows"_s))
729 manager->registerElementPass(std::make_unique<ControlsNativeValidatorPass>(manager));
730}
731
733 : QQmlSA::ElementPass(manager)
734 , m_propertyChanges(resolveType("QtQuick", "PropertyChanges"))
735{
736}
737
739{
740 return !m_propertyChanges.isNull() && element.inherits(m_propertyChanges);
741}
742
744{
745 const QQmlSA::Binding::Bindings bindings = element.ownPropertyBindings();
746
747 const auto target =
748 std::find_if(bindings.constBegin(), bindings.constEnd(),
749 [](const auto binding) { return binding.propertyName() == u"target"_s; });
750 if (target == bindings.constEnd())
751 return;
752
753 QString targetId = u"<id>"_s;
754 const QString targetBinding = sourceCode(target.value().sourceLocation());
755 const QQmlSA::Element targetElement = resolveIdToElement(targetBinding, element);
756 if (!targetElement.isNull())
757 targetId = targetBinding;
758
759 for (auto it = bindings.constBegin(); it != bindings.constEnd(); ++it) {
760 const auto &propertyName = it.key();
761 const auto &propertyBinding = it.value();
762 if (element.hasProperty(propertyName))
763 continue;
764
765 const QQmlSA::SourceLocation bindingLocation = propertyBinding.sourceLocation();
766 if (!targetElement.isNull() && !targetElement.hasProperty(propertyName)) {
768 "Unknown property \"%1\" in PropertyChanges."_L1.arg(propertyName),
769 quickPropertyChangesParsed, bindingLocation);
770 continue;
771 }
772
773 QString binding = sourceCode(bindingLocation);
774 if (binding.length() > 16)
775 binding = binding.left(13) + "..."_L1;
776
777 emitWarning("Property \"%1\" is custom-parsed in PropertyChanges. "
778 "You should phrase this binding as \"%2.%1: %3\""_L1.arg(propertyName, targetId,
779 binding),
780 quickPropertyChangesParsed, bindingLocation);
781 }
782}
783
785
786#include "moc_quicklintplugin.cpp"
bool shouldRun(const QQmlSA::Element &element) override
Returns true if 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
Returns true if the run() function should be executed on the given element.
bool shouldRun(const QQmlSA::Element &element) override
Returns true if 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
Returns true if 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
Returns true if 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:1071
T & value() const noexcept
Returns a modifiable reference to the current item's value.
Definition qhash.h:1111
\inmodule QtCore
Definition qhash.h:818
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1202
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
auto asKeyValueRange() &
Definition qhash.h:1218
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
const_iterator cend() const noexcept
Definition qhash.h:1208
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
qsizetype removeIf(Predicate pred)
Definition qlist.h:587
\inmodule QtCore
Definition qhash.h:1348
QPair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:2128
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1930
auto asKeyValueRange() &
Definition qhash.h:1842
\inmodule QtQmlCompiler
Definition qqmlsa.h:63
QMultiHash< QString, Binding >::const_iterator constBegin() const
Returns an iterator to the beginning of the bindings.
Definition qqmlsa.cpp:68
QMultiHash< QString, Binding >::const_iterator begin() const
Definition qqmlsa.h:71
QMultiHash< QString, Binding >::const_iterator constEnd() const
Returns an iterator to the end of the bindings.
Definition qqmlsa.cpp:82
\inmodule QtQmlCompiler
Definition qqmlsa.h:58
BindingType bindingType() const
Definition qqmlsa.cpp:167
static bool isLiteralBinding(BindingType)
Returns true if bindingType is a literal type, and false otherwise.
Definition qqmlsa.cpp:259
Element objectType() const
Returns the type of the associated object if the content type of this binding is Object,...
Definition qqmlsa.cpp:237
QQmlSA::SourceLocation sourceLocation() const
Returns the location in the QML code where this binding is defined.
Definition qqmlsa.cpp:201
\inmodule QtQmlCompiler
Definition qqmlsa.h:197
QString internalId() const
Definition qqmlsa.cpp:590
Element baseType() const
Returns the Element this Element derives from.
Definition qqmlsa.cpp:553
bool hasMethod(const QString &methodName) const
Returns whether this Element has a method with the name methodName.
Definition qqmlsa.cpp:650
bool isPropertyRequired(const QString &propertyName) const
Returns whether the property with the name propertyName resolved on this Element is required.
Definition qqmlsa.cpp:633
QString name() const
Returns the name of this Element.
Definition qqmlsa.cpp:794
QQmlSA::SourceLocation sourceLocation() const
Returns the location in the QML code where this method is defined.
Definition qqmlsa.cpp:674
bool isNull() const
Definition qqmlsa.cpp:582
Binding::Bindings ownPropertyBindings() const
Returns this Element's property bindings which are not defined on its base or extension objects.
Definition qqmlsa.cpp:709
QList< Binding > propertyBindings(const QString &propertyName) const
Returns this Element's property bindings that have the name propertyName.
Definition qqmlsa.cpp:727
bool isComposite() const
Returns true for objects defined from Qml, and false for objects declared from C++.
Definition qqmlsa.cpp:607
bool hasProperty(const QString &propertyName) const
Returns whether this Element has a property with the name propertyName.
Definition qqmlsa.cpp:615
Element parentScope() const
Returns the Element that encloses this Element.
Definition qqmlsa.cpp:569
bool inherits(const Element &) const
Returns whether this Element inherits from element.
Definition qqmlsa.cpp:577
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:700
\inmodule QtQmlCompiler
Definition qqmlsa.h:392
void setHint(const QString &)
Sets hint as the hint for this fix suggestion.
Definition qqmlsa.cpp:1641
Element resolveLiteralType(const Binding &binding)
Returns the element representing the type of literal in binding.
Definition qqmlsa.cpp:958
void emitWarning(QAnyStringView diagnostic, QQmlJS::LoggerWarningId id)
Emits a warning message diagnostic about an issue of type id.
Definition qqmlsa.cpp:850
Element resolveBuiltinType(QAnyStringView typeName) const
Returns the type of the built-in type identified by typeName.
Definition qqmlsa.cpp:930
Element resolveAttached(QAnyStringView moduleName, QAnyStringView typeName)
Returns the attached type of typeName defined in module moduleName.
Definition qqmlsa.cpp:948
QString resolveElementToId(const Element &element, const Element &context)
Returns the id of element in a given context.
Definition qqmlsa.cpp:979
Element resolveTypeInFileScope(QAnyStringView typeName)
Returns the type corresponding to typeName inside the currently analysed file.
Definition qqmlsa.cpp:888
QString sourceCode(QQmlSA::SourceLocation location)
Returns the source code located within location.
Definition qqmlsa.cpp:990
Element resolveAttachedInFileScope(QAnyStringView typeName)
Returns the attached type corresponding to typeName used inside the currently analysed file.
Definition qqmlsa.cpp:900
Element resolveIdToElement(QAnyStringView id, const Element &context)
Returns the element in context that has id id.
Definition qqmlsa.cpp:967
Element resolveType(QAnyStringView moduleName, QAnyStringView typeName)
Returns the type of typeName defined in module moduleName.
Definition qqmlsa.cpp:914
\inmodule QtQmlCompiler
Definition qqmlsa.h:309
\inmodule QtQmlCompiler
quint32 startColumn() const
Returns the column number containing the beginning of this source location.
quint32 startLine() const
Returns the line number containing the beginning of this source location.
quint32 offset() const
Returns the offset of the beginning of this source location.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
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 QQmlJS::LoggerWarningId qmlAttachedPropertyReuse
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)
@ desc
ptrdiff_t qsizetype
Definition qtypes.h:70
static constexpr QQmlJS::LoggerWarningId quickPropertyChangesParsed
static constexpr QQmlJS::LoggerWarningId quickControlsAttachedPropertyReuse
static constexpr QQmlJS::LoggerWarningId quickAttachedPropertyType
static constexpr QQmlJS::LoggerWarningId quickControlsNativeCustomize
static constexpr QQmlJS::LoggerWarningId quickUnexpectedVarType
static constexpr QQmlJS::LoggerWarningId quickAnchorCombinations
static constexpr QQmlJS::LoggerWarningId quickLayoutPositioning
static constexpr QQmlJS::LoggerWarningId quickAttachedPropertyReuse
const char property[13]
Definition qwizard.cpp:101
QNetworkAccessManager manager
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent