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