1// Copyright (C) 2019 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 "qqmljsscope_p.h"
5#include "qqmljstypereader_p.h"
6#include "qqmljsimporter_p.h"
7#include "qqmljsutils_p.h"
8#include "qqmlsa.h"
9#include "qqmlsa_p.h"
10
11#include <QtCore/qqueue.h>
12#include <QtCore/qsharedpointer.h>
13
14#include <private/qduplicatetracker_p.h>
15
16#include <algorithm>
17#include <type_traits>
18
19QT_BEGIN_NAMESPACE
20
21/*!
22 \class QQmlJSScope
23 \internal
24 \brief Tracks the types for the QmlCompiler
25
26 QQmlJSScope tracks the types used in qml for the QmlCompiler.
27
28 Multiple QQmlJSScope objects might be created for the same conceptual type, except when reused
29 due to extensive caching. Two QQmlJSScope objects are considered equal when they are backed
30 by the same implementation, that is, they have the same internalName.
31 The qualifiedName of the QQmlJSScope for a type imported from multiple modules will contain the
32 name of one of the modules that imported it, which is not unique and might change depending
33 on the caching in .
34*/
35
36using namespace Qt::StringLiterals;
37
38QQmlJSScope::QQmlJSScope(const QString &internalName) : QQmlJSScope{}
39{
40 m_internalName = internalName;
41}
42
43void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope)
44{
45 if (const QQmlJSScope::Ptr parent = childScope->m_parentScope.toStrongRef())
46 parent->m_childScopes.removeOne(t: childScope);
47 if (parentScope)
48 parentScope->m_childScopes.append(t: childScope);
49 childScope->m_parentScope = parentScope;
50}
51
52QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
53{
54 if (origin.isNull())
55 return QQmlJSScope::Ptr();
56 QQmlJSScope::Ptr cloned = create();
57 *cloned = *origin;
58 if (QQmlJSScope::Ptr parent = cloned->parentScope())
59 parent->m_childScopes.append(t: cloned);
60 return cloned;
61}
62
63void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
64{
65 Q_ASSERT(m_scopeType != QQmlSA::ScopeType::QMLScope);
66 if (identifier.kind == JavaScriptIdentifier::LexicalScoped
67 || identifier.kind == JavaScriptIdentifier::Injected
68 || m_scopeType == QQmlSA::ScopeType::JSFunctionScope) {
69 m_jsIdentifiers.insert(key: name, value: identifier);
70 } else {
71 auto targetScope = parentScope();
72 while (targetScope->m_scopeType != QQmlSA::ScopeType::JSFunctionScope)
73 targetScope = targetScope->parentScope();
74 targetScope->m_jsIdentifiers.insert(key: name, value: identifier);
75 }
76}
77
78void QQmlJSScope::insertPropertyIdentifier(const QQmlJSMetaProperty &property)
79{
80 addOwnProperty(prop: property);
81 QQmlJSMetaMethod method(property.propertyName() + u"Changed"_s, u"void"_s);
82 method.setMethodType(QQmlJSMetaMethodType::Signal);
83 method.setIsImplicitQmlPropertyChangeSignal(true);
84 addOwnMethod(method);
85}
86
87bool QQmlJSScope::isIdInCurrentScope(const QString &id) const
88{
89 return isIdInCurrentQmlScopes(id) || isIdInCurrentJSScopes(id);
90}
91
92bool QQmlJSScope::hasMethod(const QString &name) const
93{
94 return QQmlJSUtils::searchBaseAndExtensionTypes(
95 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
96 if (mode == QQmlJSScope::ExtensionNamespace)
97 return false;
98 return scope->m_methods.contains(key: name);
99 });
100}
101
102/*!
103 Returns all methods visible from this scope including those of
104 base types and extensions.
105
106 \note Methods that get shadowed are not included and only the
107 version visible from this scope is contained. Additionally method
108 overrides are not included either, only the first visible version
109 of any method is included.
110*/
111QHash<QString, QQmlJSMetaMethod> QQmlJSScope::methods() const
112{
113 QHash<QString, QQmlJSMetaMethod> results;
114 QQmlJSUtils::searchBaseAndExtensionTypes(
115 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
116 if (mode == QQmlJSScope::ExtensionNamespace)
117 return false;
118 for (auto it = scope->m_methods.constBegin(); it != scope->m_methods.constEnd();
119 it++) {
120 if (!results.contains(key: it.key()))
121 results.insert(key: it.key(), value: it.value());
122 }
123 return false;
124 });
125
126 return results;
127}
128
129QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name) const
130{
131 QList<QQmlJSMetaMethod> results;
132
133 QQmlJSUtils::searchBaseAndExtensionTypes(
134 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
135 if (mode == QQmlJSScope::ExtensionNamespace)
136 return false;
137 results.append(other: scope->ownMethods(name));
138 return false;
139 });
140 return results;
141}
142
143QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name, QQmlJSMetaMethodType type) const
144{
145 QList<QQmlJSMetaMethod> results;
146
147 QQmlJSUtils::searchBaseAndExtensionTypes(
148 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
149 if (mode == QQmlJSScope::ExtensionNamespace)
150 return false;
151 const auto ownMethods = scope->ownMethods(name);
152 for (const auto &method : ownMethods) {
153 if (method.methodType() == type)
154 results.append(t: method);
155 }
156 return false;
157 });
158 return results;
159}
160
161bool QQmlJSScope::hasEnumeration(const QString &name) const
162{
163 return QQmlJSUtils::searchBaseAndExtensionTypes(
164 type: this, check: [&](const QQmlJSScope *scope) { return scope->m_enumerations.contains(key: name); });
165}
166
167bool QQmlJSScope::hasEnumerationKey(const QString &name) const
168{
169 return QQmlJSUtils::searchBaseAndExtensionTypes(type: this, check: [&](const QQmlJSScope *scope) {
170 for (const auto &e : scope->m_enumerations) {
171 if (e.keys().contains(str: name))
172 return true;
173 }
174 return false;
175 });
176}
177
178QQmlJSMetaEnum QQmlJSScope::enumeration(const QString &name) const
179{
180 QQmlJSMetaEnum result;
181
182 QQmlJSUtils::searchBaseAndExtensionTypes(type: this, check: [&](const QQmlJSScope *scope) {
183 const auto it = scope->m_enumerations.find(key: name);
184 if (it == scope->m_enumerations.end())
185 return false;
186 result = *it;
187 return true;
188 });
189
190 return result;
191}
192
193QHash<QString, QQmlJSMetaEnum> QQmlJSScope::enumerations() const
194{
195 QHash<QString, QQmlJSMetaEnum> results;
196
197 QQmlJSUtils::searchBaseAndExtensionTypes(type: this, check: [&](const QQmlJSScope *scope) {
198 for (auto it = scope->m_enumerations.constBegin(); it != scope->m_enumerations.constEnd();
199 it++) {
200 if (!results.contains(key: it.key()))
201 results.insert(key: it.key(), value: it.value());
202 }
203 return false;
204 });
205
206 return results;
207}
208
209QString QQmlJSScope::prettyName(QAnyStringView name)
210{
211 const auto internal = "$internal$."_L1;
212 const QString anonymous = "$anonymous$."_L1;
213
214 QString pretty = name.toString();
215
216 if (pretty.startsWith(s: internal))
217 pretty = pretty.mid(position: internal.size());
218 else if (pretty.startsWith(s: anonymous))
219 pretty = pretty.mid(position: anonymous.size());
220
221 if (pretty == u"std::nullptr_t")
222 return u"null"_s;
223
224 if (pretty == u"void")
225 return u"undefined"_s;
226
227 return pretty;
228}
229
230/*!
231 \internal
232 Returns if assigning \a assignedType to \a property would require an
233 implicit component wrapping.
234 */
235bool QQmlJSScope::causesImplicitComponentWrapping(const QQmlJSMetaProperty &property,
236 const QQmlJSScope::ConstPtr &assignedType)
237{
238 // See QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents()
239 // for the logic in qqmltypecompiler
240
241 // Note: unlike findAndRegisterImplicitComponents() we do not check whether
242 // the property type is *derived* from QQmlComponent at some point because
243 // this is actually meaningless (and in the case of QQmlComponent::create()
244 // gets rejected in QQmlPropertyValidator): if the type is not a
245 // QQmlComponent, we have a type mismatch because of assigning a Component
246 // object to a non-Component property
247 const bool propertyVerdict = property.type()->internalName() == u"QQmlComponent";
248
249 const bool assignedTypeVerdict = [&assignedType]() {
250 // Note: nonCompositeBaseType covers the case when assignedType itself
251 // is non-composite
252 auto cppBase = nonCompositeBaseType(type: assignedType);
253 Q_ASSERT(cppBase); // any QML type has (or must have) a C++ base type
254
255 // See isUsableComponent() in qqmltypecompiler.cpp: along with checking
256 // whether a type has a QQmlComponent static meta object (which we
257 // substitute here with checking the first non-composite base for being
258 // a QQmlComponent), it also excludes QQmlAbstractDelegateComponent
259 // subclasses from implicit wrapping
260 if (cppBase->internalName() == u"QQmlComponent")
261 return false;
262 for (; cppBase; cppBase = cppBase->baseType()) {
263 if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
264 return false;
265 }
266 return true;
267 }();
268
269 return propertyVerdict && assignedTypeVerdict;
270}
271
272/*!
273 \internal
274 Returns true if the scope is the outermost element of a separate Component
275 Either because it has been implicitly wrapped, e.g. due to an assignment to
276 a Component property, or because it is the first (and only) child of a
277 Component.
278 For visitors: This method should only be called after implicit components
279 are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
280 was called.
281 */
282bool QQmlJSScope::isComponentRootElement() const {
283 if (m_flags.testFlag(flag: WrappedInImplicitComponent))
284 return true;
285
286 auto base = nonCompositeBaseType(type: parentScope()); // handles null parentScope()
287 if (!base)
288 return false;
289 return base->internalName() == u"QQmlComponent";
290}
291
292bool QQmlJSScope::isIdInCurrentQmlScopes(const QString &id) const
293{
294 if (m_scopeType == QQmlSA::ScopeType::QMLScope)
295 return m_properties.contains(key: id) || m_methods.contains(key: id) || m_enumerations.contains(key: id);
296
297 const auto qmlScope = findCurrentQMLScope(scope: parentScope());
298 return qmlScope->m_properties.contains(key: id)
299 || qmlScope->m_methods.contains(key: id)
300 || qmlScope->m_enumerations.contains(key: id);
301}
302
303bool QQmlJSScope::isIdInCurrentJSScopes(const QString &id) const
304{
305 if (m_scopeType != QQmlSA::ScopeType::QMLScope && m_jsIdentifiers.contains(key: id))
306 return true;
307
308 for (auto jsScope = parentScope(); jsScope; jsScope = jsScope->parentScope()) {
309 if (jsScope->m_scopeType != QQmlSA::ScopeType::QMLScope
310 && jsScope->m_jsIdentifiers.contains(key: id))
311 return true;
312 }
313
314 return false;
315}
316
317bool QQmlJSScope::isIdInjectedFromSignal(const QString &id) const
318{
319 const auto found = findJSIdentifier(id);
320 return found.has_value() && found->kind == JavaScriptIdentifier::Injected;
321}
322
323std::optional<QQmlJSScope::JavaScriptIdentifier>
324QQmlJSScope::findJSIdentifier(const QString &id) const
325{
326 for (const auto *scope = this; scope; scope = scope->parentScope().data()) {
327 if (scope->m_scopeType == QQmlSA::ScopeType::JSFunctionScope
328 || scope->m_scopeType == QQmlSA::ScopeType::JSLexicalScope) {
329 auto it = scope->m_jsIdentifiers.find(key: id);
330 if (it != scope->m_jsIdentifiers.end())
331 return *it;
332 }
333 }
334
335 return std::optional<JavaScriptIdentifier>{};
336}
337
338std::optional<QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::JSIdentifier(const QString &id) const
339{
340 auto it = m_jsIdentifiers.find(key: id);
341 if (it != m_jsIdentifiers.end())
342 return *it;
343
344 return std::optional<JavaScriptIdentifier>{};
345}
346
347static QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr>
348qFindInlineComponents(QStringView typeName, const QQmlJSScope::ContextualTypes &contextualTypes)
349{
350 const int separatorIndex = typeName.lastIndexOf(c: u'.');
351 // do not crash in typeName.sliced() when it starts or ends with an '.'.
352 if (separatorIndex < 1 || separatorIndex >= typeName.size() - 1)
353 return {};
354
355 const auto parentIt = contextualTypes.types().constFind(key: typeName.first(n: separatorIndex).toString());
356 if (parentIt == contextualTypes.types().constEnd())
357 return {};
358
359 auto inlineComponentParent = *parentIt;
360
361 // find the inline components using BFS, as inline components defined in childrens are also
362 // accessible from other qml documents. Same for inline components defined in a base class of
363 // the parent. Use BFS over DFS as the inline components are probably not deeply-nested.
364
365 QStringView inlineComponentName = typeName.sliced(pos: separatorIndex + 1);
366 QQueue<QQmlJSScope::ConstPtr> candidatesForInlineComponents;
367 candidatesForInlineComponents.enqueue(t: inlineComponentParent.scope);
368 while (candidatesForInlineComponents.size()) {
369 QQmlJSScope::ConstPtr current = candidatesForInlineComponents.dequeue();
370 if (!current) // if some type was not resolved, ignore it instead of crashing
371 continue;
372 if (current->isInlineComponent() && current->inlineComponentName() == inlineComponentName) {
373 return { .scope: current, .revision: inlineComponentParent.revision };
374 }
375 // check alternatively the inline components at layer 1 in current and basetype, then at
376 // layer 2, etc...
377 candidatesForInlineComponents.append(other: current->childScopes());
378 if (const auto base = current->baseType())
379 candidatesForInlineComponents.enqueue(t: base);
380 }
381 return {};
382}
383
384QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType(
385 const QString &name, const QQmlJSScope::ContextualTypes &contextualTypes,
386 QSet<QString> *usedTypes)
387{
388 const auto useType = [&]() {
389 if (usedTypes != nullptr)
390 usedTypes->insert(value: name);
391 };
392
393 auto type = contextualTypes.types().constFind(key: name);
394
395 if (type != contextualTypes.types().constEnd()) {
396 useType();
397 return *type;
398 }
399
400 const auto findListType = [&](const QString &prefix, const QString &postfix)
401 -> ImportedScope<ConstPtr> {
402 if (name.startsWith(s: prefix) && name.endsWith(s: postfix)) {
403 const qsizetype prefixLength = prefix.length();
404 const QString &elementName
405 = name.mid(position: prefixLength, n: name.length() - prefixLength - postfix.length());
406 const ImportedScope<ConstPtr> element
407 = findType(name: elementName, contextualTypes, usedTypes);
408 if (element.scope) {
409 useType();
410 return { .scope: element.scope->listType(), .revision: element.revision };
411 }
412 }
413
414 return {};
415 };
416
417 switch (contextualTypes.context()) {
418 case ContextualTypes::INTERNAL: {
419 if (const auto listType = findListType(u"QList<"_s, u">"_s);
420 listType.scope && !listType.scope->isReferenceType()) {
421 return listType;
422 }
423
424 if (const auto listType = findListType(u"QQmlListProperty<"_s, u">"_s);
425 listType.scope && listType.scope->isReferenceType()) {
426 return listType;
427 }
428
429 // look for c++ namescoped enums!
430 const auto colonColon = name.lastIndexOf(QStringLiteral("::"));
431 if (colonColon == -1)
432 break;
433
434 const QString outerTypeName = name.left(n: colonColon);
435 const auto outerType = contextualTypes.types().constFind(key: outerTypeName);
436 if (outerType == contextualTypes.types().constEnd())
437 break;
438
439 for (const auto &innerType : std::as_const(t: outerType->scope->m_childScopes)) {
440 if (innerType->m_internalName == name) {
441 useType();
442 return { .scope: innerType, .revision: outerType->revision };
443 }
444 }
445
446 break;
447 }
448 case ContextualTypes::QML: {
449 // look after inline components
450 const auto inlineComponent = qFindInlineComponents(typeName: name, contextualTypes);
451 if (inlineComponent.scope) {
452 useType();
453 return inlineComponent;
454 }
455
456 if (const auto listType = findListType(u"list<"_s, u">"_s); listType.scope)
457 return listType;
458
459 break;
460 }
461 }
462 return {};
463}
464
465QTypeRevision QQmlJSScope::resolveType(
466 const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &context,
467 QSet<QString> *usedTypes)
468{
469 if (self->accessSemantics() == AccessSemantics::Sequence
470 && self->internalName().startsWith(s: u"QQmlListProperty<"_s)) {
471 self->setIsListProperty(true);
472 }
473
474 const QString baseTypeName = self->baseTypeName();
475 const auto baseType = findType(name: baseTypeName, contextualTypes: context, usedTypes);
476 if (!self->m_baseType.scope && !baseTypeName.isEmpty())
477 self->m_baseType = { .scope: baseType.scope, .revision: baseType.revision };
478
479 if (!self->m_attachedType && !self->m_attachedTypeName.isEmpty())
480 self->m_attachedType = findType(name: self->m_attachedTypeName, contextualTypes: context, usedTypes).scope;
481
482 if (!self->m_valueType && !self->m_valueTypeName.isEmpty())
483 self->m_valueType = findType(name: self->m_valueTypeName, contextualTypes: context, usedTypes).scope;
484
485 if (!self->m_extensionType) {
486 if (self->m_extensionTypeName.isEmpty()) {
487 if (self->accessSemantics() == AccessSemantics::Sequence) {
488 // All sequence types are implicitly extended by JS Array.
489 self->setExtensionTypeName(u"Array"_s);
490 self->m_extensionType = context.arrayType();
491 }
492 } else {
493 self->m_extensionType = findType(name: self->m_extensionTypeName, contextualTypes: context, usedTypes).scope;
494 }
495 }
496
497
498 for (auto it = self->m_properties.begin(), end = self->m_properties.end(); it != end; ++it) {
499 const QString typeName = it->typeName();
500 if (it->type() || typeName.isEmpty())
501 continue;
502
503 if (const auto type = findType(name: typeName, contextualTypes: context, usedTypes); type.scope) {
504 it->setType(it->isList() ? type.scope->listType() : type.scope);
505 continue;
506 }
507
508 const auto enumeration = self->m_enumerations.find(key: typeName);
509 if (enumeration != self->m_enumerations.end()) {
510 it->setType(it->isList()
511 ? enumeration->type()->listType()
512 : QQmlJSScope::ConstPtr(enumeration->type()));
513 }
514 }
515
516 for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) {
517 const QString returnTypeName = it->returnTypeName();
518 if (!it->returnType() && !returnTypeName.isEmpty()) {
519 const auto returnType = findType(name: returnTypeName, contextualTypes: context, usedTypes);
520 it->setReturnType(returnType.scope);
521 }
522
523 auto parameters = it->parameters();
524 for (int i = 0, length = parameters.size(); i < length; ++i) {
525 auto &parameter = parameters[i];
526 if (const QString typeName = parameter.typeName();
527 !parameter.type() && !typeName.isEmpty()) {
528 auto type = findType(name: typeName, contextualTypes: context, usedTypes);
529 if (type.scope && parameter.isList()) {
530 type.scope = type.scope->listType();
531 parameter.setIsList(false);
532 parameter.setIsPointer(false);
533 parameter.setTypeName(type.scope ? type.scope->internalName() : QString());
534 } else if (type.scope && type.scope->isReferenceType()) {
535 parameter.setIsPointer(true);
536 }
537 parameter.setType({ type.scope });
538 }
539 }
540
541 it->setParameters(parameters);
542 }
543
544 for (auto it = self->m_jsIdentifiers.begin(); it != self->m_jsIdentifiers.end(); ++it) {
545 if (it->typeName)
546 it->scope = findType(name: it->typeName.value(), contextualTypes: context, usedTypes).scope;
547 }
548
549 return baseType.revision;
550}
551
552void QQmlJSScope::updateChildScope(
553 const QQmlJSScope::Ptr &childScope, const QQmlJSScope::Ptr &self,
554 const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
555{
556 switch (childScope->scopeType()) {
557 case QQmlSA::ScopeType::GroupedPropertyScope:
558 QQmlJSUtils::searchBaseAndExtensionTypes(
559 type: self.data(), check: [&](const QQmlJSScope *type, QQmlJSScope::ExtensionKind mode) {
560 if (mode == QQmlJSScope::ExtensionNamespace)
561 return false;
562 const auto propertyIt = type->m_properties.find(key: childScope->internalName());
563 if (propertyIt != type->m_properties.end()) {
564 childScope->m_baseType.scope = QQmlJSScope::ConstPtr(propertyIt->type());
565 if (propertyIt->type())
566 childScope->m_semantics = propertyIt->type()->accessSemantics();
567 childScope->setBaseTypeName(propertyIt->typeName());
568 return true;
569 }
570 return false;
571 });
572 break;
573 case QQmlSA::ScopeType::AttachedPropertyScope:
574 if (const auto attachedBase = findType(
575 name: childScope->internalName(), contextualTypes, usedTypes).scope) {
576 childScope->m_baseType.scope = attachedBase->attachedType();
577 childScope->setBaseTypeName(attachedBase->attachedTypeName());
578 }
579 break;
580 default:
581 break;
582 }
583}
584
585template<typename Resolver, typename ChildScopeUpdater>
586static QTypeRevision resolveTypesInternal(
587 Resolver resolve, ChildScopeUpdater update, const QQmlJSScope::Ptr &self,
588 const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
589{
590 const QTypeRevision revision = resolve(self, contextualTypes, usedTypes);
591 // NB: constness ensures no detach
592 const auto childScopes = self->childScopes();
593 for (auto it = childScopes.begin(), end = childScopes.end(); it != end; ++it) {
594 const auto childScope = *it;
595 update(childScope, self, contextualTypes, usedTypes);
596 resolveTypesInternal(resolve, update, childScope, contextualTypes, usedTypes); // recursion
597 }
598 return revision;
599}
600
601QTypeRevision QQmlJSScope::resolveTypes(
602 const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
603 QSet<QString> *usedTypes)
604{
605 const auto resolveAll = [](const QQmlJSScope::Ptr &self,
606 const QQmlJSScope::ContextualTypes &contextualTypes,
607 QSet<QString> *usedTypes) {
608 resolveEnums(self, contextualTypes, usedTypes);
609 resolveList(self, arrayType: contextualTypes.arrayType());
610 return resolveType(self, context: contextualTypes, usedTypes);
611 };
612 return resolveTypesInternal(resolve: resolveAll, update: updateChildScope, self, contextualTypes, usedTypes);
613}
614
615void QQmlJSScope::resolveNonEnumTypes(
616 const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
617 QSet<QString> *usedTypes)
618{
619 resolveTypesInternal(resolve: resolveType, update: updateChildScope, self, contextualTypes, usedTypes);
620}
621
622static QString flagStorage(const QString &underlyingType)
623{
624 // All numeric types are builtins. Therefore we can exhaustively check the internal names.
625
626 if (underlyingType == u"uint"
627 || underlyingType == u"quint8"
628 || underlyingType == u"ushort"
629 || underlyingType == u"ulonglong") {
630 return u"uint"_s;
631 }
632
633 if (underlyingType == u"int"
634 || underlyingType == u"qint8"
635 || underlyingType == u"short"
636 || underlyingType == u"longlong") {
637 return u"int"_s;
638 }
639
640 // Will fail to resolve and produce an error on usage.
641 // It's harmless if you never use the enum.
642 return QString();
643}
644
645/*!
646 \internal
647 Resolves all enums of self.
648
649 Some enums happen to have an alias, e.g. when an enum is used as a flag, the enum will exist in
650 two versions, once as enum (e.g. Qt::MouseButton) and once as a flag (e.g. Qt::MouseButtons). In
651 this case, normally only the flag is exposed to the qt metatype system and tools like qmltc will
652 have troubles when encountering the enum in signal parameters etc. To solve this problem,
653 resolveEnums() will create a QQmlJSMetaEnum copy for the alias in case the 'self'-scope already
654 does not have an enum called like the alias.
655 */
656void QQmlJSScope::resolveEnums(
657 const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
658 QSet<QString> *usedTypes)
659{
660 // temporary hash to avoid messing up m_enumerations while iterators are active on it
661 QHash<QString, QQmlJSMetaEnum> toBeAppended;
662 for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end; ++it) {
663 if (it->type())
664 continue;
665 QQmlJSScope::Ptr enumScope = QQmlJSScope::create();
666 reparent(parentScope: self, childScope: enumScope);
667 enumScope->m_scopeType = QQmlSA::ScopeType::EnumScope;
668
669 QString typeName = it->typeName();
670 if (typeName.isEmpty())
671 typeName = QStringLiteral("int");
672 else if (it->isFlag())
673 typeName = flagStorage(underlyingType: typeName);
674 enumScope->setBaseTypeName(typeName);
675 const auto type = findType(name: typeName, contextualTypes, usedTypes);
676 enumScope->m_baseType = { .scope: type.scope, .revision: type.revision };
677
678 enumScope->m_semantics = AccessSemantics::Value;
679 enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
680 if (QString alias = it->alias(); !alias.isEmpty()
681 && self->m_enumerations.constFind(key: alias) == self->m_enumerations.constEnd()) {
682 auto aliasScope = QQmlJSScope::clone(origin: enumScope);
683 aliasScope->m_internalName = self->internalName() + QStringLiteral("::") + alias;
684 QQmlJSMetaEnum cpy(*it);
685 cpy.setType(QQmlJSScope::ConstPtr(aliasScope));
686 toBeAppended.insert(key: alias, value: cpy);
687 }
688 it->setType(QQmlJSScope::ConstPtr(enumScope));
689 }
690 // no more iterators active on m_enumerations, so it can be changed safely now
691 self->m_enumerations.insert(hash: toBeAppended);
692}
693
694void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType)
695{
696 if (self->listType() || self->accessSemantics() == AccessSemantics::Sequence)
697 return;
698
699 Q_ASSERT(!arrayType.isNull());
700 QQmlJSScope::Ptr listType = QQmlJSScope::create();
701 listType->setAccessSemantics(AccessSemantics::Sequence);
702 listType->setValueTypeName(self->internalName());
703
704 if (self->isComposite()) {
705 // There is no internalName for this thing. Just set the value type right away
706 listType->setInternalName(u"QQmlListProperty<>"_s);
707 listType->m_valueType = QQmlJSScope::ConstPtr(self);
708 } else if (self->isReferenceType()) {
709 listType->setInternalName(u"QQmlListProperty<%2>"_s.arg(a: self->internalName()));
710 // Do not set a filePath on the list type, so that we have to generalize it
711 // even in direct mode.
712 } else {
713 listType->setInternalName(u"QList<%2>"_s.arg(a: self->internalName()));
714 listType->setFilePath(self->filePath());
715 }
716
717 const QQmlJSImportedScope element = {.scope: self, .revision: QTypeRevision()};
718 const QQmlJSImportedScope array = {.scope: arrayType, .revision: QTypeRevision()};
719 QQmlJSScope::ContextualTypes contextualTypes(
720 QQmlJSScope::ContextualTypes::INTERNAL, { { self->internalName(), element }, },
721 arrayType);
722 QQmlJSScope::resolveTypes(self: listType, contextualTypes);
723
724 Q_ASSERT(listType->valueType() == self);
725 self->m_listType = listType;
726}
727
728void QQmlJSScope::resolveGeneralizedGroup(
729 const Ptr &self, const ConstPtr &baseType,
730 const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
731{
732 Q_ASSERT(baseType);
733 // Generalized group properties are always composite,
734 // which means we expect contextualTypes to be QML names.
735 Q_ASSERT(self->isComposite());
736
737 self->m_baseType.scope = baseType;
738 self->m_semantics = baseType->accessSemantics();
739 resolveNonEnumTypes(self, contextualTypes, usedTypes);
740}
741
742QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
743{
744 auto qmlScope = scope;
745 while (qmlScope && qmlScope->m_scopeType != QQmlSA::ScopeType::QMLScope)
746 qmlScope = qmlScope->parentScope();
747 return qmlScope;
748}
749
750bool QQmlJSScope::hasProperty(const QString &name) const
751{
752 return QQmlJSUtils::searchBaseAndExtensionTypes(
753 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
754 if (mode == QQmlJSScope::ExtensionNamespace)
755 return false;
756 return scope->m_properties.contains(key: name);
757 });
758}
759
760QQmlJSMetaProperty QQmlJSScope::property(const QString &name) const
761{
762 QQmlJSMetaProperty prop;
763 QQmlJSUtils::searchBaseAndExtensionTypes(
764 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
765 if (mode == QQmlJSScope::ExtensionNamespace)
766 return false;
767 const auto it = scope->m_properties.find(key: name);
768 if (it == scope->m_properties.end())
769 return false;
770 prop = *it;
771 return true;
772 });
773 return prop;
774}
775
776/*!
777 Returns all properties visible from this scope including those of
778 base types and extensions.
779
780 \note Properties that get shadowed are not included and only the
781 version visible from this scope is contained.
782*/
783QHash<QString, QQmlJSMetaProperty> QQmlJSScope::properties() const
784{
785 QHash<QString, QQmlJSMetaProperty> results;
786 QQmlJSUtils::searchBaseAndExtensionTypes(
787 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
788 if (mode == QQmlJSScope::ExtensionNamespace)
789 return false;
790 for (auto it = scope->m_properties.constBegin();
791 it != scope->m_properties.constEnd(); it++) {
792 if (!results.contains(key: it.key()))
793 results.insert(key: it.key(), value: it.value());
794 }
795 return false;
796 });
797 return results;
798}
799
800QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfProperty(const QQmlJSScope::ConstPtr &self,
801 const QString &name)
802{
803 QQmlJSScope::AnnotatedScope owner;
804 QQmlJSUtils::searchBaseAndExtensionTypes(
805 type: self, check: [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
806 if (mode == QQmlJSScope::ExtensionNamespace)
807 return false;
808 if (scope->hasOwnProperty(name)) {
809 owner = { .scope: scope, .extensionSpecifier: mode };
810 return true;
811 }
812 return false;
813 });
814 return owner;
815}
816
817void QQmlJSScope::setPropertyLocallyRequired(const QString &name, bool isRequired)
818{
819 if (!isRequired)
820 m_requiredPropertyNames.removeOne(t: name);
821 else if (!m_requiredPropertyNames.contains(str: name))
822 m_requiredPropertyNames.append(t: name);
823}
824
825bool QQmlJSScope::isPropertyRequired(const QString &name) const
826{
827 bool isRequired = false;
828 QQmlJSUtils::searchBaseAndExtensionTypes(
829 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
830 if (scope->isPropertyLocallyRequired(name)) {
831 isRequired = true;
832 return true;
833 }
834
835 // the hasOwnProperty() below only makes sense if our scope is
836 // not an extension namespace
837 if (mode == QQmlJSScope::ExtensionNamespace)
838 return false;
839
840 // If it has a property of that name, and that is not required, then none of the
841 // base types matter. You cannot make a derived type's property required with
842 // a "required" specification in a base type.
843 return scope->hasOwnProperty(name);
844 });
845 return isRequired;
846}
847
848bool QQmlJSScope::isPropertyLocallyRequired(const QString &name) const
849{
850 return m_requiredPropertyNames.contains(str: name);
851}
852
853static_assert(QTypeInfo<QQmlJSScope::QmlIRCompatibilityBindingData>::isRelocatable,
854 "We really want T to be relocatable as it improves QList<T> performance");
855
856void QQmlJSScope::addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
857 BindingTargetSpecifier specifier)
858{
859 // the order:
860 // * ordinary bindings are prepended to the binding array
861 // * list bindings are properly ordered within each other, so basically
862 // prepended "in bulk"
863 // * bindings to default properties (which are not explicitly mentioned in
864 // binding expression) are inserted by source location's offset
865
866 switch (specifier) {
867 case BindingTargetSpecifier::SimplePropertyTarget: {
868 m_propertyBindingsArray.emplaceFront(args: binding.propertyName(),
869 args: binding.sourceLocation().offset);
870 break;
871 }
872 case BindingTargetSpecifier::ListPropertyTarget: {
873 const auto bindingOnTheSameProperty =
874 [&](const QQmlJSScope::QmlIRCompatibilityBindingData &x) {
875 return x.propertyName == binding.propertyName();
876 };
877 // fake "prepend in bulk" by appending a list binding to the sequence of
878 // bindings to the same property. there's an implicit QML language
879 // guarantee that such sequence does not contain arbitrary in-between
880 // bindings that do not belong to the same list property
881 auto pos = std::find_if_not(first: m_propertyBindingsArray.begin(), last: m_propertyBindingsArray.end(),
882 pred: bindingOnTheSameProperty);
883 Q_ASSERT(pos == m_propertyBindingsArray.begin()
884 || std::prev(pos)->propertyName == binding.propertyName());
885 m_propertyBindingsArray.emplace(before: pos, args: binding.propertyName(),
886 args: binding.sourceLocation().offset);
887 break;
888 }
889 case BindingTargetSpecifier::UnnamedPropertyTarget: {
890 // see QmlIR::PoolList<>::findSortedInsertionPoint()
891 const auto findInsertionPoint = [this](const QQmlJSMetaPropertyBinding &x) {
892 qsizetype pos = -1;
893 for (auto it = m_propertyBindingsArray.cbegin(); it != m_propertyBindingsArray.cend();
894 ++it) {
895 if (!(it->sourceLocationOffset <= x.sourceLocation().offset))
896 break;
897 ++pos;
898 }
899 return pos;
900 };
901
902 // see QmlIR::PoolList<>::insertAfter()
903 const auto insertAfter = [this](qsizetype pos, const QQmlJSMetaPropertyBinding &x) {
904 if (pos == -1) {
905 m_propertyBindingsArray.emplaceFront(args: x.propertyName(), args: x.sourceLocation().offset);
906 } else if (pos == m_propertyBindingsArray.size()) {
907 m_propertyBindingsArray.emplaceBack(args: x.propertyName(), args: x.sourceLocation().offset);
908 } else {
909 // since we insert *after*, use (pos + 1) as insertion point
910 m_propertyBindingsArray.emplace(i: pos + 1, args: x.propertyName(),
911 args: x.sourceLocation().offset);
912 }
913 };
914
915 const qsizetype insertionPos = findInsertionPoint(binding);
916 insertAfter(insertionPos, binding);
917 break;
918 }
919 default: {
920 Q_UNREACHABLE();
921 break;
922 }
923 }
924}
925
926QList<QQmlJSMetaPropertyBinding> QQmlJSScope::ownPropertyBindingsInQmlIROrder() const
927{
928 QList<QQmlJSMetaPropertyBinding> qmlIrOrdered;
929 qmlIrOrdered.reserve(asize: m_propertyBindingsArray.size());
930
931 for (const auto &data : m_propertyBindingsArray) {
932 const auto [first, last] = m_propertyBindings.equal_range(key: data.propertyName);
933 Q_ASSERT(first != last);
934 auto binding = std::find_if(first: first, last: last, pred: [&](const QQmlJSMetaPropertyBinding &x) {
935 return x.sourceLocation().offset == data.sourceLocationOffset;
936 });
937 Q_ASSERT(binding != last);
938 qmlIrOrdered.append(t: *binding);
939 }
940
941 return qmlIrOrdered;
942}
943
944bool QQmlJSScope::hasPropertyBindings(const QString &name) const
945{
946 return QQmlJSUtils::searchBaseAndExtensionTypes(
947 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
948 if (mode != QQmlJSScope::NotExtension) {
949 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
950 return false;
951 }
952 return scope->hasOwnPropertyBindings(name);
953 });
954}
955
956QList<QQmlJSMetaPropertyBinding> QQmlJSScope::propertyBindings(const QString &name) const
957{
958 QList<QQmlJSMetaPropertyBinding> bindings;
959 QQmlJSUtils::searchBaseAndExtensionTypes(
960 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
961 if (mode != QQmlJSScope::NotExtension) {
962 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
963 return false;
964 }
965 const auto range = scope->ownPropertyBindings(name);
966 for (auto it = range.first; it != range.second; ++it)
967 bindings.append(t: *it);
968 return false;
969 });
970 return bindings;
971}
972
973bool QQmlJSScope::hasInterface(const QString &name) const
974{
975 return QQmlJSUtils::searchBaseAndExtensionTypes(
976 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
977 if (mode != QQmlJSScope::NotExtension)
978 return false;
979 return scope->m_interfaceNames.contains(str: name);
980 });
981}
982
983bool QQmlJSScope::isNameDeferred(const QString &name) const
984{
985 bool isDeferred = false;
986
987 QQmlJSUtils::searchBaseAndExtensionTypes(type: this, check: [&](const QQmlJSScope *scope) {
988 const QStringList immediate = scope->ownImmediateNames();
989 if (!immediate.isEmpty()) {
990 isDeferred = !immediate.contains(str: name);
991 return true;
992 }
993 const QStringList deferred = scope->ownDeferredNames();
994 if (!deferred.isEmpty()) {
995 isDeferred = deferred.contains(str: name);
996 return true;
997 }
998 return false;
999 });
1000
1001 return isDeferred;
1002}
1003
1004QString QQmlJSScope::qualifiedNameFrom(const QString &moduleName, const QString &typeName,
1005 const QTypeRevision &firstRevision,
1006 const QTypeRevision &lastRevision)
1007{
1008 QString qualifiedName =
1009 u"%1/%2 %3.%4"_s.arg(args: moduleName, args: typeName)
1010 .arg(a: firstRevision.hasMajorVersion() ? firstRevision.majorVersion() : 0)
1011 .arg(a: firstRevision.hasMinorVersion() ? firstRevision.minorVersion() : 0);
1012 if (firstRevision != lastRevision) {
1013 qualifiedName += u"-%1.%2"_s
1014 .arg(a: lastRevision.hasMajorVersion() ? lastRevision.majorVersion() : 0)
1015 .arg(a: lastRevision.hasMinorVersion() ? lastRevision.minorVersion() : 0);
1016 }
1017 return qualifiedName;
1018}
1019
1020void QQmlJSScope::setBaseTypeName(const QString &baseTypeName)
1021{
1022 m_flags.setFlag(flag: HasBaseTypeError, on: false);
1023 m_baseTypeNameOrError = baseTypeName;
1024}
1025
1026QString QQmlJSScope::baseTypeName() const
1027{
1028 return m_flags.testFlag(flag: HasBaseTypeError) ? QString() : m_baseTypeNameOrError;
1029}
1030
1031void QQmlJSScope::setBaseTypeError(const QString &baseTypeError)
1032{
1033 m_flags.setFlag(flag: HasBaseTypeError);
1034 m_baseTypeNameOrError = baseTypeError;
1035}
1036
1037QString QQmlJSScope::baseTypeError() const
1038{
1039 return m_flags.testFlag(flag: HasBaseTypeError) ? m_baseTypeNameOrError : QString();
1040}
1041
1042QString QQmlJSScope::attachedTypeName() const
1043{
1044 QString name;
1045 QQmlJSUtils::searchBaseAndExtensionTypes(
1046 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1047 if (mode != QQmlJSScope::NotExtension)
1048 return false;
1049 if (scope->ownAttachedType().isNull())
1050 return false;
1051 name = scope->ownAttachedTypeName();
1052 return true;
1053 });
1054
1055 return name;
1056}
1057
1058QQmlJSScope::ConstPtr QQmlJSScope::attachedType() const
1059{
1060 QQmlJSScope::ConstPtr ptr;
1061 QQmlJSUtils::searchBaseAndExtensionTypes(
1062 type: this, check: [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1063 if (mode != QQmlJSScope::NotExtension)
1064 return false;
1065 if (scope->ownAttachedType().isNull())
1066 return false;
1067 ptr = scope->ownAttachedType();
1068 return true;
1069 });
1070
1071 return ptr;
1072}
1073
1074bool QQmlJSScope::isResolved() const
1075{
1076 const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope
1077 || m_scopeType == ScopeType::GroupedPropertyScope)
1078 ? m_internalName.isEmpty()
1079 : m_baseTypeNameOrError.isEmpty();
1080 if (nameIsEmpty)
1081 return true;
1082 if (m_baseType.scope.isNull())
1083 return false;
1084 if (isComposite() && !nonCompositeBaseType(type: baseType()))
1085 return false;
1086 return true;
1087}
1088
1089QString QQmlJSScope::defaultPropertyName() const
1090{
1091 QString name;
1092 QQmlJSUtils::searchBaseAndExtensionTypes(type: this, check: [&](const QQmlJSScope *scope) {
1093 name = scope->ownDefaultPropertyName();
1094 return !name.isEmpty();
1095 });
1096 return name;
1097}
1098
1099QString QQmlJSScope::parentPropertyName() const
1100{
1101 QString name;
1102 QQmlJSUtils::searchBaseAndExtensionTypes(type: this, check: [&](const QQmlJSScope *scope) {
1103 name = scope->ownParentPropertyName();
1104 return !name.isEmpty();
1105 });
1106 return name;
1107}
1108
1109bool QQmlJSScope::isFullyResolved() const
1110{
1111 bool baseResolved = true;
1112 QQmlJSUtils::searchBaseAndExtensionTypes(type: this, check: [&](const QQmlJSScope *scope) {
1113 if (!scope->isResolved()) {
1114 baseResolved = false;
1115 return true;
1116 }
1117 return false;
1118 });
1119
1120 return baseResolved;
1121}
1122
1123QQmlJSScope::Import::Import(QString prefix, QString name, QTypeRevision version, bool isFile,
1124 bool isDependency) :
1125 m_prefix(std::move(prefix)),
1126 m_name(std::move(name)),
1127 m_version(version),
1128 m_isFile(isFile),
1129 m_isDependency(isDependency)
1130{
1131}
1132
1133bool QQmlJSScope::Import::isValid() const
1134{
1135 return !m_name.isEmpty();
1136}
1137
1138QQmlJSScope::Export::Export(
1139 QString package, QString type, QTypeRevision version, QTypeRevision revision)
1140 : m_package(std::move(package))
1141 , m_type(std::move(type))
1142 , m_version(std::move(version))
1143 , m_revision(std::move(revision))
1144{
1145}
1146
1147bool QQmlJSScope::Export::isValid() const
1148{
1149 return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
1150}
1151
1152void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
1153{
1154 scope->setQualifiedName(m_qualifiedName);
1155 scope->setModuleName(m_moduleName);
1156 QQmlJSTypeReader typeReader(m_importer, m_filePath);
1157 typeReader(scope);
1158 m_importer->m_globalWarnings.append(other: typeReader.errors());
1159 scope->setInternalName(internalName());
1160 QQmlJSScope::resolveEnums(self: scope, contextualTypes: m_importer->builtinInternalNames());
1161 QQmlJSScope::resolveList(self: scope, arrayType: m_importer->builtinInternalNames().arrayType());
1162
1163 if (m_isSingleton && !scope->isSingleton()) {
1164 m_importer->m_globalWarnings.append(
1165 t: { QStringLiteral(
1166 "Type %1 declared as singleton in qmldir but missing pragma Singleton")
1167 .arg(a: scope->internalName()),
1168 .type: QtCriticalMsg, .loc: QQmlJS::SourceLocation() });
1169 scope->setIsSingleton(true);
1170 } else if (!m_isSingleton && scope->isSingleton()) {
1171 m_importer->m_globalWarnings.append(
1172 t: { QStringLiteral("Type %1 not declared as singleton in qmldir "
1173 "but using pragma Singleton")
1174 .arg(a: scope->internalName()),
1175 .type: QtCriticalMsg, .loc: QQmlJS::SourceLocation() });
1176 scope->setIsSingleton(false);
1177 }
1178}
1179
1180bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
1181{
1182 if (!derived)
1183 return false;
1184
1185 // expect this and derived types to have non-composite bases
1186 Q_ASSERT(!isComposite() || nonCompositeBaseType(baseType()));
1187 Q_ASSERT(nonCompositeBaseType(derived));
1188
1189 // the logic with isBaseComponent (as well as the way we set this flag)
1190 // feels wrong - QTBUG-101940
1191 const bool isBaseComponent = [this]() {
1192 if (internalName() == u"QQmlComponent")
1193 return true;
1194 else if (isComposite())
1195 return false;
1196 for (auto cppBase = nonCompositeBaseType(type: baseType()); cppBase;
1197 cppBase = cppBase->baseType()) {
1198 if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
1199 return true;
1200 }
1201 return false;
1202 }();
1203
1204 QDuplicateTracker<QQmlJSScope::ConstPtr> seen;
1205 for (auto scope = derived; !scope.isNull() && !seen.hasSeen(s: scope);
1206 scope = scope->baseType()) {
1207 if (isSameType(otherScope: scope))
1208 return true;
1209 if (isBaseComponent && scope->internalName() == u"QObject"_s)
1210 return true;
1211 }
1212
1213 if (internalName() == u"QVariant"_s || internalName() == u"QJSValue"_s)
1214 return true;
1215
1216 return isListProperty() && valueType()->canAssign(derived);
1217}
1218
1219bool QQmlJSScope::isInCustomParserParent() const
1220{
1221 for (const auto *scope = this; scope; scope = scope->parentScope().get()) {
1222 if (!scope->baseType().isNull() && scope->baseType()->hasCustomParser())
1223 return true;
1224 }
1225
1226 return false;
1227}
1228
1229/*!
1230 * \internal
1231 * if this->isInlineComponent(), then this getter returns the name of the inline
1232 * component.
1233 */
1234std::optional<QString> QQmlJSScope::inlineComponentName() const
1235{
1236 Q_ASSERT(isInlineComponent() == m_inlineComponentName.has_value());
1237 return m_inlineComponentName;
1238}
1239
1240/*!
1241 * \internal
1242 * If this type is part of an inline component, return its name. Otherwise, if this type
1243 * is part of the document root, return the document root name.
1244 */
1245QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineComponentName() const
1246{
1247 for (auto *type = this; type; type = type->parentScope().get()) {
1248 if (type->isInlineComponent())
1249 return *type->inlineComponentName();
1250 }
1251 return RootDocumentNameType();
1252}
1253
1254/*!
1255 \internal
1256 Returns true if the current type is creatable by checking all the required base classes.
1257 "Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
1258
1259 For the exact definition:
1260 A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
1261 following criteria:
1262 \list
1263 \li the base type is a singleton, or
1264 \li the base type is an attached type, or
1265 \li the base type is a C++ type with the QML_UNCREATABLE or QML_ANONYMOUS macro, or
1266 \li the base type is a type without default constructor (in that case, it really needs QML_UNCREATABLE or QML_ANONYMOUS)
1267 \endlist
1268 */
1269bool QQmlJSScope::isCreatable() const
1270{
1271 auto isCreatableNonRecursive = [](const QQmlJSScope *scope) {
1272 return scope->hasCreatableFlag() && !scope->isSingleton()
1273 && scope->scopeType() == QQmlSA::ScopeType::QMLScope;
1274 };
1275
1276 for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) {
1277 if (!scope->isComposite()) {
1278 // just check the first nonComposite (c++) base for isCreatableNonRecursive() and then stop
1279 return isCreatableNonRecursive(scope);
1280 } else {
1281 // check all composite (qml) bases for isCreatableNonRecursive().
1282 if (isCreatableNonRecursive(scope))
1283 return true;
1284 }
1285 }
1286 // no uncreatable bases found
1287 return false;
1288}
1289
1290QQmlSA::Element QQmlJSScope::createQQmlSAElement(const ConstPtr &ptr)
1291{
1292 QQmlSA::Element element;
1293 auto &wrappedPtr = reinterpret_cast<QQmlJSScope::ConstPtr &>(element.m_data);
1294 wrappedPtr = ptr;
1295 return element;
1296}
1297
1298QQmlSA::Element QQmlJSScope::createQQmlSAElement(ConstPtr &&ptr)
1299{
1300 QQmlSA::Element element;
1301 auto &wrappedPtr = reinterpret_cast<QQmlJSScope::ConstPtr &>(element.m_data);
1302 wrappedPtr = std::move(ptr);
1303 return element;
1304}
1305
1306const QQmlJSScope::ConstPtr &QQmlJSScope::scope(const QQmlSA::Element &element)
1307{
1308 return reinterpret_cast<const QQmlJSScope::ConstPtr &>(element.m_data);
1309}
1310
1311QT_END_NAMESPACE
1312

source code of qtdeclarative/src/qmlcompiler/qqmljsscope.cpp