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{ qmlMissingEnumEntry, std::nullopt },
69 QmltcLoggingCategoryOverride{ qmlMissingProperty, Error },
70 QmltcLoggingCategoryOverride{ qmlMissingType, Error },
71 QmltcLoggingCategoryOverride{ qmlMultilineStrings, std::nullopt },
72 QmltcLoggingCategoryOverride{ qmlNonListProperty, Error },
73 QmltcLoggingCategoryOverride{ qmlNonRootEnums, std::nullopt },
74 QmltcLoggingCategoryOverride{ qmlPropertyOverride, std::nullopt },
75 QmltcLoggingCategoryOverride{ qmlUnterminatedCase, std::nullopt },
76 QmltcLoggingCategoryOverride{ qmlPreferNonVarProperties, std::nullopt },
77 QmltcLoggingCategoryOverride{ qmlPrefixedImportType, std::nullopt },
78 QmltcLoggingCategoryOverride{ qmlReadOnlyProperty, Error },
79 QmltcLoggingCategoryOverride{ qmlRecursionDepthErrors, Error },
80 QmltcLoggingCategoryOverride{ qmlRedundantOptionalChaining, std::nullopt },
81 QmltcLoggingCategoryOverride{ qmlRenamedType, std::nullopt },
82 QmltcLoggingCategoryOverride{ qmlRequired, Error },
83 QmltcLoggingCategoryOverride{ qmlShadow, std::nullopt },
84 QmltcLoggingCategoryOverride{ qmlSignalParameters, Error },
85 QmltcLoggingCategoryOverride{ qmlStalePropertyRead, std::nullopt },
86 QmltcLoggingCategoryOverride{ qmlSyntax, std::nullopt },
87 QmltcLoggingCategoryOverride{ qmlSyntaxDuplicateIds, Error },
88 QmltcLoggingCategoryOverride{ qmlSyntaxIdQuotation, std::nullopt },
89 QmltcLoggingCategoryOverride{ qmlTypeInstantiatedRecursively, Error },
90 QmltcLoggingCategoryOverride{ qmlTopLevelComponent, Error },
91 QmltcLoggingCategoryOverride{ qmlUncreatableType, Error },
92 QmltcLoggingCategoryOverride{ qmlUnintentionalEmptyBlock, std::nullopt },
93 QmltcLoggingCategoryOverride{ qmlUnqualified, std::nullopt },
94 QmltcLoggingCategoryOverride{ qmlUnreachableCode, std::nullopt },
95 QmltcLoggingCategoryOverride{ qmlUnresolvedAlias, Error },
96 QmltcLoggingCategoryOverride{ qmlUnresolvedType, Error },
97 QmltcLoggingCategoryOverride{ qmlUnusedImports, std::nullopt },
98 QmltcLoggingCategoryOverride{ qmlUseProperFunction, std::nullopt },
99 QmltcLoggingCategoryOverride{ qmlVarUsedBeforeDeclaration, std::nullopt },
100 QmltcLoggingCategoryOverride{ qmlVoid, std::nullopt },
101 QmltcLoggingCategoryOverride{ qmlWith, std::nullopt },
102 };
103
104 return overrides;
105}
106
107static QString uniqueNameFromPieces(const QStringList &pieces, QHash<QString, int> &repetitions)
108{
109 QString possibleName = pieces.join(u'_');
110 const int count = repetitions[possibleName]++;
111 if (count > 0)
112 possibleName.append(u"_" + QString::number(count));
113 return possibleName;
114}
115
116static bool isExplicitComponent(const QQmlJSScope::ConstPtr &type)
117{
118 if (!type->isComposite())
119 return false;
120 auto base = type->baseType();
121 return base && base->internalName() == u"QQmlComponent";
122}
123
124/*! \internal
125 Returns if type is an implicit component.
126 This method should only be called after implicit components
127 are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
128 was called.
129 */
130static bool isImplicitComponent(const QQmlJSScope::ConstPtr &type)
131{
132 // root components and inline components are explicitly components.
133 if (!type->isComposite() || type->isFileRootComponent() || type->isInlineComponent())
134 return false;
135
136 const auto cppBase = QQmlJSScope::nonCompositeBaseType(type);
137 const bool isComponentBased = (cppBase && cppBase->internalName() == u"QQmlComponent");
138 return type->componentRootStatus() != QQmlJSScope::IsComponentRoot::No && !isComponentBased;
139}
140
141/*! \internal
142 Checks if type is inside or a (implicit or explicit) component.
143 This method should only be called after implicit components
144 are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
145 was called.
146 */
148{
149 Q_ASSERT(type->isComposite()); // we're dealing with composite types here
150 for (; type; type = type->parentScope()) {
152 return true;
153 }
154 }
155 return false;
156}
157
164
166{
167 // TODO: this pass is slow: we have to do exhaustive search because some C++
168 // code could do forward declarations and they are hard to handle correctly
169 QSet<const QQmlJSScope *> visitedTypes; // we can still improve by walking all types only once
170 const auto visitType = [&visitedTypes](const QQmlJSScope::ConstPtr &type) -> bool {
172 return true;
174 return false;
175 };
176 const auto addCppInclude = [this](const QQmlJSScope::ConstPtr &type) {
179 };
180
181 const auto findInType = [&](const QQmlJSScope::ConstPtr &type) {
182 if (!type)
183 return;
184 if (visitType(type)) // optimization - don't call nonCompositeBaseType() needlessly
185 return;
186
187 // look in type
189
190 if (type->isListProperty())
192
193 // look in type's base type
194 auto base = type->baseType();
195 if (!base && type->isComposite())
196 // in this case, qqmljsimportvisitor would have already print an error message
197 // about the missing type, so just return silently without crashing
198 return;
199 if (!base || visitType(base))
200 return;
202 };
203
206 return QString();
207 Q_ASSERT(publicInclude.endsWith(u".h"_s) || publicInclude.endsWith(u".hpp"_s));
211 // check if "public" include is in fact already private
212 if (publicInclude.startsWith(u"private"))
213 return includeWithoutExtension + u"_p" + extension;
214 return u"private/" + includeWithoutExtension + u"_p" + extension;
215 };
216
217 // walk the whole type hierarchy
220 while (!types.isEmpty()) {
221 auto type = types.pop();
222 Q_ASSERT(type);
223
224 const auto scopeType = type->scopeType();
228 continue;
229 }
230
231 for (auto t = type; !type->isArrayScope() && t; t = t->baseType()) {
232 findInType(t);
233
234 // look in properties
235 const auto properties = t->ownProperties();
236 for (const QQmlJSMetaProperty &p : properties) {
237 findInType(p.type());
238
239 if (p.isPrivate()) {
242 if (!privateInclude.isEmpty())
244 }
245 }
246
247 // look in methods
248 const auto methods = t->ownMethods();
249 for (const QQmlJSMetaMethod &m : methods) {
251
252 const auto parameters = m.parameters();
253 for (const auto &param : parameters)
255 }
256 }
257
259 }
260
261 // remove own include
263}
264
265static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope)
266{
267 Q_ASSERT(scope->scopeType() == QQmlSA::ScopeType::QMLScope);
268 Q_ASSERT(!scope->isArrayScope());
269 Q_ASSERT(!scope->baseTypeName().isEmpty());
270 // the scope is guaranteed to be a new QML type, so any prefixes (separated
271 // by dots) should be import namespaces
272 const std::optional<QString> &inlineComponentName = scope->inlineComponentName();
273 QString name = inlineComponentName ? *inlineComponentName : scope->baseTypeName();
274 names->append(name.replace(u'.', u'_'));
275}
276
278{
280 return false;
281
282 // we're not interested in non-QML scopes
284 return true;
285
289 }
290
291 if (m_currentScope != m_exportedRootScope) // not document root
293 // give C++-relevant internal names to QMLScopes, we can use them later in compiler
296
297 return true;
298}
299
306
308{
310 return false;
311
312 if (m_currentScope != m_exportedRootScope) // not document root
314 // give C++-relevant internal names to QMLScopes, we can use them later in compiler
316
318 return true;
319}
320
326
328{
330 return false;
331
332 // augment property: set its write/read/etc. methods
334 const auto name = publicMember->name.toString();
335
340 if (!property.isAlias()) { // aliases are covered separately
342 if (property.read().isEmpty())
344 if (!property.isList()) {
347 // Note: prefer BINDABLE to NOTIFY
348 if (property.bindable().isEmpty())
350 }
352 }
353
355 // also check that notify is already a method of the scope
356 {
359 if (methods.size() != 1) {
360 const QString errorString =
361 methods.isEmpty() ? u"no signal"_s : u"too many signals"_s;
362 m_logger->log(
363 u"internal error: %1 found for property '%2'"_s.arg(errorString, name),
365 return false;
366 } else if (methods[0].methodType() != QQmlJSMetaMethodType::Signal) {
367 m_logger->log(u"internal error: method %1 of property %2 must be a signal"_s.arg(
370 return false;
371 }
372 }
373 }
374
375 return true;
376}
377
379{
381 return false;
382
383 {
384 const auto id = scriptBinding->qualifiedId;
385 if (!id->next && id->name == QLatin1String("id"))
386 m_typesWithId[m_currentScope] = -1; // temporary value
387 }
388
389 return true;
390}
391
393{
395 return false;
396 return true;
397}
398
400{
402 if (!rootScopeIsValid()) // in case we failed badly
403 return;
404
406
407 // Yes, we want absolutely all bindings in the document.
408 // Not only the ones immediately assigned to QML types.
411
413 setupAliases();
414
415 if (m_mode != Mode::Compile)
416 return;
417
419
421 for (const QQmlJSScope::ConstPtr &type : qmlTypes)
423}
424
426{
428 m_seenCustomParsers = true;
429 return false;
430}
431
437
438QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
439{
440 switch (binding.bindingType()) {
441 case QQmlSA::BindingType::Object:
442 return binding.objectType();
443 case QQmlSA::BindingType::Interceptor:
444 return binding.interceptorType();
445 case QQmlSA::BindingType::ValueSource:
446 return binding.valueSourceType();
447 case QQmlSA::BindingType::AttachedProperty:
448 return binding.attachedType();
449 case QQmlSA::BindingType::GroupProperty:
450 return binding.groupType();
451 default:
452 return {};
453 }
454 Q_UNREACHABLE_RETURN({});
455}
456
457template<typename TypePredicate, typename BindingPredicate>
459 const QQmlJSScope::ConstPtr &root,
462{
463 // NB: depth-first-search is used here to mimic various QmlIR passes
464 QStack<QQmlJSScope::ConstPtr> types;
465 types.push(root);
466 while (!types.isEmpty()) {
467 auto current = types.pop();
468
469 if (typePredicate(current))
470 continue;
471
472 Q_ASSERT(qmlIrOrderedBindings.contains(current));
473 const auto &bindings = qmlIrOrderedBindings[current];
474 // reverse the binding order here, because stack processes right-most
475 // child first and we need left-most first
476 for (auto it = bindings.rbegin(); it != bindings.rend(); ++it) {
477 const auto &binding = *it;
478
479 if (bindingPredicate(current, binding))
480 continue;
481
482 if (auto type = fetchType(binding))
483 types.push(type);
484 }
485 }
486}
487
488template<typename Predicate>
490 const QQmlJSScope::ConstPtr &root,
493{
494 iterateBindings(root, qmlIrOrderedBindings, [predicate](const QQmlJSScope::ConstPtr &current) {
495 return predicate(current) || isOrUnderComponent(current);
496 }, [](const QQmlJSScope::ConstPtr &, const QQmlJSMetaPropertyBinding &) {
497 return false;
498 });
499}
500
501/*! \internal
502 This is a special function that must be called after
503 QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiProgram *). It is used to
504 resolve things that couldn't be resolved during the AST traversal, such
505 as anything that is dependent on implicit or explicit components
506*/
509{
510
511 // add the document root (that is not an inline component), as we usually
512 // want to iterate on all inline components, followed by the document root
515
516 // match scopes to indices of QmlIR::Object from QmlIR::Document
517 qsizetype count = 0;
518 const auto setIndex = [&](const QQmlJSScope::Ptr &current) {
520 return;
523 ++count;
524 };
526
527 // find types that are part of the deferred bindings (we care about *types*
528 // exclusively here)
530 const auto findDeferred = [&](const QQmlJSScope::ConstPtr &type,
536
539 return true;
540 }
541 }
542 return false;
543 };
547 }
548
550 for (; type; type = type->parentScope()) {
552 return true;
553 }
554 return false;
555 };
556
557 // find all "pure" QML types
559 for (qsizetype i = 0; i < m_qmlTypes.size(); ++i) {
561
563 // root is special: we compile Component roots. root is also never
564 // deferred, so in case `isOrUnderDeferred(type)` returns true, we
565 // always continue here
566 if (type != m_exportedRootScope) {
567 // if a type is an explicit component, its id "leaks" into the
568 // document context
571 continue;
572 }
573 }
574
580 }
581
582 // update the typeCounts
583 for (const auto &inlineComponent : std::as_const(m_inlineComponentNames)) {
585 }
586
587 // add explicit components to the object creation indices
588 {
596 }
597 }
598
599 // m_qmlTypesWithQmlBases should contain the types to be compiled.
600 // Some types should not be compiled and are therefore filtered out:
601 // * deferred types
602 // * types inside of capital-c-Components (implicit and explicit)
603 // * non-composite types (that is, types not defined in qml)
604 //
605 // This can not be done earlier as implicitly wrapped Components are
606 // only known after visitation is over!
607
608 // filter step:
609 for (const auto &inlineComponent : std::as_const(m_inlineComponentNames)) {
616 [&](const QQmlJSScope::ConstPtr &type) {
617 auto base = type->baseType();
620 });
622 }
623
624 // count QmlIR::Objects in the document - the amount is used to calculate
625 // object indices of implicit components
627 const auto countQmlScopes = [&](const QQmlJSScope::ConstPtr &scope) {
628 if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope
629 return;
630 switch (scope->scopeType()) {
635 break;
636 }
637 default:
638 return;
639 }
640 return;
641 };
642 // order doesn't matter (so re-use QQmlJSUtils)
644
645 // figure synthetic indices of QQmlComponent-wrapped types
647 const auto addSyntheticIndex = [&](const QQmlJSScope::ConstPtr &type) {
648 // explicit component
651 return true;
652 }
653 // implicit component
655 const int index =
658 return true;
659 }
660 return false;
661 };
662
664 syntheticCreationIndex = 0; // reset for each inline component
667 }
668
669 // figure runtime object ids for non-component wrapped types
670 int currentId;
671 const auto setRuntimeId = [&](const QQmlJSScope::ConstPtr &type) {
672 // any type wrapped in an implicit component shouldn't be processed
673 // here. even if it has id, it doesn't need to be set by qmltc
674 if (type->isInlineComponent()) {
675 // explicit inline component
676 } else if (type->isFileRootComponent()) {
677 // explicit root component
679 // Wrapped in implicit component, assigned to unknown property, or child of scope
680 // called "QQmlComponent". We consider this an "implicit component".
681 return true;
682 }
683
686 }
687
688 return false;
689 };
690
692 currentId = 0; // reset for each inline component
694 }
695}
696
697static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedAlias &origin)
698{
699 Q_ASSERT(origin.kind != QQmlJSUtils::AliasTarget_Invalid);
700 PropertyData compiledData(*alias);
701 if (alias->read().isEmpty())
702 alias->setRead(compiledData.read);
703 if (origin.kind == QQmlJSUtils::AliasTarget_Object) // id-pointing aliases only have READ method
704 return;
705 if (origin.property.isWritable() && alias->write().isEmpty())
706 alias->setWrite(compiledData.write);
707
708 // the engine always compiles a notify for properties/aliases defined in qml code
709 // Yes, this generated notify will never be emitted.
710 if (alias->notify().isEmpty())
711 alias->setNotify(compiledData.notify);
712
713 if (!origin.property.bindable().isEmpty() && alias->bindable().isEmpty())
714 alias->setBindable(compiledData.bindable);
715}
716
717void Visitor::setupAliases()
718{
721
722 while (!types.isEmpty()) {
725
727 if (!p.isAlias())
728 continue;
729
733 m_logger->log(QStringLiteral("Cannot resolve alias \"%1\"").arg(p.propertyName()),
735 continue;
736 }
739 }
740 }
741}
742
744{
745 static const QString cppKeywords[] = {
746 u"alignas"_s,
747 u"alignof"_s,
748 u"and"_s,
749 u"and_eq"_s,
750 u"asm"_s,
751 u"atomic_cancel"_s,
752 u"atomic_commit"_s,
753 u"atomic_noexcept"_s,
754 u"auto"_s,
755 u"bitand"_s,
756 u"bitor"_s,
757 u"bool"_s,
758 u"break"_s,
759 u"case"_s,
760 u"catch"_s,
761 u"char"_s,
762 u"char16_t"_s,
763 u"char32_t"_s,
764 u"char8_t"_s,
765 u"class"_s,
766 u"co_await"_s,
767 u"co_return"_s,
768 u"co_yield"_s,
769 u"compl"_s,
770 u"concept"_s,
771 u"const"_s,
772 u"const_cast"_s,
773 u"consteval"_s,
774 u"constexpr"_s,
775 u"constinit"_s,
776 u"continue"_s,
777 u"decltype"_s,
778 u"default"_s,
779 u"delete"_s,
780 u"do"_s,
781 u"double"_s,
782 u"dynamic_cast"_s,
783 u"else"_s,
784 u"enum"_s,
785 u"explicit"_s,
786 u"export"_s,
787 u"extern"_s,
788 u"false"_s,
789 u"float"_s,
790 u"for"_s,
791 u"friend"_s,
792 u"goto"_s,
793 u"if"_s,
794 u"inline"_s,
795 u"int"_s,
796 u"long"_s,
797 u"mutable"_s,
798 u"namespace"_s,
799 u"new"_s,
800 u"noexcept"_s,
801 u"not"_s,
802 u"not_eq"_s,
803 u"nullptr"_s,
804 u"operator"_s,
805 u"or"_s,
806 u"or_eq"_s,
807 u"private"_s,
808 u"protected"_s,
809 u"public"_s,
810 u"reflexpr"_s,
811 u"register"_s,
812 u"reinterpret_cast"_s,
813 u"requires"_s,
814 u"return"_s,
815 u"short"_s,
816 u"signed"_s,
817 u"sizeof"_s,
818 u"static"_s,
819 u"static_assert"_s,
820 u"static_cast"_s,
821 u"struct"_s,
822 u"switch"_s,
823 u"synchronized"_s,
824 u"template"_s,
825 u"this"_s,
826 u"thread_local"_s,
827 u"throw"_s,
828 u"true"_s,
829 u"try"_s,
830 u"typedef"_s,
831 u"typeid"_s,
832 u"typename"_s,
833 u"union"_s,
834 u"unsigned"_s,
835 u"using"_s,
836 u"virtual"_s,
837 u"void"_s,
838 u"volatile"_s,
839 u"wchar_t"_s,
840 u"while"_s,
841 u"xor"_s,
842 u"xor_eq"_s,
843 };
845
846 const auto isReserved = [&](QStringView word) {
847 if (word.startsWith(QChar(u'_')) && word.size() >= 2
848 && (word[1].isUpper() || word[1] == QChar(u'_'))) {
849 return true; // Identifiers starting with underscore and uppercase are reserved in C++
850 }
852 };
853
854 const auto validate = [&](QStringView name, QStringView errorPrefix) {
855 if (!isReserved(name))
856 return;
857 m_logger->log(errorPrefix + u" '" + name + u"' is a reserved C++ word, consider renaming",
859 };
860
861 const auto validateType = [&type, this](const QQmlJSScope::ConstPtr &typeToCheck,
863 if (typeToCheck.isNull()) {
864 m_logger->log(
866 "Can't compile the %1 type \"%2\" to C++ because it cannot be resolved")
869 return;
870 }
871
872 if (type->moduleName().isEmpty())
873 return;
874
876 m_logger->log(
878 "Can't compile the %1 type \"%2\" to C++ because it "
879 "lives in \"%3\" instead of the current file's \"%4\" QML module.")
882 }
883 };
884
885 validateType(type->baseType(), type->baseTypeName(), u"QML base");
886
887 const auto enums = type->ownEnumerations();
888 for (auto it = enums.cbegin(); it != enums.cend(); ++it) {
889 const QQmlJSMetaEnum e = it.value();
890 validate(e.name(), u"Enumeration");
891
892 const auto enumKeys = e.keys();
893 for (const auto &key : enumKeys)
894 validate(key, u"Enumeration '%1' key"_s.arg(e.name()));
895 }
896
897 const auto properties = type->ownProperties();
898 for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
899 const QQmlJSMetaProperty &p = it.value();
900 validate(p.propertyName(), u"Property");
901
902 if (!p.isAlias() && !p.typeName().isEmpty())
903 validateType(p.type(), p.typeName(), u"QML property");
904 }
905
906 const auto methods = type->ownMethods();
907 for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
908 const QQmlJSMetaMethod &m = it.value();
909 validate(m.methodName(), u"Method");
910 if (!m.returnTypeName().isEmpty())
911 validateType(m.returnType(), m.returnTypeName(), u"QML method return");
912
913 for (const auto &parameter : m.parameters()) {
914 validate(parameter.name(), u"Method '%1' parameter"_s.arg(m.methodName()));
915 if (!parameter.typeName().isEmpty())
916 validateType(parameter.type(), parameter.typeName(), u"QML parameter");
917 }
918 }
919
920 // TODO: one could also test signal handlers' parameters but we do not store
921 // this information in QQmlJSMetaPropertyBinding currently
922}
923
924/*! \internal
925 * Returns the file path for the C++ header of \a scope or the header created
926 * by qmltc for it and its inline components.
927 */
929{
930 const QString filePath = scope->filePath();
931 if (!filePath.endsWith(u".qml")) // assume the correct path is set
932 return scope->filePath();
933
938 [](const QString &x) { return x.endsWith(u".h"_s); });
939 if (firstHeader == paths.cend()) {
940 const QString matchedPaths = paths.isEmpty() ? u"<none>"_s : paths.join(u", ");
942 "Failed to find a header file name for path %s. Paths checked:\n%s",
944 return QString();
945 }
946 // NB: get the file name to avoid prefixes
947 return QFileInfo(*firstHeader).fileName();
948}
949
951{
953 if (const QString *srcDirPath = std::get_if<QString>(&result))
954 return *srcDirPath;
955
959 // return input as a fallback
960 return path;
961}
962
963} // namespace QQmltc
964
965QT_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")