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
432QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
433{
434 switch (binding.bindingType()) {
435 case QQmlSA::BindingType::Object:
436 return binding.objectType();
437 case QQmlSA::BindingType::Interceptor:
438 return binding.interceptorType();
439 case QQmlSA::BindingType::ValueSource:
440 return binding.valueSourceType();
441 case QQmlSA::BindingType::AttachedProperty:
442 return binding.attachedType();
443 case QQmlSA::BindingType::GroupProperty:
444 return binding.groupType();
445 default:
446 return {};
447 }
448 Q_UNREACHABLE_RETURN({});
449}
450
451template<typename TypePredicate, typename BindingPredicate>
453 const QQmlJSScope::ConstPtr &root,
456{
457 // NB: depth-first-search is used here to mimic various QmlIR passes
458 QStack<QQmlJSScope::ConstPtr> types;
459 types.push(root);
460 while (!types.isEmpty()) {
461 auto current = types.pop();
462
463 if (typePredicate(current))
464 continue;
465
466 Q_ASSERT(qmlIrOrderedBindings.contains(current));
467 const auto &bindings = qmlIrOrderedBindings[current];
468 // reverse the binding order here, because stack processes right-most
469 // child first and we need left-most first
470 for (auto it = bindings.rbegin(); it != bindings.rend(); ++it) {
471 const auto &binding = *it;
472
473 if (bindingPredicate(current, binding))
474 continue;
475
476 if (auto type = fetchType(binding))
477 types.push(type);
478 }
479 }
480}
481
482template<typename Predicate>
484 const QQmlJSScope::ConstPtr &root,
487{
488 iterateBindings(root, qmlIrOrderedBindings, [predicate](const QQmlJSScope::ConstPtr &current) {
489 return predicate(current) || isOrUnderComponent(current);
490 }, [](const QQmlJSScope::ConstPtr &, const QQmlJSMetaPropertyBinding &) {
491 return false;
492 });
493}
494
495/*! \internal
496 This is a special function that must be called after
497 QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiProgram *). It is used to
498 resolve things that couldn't be resolved during the AST traversal, such
499 as anything that is dependent on implicit or explicit components
500*/
503{
504
505 // add the document root (that is not an inline component), as we usually
506 // want to iterate on all inline components, followed by the document root
509
510 // match scopes to indices of QmlIR::Object from QmlIR::Document
511 qsizetype count = 0;
512 const auto setIndex = [&](const QQmlJSScope::Ptr &current) {
514 return;
517 ++count;
518 };
520
521 // find types that are part of the deferred bindings (we care about *types*
522 // exclusively here)
524 const auto findDeferred = [&](const QQmlJSScope::ConstPtr &type,
530
533 return true;
534 }
535 }
536 return false;
537 };
541 }
542
544 for (; type; type = type->parentScope()) {
546 return true;
547 }
548 return false;
549 };
550
551 // find all "pure" QML types
553 for (qsizetype i = 0; i < m_qmlTypes.size(); ++i) {
555
557 // root is special: we compile Component roots. root is also never
558 // deferred, so in case `isOrUnderDeferred(type)` returns true, we
559 // always continue here
560 if (type != m_exportedRootScope) {
561 // if a type is an explicit component, its id "leaks" into the
562 // document context
565 continue;
566 }
567 }
568
574 }
575
576 // update the typeCounts
577 for (const auto &inlineComponent : std::as_const(m_inlineComponentNames)) {
579 }
580
581 // add explicit components to the object creation indices
582 {
590 }
591 }
592
593 // m_qmlTypesWithQmlBases should contain the types to be compiled.
594 // Some types should not be compiled and are therefore filtered out:
595 // * deferred types
596 // * types inside of capital-c-Components (implicit and explicit)
597 // * non-composite types (that is, types not defined in qml)
598 //
599 // This can not be done earlier as implicitly wrapped Components are
600 // only known after visitation is over!
601
602 // filter step:
603 for (const auto &inlineComponent : std::as_const(m_inlineComponentNames)) {
610 [&](const QQmlJSScope::ConstPtr &type) {
611 auto base = type->baseType();
614 });
616 }
617
618 // count QmlIR::Objects in the document - the amount is used to calculate
619 // object indices of implicit components
621 const auto countQmlScopes = [&](const QQmlJSScope::ConstPtr &scope) {
622 if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope
623 return;
624 switch (scope->scopeType()) {
629 break;
630 }
631 default:
632 return;
633 }
634 return;
635 };
636 // order doesn't matter (so re-use QQmlJSUtils)
638
639 // figure synthetic indices of QQmlComponent-wrapped types
641 const auto addSyntheticIndex = [&](const QQmlJSScope::ConstPtr &type) {
642 // explicit component
645 return true;
646 }
647 // implicit component
649 const int index =
652 return true;
653 }
654 return false;
655 };
656
658 syntheticCreationIndex = 0; // reset for each inline component
661 }
662
663 // figure runtime object ids for non-component wrapped types
664 int currentId;
665 const auto setRuntimeId = [&](const QQmlJSScope::ConstPtr &type) {
666 // any type wrapped in an implicit component shouldn't be processed
667 // here. even if it has id, it doesn't need to be set by qmltc
668 if (type->isInlineComponent()) {
669 // explicit inline component
670 } else if (type->isFileRootComponent()) {
671 // explicit root component
673 // Wrapped in implicit component, assigned to unknown property, or child of scope
674 // called "QQmlComponent". We consider this an "implicit component".
675 return true;
676 }
677
680 }
681
682 return false;
683 };
684
686 currentId = 0; // reset for each inline component
688 }
689}
690
691static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedAlias &origin)
692{
693 Q_ASSERT(origin.kind != QQmlJSUtils::AliasTarget_Invalid);
694 PropertyData compiledData(*alias);
695 if (alias->read().isEmpty())
696 alias->setRead(compiledData.read);
697 if (origin.kind == QQmlJSUtils::AliasTarget_Object) // id-pointing aliases only have READ method
698 return;
699 if (origin.property.isWritable() && alias->write().isEmpty())
700 alias->setWrite(compiledData.write);
701
702 // the engine always compiles a notify for properties/aliases defined in qml code
703 // Yes, this generated notify will never be emitted.
704 if (alias->notify().isEmpty())
705 alias->setNotify(compiledData.notify);
706
707 if (!origin.property.bindable().isEmpty() && alias->bindable().isEmpty())
708 alias->setBindable(compiledData.bindable);
709}
710
711void Visitor::setupAliases()
712{
715
716 while (!types.isEmpty()) {
719
721 if (!p.isAlias())
722 continue;
723
727 m_logger->log(QStringLiteral("Cannot resolve alias \"%1\"").arg(p.propertyName()),
729 continue;
730 }
733 }
734 }
735}
736
738{
739 static const QString cppKeywords[] = {
740 u"alignas"_s,
741 u"alignof"_s,
742 u"and"_s,
743 u"and_eq"_s,
744 u"asm"_s,
745 u"atomic_cancel"_s,
746 u"atomic_commit"_s,
747 u"atomic_noexcept"_s,
748 u"auto"_s,
749 u"bitand"_s,
750 u"bitor"_s,
751 u"bool"_s,
752 u"break"_s,
753 u"case"_s,
754 u"catch"_s,
755 u"char"_s,
756 u"char16_t"_s,
757 u"char32_t"_s,
758 u"char8_t"_s,
759 u"class"_s,
760 u"co_await"_s,
761 u"co_return"_s,
762 u"co_yield"_s,
763 u"compl"_s,
764 u"concept"_s,
765 u"const"_s,
766 u"const_cast"_s,
767 u"consteval"_s,
768 u"constexpr"_s,
769 u"constinit"_s,
770 u"continue"_s,
771 u"decltype"_s,
772 u"default"_s,
773 u"delete"_s,
774 u"do"_s,
775 u"double"_s,
776 u"dynamic_cast"_s,
777 u"else"_s,
778 u"enum"_s,
779 u"explicit"_s,
780 u"export"_s,
781 u"extern"_s,
782 u"false"_s,
783 u"float"_s,
784 u"for"_s,
785 u"friend"_s,
786 u"goto"_s,
787 u"if"_s,
788 u"inline"_s,
789 u"int"_s,
790 u"long"_s,
791 u"mutable"_s,
792 u"namespace"_s,
793 u"new"_s,
794 u"noexcept"_s,
795 u"not"_s,
796 u"not_eq"_s,
797 u"nullptr"_s,
798 u"operator"_s,
799 u"or"_s,
800 u"or_eq"_s,
801 u"private"_s,
802 u"protected"_s,
803 u"public"_s,
804 u"reflexpr"_s,
805 u"register"_s,
806 u"reinterpret_cast"_s,
807 u"requires"_s,
808 u"return"_s,
809 u"short"_s,
810 u"signed"_s,
811 u"sizeof"_s,
812 u"static"_s,
813 u"static_assert"_s,
814 u"static_cast"_s,
815 u"struct"_s,
816 u"switch"_s,
817 u"synchronized"_s,
818 u"template"_s,
819 u"this"_s,
820 u"thread_local"_s,
821 u"throw"_s,
822 u"true"_s,
823 u"try"_s,
824 u"typedef"_s,
825 u"typeid"_s,
826 u"typename"_s,
827 u"union"_s,
828 u"unsigned"_s,
829 u"using"_s,
830 u"virtual"_s,
831 u"void"_s,
832 u"volatile"_s,
833 u"wchar_t"_s,
834 u"while"_s,
835 u"xor"_s,
836 u"xor_eq"_s,
837 };
839
840 const auto isReserved = [&](QStringView word) {
841 if (word.startsWith(QChar(u'_')) && word.size() >= 2
842 && (word[1].isUpper() || word[1] == QChar(u'_'))) {
843 return true; // Identifiers starting with underscore and uppercase are reserved in C++
844 }
846 };
847
848 const auto validate = [&](QStringView name, QStringView errorPrefix) {
849 if (!isReserved(name))
850 return;
851 m_logger->log(errorPrefix + u" '" + name + u"' is a reserved C++ word, consider renaming",
853 };
854
855 const auto validateType = [&type, this](const QQmlJSScope::ConstPtr &typeToCheck,
857 if (typeToCheck.isNull()) {
858 m_logger->log(
860 "Can't compile the %1 type \"%2\" to C++ because it cannot be resolved")
863 return;
864 }
865
866 if (type->moduleName().isEmpty())
867 return;
868
870 m_logger->log(
872 "Can't compile the %1 type \"%2\" to C++ because it "
873 "lives in \"%3\" instead of the current file's \"%4\" QML module.")
876 }
877 };
878
879 validateType(type->baseType(), type->baseTypeName(), u"QML base");
880
881 const auto enums = type->ownEnumerations();
882 for (auto it = enums.cbegin(); it != enums.cend(); ++it) {
883 const QQmlJSMetaEnum e = it.value();
884 validate(e.name(), u"Enumeration");
885
886 const auto enumKeys = e.keys();
887 for (const auto &key : enumKeys)
888 validate(key, u"Enumeration '%1' key"_s.arg(e.name()));
889 }
890
891 const auto properties = type->ownProperties();
892 for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
893 const QQmlJSMetaProperty &p = it.value();
894 validate(p.propertyName(), u"Property");
895
896 if (!p.isAlias() && !p.typeName().isEmpty())
897 validateType(p.type(), p.typeName(), u"QML property");
898 }
899
900 const auto methods = type->ownMethods();
901 for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
902 const QQmlJSMetaMethod &m = it.value();
903 validate(m.methodName(), u"Method");
904 if (!m.returnTypeName().isEmpty())
905 validateType(m.returnType(), m.returnTypeName(), u"QML method return");
906
907 for (const auto &parameter : m.parameters()) {
908 validate(parameter.name(), u"Method '%1' parameter"_s.arg(m.methodName()));
909 if (!parameter.typeName().isEmpty())
910 validateType(parameter.type(), parameter.typeName(), u"QML parameter");
911 }
912 }
913
914 // TODO: one could also test signal handlers' parameters but we do not store
915 // this information in QQmlJSMetaPropertyBinding currently
916}
917
918/*! \internal
919 * Returns the file path for the C++ header of \a scope or the header created
920 * by qmltc for it and its inline components.
921 */
923{
924 const QString filePath = scope->filePath();
925 if (!filePath.endsWith(u".qml")) // assume the correct path is set
926 return scope->filePath();
927
932 [](const QString &x) { return x.endsWith(u".h"_s); });
933 if (firstHeader == paths.cend()) {
934 const QString matchedPaths = paths.isEmpty() ? u"<none>"_s : paths.join(u", ");
936 "Failed to find a header file name for path %s. Paths checked:\n%s",
938 return QString();
939 }
940 // NB: get the file name to avoid prefixes
941 return QFileInfo(*firstHeader).fileName();
942}
943
945{
947 if (const QString *srcDirPath = std::get_if<QString>(&result))
948 return *srcDirPath;
949
953 // return input as a fallback
954 return path;
955}
956
957} // namespace QQmltc
958
959QT_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")