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
qqmltcvisitor.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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/qqmltcvisitor_p.h>
8
9#include <QtCore/qfileinfo.h>
10#include <QtCore/qstack.h>
11#include <QtCore/qdir.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtQml/private/qqmlsignalnames_p.h>
14
15#include <private/qqmljsutils_p.h>
16
17#include <algorithm>
18#include <array>
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
25
26namespace QQmltc {
27
29{
30 const auto Error = std::make_optional(QQmlJS::WarningSeverity::Error);
31
32 // We want qmltc to be stricter than qmllint. Override certain categories.
33 // Kept in sync via tst_qmltc::ensureWarningCategoryOverridesSync
34 static const std::array overrides{
35 QmltcLoggingCategoryOverride{ qmlAccessSingleton, std::nullopt },
36 QmltcLoggingCategoryOverride{ qmlAliasCycle, Error },
37 QmltcLoggingCategoryOverride{ qmlAssignmentInCondition, std::nullopt },
38 QmltcLoggingCategoryOverride{ qmlAttachedPropertyReuse, std::nullopt },
39 QmltcLoggingCategoryOverride{ qmlBlockScopeVarDeclaration, std::nullopt },
40 QmltcLoggingCategoryOverride{ qmlComma, std::nullopt },
41 QmltcLoggingCategoryOverride{ qmlCompiler, Error },
42 QmltcLoggingCategoryOverride{ qmlComponentChildrenCount, Error },
43 QmltcLoggingCategoryOverride{ qmlConfusingExpressionStatement, std::nullopt },
44 QmltcLoggingCategoryOverride{ qmlConfusingMinuses, std::nullopt },
45 QmltcLoggingCategoryOverride{ qmlConfusingPluses, std::nullopt },
46 QmltcLoggingCategoryOverride{ qmlContextProperties, std::nullopt },
47 QmltcLoggingCategoryOverride{ qmlDeferredPropertyId, std::nullopt },
48 QmltcLoggingCategoryOverride{ qmlEnumsAreNotTypes, Error },
49 QmltcLoggingCategoryOverride{ qmlEqualityTypeCoercion, std::nullopt },
50 QmltcLoggingCategoryOverride{ qmlDeprecated, std::nullopt },
51 QmltcLoggingCategoryOverride{ qmlDuplicateEnumEntries, std::nullopt },
52 QmltcLoggingCategoryOverride{ qmlDuplicateImport, std::nullopt },
53 QmltcLoggingCategoryOverride{ qmlDuplicateInlineComponent, Error },
54 QmltcLoggingCategoryOverride{ qmlDuplicatePropertyBinding, Error },
55 QmltcLoggingCategoryOverride{ qmlDuplicatedName, Error },
56 QmltcLoggingCategoryOverride{ qmlEnumEntryMatchesEnum, std::nullopt },
57 QmltcLoggingCategoryOverride{ qmlEnumKeyCase, std::nullopt },
58 QmltcLoggingCategoryOverride{ qmlEval, std::nullopt },
59 QmltcLoggingCategoryOverride{ qmlFunctionUsedBeforeDeclaration, std::nullopt },
60 QmltcLoggingCategoryOverride{ qmlIdShadowsMember, std::nullopt },
61 QmltcLoggingCategoryOverride{ qmlImport, Error },
62 QmltcLoggingCategoryOverride{ qmlImportFileSelector, std::nullopt },
63 QmltcLoggingCategoryOverride{ qmlIncompatibleType, Error },
64 QmltcLoggingCategoryOverride{ qmlInheritanceCycle, Error },
65 QmltcLoggingCategoryOverride{ qmlInlineComponentEnums, std::nullopt },
66 QmltcLoggingCategoryOverride{ qmlInvalidLintDirective, std::nullopt },
67 QmltcLoggingCategoryOverride{ qmlLiteralConstructor, Error },
68 QmltcLoggingCategoryOverride{ qmlMaxWarningsExceeded, std::nullopt },
69 QmltcLoggingCategoryOverride{ qmlMissingEnumEntry, std::nullopt },
70 QmltcLoggingCategoryOverride{ qmlMissingProperty, Error },
71 QmltcLoggingCategoryOverride{ qmlMissingType, Error },
72 QmltcLoggingCategoryOverride{ qmlMultilineStrings, std::nullopt },
73 QmltcLoggingCategoryOverride{ qmlNonListProperty, Error },
74 QmltcLoggingCategoryOverride{ qmlNonRootEnums, std::nullopt },
75 QmltcLoggingCategoryOverride{ qmlPropertyOverride, std::nullopt },
76 QmltcLoggingCategoryOverride{ qmlUnterminatedCase, std::nullopt },
77 QmltcLoggingCategoryOverride{ qmlPreferNonVarProperties, std::nullopt },
78 QmltcLoggingCategoryOverride{ qmlPrefixedImportType, std::nullopt },
79 QmltcLoggingCategoryOverride{ qmlReadOnlyProperty, Error },
80 QmltcLoggingCategoryOverride{ qmlRecursionDepthErrors, Error },
81 QmltcLoggingCategoryOverride{ qmlRedundantOptionalChaining, std::nullopt },
82 QmltcLoggingCategoryOverride{ qmlRenamedType, std::nullopt },
83 QmltcLoggingCategoryOverride{ qmlRequired, Error },
84 QmltcLoggingCategoryOverride{ qmlShadow, std::nullopt },
85 QmltcLoggingCategoryOverride{ qmlSignalHandlerNameClash, Error },
86 QmltcLoggingCategoryOverride{ qmlSignalParameters, Error },
87 QmltcLoggingCategoryOverride{ qmlStalePropertyRead, std::nullopt },
88 QmltcLoggingCategoryOverride{ qmlSyntax, std::nullopt },
89 QmltcLoggingCategoryOverride{ qmlSyntaxDuplicateIds, Error },
90 QmltcLoggingCategoryOverride{ qmlSyntaxIdQuotation, std::nullopt },
91 QmltcLoggingCategoryOverride{ qmlTypeInstantiatedRecursively, Error },
92 QmltcLoggingCategoryOverride{ qmlTopLevelComponent, Error },
93 QmltcLoggingCategoryOverride{ qmlUncreatableType, Error },
94 QmltcLoggingCategoryOverride{ qmlUnintentionalEmptyBlock, std::nullopt },
95 QmltcLoggingCategoryOverride{ qmlUnqualified, std::nullopt },
96 QmltcLoggingCategoryOverride{ qmlUnreachableCode, std::nullopt },
97 QmltcLoggingCategoryOverride{ qmlUnresolvedAlias, Error },
98 QmltcLoggingCategoryOverride{ qmlUnresolvedType, Error },
99 QmltcLoggingCategoryOverride{ qmlUnusedImports, std::nullopt },
100 QmltcLoggingCategoryOverride{ qmlUseProperFunction, std::nullopt },
101 QmltcLoggingCategoryOverride{ qmlVarUsedBeforeDeclaration, std::nullopt },
102 QmltcLoggingCategoryOverride{ qmlVoid, std::nullopt },
103 QmltcLoggingCategoryOverride{ qmlWith, std::nullopt },
104 };
105
106 return overrides;
107}
108
109static QString uniqueNameFromPieces(const QStringList &pieces, QHash<QString, int> &repetitions)
110{
111 QString possibleName = pieces.join(u'_');
112 const int count = repetitions[possibleName]++;
113 if (count > 0)
114 possibleName.append(u"_" + QString::number(count));
115 return possibleName;
116}
117
118static bool isExplicitComponent(const QQmlJSScope::ConstPtr &type)
119{
120 if (!type->isComposite())
121 return false;
122 auto base = type->baseType();
123 return base && base->internalName() == u"QQmlComponent";
124}
125
126/*! \internal
127 Returns if type is an implicit component.
128 This method should only be called after implicit components
129 are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
130 was called.
131 */
132static bool isImplicitComponent(const QQmlJSScope::ConstPtr &type)
133{
134 // root components and inline components are explicitly components.
135 if (!type->isComposite() || type->isFileRootComponent() || type->isInlineComponent())
136 return false;
137
138 const auto cppBase = QQmlJSScope::nonCompositeBaseType(type);
139 const bool isComponentBased = (cppBase && cppBase->internalName() == u"QQmlComponent");
140 return type->componentRootStatus() != QQmlJSScope::IsComponentRoot::No && !isComponentBased;
141}
142
143/*! \internal
144 Checks if type is inside or a (implicit or explicit) component.
145 This method should only be called after implicit components
146 are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
147 was called.
148 */
150{
151 Q_ASSERT(type->isComposite()); // we're dealing with composite types here
152 for (; type; type = type->parentScope()) {
154 return true;
155 }
156 }
157 return false;
158}
159
166
168{
169 // TODO: this pass is slow: we have to do exhaustive search because some C++
170 // code could do forward declarations and they are hard to handle correctly
171 QSet<const QQmlJSScope *> visitedTypes; // we can still improve by walking all types only once
172 const auto visitType = [&visitedTypes](const QQmlJSScope::ConstPtr &type) -> bool {
174 return true;
176 return false;
177 };
178 const auto addCppInclude = [this](const QQmlJSScope::ConstPtr &type) {
181 };
182
183 const auto findInType = [&](const QQmlJSScope::ConstPtr &type) {
184 if (!type)
185 return;
186 if (visitType(type)) // optimization - don't call nonCompositeBaseType() needlessly
187 return;
188
189 // look in type
191
192 if (type->isListProperty())
194
195 // look in type's base type
196 auto base = type->baseType();
197 if (!base && type->isComposite())
198 // in this case, qqmljsimportvisitor would have already print an error message
199 // about the missing type, so just return silently without crashing
200 return;
201 if (!base || visitType(base))
202 return;
204 };
205
208 return QString();
209 Q_ASSERT(publicInclude.endsWith(u".h"_s) || publicInclude.endsWith(u".hpp"_s));
213 // check if "public" include is in fact already private
214 if (publicInclude.startsWith(u"private"))
215 return includeWithoutExtension + u"_p" + extension;
216 return u"private/" + includeWithoutExtension + u"_p" + extension;
217 };
218
219 // walk the whole type hierarchy
222 while (!types.isEmpty()) {
223 auto type = types.pop();
224 Q_ASSERT(type);
225
226 const auto scopeType = type->scopeType();
230 continue;
231 }
232
233 for (auto t = type; !type->isArrayScope() && t; t = t->baseType()) {
234 findInType(t);
235
236 // look in properties
237 const auto properties = t->ownProperties();
238 for (const QQmlJSMetaProperty &p : properties) {
239 findInType(p.type());
240
241 if (p.isPrivate()) {
244 if (!privateInclude.isEmpty())
246 }
247 }
248
249 // look in methods
250 const auto methods = t->ownMethods();
251 for (const QQmlJSMetaMethod &m : methods) {
253
254 const auto parameters = m.parameters();
255 for (const auto &param : parameters)
257 }
258 }
259
261 }
262
263 // remove own include
265}
266
267static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope)
268{
269 Q_ASSERT(scope->scopeType() == QQmlSA::ScopeType::QMLScope);
270 Q_ASSERT(!scope->isArrayScope());
271 Q_ASSERT(!scope->baseTypeName().isEmpty());
272 // the scope is guaranteed to be a new QML type, so any prefixes (separated
273 // by dots) should be import namespaces
274 const std::optional<QString> &inlineComponentName = scope->inlineComponentName();
275 QString name = inlineComponentName ? *inlineComponentName : scope->baseTypeName();
276 names->append(name.replace(u'.', u'_'));
277}
278
280{
282 return false;
283
284 // we're not interested in non-QML scopes
286 return true;
287
291 }
292
293 if (m_currentScope != m_exportedRootScope) // not document root
295 // give C++-relevant internal names to QMLScopes, we can use them later in compiler
298
299 return true;
300}
301
308
310{
312 return false;
313
314 if (m_currentScope != m_exportedRootScope) // not document root
316 // give C++-relevant internal names to QMLScopes, we can use them later in compiler
318
320 return true;
321}
322
328
330{
332 return false;
333
334 // augment property: set its write/read/etc. methods
336 const auto name = publicMember->name.toString();
337
342 if (!property.isAlias()) { // aliases are covered separately
344 if (property.read().isEmpty())
346 if (!property.isList()) {
349 // Note: prefer BINDABLE to NOTIFY
350 if (property.bindable().isEmpty())
352 }
354 }
355
357 // also check that notify is already a method of the scope
358 {
361 if (methods.size() != 1) {
362 const QString errorString =
363 methods.isEmpty() ? u"no signal"_s : u"too many signals"_s;
364 m_logger->log(
365 u"internal error: %1 found for property '%2'"_s.arg(errorString, name),
367 return false;
368 } else if (methods[0].methodType() != QQmlJSMetaMethodType::Signal) {
369 m_logger->log(u"internal error: method %1 of property %2 must be a signal"_s.arg(
372 return false;
373 }
374 }
375 }
376
377 return true;
378}
379
381{
383 return false;
384
385 {
386 const auto id = scriptBinding->qualifiedId;
387 if (!id->next && id->name == QLatin1String("id"))
388 m_typesWithId[m_currentScope] = -1; // temporary value
389 }
390
391 return true;
392}
393
395{
397 return false;
398 return true;
399}
400
402{
404 if (!rootScopeIsValid()) // in case we failed badly
405 return;
406
408
409 // Yes, we want absolutely all bindings in the document.
410 // Not only the ones immediately assigned to QML types.
413
415 setupAliases();
416
417 if (m_mode != Mode::Compile)
418 return;
419
421
423 for (const QQmlJSScope::ConstPtr &type : qmlTypes)
425}
426
428{
430 m_seenCustomParsers = true;
431 return false;
432}
433
439
440QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
441{
442 switch (binding.bindingType()) {
443 case QQmlSA::BindingType::Object:
444 return binding.objectType();
445 case QQmlSA::BindingType::Interceptor:
446 return binding.interceptorType();
447 case QQmlSA::BindingType::ValueSource:
448 return binding.valueSourceType();
449 case QQmlSA::BindingType::AttachedProperty:
450 return binding.attachedType();
451 case QQmlSA::BindingType::GroupedProperty:
452 return binding.groupType();
453 default:
454 return {};
455 }
456 Q_UNREACHABLE_RETURN({});
457}
458
459template<typename TypePredicate, typename BindingPredicate>
461 const QQmlJSScope::ConstPtr &root,
464{
465 // NB: depth-first-search is used here to mimic various QmlIR passes
466 QStack<QQmlJSScope::ConstPtr> types;
467 types.push(root);
468 while (!types.isEmpty()) {
469 auto current = types.pop();
470
471 if (typePredicate(current))
472 continue;
473
474 Q_ASSERT(qmlIrOrderedBindings.contains(current));
475 const auto &bindings = qmlIrOrderedBindings[current];
476 // reverse the binding order here, because stack processes right-most
477 // child first and we need left-most first
478 for (auto it = bindings.rbegin(); it != bindings.rend(); ++it) {
479 const auto &binding = *it;
480
481 if (bindingPredicate(current, binding))
482 continue;
483
484 if (auto type = fetchType(binding))
485 types.push(type);
486 }
487 }
488}
489
490template<typename Predicate>
492 const QQmlJSScope::ConstPtr &root,
495{
496 iterateBindings(root, qmlIrOrderedBindings, [predicate](const QQmlJSScope::ConstPtr &current) {
497 return predicate(current) || isOrUnderComponent(current);
498 }, [](const QQmlJSScope::ConstPtr &, const QQmlJSMetaPropertyBinding &) {
499 return false;
500 });
501}
502
503/*! \internal
504 This is a special function that must be called after
505 QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiProgram *). It is used to
506 resolve things that couldn't be resolved during the AST traversal, such
507 as anything that is dependent on implicit or explicit components
508*/
511{
512
513 // add the document root (that is not an inline component), as we usually
514 // want to iterate on all inline components, followed by the document root
517
518 // match scopes to indices of QmlIR::Object from QmlIR::Document
519 qsizetype count = 0;
520 const auto setIndex = [&](const QQmlJSScope::Ptr &current) {
522 return;
525 ++count;
526 };
528
529 // find types that are part of the deferred bindings (we care about *types*
530 // exclusively here)
532 const auto findDeferred = [&](const QQmlJSScope::ConstPtr &type,
538
541 return true;
542 }
543 }
544 return false;
545 };
549 }
550
552 for (; type; type = type->parentScope()) {
554 return true;
555 }
556 return false;
557 };
558
559 // find all "pure" QML types
561 for (qsizetype i = 0; i < m_qmlTypes.size(); ++i) {
563
565 // root is special: we compile Component roots. root is also never
566 // deferred, so in case `isOrUnderDeferred(type)` returns true, we
567 // always continue here
568 if (type != m_exportedRootScope) {
569 // if a type is an explicit component, its id "leaks" into the
570 // document context
573 continue;
574 }
575 }
576
582 }
583
584 // update the typeCounts
585 for (const auto &inlineComponent : std::as_const(m_inlineComponentNames)) {
587 }
588
589 // add explicit components to the object creation indices
590 {
598 }
599 }
600
601 // m_qmlTypesWithQmlBases should contain the types to be compiled.
602 // Some types should not be compiled and are therefore filtered out:
603 // * deferred types
604 // * types inside of capital-c-Components (implicit and explicit)
605 // * non-composite types (that is, types not defined in qml)
606 //
607 // This can not be done earlier as implicitly wrapped Components are
608 // only known after visitation is over!
609
610 // filter step:
611 for (const auto &inlineComponent : std::as_const(m_inlineComponentNames)) {
618 [&](const QQmlJSScope::ConstPtr &type) {
619 auto base = type->baseType();
622 });
624 }
625
626 // count QmlIR::Objects in the document - the amount is used to calculate
627 // object indices of implicit components
629 const auto countQmlScopes = [&](const QQmlJSScope::ConstPtr &scope) {
630 if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope
631 return;
632 switch (scope->scopeType()) {
637 break;
638 }
639 default:
640 return;
641 }
642 return;
643 };
644 // order doesn't matter (so re-use QQmlJSUtils)
646
647 // figure synthetic indices of QQmlComponent-wrapped types
649 const auto addSyntheticIndex = [&](const QQmlJSScope::ConstPtr &type) {
650 // explicit component
653 return true;
654 }
655 // implicit component
657 const int index =
660 return true;
661 }
662 return false;
663 };
664
666 syntheticCreationIndex = 0; // reset for each inline component
669 }
670
671 // figure runtime object ids for non-component wrapped types
672 int currentId;
673 const auto setRuntimeId = [&](const QQmlJSScope::ConstPtr &type) {
674 // any type wrapped in an implicit component shouldn't be processed
675 // here. even if it has id, it doesn't need to be set by qmltc
676 if (type->isInlineComponent()) {
677 // explicit inline component
678 } else if (type->isFileRootComponent()) {
679 // explicit root component
681 // Wrapped in implicit component, assigned to unknown property, or child of scope
682 // called "QQmlComponent". We consider this an "implicit component".
683 return true;
684 }
685
688 }
689
690 return false;
691 };
692
694 currentId = 0; // reset for each inline component
696 }
697}
698
699static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedAlias &origin)
700{
701 Q_ASSERT(origin.kind != QQmlJSUtils::AliasTarget_Invalid);
702 PropertyData compiledData(*alias);
703 if (alias->read().isEmpty())
704 alias->setRead(compiledData.read);
705 if (origin.kind == QQmlJSUtils::AliasTarget_Object) // id-pointing aliases only have READ method
706 return;
707 if (origin.property.isWritable() && alias->write().isEmpty())
708 alias->setWrite(compiledData.write);
709
710 // the engine always compiles a notify for properties/aliases defined in qml code
711 // Yes, this generated notify will never be emitted.
712 if (alias->notify().isEmpty())
713 alias->setNotify(compiledData.notify);
714
715 if (!origin.property.bindable().isEmpty() && alias->bindable().isEmpty())
716 alias->setBindable(compiledData.bindable);
717}
718
719void Visitor::setupAliases()
720{
723
724 while (!types.isEmpty()) {
727
729 if (!p.isAlias())
730 continue;
731
735 m_logger->log(QStringLiteral("Cannot resolve alias \"%1\"").arg(p.propertyName()),
737 continue;
738 }
741 }
742 }
743}
744
746{
747 static const QString cppKeywords[] = {
748 u"alignas"_s,
749 u"alignof"_s,
750 u"and"_s,
751 u"and_eq"_s,
752 u"asm"_s,
753 u"atomic_cancel"_s,
754 u"atomic_commit"_s,
755 u"atomic_noexcept"_s,
756 u"auto"_s,
757 u"bitand"_s,
758 u"bitor"_s,
759 u"bool"_s,
760 u"break"_s,
761 u"case"_s,
762 u"catch"_s,
763 u"char"_s,
764 u"char16_t"_s,
765 u"char32_t"_s,
766 u"char8_t"_s,
767 u"class"_s,
768 u"co_await"_s,
769 u"co_return"_s,
770 u"co_yield"_s,
771 u"compl"_s,
772 u"concept"_s,
773 u"const"_s,
774 u"const_cast"_s,
775 u"consteval"_s,
776 u"constexpr"_s,
777 u"constinit"_s,
778 u"continue"_s,
779 u"decltype"_s,
780 u"default"_s,
781 u"delete"_s,
782 u"do"_s,
783 u"double"_s,
784 u"dynamic_cast"_s,
785 u"else"_s,
786 u"enum"_s,
787 u"explicit"_s,
788 u"export"_s,
789 u"extern"_s,
790 u"false"_s,
791 u"float"_s,
792 u"for"_s,
793 u"friend"_s,
794 u"goto"_s,
795 u"if"_s,
796 u"inline"_s,
797 u"int"_s,
798 u"long"_s,
799 u"mutable"_s,
800 u"namespace"_s,
801 u"new"_s,
802 u"noexcept"_s,
803 u"not"_s,
804 u"not_eq"_s,
805 u"nullptr"_s,
806 u"operator"_s,
807 u"or"_s,
808 u"or_eq"_s,
809 u"private"_s,
810 u"protected"_s,
811 u"public"_s,
812 u"reflexpr"_s,
813 u"register"_s,
814 u"reinterpret_cast"_s,
815 u"requires"_s,
816 u"return"_s,
817 u"short"_s,
818 u"signed"_s,
819 u"sizeof"_s,
820 u"static"_s,
821 u"static_assert"_s,
822 u"static_cast"_s,
823 u"struct"_s,
824 u"switch"_s,
825 u"synchronized"_s,
826 u"template"_s,
827 u"this"_s,
828 u"thread_local"_s,
829 u"throw"_s,
830 u"true"_s,
831 u"try"_s,
832 u"typedef"_s,
833 u"typeid"_s,
834 u"typename"_s,
835 u"union"_s,
836 u"unsigned"_s,
837 u"using"_s,
838 u"virtual"_s,
839 u"void"_s,
840 u"volatile"_s,
841 u"wchar_t"_s,
842 u"while"_s,
843 u"xor"_s,
844 u"xor_eq"_s,
845 };
847
848 const auto isReserved = [&](QStringView word) {
849 if (word.startsWith(QChar(u'_')) && word.size() >= 2
850 && (word[1].isUpper() || word[1] == QChar(u'_'))) {
851 return true; // Identifiers starting with underscore and uppercase are reserved in C++
852 }
854 };
855
856 const auto validate = [&](QStringView name, QStringView errorPrefix) {
857 if (!isReserved(name))
858 return;
859 m_logger->log(errorPrefix + u" '" + name + u"' is a reserved C++ word, consider renaming",
861 };
862
863 const auto validateType = [&type, this](const QQmlJSScope::ConstPtr &typeToCheck,
865 if (typeToCheck.isNull()) {
866 m_logger->log(
868 "Can't compile the %1 type \"%2\" to C++ because it cannot be resolved")
871 return;
872 }
873
874 if (type->moduleName().isEmpty())
875 return;
876
878 m_logger->log(
880 "Can't compile the %1 type \"%2\" to C++ because it "
881 "lives in \"%3\" instead of the current file's \"%4\" QML module.")
884 }
885 };
886
887 validateType(type->baseType(), type->baseTypeName(), u"QML base");
888
889 const auto enums = type->ownEnumerations();
890 for (auto it = enums.cbegin(); it != enums.cend(); ++it) {
891 const QQmlJSMetaEnum e = it.value();
892 validate(e.name(), u"Enumeration");
893
894 const auto enumKeys = e.keys();
895 for (const auto &key : enumKeys)
896 validate(key, u"Enumeration '%1' key"_s.arg(e.name()));
897 }
898
899 const auto properties = type->ownProperties();
900 for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
901 const QQmlJSMetaProperty &p = it.value();
902 validate(p.propertyName(), u"Property");
903
904 if (!p.isAlias() && !p.typeName().isEmpty())
905 validateType(p.type(), p.typeName(), u"QML property");
906 }
907
908 const auto methods = type->ownMethods();
909 for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
910 const QQmlJSMetaMethod &m = it.value();
911 validate(m.methodName(), u"Method");
912 if (!m.returnTypeName().isEmpty())
913 validateType(m.returnType(), m.returnTypeName(), u"QML method return");
914
915 for (const auto &parameter : m.parameters()) {
916 validate(parameter.name(), u"Method '%1' parameter"_s.arg(m.methodName()));
917 if (!parameter.typeName().isEmpty())
918 validateType(parameter.type(), parameter.typeName(), u"QML parameter");
919 }
920 }
921
922 // TODO: one could also test signal handlers' parameters but we do not store
923 // this information in QQmlJSMetaPropertyBinding currently
924}
925
926/*! \internal
927 * Returns the file path for the C++ header of \a scope or the header created
928 * by qmltc for it and its inline components.
929 */
931{
932 const QString filePath = scope->filePath();
933 if (!filePath.endsWith(u".qml")) // assume the correct path is set
934 return scope->filePath();
935
940 [](const QString &x) { return x.endsWith(u".h"_s); });
941 if (firstHeader == paths.cend()) {
942 const QString matchedPaths = paths.isEmpty() ? u"<none>"_s : paths.join(u", ");
944 "Failed to find a header file name for path %s. Paths checked:\n%s",
946 return QString();
947 }
948 // NB: get the file name to avoid prefixes
949 return QFileInfo(*firstHeader).fileName();
950}
951
953{
955 if (const QString *srcDirPath = std::get_if<QString>(&result))
956 return *srcDirPath;
957
961 // return input as a fallback
962 return path;
963}
964
965} // namespace QQmltc
966
967QT_END_NAMESPACE
static bool isImplicitComponent(const QQmlJSScope::ConstPtr &type)
void iterateBindings(const QQmlJSScope::ConstPtr &root, const QHash< QQmlJSScope::ConstPtr, QList< QQmlJSMetaPropertyBinding > > &qmlIrOrderedBindings, TypePredicate typePredicate, BindingPredicate bindingPredicate)
QSpan< const QmltcLoggingCategoryOverride > categoryOverrides()
static bool isOrUnderComponent(QQmlJSScope::ConstPtr type)
static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedAlias &origin)
static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope)
void iterateTypes(const QQmlJSScope::ConstPtr &root, const QHash< QQmlJSScope::ConstPtr, QList< QQmlJSMetaPropertyBinding > > &qmlIrOrderedBindings, Predicate predicate)
QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
static bool isExplicitComponent(const QQmlJSScope::ConstPtr &type)
static QString uniqueNameFromPieces(const QStringList &pieces, QHash< QString, int > &repetitions)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")