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