148QQmlJSImportVisitor::QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
149 const QString &implicitImportDirectory,
150 const QStringList &qmldirFiles)
151 : m_implicitImportDirectory(implicitImportDirectory),
152 m_qmldirFiles(qmldirFiles),
153 m_exportedRootScope(QQmlJSScope::resetForReparse(importer->importFile(logger->filePath()))),
154 m_importer(importer),
156 m_rootScopeImports(QQmlJS::ContextualTypes(
157 QQmlJS::ContextualTypes::QML, { }, { },
158 importer->builtinInternalNames().contextualTypes().arrayType()),
164 m_exportedRootScope->setScopeType(QQmlSA::ScopeType::QMLScope);
165 m_exportedRootScope->setBaseTypeName(QQmlJSImporter::s_inProcessMarker);
166 m_exportedRootScope->setFilePath(m_logger->filePath());
167 m_exportedRootScope->setIsComposite(
true);
170
171
172
173
174
175 auto globalScope = QQmlJSScope::create();
176 globalScope->setInternalName(u"global"_s);
177 globalScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
179 QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
180 QQmlJSScope::JavaScriptIdentifier::LexicalScoped, QQmlJS::SourceLocation(), std::nullopt,
184 QV4::Compiler::Codegen::forEachGlobalName([&](QLatin1StringView globalName) {
185 globalScope->insertJSIdentifier(globalName, globalJavaScript);
188 m_globalScope = globalScope;
189 m_currentScope = globalScope;
192QQmlJSImportVisitor::~QQmlJSImportVisitor() =
default;
194void QQmlJSImportVisitor::populateCurrentScope(
195 QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
197 m_currentScope->setScopeType(type);
198 m_currentScope->setIsComposite(
true);
199 m_currentScope->setFilePath(m_logger->filePath());
200 m_currentScope->setSourceLocation(location);
201 setScopeName(m_currentScope, type, name);
202 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
205void QQmlJSImportVisitor::enterRootScope(QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
207 Q_ASSERT(m_currentScope == m_globalScope);
208 QQmlJSScope::reparent(m_currentScope, m_exportedRootScope);
209 m_currentScope = m_exportedRootScope;
210 populateCurrentScope(type, name, location);
213void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type,
const QString &name,
214 const QQmlJS::SourceLocation &location)
216 QQmlJSScope::Ptr newScope = QQmlJSScope::create();
217 QQmlJSScope::reparent(m_currentScope, newScope);
218 m_currentScope = std::move(newScope);
219 populateCurrentScope(type, name, location);
222bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type,
224 const QQmlJS::SourceLocation &location)
226 Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope
227 || type == QQmlSA::ScopeType::AttachedPropertyScope);
229 const auto pred = [&](
const QQmlJSScope::ConstPtr &s) {
232 return s->internalName() == name;
234 const auto scopes = m_currentScope->childScopes();
237 auto it = std::find_if(scopes.begin(), scopes.end(), pred);
238 if (it == scopes.end()) {
240 enterEnvironment(type, name, location);
244 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, *it);
245 m_currentScope = *it;
249void QQmlJSImportVisitor::leaveEnvironment()
251 m_currentScope = m_currentScope->parentScope();
254void QQmlJSImportVisitor::warnUnresolvedType(
const QQmlJSScope::ConstPtr &type)
const
256 m_logger->log(QStringLiteral(
"Type %1 is used but it is not resolved")
257 .arg(QQmlJSUtils::getScopeName(type, type->scopeType())),
258 qmlUnresolvedType, type->sourceLocation());
261void QQmlJSImportVisitor::warnMissingPropertyForBinding(
262 const QString &property,
const QQmlJS::SourceLocation &location,
263 const std::optional<QQmlJSFixSuggestion> &fixSuggestion)
265 m_logger->log(QStringLiteral(
"Could not find property \"%1\".").arg(property),
266 qmlMissingProperty, location,
true,
true, fixSuggestion);
274bool QQmlJSImportVisitor::resolveAliasProperty(
const QQmlJSScope::Ptr &object,
275 const QQmlJSMetaProperty &property)
277 bool doRequeue =
false;
278 QStringList components = property.aliasExpression().split(u'.');
279 QQmlJSMetaProperty targetProperty;
281 bool foundProperty =
false;
282 bool hasWarnedAlready =
false;
285 QQmlJSScope::ConstPtr type = m_scopesById.scope(components.takeFirst(), object);
286 QQmlJSScope::ConstPtr typeScope;
287 if (!type.isNull()) {
288 foundProperty =
true;
295 while (type && !components.isEmpty()) {
296 const QString name = components.takeFirst();
298 if (!checkTypeResolved(type)) {
299 hasWarnedAlready =
true;
304 if (!type->hasProperty(name)) {
305 foundProperty =
false;
310 const auto target = type->property(name);
311 if (!target.type()) {
312 if (target.isAlias()) {
317 hasWarnedAlready = QQmlJSScope::ownerOfProperty(type, name).scope->filePath()
318 == m_exportedRootScope->filePath();
322 type = target.type();
323 targetProperty = target;
330 if (!hasWarnedAlready) {
332 m_logger->log(QStringLiteral(
"Cannot deduce type of alias \"%1\"")
333 .arg(property.propertyName()),
334 qmlMissingType, property.sourceLocation());
337 QStringLiteral(
"Cannot resolve alias \"%1\"").arg(property.propertyName()),
338 qmlUnresolvedAlias, property.sourceLocation());
342 Q_ASSERT(property.index() >= 0);
343 object->addOwnProperty(property);
346 QQmlJSMetaProperty newProperty = property;
347 newProperty.setType(type);
349 newProperty.setIsList(targetProperty.isList());
350 newProperty.setIsWritable(targetProperty.isWritable());
351 newProperty.setIsFinal(targetProperty.isFinal());
352 newProperty.setIsPointer(targetProperty.isPointer());
354 const bool onlyId = !property.aliasExpression().contains(u'.');
356 newProperty.setAliasTargetScope(type);
357 newProperty.setAliasTargetName(QStringLiteral(
"id-only-alias"));
359 const auto &ownerScope =
360 QQmlJSScope::ownerOfProperty(typeScope, targetProperty.propertyName()).scope;
361 newProperty.setAliasTargetScope(ownerScope);
362 newProperty.setAliasTargetName(targetProperty.propertyName());
365 if (
const QString internalName = type->internalName(); !internalName.isEmpty())
366 newProperty.setTypeName(internalName);
368 Q_ASSERT(newProperty.index() >= 0);
369 object->addOwnProperty(newProperty);
370 m_aliasDefinitions.append({ object, property.propertyName() });
375void QQmlJSImportVisitor::resolveAliases()
377 QQueue<QQmlJSScope::Ptr> objects;
378 objects.enqueue(m_exportedRootScope);
380 qsizetype lastRequeueLength = std::numeric_limits<qsizetype>::max();
381 QQueue<QQmlJSScope::Ptr> requeue;
383 while (!objects.isEmpty()) {
384 const QQmlJSScope::Ptr object = objects.dequeue();
385 const auto properties = object->ownProperties();
387 bool doRequeue =
false;
388 for (
const auto &property : properties) {
389 if (!property.isAlias() || !property.type().isNull())
391 doRequeue |= resolveAliasProperty(object, property);
394 const auto childScopes = object->childScopes();
395 for (
const auto &childScope : childScopes)
396 objects.enqueue(childScope);
399 requeue.enqueue(object);
401 if (objects.isEmpty() && requeue.size() < lastRequeueLength) {
402 lastRequeueLength = requeue.size();
403 objects.swap(requeue);
407 while (!requeue.isEmpty()) {
408 const QQmlJSScope::Ptr object = requeue.dequeue();
409 const auto properties = object->ownProperties();
410 for (
const auto &property : properties) {
411 if (!property.isAlias() || property.type())
413 m_logger->log(QStringLiteral(
"Alias \"%1\" is part of an alias cycle")
414 .arg(property.propertyName()),
415 qmlAliasCycle, property.sourceLocation());
420void QQmlJSImportVisitor::resolveGroupedProperties()
422 QQueue<QQmlJSScope::Ptr> objects;
423 objects.enqueue(m_exportedRootScope);
425 while (!objects.isEmpty()) {
426 const QQmlJSScope::Ptr object = objects.dequeue();
427 const auto childScopes = object->childScopes();
428 for (
const auto &childScope : childScopes) {
429 if (mayBeUnresolvedGroupedProperty(childScope)) {
430 const QString name = childScope->internalName();
431 if (object->isNameDeferred(name)) {
432 const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
433 if (!deferred.isNull()) {
434 QQmlJSScope::resolveGroup(childScope, deferred,
435 m_rootScopeImports.contextualTypes(),
438 }
else if (
const QQmlJSScope::ConstPtr propType = object->property(name).type()) {
439 QQmlJSScope::resolveGroup(childScope, propType,
440 m_rootScopeImports.contextualTypes(), usedTypes());
443 objects.enqueue(childScope);
448QString QQmlJSImportVisitor::implicitImportDirectory(
const QString &localFile,
449 const QQmlJSResourceFileMapper *mapper)
452 const auto resource = mapper->entry(
453 QQmlJSResourceFileMapper::localFileFilter(localFile));
454 if (resource.isValid()) {
455 return resource.resourcePath.contains(u'/')
456 ? (u':' + resource.resourcePath.left(
457 resource.resourcePath.lastIndexOf(u'/') + 1))
458 : QStringLiteral(
":/");
462 return QFileInfo(localFile).canonicalPath() + u'/';
465void QQmlJSImportVisitor::processImportWarnings(
466 const QString &what,
const QList<QQmlJS::DiagnosticMessage> &warnings,
467 const QQmlJS::SourceLocation &srcLocation)
469 if (warnings.isEmpty())
472 QList<QQmlJS::DiagnosticMessage> importWarnings = warnings;
475 auto fileSelectorWarningsIt = std::partition(importWarnings.begin(), importWarnings.end(),
476 [](
const QQmlJS::DiagnosticMessage &message) {
477 return message.type != QtMsgType::QtInfoMsg;
479 if (fileSelectorWarningsIt != importWarnings.end()) {
480 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImportFileSelector,
482 m_logger->processMessages(QSpan(fileSelectorWarningsIt, importWarnings.end()),
483 qmlImportFileSelector, srcLocation);
486 if (fileSelectorWarningsIt == importWarnings.begin())
489 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImport,
491 m_logger->processMessages(QSpan(importWarnings.begin(), fileSelectorWarningsIt), qmlImport,
495void QQmlJSImportVisitor::importBaseModules()
497 Q_ASSERT(m_rootScopeImports.isEmpty());
498 m_rootScopeImports = m_importer->importHardCodedBuiltins();
500
501
502
503
504
505 m_rootScopeImports.setCurrentFileSelector(
506 QQmlJSUtils::fileSelectorFor(m_exportedRootScope));
508 const QQmlJS::SourceLocation invalidLoc;
509 const auto types = m_rootScopeImports.types();
510 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
511 addImportWithLocation(*it, invalidLoc,
false);
513 if (!m_qmldirFiles.isEmpty())
514 m_rootScopeImports.addWarnings(m_importer->importQmldirs(m_qmldirFiles));
518 if (!m_logger->filePath().endsWith(u".qmltypes"_s)) {
519 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
520 m_rootScopeImports.add(m_importer->importDirectory(m_implicitImportDirectory, precedence));
525 if (
const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper()) {
526 const QStringList resourcePaths = mapper->resourcePaths(QQmlJSResourceFileMapper::Filter {
527 m_logger->filePath(), QStringList(), QQmlJSResourceFileMapper::Resource });
528 for (
const QString &path : resourcePaths) {
529 const qsizetype lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
532 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
533 m_rootScopeImports.add(m_importer->importDirectory(path.first(lastSlash),
539 processImportWarnings(QStringLiteral(
"base modules"), m_rootScopeImports.warnings());
542bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiProgram *)
546 if (
auto elementName = QFileInfo(m_logger->filePath()).baseName();
547 !elementName.isEmpty() && elementName[0].isUpper()) {
548 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
549 m_rootScopeImports.setType(elementName,
550 { m_exportedRootScope, QTypeRevision{ }, precedence });
556void QQmlJSImportVisitor::endVisit(UiProgram *)
558 for (
const auto &scope : std::as_const(m_objectBindingScopes)) {
559 breakInheritanceCycles(scope);
560 checkDeprecation(scope);
561 checkForComponentTypeWithProperties(scope);
564 for (
const auto &scope : std::as_const(m_objectDefinitionScopes)) {
565 if (m_pendingDefaultProperties.contains(scope))
567 breakInheritanceCycles(scope);
568 checkDeprecation(scope);
569 checkForComponentTypeWithProperties(scope);
572 const auto &keys = m_pendingDefaultProperties.keys();
573 for (
const auto &scope : keys) {
574 breakInheritanceCycles(scope);
575 checkDeprecation(scope);
576 checkForComponentTypeWithProperties(scope);
580 resolveGroupedProperties();
582 checkGroupedAndAttachedScopes();
585 processDefaultProperties();
586 processPropertyTypes();
587 processMethodTypes();
588 processPropertyBindings();
589 processPropertyBindingObjects();
590 checkRequiredProperties();
592 populateRuntimeFunctionIndicesForDocument();
597 ExpressionStatement *expr = cast<ExpressionStatement *>(statement);
599 if (!statement || !expr->expression)
602 switch (expr->expression->kind) {
603 case Node::Kind_StringLiteral:
604 return cast<StringLiteral *>(expr->expression)->value.toString();
605 case Node::Kind_NumericLiteral:
606 return cast<NumericLiteral *>(expr->expression)->value;
612QList<QQmlJSAnnotation> QQmlJSImportVisitor::parseAnnotations(QQmlJS::AST::UiAnnotationList *list)
615 QList<QQmlJSAnnotation> annotationList;
617 for (UiAnnotationList *item = list; item !=
nullptr; item = item->next) {
618 UiAnnotation *annotation = item->annotation;
620 QQmlJSAnnotation qqmljsAnnotation;
621 qqmljsAnnotation.name = buildName(annotation->qualifiedTypeNameId);
623 for (UiObjectMemberList *memberItem = annotation->initializer->members; memberItem !=
nullptr; memberItem = memberItem->next) {
624 switch (memberItem->member->kind) {
625 case Node::Kind_UiScriptBinding: {
626 auto *scriptBinding = QQmlJS::AST::cast<UiScriptBinding*>(memberItem->member);
627 qqmljsAnnotation.bindings[buildName(scriptBinding->qualifiedId)]
628 = bindingToVariant(scriptBinding->statement);
637 annotationList.append(qqmljsAnnotation);
640 return annotationList;
643void QQmlJSImportVisitor::setAllBindings()
645 using Key = std::pair<QQmlJSScope::ConstPtr, QString>;
646 QHash<Key, QQmlJS::SourceLocation> foundBindings;
648 for (
auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
650 const QQmlJSScope::Ptr type = it->owner;
651 if (!checkTypeResolved(type))
660 if (!type->isFullyResolved())
662 auto binding = it->create();
663 if (!binding.isValid())
665 type->addOwnPropertyBinding(binding, it->specifier);
668 if (binding.hasInterceptor() || binding.hasValueSource())
670 const QString propertyName = binding.propertyName();
671 QQmlJSMetaProperty property = type->property(propertyName);
674
675
676
677
678
679 if (!property.isValid())
683 if (property.isList())
686 const Key key = std::make_pair(type, propertyName);
687 auto sourceLocationIt = foundBindings.constFind(key);
688 if (sourceLocationIt == foundBindings.constEnd()) {
689 foundBindings.insert(key, binding.sourceLocation());
693 const QQmlJS::SourceLocation location = binding.sourceLocation();
694 m_logger->log(
"Duplicate binding on property '%1'"_L1.arg(propertyName),
695 qmlDuplicatePropertyBinding, location);
696 m_logger->log(
"Note: previous binding on '%1' here"_L1.arg(propertyName),
697 qmlDuplicatePropertyBinding, *sourceLocationIt,
true,
true, {},
702void QQmlJSImportVisitor::processDefaultProperties()
704 for (
auto it = m_pendingDefaultProperties.constBegin();
705 it != m_pendingDefaultProperties.constEnd(); ++it) {
706 QQmlJSScope::ConstPtr parentScope = it.key();
709 if (checkCustomParser(parentScope))
712 if (!checkTypeResolved(parentScope))
716
717
718
719
720
721
722
723
724
726 parentScope = parentScope->baseType();
728 const QString defaultPropertyName =
729 parentScope ? parentScope->defaultPropertyName() : QString();
731 if (defaultPropertyName.isEmpty()) {
734 bool isComponent =
false;
735 for (QQmlJSScope::ConstPtr s = parentScope; s; s = s->baseType()) {
736 if (s->internalName() == QStringLiteral(
"QQmlComponent")) {
742 if (!isComponent && checkTypeResolved(parentScope)) {
743 m_logger->log(QStringLiteral(
"Cannot assign to non-existent default property"),
744 qmlMissingProperty, it.value().constFirst()->sourceLocation());
750 const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
751 auto propType = defaultProp.type();
752 const auto handleUnresolvedDefaultProperty = [&](
const QQmlJSScope::ConstPtr &) {
754 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
755 "missing an import.")
756 .arg(defaultPropertyName)
757 .arg(defaultProp.typeName()),
758 qmlUnresolvedType, it.value().constFirst()->sourceLocation());
761 const auto assignToUnknownProperty = [&]() {
764 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it))
765 scope->setAssignedToUnknownProperty(
true);
768 if (propType.isNull()) {
769 if (checkTypeResolved(parentScope)
770 && QQmlJSScope::ownerOfProperty(parentScope, defaultPropertyName).scope->filePath()
771 != m_exportedRootScope->filePath()) {
772 handleUnresolvedDefaultProperty(propType);
774 assignToUnknownProperty();
778 if (it.value().size() > 1
779 && !defaultProp.isList()
780 && !propType->isListProperty()) {
782 QStringLiteral(
"Cannot assign multiple objects to a default non-list property"),
783 qmlNonListProperty, it.value().constFirst()->sourceLocation());
786 if (!checkTypeResolved(propType, handleUnresolvedDefaultProperty)) {
787 assignToUnknownProperty();
791 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
792 if (!checkTypeResolved(scope))
797 if (propType->canAssign(scope)) {
798 scope->setIsWrappedInImplicitComponent(
799 causesImplicitComponentWrapping(defaultProp, scope));
803 m_logger->log(QStringLiteral(
"Cannot assign to default property of incompatible type"),
804 qmlIncompatibleType, scope->sourceLocation());
809void QQmlJSImportVisitor::processPropertyTypes()
811 for (
const PendingPropertyType &type : std::as_const(m_pendingPropertyTypes)) {
812 Q_ASSERT(type.scope->hasOwnProperty(type.name));
814 auto property = type.scope->ownProperty(type.name);
816 if (
const auto propertyType = QQmlJSScope::findType(
817 property.typeName(), m_rootScopeImports.contextualTypes()).scope) {
818 property.setType(property.isList() ? propertyType->listType() : propertyType);
819 type.scope->addOwnProperty(property);
821 QString msg = property.typeName() +
' '_L1 + wasNotFound;
822 if (property.typeName() ==
"list"_L1)
823 msg +=
" list is not a type. It requires an element type argument (eg. list<int>)"_L1;
824 m_logger->log(msg, qmlImport, type.location);
829void QQmlJSImportVisitor::processMethodTypes()
831 const auto isEnumUsedAsType = [&](QStringView typeName,
const QQmlJS::SourceLocation &loc) {
832 if (typeName ==
"enum"_L1) {
836 const auto split = typeName.tokenize(u'.').toContainer<QVarLengthArray<QStringView, 4>>();
837 if (split.size() != 2)
840 const QStringView scopeName = split[0];
841 const QStringView enumName = split[1];
843 if (
auto scope = QQmlJSScope::findType(scopeName.toString(),
844 m_rootScopeImports.contextualTypes()).scope) {
845 if (scope->enumeration(enumName.toString()).isValid()) {
847 "QML enumerations are not types. Use int, or use double if the enum's underlying type does not fit into int."_L1,
848 qmlEnumsAreNotTypes, loc);
855 for (
const auto &method : std::as_const(m_pendingMethodTypeAnnotations)) {
856 for (
auto [it, end] = method.scope->mutableOwnMethodsRange(method.methodName); it != end; ++it) {
857 const auto [parameterBegin, parameterEnd] = it->mutableParametersRange();
858 for (
auto parameter = parameterBegin; parameter != parameterEnd; ++parameter) {
859 const int parameterIndex = parameter - parameterBegin;
860 if (isEnumUsedAsType(parameter->typeName(), method.locations[parameterIndex]))
862 if (
const auto parameterType = QQmlJSScope::findType(
863 parameter->typeName(), m_rootScopeImports.contextualTypes()).scope) {
864 parameter->setType({ parameterType });
867 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
868 .arg(parameter->typeName(), parameter->name(), it->methodName()),
869 qmlUnresolvedType, method.locations[parameter - parameterBegin]);
873 if (isEnumUsedAsType(it->returnTypeName(), method.locations.last()))
875 if (
const auto returnType = QQmlJSScope::findType(
876 it->returnTypeName(), m_rootScopeImports.contextualTypes()).scope) {
877 it->setReturnType({ returnType });
879 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
880 it->returnTypeName(), it->methodName()),
881 qmlUnresolvedType, method.locations.last());
889
890
891
892
893
894
895
899 for (QStringView propertyName: possiblyGroupedProperty.tokenize(u".")) {
900 property = scope->property(propertyName.toString());
901 if (property.isValid())
902 scope = property.type();
909void QQmlJSImportVisitor::processPropertyBindingObjects()
911 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundLiterals;
919 QSet<std::pair<QQmlJSScope::Ptr, QString>> visited;
920 for (
const PendingPropertyObjectBinding &objectBinding :
921 std::as_const(m_pendingPropertyObjectBindings)) {
923 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
924 if (visited.contains(uniqueBindingId))
926 visited.insert(uniqueBindingId);
928 auto [existingBindingsBegin, existingBindingsEnd] =
929 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
930 const bool hasLiteralBindings =
931 std::any_of(existingBindingsBegin, existingBindingsEnd,
932 [](
const QQmlJSMetaPropertyBinding &x) {
return x.hasLiteral(); });
933 if (hasLiteralBindings)
934 foundLiterals.insert(uniqueBindingId);
938 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundObjects;
939 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundInterceptors;
940 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundValueSources;
942 for (
const PendingPropertyObjectBinding &objectBinding :
943 std::as_const(m_pendingPropertyObjectBindings)) {
944 const QString propertyName = objectBinding.name;
945 QQmlJSScope::Ptr childScope = objectBinding.childScope;
947 const auto assignToUnknownProperty = [&]() {
950 childScope->setAssignedToUnknownProperty(
true);
954 if (!checkTypeResolved(objectBinding.scope)) {
955 assignToUnknownProperty();
959 QQmlJSMetaProperty property = resolveProperty(propertyName, objectBinding.scope);
961 if (!property.isValid()) {
962 warnMissingPropertyForBinding(propertyName, objectBinding.location);
965 const auto handleUnresolvedProperty = [&](
const QQmlJSScope::ConstPtr &) {
967 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
968 "missing an import.")
970 .arg(property.typeName()),
971 qmlUnresolvedType, objectBinding.location);
974 if (property.type().isNull()) {
975 assignToUnknownProperty();
976 if (checkTypeResolved(objectBinding.scope)
977 && QQmlJSScope::ownerOfProperty(objectBinding.scope, propertyName).scope->filePath()
978 != m_exportedRootScope->filePath()) {
981 handleUnresolvedProperty(property.type());
987 if (!checkTypeResolved(property.type(), handleUnresolvedProperty)) {
988 assignToUnknownProperty();
990 }
else if (!checkTypeResolved(childScope)) {
994 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
995 m_logger->log(QStringLiteral(
"Cannot assign object of type %1 to %2")
996 .arg(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope))
997 .arg(property.typeName()),
998 qmlIncompatibleType, childScope->sourceLocation());
1002 childScope->setIsWrappedInImplicitComponent(
1003 causesImplicitComponentWrapping(property, childScope));
1006 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
1007 const QString typeName = QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
1009 auto isConditionalBinding = [&]() ->
bool {
1011
1012
1013
1014
1015 return childScope->hasOwnPropertyBindings(u"enabled"_s)
1016 || childScope->hasOwnPropertyBindings(u"when"_s)
1017 || childScope->hasOwnPropertyBindings(u"running"_s);
1020 if (objectBinding.onToken) {
1021 if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueInterceptor"))) {
1022 if (foundInterceptors.contains(uniqueBindingId)) {
1023 if (!isConditionalBinding()) {
1024 m_logger->log(QStringLiteral(
"Duplicate interceptor on property \"%1\"")
1026 qmlDuplicatePropertyBinding, objectBinding.location);
1029 foundInterceptors.insert(uniqueBindingId);
1031 }
else if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueSource"))) {
1032 if (foundValueSources.contains(uniqueBindingId)) {
1033 if (!isConditionalBinding()) {
1034 m_logger->log(QStringLiteral(
"Duplicate value source on property \"%1\"")
1036 qmlDuplicatePropertyBinding, objectBinding.location);
1038 }
else if (foundObjects.contains(uniqueBindingId)
1039 || foundLiterals.contains(uniqueBindingId)) {
1040 if (!isConditionalBinding()) {
1041 m_logger->log(QStringLiteral(
"Cannot combine value source and binding on "
1044 qmlDuplicatePropertyBinding, objectBinding.location);
1047 foundValueSources.insert(uniqueBindingId);
1050 m_logger->log(QStringLiteral(
"On-binding for property \"%1\" has wrong type \"%2\"")
1053 qmlIncompatibleType, objectBinding.location);
1056 if (foundValueSources.contains(uniqueBindingId)) {
1057 if (!isConditionalBinding()) {
1059 QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
1061 qmlDuplicatePropertyBinding, objectBinding.location);
1064 foundObjects.insert(uniqueBindingId);
1072 QList<QQmlJSScope::ConstPtr> descendants;
1073 std::vector<QQmlJSScope::ConstPtr> toVisit;
1075 toVisit.push_back(scope);
1076 while (!toVisit.empty()) {
1077 const QQmlJSScope::ConstPtr s = toVisit.back();
1083 toVisit.insert(toVisit.end(), s->childScopesBegin(), s->childScopesEnd());
1090void QQmlJSImportVisitor::populatePropertyAliases()
1092 for (
const auto &alias : std::as_const(m_aliasDefinitions)) {
1093 const auto &[aliasScope, aliasName] = alias;
1094 if (aliasScope.isNull())
1097 auto property = aliasScope->ownProperty(aliasName);
1098 if (!property.isValid() || !property.aliasTargetScope())
1101 Property target(property.aliasTargetScope(), property.aliasTargetName());
1104 m_propertyAliases[target].append(alias);
1105 property = target.scope->property(target.name);
1106 target = Property(property.aliasTargetScope(), property.aliasTargetName());
1107 }
while (property.isAlias() && target.scope);
1111void QQmlJSImportVisitor::checkRequiredProperties()
1113 for (
const auto &required : std::as_const(m_requiredProperties)) {
1114 if (!required.scope->hasProperty(required.name)) {
1116 QStringLiteral(
"Property \"%1\" was marked as required but does not exist.")
1117 .arg(required.name),
1118 qmlRequired, required.location);
1122 const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
1123 const auto isComponentRoot = [&](
const QQmlJSScope::ConstPtr &requiredScope) {
1124 if (requiredScope->isWrappedInImplicitComponent())
1126 if (
const auto s = requiredScope->parentScope(); s && s->baseType() == compType)
1131 const auto scopeRequiresProperty = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1132 const QString &propName,
1133 const QQmlJSScope::ConstPtr &descendant) {
1134 if (!requiredScope->isPropertyLocallyRequired(propName))
1138 return QQmlJSScope::ownerOfProperty(requiredScope, propName).scope
1139 == QQmlJSScope::ownerOfProperty(descendant, propName).scope;
1142 const auto requiredHasBinding = [](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1143 const QQmlJSScope::ConstPtr &owner,
1144 const QString &propName) {
1145 for (
const auto &scope : scopesToSearch) {
1146 if (scope->property(propName).isAlias())
1148 const auto &[begin, end] = scope->ownPropertyBindings(propName);
1149 for (
auto it = begin; it != end; ++it) {
1151 const bool isRelevantBinding = QQmlSA::isRegularBindingType(it->bindingType())
1152 || it->bindingType() == QQmlSA::BindingType::Interceptor
1153 || it->bindingType() == QQmlSA::BindingType::ValueSource;
1154 if (!isRelevantBinding)
1156 if (QQmlJSScope::ownerOfProperty(scope, propName).scope == owner)
1164 const auto requiredUsedInRootAlias = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1165 const QString &propName) {
1166 const Property target(requiredScope, propName);
1169 const auto allAliasesToTargetIt = m_propertyAliases.constFind(target);
1170 if (allAliasesToTargetIt == m_propertyAliases.constEnd())
1177 allAliasesToTargetIt->constBegin(), allAliasesToTargetIt->constEnd(),
1178 [](
const Property &property) {
return property.scope->isFileRootComponent(); });
1181 const auto requiredSetThroughAlias = [&](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1182 const QQmlJSScope::ConstPtr &requiredScope,
1183 const QString &propName) {
1184 const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
1185 const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
1186 for (
const auto &alias : propertyAliases) {
1187 for (
const auto &s : scopesToSearch) {
1188 if (s->hasOwnPropertyBindings(alias.name))
1195 const auto warn = [
this](
const QQmlJSScope::ConstPtr &prevRequiredScope,
1196 const QString &propName,
const QQmlJSScope::ConstPtr &defScope,
1197 const QQmlJSScope::ConstPtr &requiredScope,
1198 const QQmlJSScope::ConstPtr &descendant) {
1199 const auto &propertyScope = QQmlJSScope::ownerOfProperty(requiredScope, propName).scope;
1200 const QString propertyScopeName = !propertyScope.isNull()
1201 ? QQmlJSUtils::getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
1204 std::optional<QQmlJSFixSuggestion> suggestion;
1206 QString message = QStringLiteral(
"Component is missing required property %1 from %2")
1208 .arg(propertyScopeName);
1209 if (requiredScope != descendant) {
1210 const QString requiredScopeName = prevRequiredScope
1211 ? QQmlJSUtils::getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
1214 if (!prevRequiredScope.isNull()) {
1215 if (
auto sourceScope = prevRequiredScope->baseType()) {
1216 suggestion = QQmlJSFixSuggestion{
1217 "%1:%2:%3: Property marked as required in %4."_L1
1218 .arg(sourceScope->filePath())
1219 .arg(sourceScope->sourceLocation().startLine)
1220 .arg(sourceScope->sourceLocation().startColumn)
1221 .arg(requiredScopeName),
1222 sourceScope->sourceLocation()
1226 if (sourceScope->isComposite())
1227 suggestion->setFilename(sourceScope->filePath());
1230 message +=
" (marked as required by %1)"_L1.arg(requiredScopeName);
1234 m_logger->log(message, qmlRequired, defScope->sourceLocation(),
true,
true, suggestion);
1237 populatePropertyAliases();
1239 for (
const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
1240 if (defScope->isFileRootComponent() || defScope->isInlineComponent()
1241 || defScope->componentRootStatus() != QQmlJSScope::IsComponentRoot::No
1242 || defScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
1246 QList<QQmlJSScope::ConstPtr> scopesToSearch;
1247 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
1248 const auto descendants = QList<QQmlJSScope::ConstPtr>()
1249 << scope << qmlScopeDescendants(scope);
1250 for (
const QQmlJSScope::ConstPtr &descendant : std::as_const(descendants)) {
1253 if (descendant != scope && descendant->isInlineComponent())
1255 scopesToSearch << descendant;
1256 const auto ownProperties = descendant->ownProperties();
1257 for (
auto propertyIt = ownProperties.constBegin();
1258 propertyIt != ownProperties.constEnd(); ++propertyIt) {
1259 const QString propName = propertyIt.key();
1260 if (descendant->hasOwnPropertyBindings(propName))
1263 QQmlJSScope::ConstPtr prevRequiredScope;
1264 for (
const QQmlJSScope::ConstPtr &requiredScope : std::as_const(scopesToSearch)) {
1267 if (isComponentRoot(requiredScope))
1270 if (!scopeRequiresProperty(requiredScope, propName, descendant)) {
1271 prevRequiredScope = requiredScope;
1275 if (requiredHasBinding(scopesToSearch, descendant, propName))
1278 if (requiredUsedInRootAlias(requiredScope, propName))
1281 if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
1284 warn(prevRequiredScope, propName, defScope, requiredScope, descendant);
1285 prevRequiredScope = requiredScope;
1293void QQmlJSImportVisitor::processPropertyBindings()
1295 for (
auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
1296 QQmlJSScope::Ptr scope = it.key();
1297 for (
auto &[visibilityScope, location, name] : it.value()) {
1298 if (!scope->hasProperty(name) && !m_logger->isDisabled()) {
1302 if (checkCustomParser(scope))
1306 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1308 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
1309 baseScope = baseScope->baseType()) {
1310 if (
auto suggestion = QQmlJSUtils::didYouMean(
1311 name, baseScope->ownProperties().keys(), m_logger->filePath(), location);
1312 suggestion.has_value()) {
1313 fixSuggestion = suggestion;
1318 if (checkTypeResolved(scope))
1319 warnMissingPropertyForBinding(name, location, fixSuggestion);
1323 const auto property = scope->property(name);
1324 if (!property.type()) {
1325 m_logger->log(QStringLiteral(
"No type found for property \"%1\". This may be due "
1326 "to a missing import statement or incomplete "
1329 qmlMissingType, location);
1332 const auto &annotations = property.annotations();
1334 const auto deprecationAnn =
1335 std::find_if(annotations.cbegin(), annotations.cend(),
1336 [](
const QQmlJSAnnotation &ann) {
return ann.isDeprecation(); });
1338 if (deprecationAnn != annotations.cend()) {
1339 const auto deprecation = deprecationAnn->deprecation();
1341 QString message = QStringLiteral(
"Binding on deprecated property \"%1\"")
1342 .arg(property.propertyName());
1344 if (!deprecation.reason.isEmpty())
1345 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1347 m_logger->log(message, qmlDeprecated, location);
1353void QQmlJSImportVisitor::checkSignal(
1354 const QQmlJSScope::ConstPtr &signalScope,
const QQmlJS::SourceLocation &location,
1355 const QString &handlerName,
const QStringList &handlerParameters)
1357 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1359 std::optional<QQmlJSMetaMethod> signalMethod;
1360 const auto setSignalMethod = [&](
const QQmlJSScope::ConstPtr &scope,
const QString &name) {
1361 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1362 if (!methods.isEmpty())
1363 signalMethod = methods[0];
1366 if (signal.has_value()) {
1367 if (signalScope->hasMethod(*signal)) {
1368 setSignalMethod(signalScope, *signal);
1369 }
else if (
auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1374 if (
auto notify = p->notify(); !notify.isEmpty()) {
1375 setSignalMethod(signalScope, notify);
1377 Q_ASSERT(!p->bindable().isEmpty());
1378 signalMethod = QQmlJSMetaMethod {};
1383 if (!signalMethod.has_value()) {
1388 if (signalScope->baseTypeName() == QStringLiteral(
"Connections")) {
1390 u"Implicitly defining \"%1\" as signal handler in Connections is deprecated. "
1391 u"Create a function instead: \"function %2(%3) { ... }\"."_s.arg(
1392 handlerName, handlerName, handlerParameters.join(u", ")),
1393 qmlUnqualified, location,
true,
true);
1397 auto baseType = QQmlJSScope::nonCompositeBaseType(signalScope);
1398 if (baseType && baseType->hasCustomParser())
1402 QStringLiteral(
"no matching signal found for handler \"%1\"").arg(handlerName),
1403 qmlUnqualified, location,
true,
true);
1407 const auto signalParameters = signalMethod->parameters();
1408 QHash<QString, qsizetype> parameterNameIndexes;
1410 for (
int i = 0, end = signalParameters.size(); i < end; i++) {
1411 auto &p = signalParameters[i];
1412 parameterNameIndexes[p.name()] = i;
1414 auto signalName = [&]() {
1416 return u" called %1"_s.arg(*signal);
1419 auto type = p.type();
1422 "Type %1 of parameter %2 in signal%3 was not found, but is required to compile "
1423 "%4."_L1.arg(p.typeName(), p.name(), signalName(), handlerName),
1424 qmlSignalParameters, location);
1428 if (type->isComposite())
1436 auto parameterName = [&]() {
1437 if (p.name().isEmpty())
1439 return u" called %1"_s.arg(p.name());
1441 switch (type->accessSemantics()) {
1442 case QQmlJSScope::AccessSemantics::Reference:
1444 m_logger->log(QStringLiteral(
"Type %1 of parameter%2 in signal%3 should be "
1445 "passed by pointer to be able to compile %4. ")
1446 .arg(p.typeName(), parameterName(), signalName(),
1448 qmlSignalParameters, location);
1450 case QQmlJSScope::AccessSemantics::Value:
1451 case QQmlJSScope::AccessSemantics::Sequence:
1455 "Type %1 of parameter%2 in signal%3 should be passed by "
1456 "value or const reference to be able to compile %4. ")
1457 .arg(p.typeName(), parameterName(), signalName(),
1459 qmlSignalParameters, location);
1461 case QQmlJSScope::AccessSemantics::None:
1463 QStringLiteral(
"Type %1 of parameter%2 in signal%3 required by the "
1464 "compilation of %4 cannot be used. ")
1465 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1466 qmlSignalParameters, location);
1471 if (handlerParameters.size() > signalParameters.size()) {
1472 m_logger->log(QStringLiteral(
"Signal handler for \"%2\" has more formal"
1473 " parameters than the signal it handles.")
1475 qmlSignalParameters, location);
1479 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1480 const QStringView handlerParameter = handlerParameters.at(i);
1481 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1482 if (it == parameterNameIndexes.constEnd())
1484 const qsizetype j = *it;
1489 m_logger->log(QStringLiteral(
"Parameter %1 to signal handler for \"%2\""
1490 " is called \"%3\". The signal has a parameter"
1491 " of the same name in position %4.")
1493 .arg(handlerName, handlerParameter)
1495 qmlSignalParameters, location);
1499void QQmlJSImportVisitor::addDefaultProperties()
1501 QQmlJSScope::ConstPtr parentScope = m_currentScope->parentScope();
1502 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1503 || m_currentScope->isInlineComponent())
1506 m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
1508 if (checkCustomParser(parentScope))
1512
1513
1514
1515
1516
1517
1518
1519
1520
1522 parentScope = parentScope->baseType();
1524 const QString defaultPropertyName =
1525 parentScope ? parentScope->defaultPropertyName() : QString();
1527 if (defaultPropertyName.isEmpty())
1532 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1533 binding.setObject(QQmlJSUtils::getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
1534 QQmlJSScope::ConstPtr(m_currentScope));
1535 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() {
return binding; },
1536 QQmlJSScope::UnnamedPropertyTarget });
1539void QQmlJSImportVisitor::breakInheritanceCycles(
const QQmlJSScope::Ptr &originalScope)
1541 QList<QQmlJSScope::ConstPtr> scopes;
1542 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1543 if (scopes.contains(scope)) {
1544 QString inheritenceCycle;
1545 for (
const auto &seen : std::as_const(scopes)) {
1546 inheritenceCycle.append(seen->baseTypeName());
1547 inheritenceCycle.append(QLatin1String(
" -> "));
1549 inheritenceCycle.append(scopes.first()->baseTypeName());
1551 const QString message = QStringLiteral(
"%1 is part of an inheritance cycle: %2")
1552 .arg(originalScope->baseTypeName(), inheritenceCycle);
1553 m_logger->log(message, qmlInheritanceCycle, scope->sourceLocation());
1554 originalScope->clearBaseType();
1555 originalScope->setBaseTypeError(message);
1559 scopes.append(scope);
1561 const auto newScope = scope->baseType();
1562 if (newScope.isNull()) {
1563 const QString error = scope->baseTypeError();
1564 const QString name = scope->baseTypeName();
1565 if (!error.isEmpty()) {
1566 m_logger->log(error, qmlImport, scope->sourceLocation(),
true,
true);
1567 }
else if (!name.isEmpty() && !m_unresolvedTypes.hasSeen(scope)
1568 && !m_logger->isDisabled()) {
1569 m_logger->log(name +
' '_L1 + wasNotFound +
' '_L1, qmlImport,
1570 scope->sourceLocation(),
true,
true,
1571 QQmlJSUtils::didYouMean(
1572 scope->baseTypeName(), m_rootScopeImports.types().keys(),
1573 m_logger->filePath(), scope->sourceLocation()));
1581void QQmlJSImportVisitor::checkDeprecation(
const QQmlJSScope::ConstPtr &originalScope)
1583 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1584 for (
const QQmlJSAnnotation &annotation : scope->annotations()) {
1585 if (annotation.isDeprecation()) {
1586 QQQmlJSDeprecation deprecation = annotation.deprecation();
1589 QStringLiteral(
"Type \"%1\" is deprecated").arg(scope->internalName());
1591 if (!deprecation.reason.isEmpty())
1592 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1594 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1600void QQmlJSImportVisitor::checkForComponentTypeWithProperties(
const QQmlJSScope::ConstPtr &scope)
1602 const QQmlJSScope::ConstPtr base = scope->baseType();
1609 if (base->isComposite())
1612 if (base->internalName() !=
"QQmlComponent"_L1)
1615 const auto ownProperties = scope->ownProperties();
1616 for (
const auto &property : ownProperties) {
1617 m_logger->log(
"Component objects cannot declare new properties."_L1,
1618 qmlSyntax, property.sourceLocation());
1622bool QQmlJSImportVisitor::checkCustomParser(
const QQmlJSScope::ConstPtr &scope)
1624 return scope->isInCustomParserParent();
1627void QQmlJSImportVisitor::flushPendingSignalParameters()
1629 const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
1630 for (
const QString ¶meter : handler.signalParameters) {
1631 safeInsertJSIdentifier(m_currentScope, parameter,
1632 { QQmlJSScope::JavaScriptIdentifier::Injected,
1633 m_pendingSignalHandler, std::nullopt,
false });
1635 m_pendingSignalHandler = QQmlJS::SourceLocation();
1639
1640
1641
1642
1643
1644
1645QQmlJSMetaMethod::RelativeFunctionIndex
1646QQmlJSImportVisitor::addFunctionOrExpression(
const QQmlJSScope::ConstPtr &scope,
1647 const QString &name)
1649 auto &array = m_functionsAndExpressions[scope];
1650 array.emplaceBack(name);
1657 for (
const auto &function : std::as_const(m_functionStack))
1658 m_innerFunctions[function]++;
1659 m_functionStack.push({ scope, name });
1661 return QQmlJSMetaMethod::RelativeFunctionIndex {
int(array.size() - 1) };
1665
1666
1667
1668
1669
1670
1671
1672
1673void QQmlJSImportVisitor::forgetFunctionExpression(
const QString &name)
1675 auto nameToVerify = name.isEmpty() ? u"<anon>"_s : name;
1676 Q_UNUSED(nameToVerify);
1677 Q_ASSERT(!m_functionStack.isEmpty());
1678 Q_ASSERT(m_functionStack.top().name == nameToVerify);
1679 m_functionStack.pop();
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
1695 const QQmlJSScope::Ptr &scope,
int count)
const
1697 const auto suitableScope = [](
const QQmlJSScope::Ptr &scope) {
1698 const auto type = scope->scopeType();
1699 return type == QQmlSA::ScopeType::QMLScope
1700 || type == QQmlSA::ScopeType::GroupedPropertyScope
1701 || type == QQmlSA::ScopeType::AttachedPropertyScope;
1704 if (!suitableScope(scope))
1707 auto it = m_functionsAndExpressions.constFind(scope);
1708 if (it == m_functionsAndExpressions.cend())
1711 const auto &functionsAndExpressions = *it;
1712 for (
const QString &functionOrExpression : functionsAndExpressions) {
1713 scope->addOwnRuntimeFunctionIndex(
1714 static_cast<QQmlJSMetaMethod::AbsoluteFunctionIndex>(count));
1731 count += m_innerFunctions.value({ scope, functionOrExpression }, 0);
1737void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument()
const
1740 const auto synthesize = [&](
const QQmlJSScope::Ptr ¤t) {
1741 count = synthesizeCompilationUnitRuntimeFunctionIndices(current, count);
1743 QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, synthesize);
1746bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
1748 if (m_pendingSignalHandler.isValid()) {
1749 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope, u"signalhandler"_s,
1750 ast->firstSourceLocation());
1751 flushPendingSignalParameters();
1756void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
1758 if (m_currentScope->scopeType() == QQmlSA::ScopeType::SignalHandlerFunctionScope) {
1765 const QQmlJS::SourceLocation &srcLocation);
1768 QQmlJSLogger *logger)
1770 QStringView namespaceName{ superType };
1771 namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
1772 logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
1774 qmlUncreatableType, location,
true,
true);
1777bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
1779 const QString superType = buildName(definition->qualifiedTypeNameId);
1781 const bool isRoot = !rootScopeIsValid();
1782 Q_ASSERT(!superType.isEmpty());
1789 const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
1790 const bool looksLikeGroupedProperty = !superType.front().isUpper();
1792 if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
1793 logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
1797 if (!looksLikeGroupedProperty) {
1799 enterEnvironment(QQmlSA::ScopeType::QMLScope, superType,
1800 definition->firstSourceLocation());
1802 enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
1803 definition->firstSourceLocation());
1804 m_currentScope->setIsRootFileComponentFlag(
true);
1807 const QTypeRevision revision = m_currentScope->baseTypeRevision();
1808 if (
auto base = m_currentScope->baseType(); base) {
1809 if (isRoot && base->internalName() == u"QQmlComponent") {
1810 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1811 definition->qualifiedTypeNameId->identifierToken,
true,
true);
1813 if (base->isSingleton() && m_currentScope->isComposite()) {
1814 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1815 m_currentScope->baseTypeName()),
1816 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1819 }
else if (!base->isCreatable()) {
1821 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1822 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1826 if (m_nextIsInlineComponent) {
1827 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1828 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1829 m_currentScope->setIsInlineComponent(
true);
1830 m_currentScope->setInlineComponentName(name);
1831 m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
1832 auto precedence = quint8(QQmlJS::PrecedenceValues::InlineComponent);
1833 m_rootScopeImports.setType(name, { m_currentScope, revision, precedence });
1834 m_nextIsInlineComponent =
false;
1837 addDefaultProperties();
1838 Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope);
1839 m_qmlTypes.append(m_currentScope);
1841 m_objectDefinitionScopes << m_currentScope;
1843 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType,
1844 definition->firstSourceLocation());
1845 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType,
1846 definition->firstSourceLocation()));
1847 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(),
1851 m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
1856void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
1858 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), usedTypes());
1862bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
1864 if (!std::holds_alternative<RootDocumentNameType>(m_currentRootName)) {
1865 m_logger->log(u"Nested inline components are not supported"_s, qmlSyntax,
1866 component->firstSourceLocation());
1870 const auto it = m_seenInlineComponents.constFind(component->name);
1871 if (it != m_seenInlineComponents.cend()) {
1872 m_logger->log(
"Duplicate inline component '%1'"_L1.arg(it.key()),
1873 qmlDuplicateInlineComponent, component->firstSourceLocation());
1874 m_logger->log(
"Note: previous component named '%1' here"_L1.arg(it.key()),
1875 qmlDuplicateInlineComponent, it.value(),
true,
true, {},
1876 component->firstSourceLocation().startLine);
1878 m_seenInlineComponents[component->name] = component->firstSourceLocation();
1881 m_nextIsInlineComponent =
true;
1882 m_currentRootName = component->name.toString();
1886void QQmlJSImportVisitor::endVisit(UiInlineComponent *component)
1888 m_currentRootName = RootDocumentNameType();
1889 if (m_nextIsInlineComponent) {
1890 m_logger->log(u"Inline component declaration must be followed by a typename"_s,
1891 qmlSyntax, component->firstSourceLocation());
1893 m_nextIsInlineComponent =
false;
1896bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
1898 switch (publicMember->type) {
1899 case UiPublicMember::Signal: {
1900 const QString signalName = publicMember->name.toString();
1901 UiParameterList *param = publicMember->parameters;
1902 QQmlJSMetaMethod method;
1903 method.setMethodType(QQmlJSMetaMethodType::Signal);
1904 method.setReturnTypeName(QStringLiteral(
"void"));
1905 method.setMethodName(signalName);
1906 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1907 publicMember->lastSourceLocation()));
1908 method.setMethodIndex(
1909 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
1911 method.addParameter(
1912 QQmlJSMetaParameter(
1913 param->name.toString(),
1914 param->type ? param->type->toString() : QString()
1916 param = param->next;
1918 m_currentScope->addOwnMethod(method);
1921 case UiPublicMember::Property: {
1922 const QString propertyName = publicMember->name.toString();
1923 QString typeName = buildName(publicMember->memberType);
1924 if (typeName.contains(u'.') && typeName.front().isLower()) {
1925 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1929 const bool isAlias = (typeName == u"alias"_s);
1931 auto tryParseAlias = [&]() {
1933 if (!publicMember->statement) {
1934 m_logger->log(QStringLiteral(
"Invalid alias expression - an initializer is needed."),
1935 qmlSyntax, publicMember->memberType->firstSourceLocation());
1938 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1939 auto node = expression ? expression->expression :
nullptr;
1940 auto fex = cast<FieldMemberExpression *>(node);
1943 aliasExpr.prepend(u'.' + fex->name.toString());
1944 fex = cast<FieldMemberExpression *>(node);
1947 if (
const auto idExpression = cast<IdentifierExpression *>(node)) {
1948 aliasExpr.prepend(idExpression->name.toString());
1952 m_logger->log(QStringLiteral(
"Invalid alias expression. Only IDs and field "
1953 "member expressions can be aliased."),
1954 qmlSyntax, publicMember->statement->firstSourceLocation());
1959 QQmlJSMetaProperty prop;
1960 prop.setPropertyName(propertyName);
1961 prop.setIsList(publicMember->typeModifier == QLatin1String(
"list"));
1962 prop.setIsWritable(!publicMember->isReadonly());
1963 prop.setIsFinal(publicMember->isFinal());
1964 prop.setIsVirtual(publicMember->isVirtual());
1965 prop.setIsOverride(publicMember->isOverride());
1966 prop.setAliasExpression(aliasExpr);
1967 prop.setSourceLocation(
1968 combine(publicMember->firstSourceLocation(), publicMember->colonToken));
1970 isAlias ? QQmlJSScope::ConstPtr() : m_rootScopeImports.type(typeName).scope;
1972 const auto factory = type.factory();
1974 prop.setType(prop.isList() ? (factory ? QQmlJSScope::Ptr{ } : type->listType()) : type);
1975 const QString internalName = factory ? factory->internalName() : type->internalName();
1976 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
1982 Q_ASSERT(type.factory() == factory);
1983 }
else if (!isAlias) {
1984 m_pendingPropertyTypes << PendingPropertyType { m_currentScope, prop.propertyName(),
1985 publicMember->firstSourceLocation() };
1986 prop.setTypeName(typeName);
1988 prop.setAnnotations(parseAnnotations(publicMember->annotations));
1989 if (publicMember->isDefaultMember())
1990 m_currentScope->setOwnDefaultPropertyName(propertyName);
1991 prop.setIndex(m_currentScope->ownProperties().size());
1992 m_currentScope->addOwnProperty(prop);
1994 QQmlJSMetaMethod method(
1995 QQmlSignalNames::propertyNameToChangedSignalName(propertyName), u"void"_s);
1996 method.setMethodType(QQmlJSMetaMethodType::Signal);
1997 method.setIsImplicitQmlPropertyChangeSignal(
true);
1998 method.setMethodIndex(
1999 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2000 m_currentScope->addOwnMethod(method);
2002 if (publicMember->isRequired())
2003 m_currentScope->setPropertyLocallyRequired(prop.propertyName(),
true);
2005 BindingExpressionParseResult parseResult = BindingExpressionParseResult::Invalid;
2009 parseBindingExpression(publicMember->name.toString(), publicMember->statement,
2015 if (parseResult == BindingExpressionParseResult::Script) {
2016 Q_ASSERT(!m_savedBindingOuterScope);
2017 m_savedBindingOuterScope = m_currentScope;
2018 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope, QStringLiteral(
"binding"),
2019 publicMember->statement->firstSourceLocation());
2029void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
2031 if (m_savedBindingOuterScope) {
2032 m_currentScope = m_savedBindingOuterScope;
2033 m_savedBindingOuterScope = {};
2035 forgetFunctionExpression(publicMember->name.toString());
2039bool QQmlJSImportVisitor::visit(UiRequired *required)
2041 const QString name = required->name.toString();
2043 m_requiredProperties << RequiredProperty { m_currentScope, name,
2044 required->firstSourceLocation() };
2046 m_currentScope->setPropertyLocallyRequired(name,
true);
2050void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
2052 using namespace QQmlJS::AST;
2053 auto name = fexpr->name.toString();
2054 if (!name.isEmpty()) {
2055 QQmlJSMetaMethod method(name);
2056 method.setMethodType(QQmlJSMetaMethodType::Method);
2057 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
2059 if (!m_pendingMethodAnnotations.isEmpty()) {
2060 method.setAnnotations(m_pendingMethodAnnotations);
2061 m_pendingMethodAnnotations.clear();
2065 const bool parseTypes = m_scopesById.signaturesAreEnforced();
2067 bool formalsFullyTyped = parseTypes;
2068 bool anyFormalTyped =
false;
2069 PendingMethodTypeAnnotations pending{ m_currentScope, name, {} };
2072 for (
auto formals = fexpr->formals; formals; formals = formals->next) {
2073 PatternElement *e = formals->element;
2076 if (e->typeAnnotation && (e->bindingTarget || e->initializer))
2077 m_logger->log(
"Type annotations on default parameters are not supported"_L1,
2079 combine(e->firstSourceLocation(), e->lastSourceLocation()));
2082 if (
const auto *formals = parseTypes ? fexpr->formals :
nullptr) {
2083 const auto parameters = formals->formals();
2084 for (
const auto ¶meter : parameters) {
2085 const QString type = parameter.typeAnnotation
2086 ? parameter.typeAnnotation->type->toString()
2088 if (type.isEmpty()) {
2089 formalsFullyTyped =
false;
2090 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral(
"var")));
2091 pending.locations.emplace_back();
2093 anyFormalTyped =
true;
2094 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
2095 pending.locations.append(
2096 combine(parameter.typeAnnotation->firstSourceLocation(),
2097 parameter.typeAnnotation->lastSourceLocation()));
2103 method.setIsJavaScriptFunction(!formalsFullyTyped);
2109 if (parseTypes && fexpr->typeAnnotation) {
2110 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
2111 pending.locations.append(combine(fexpr->typeAnnotation->firstSourceLocation(),
2112 fexpr->typeAnnotation->lastSourceLocation()));
2113 }
else if (anyFormalTyped) {
2114 method.setReturnTypeName(QStringLiteral(
"void"));
2116 method.setReturnTypeName(QStringLiteral(
"var"));
2119 const auto &locs = pending.locations;
2120 if (std::any_of(locs.cbegin(), locs.cend(), [](
const auto &loc) {
return loc.isValid(); }))
2121 m_pendingMethodTypeAnnotations << pending;
2123 method.setCompiledFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
2124 method.setMethodIndex(
2125 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2127 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
2129 const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
2130 ? fexpr->identifierToken
2131 : fexpr->functionToken;
2132 safeInsertJSIdentifier(m_currentScope, name,
2133 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2134 functionLocation, method.returnTypeName(),
2137 m_currentScope->addOwnMethod(method);
2139 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
2141 addFunctionOrExpression(m_currentScope, QStringLiteral(
"<anon>"));
2142 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral(
"<anon>"),
2143 fexpr->firstSourceLocation());
2147bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
2149 visitFunctionExpressionHelper(fexpr);
2153void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
2155 forgetFunctionExpression(fexpr->name.toString());
2159bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
2161 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
2165bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
2167 if (!fdecl->name.isEmpty()) {
2168 const QString name = fdecl->name.toString();
2169 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
2170 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
2171 fdecl->identifierToken);
2172 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
2173 previousDeclaration->location);
2176 visitFunctionExpressionHelper(fdecl);
2180void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
2182 forgetFunctionExpression(fdecl->name.toString());
2186bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
2188 QQmlJSMetaProperty prop;
2189 prop.setPropertyName(ast->name.toString());
2190 m_currentScope->addOwnProperty(prop);
2191 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2192 ast->firstSourceLocation());
2196void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
2202 QQmlJS::AST::ArgumentList *args)
2204 QStringView contextString;
2205 QStringView mainString;
2206 QStringView commentString;
2207 auto registerContextString = [&](QStringView string) {
2208 contextString = string;
2211 auto registerMainString = [&](QStringView string) {
2212 mainString = string;
2215 auto registerCommentString = [&](QStringView string) {
2216 commentString = string;
2219 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
2220 QV4::CompiledData::TranslationData data) {
2221 if (type == QV4::CompiledData::Binding::Type_Translation) {
2222 binding.setTranslation(mainString, commentString, contextString, data.number);
2223 }
else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
2224 binding.setTranslationId(mainString, data.number);
2226 binding.setStringLiteral(mainString);
2229 QmlIR::tryGeneratingTranslationBindingBase(
2231 registerMainString, registerCommentString, registerContextString, finalizeBinding);
2234QQmlJSImportVisitor::BindingExpressionParseResult
2235QQmlJSImportVisitor::parseBindingExpression(
2236 const QString &name,
const QQmlJS::AST::Statement *statement,
2237 const UiPublicMember *associatedPropertyDefinition)
2239 if (statement ==
nullptr)
2240 return BindingExpressionParseResult::Invalid;
2242 const auto *exprStatement = cast<
const ExpressionStatement *>(statement);
2244 if (exprStatement ==
nullptr) {
2245 QQmlJS::SourceLocation location = statement->firstSourceLocation();
2247 if (
const auto *block = cast<
const Block *>(statement); block && block->statements) {
2248 location = block->statements->firstSourceLocation();
2251 QQmlJSMetaPropertyBinding binding(location, name);
2252 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2253 QQmlSA::ScriptBindingKind::PropertyBinding, ScriptValue_Function);
2254 m_bindings.append(UnfinishedBinding {
2256 [binding = std::move(binding)]() {
return binding; }
2258 return BindingExpressionParseResult::Script;
2261 auto expr = exprStatement->expression;
2262 QQmlJSMetaPropertyBinding binding(
2263 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
2266 ScriptBindingValueType scriptBindingValuetype = ScriptValue_Unknown;
2268 switch (expr->kind) {
2269 case Node::Kind_TrueLiteral:
2270 binding.setBoolLiteral(
true);
2272 case Node::Kind_FalseLiteral:
2273 binding.setBoolLiteral(
false);
2275 case Node::Kind_NullExpression:
2276 binding.setNullLiteral();
2278 case Node::Kind_IdentifierExpression: {
2279 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
2281 if (idExpr->name == u"undefined")
2282 scriptBindingValuetype = ScriptValue_Undefined;
2285 case Node::Kind_FunctionDeclaration:
2286 case Node::Kind_FunctionExpression:
2287 case Node::Kind_Block: {
2288 scriptBindingValuetype = ScriptValue_Function;
2291 case Node::Kind_NumericLiteral:
2292 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
2294 case Node::Kind_StringLiteral:
2295 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
2297 case Node::Kind_RegExpLiteral:
2298 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
2300 case Node::Kind_TemplateLiteral: {
2301 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
2302 Q_ASSERT(templateLit);
2303 if (templateLit->hasNoSubstitution) {
2304 binding.setStringLiteral(templateLit->value);
2306 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2307 QQmlSA::ScriptBindingKind::PropertyBinding);
2308 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
2309 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
2310 expression->accept(
this);
2316 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
2317 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
2318 binding.setNumberLiteral(-lit->value);
2319 }
else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
2320 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
2321 handleTranslationBinding(binding, base->name, call->arguments);
2326 if (!binding.isValid()) {
2328 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2329 QQmlSA::ScriptBindingKind::PropertyBinding,
2330 scriptBindingValuetype);
2332 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
2335 if (binding.bindingType() == QQmlSA::BindingType::Translation
2336 || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
2337 return BindingExpressionParseResult::Translation;
2339 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
2340 return BindingExpressionParseResult::Script;
2342 if (associatedPropertyDefinition)
2343 handleLiteralBinding(binding, associatedPropertyDefinition);
2345 return BindingExpressionParseResult::Literal;
2348bool QQmlJSImportVisitor::isImportPrefix(QString prefix)
const
2350 if (prefix.isEmpty() || !prefix.front().isUpper())
2353 return m_rootScopeImports.isNullType(prefix);
2356void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
2358 if (m_currentScope->scopeType() != QQmlJSScope::ScopeType::QMLScope) {
2359 m_logger->log(u"id declarations are only allowed in objects"_s, qmlSyntax,
2360 scriptBinding->statement->firstSourceLocation());
2363 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
2365 m_logger->log(u"id must be followed by an identifier"_s, qmlSyntax,
2366 scriptBinding->statement->firstSourceLocation());
2369 const QString name = [&]() {
2370 if (
const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2371 return idExpression->name.toString();
2372 else if (
const auto *idString = cast<StringLiteral *>(statement->expression)) {
2373 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2374 idString->firstSourceLocation());
2375 return idString->value.toString();
2377 m_logger->log(u"Failed to parse id"_s, qmlSyntax,
2378 statement->expression->firstSourceLocation());
2382 if (!name.isEmpty() && !name.front().isLower() && name.front() != u'_') {
2383 m_logger->log(u"Id must start with a lower case letter or an '_'"_s, qmlSyntax,
2384 statement->expression->firstSourceLocation());
2387 m_currentScope->setIdSourceLocation(combine(scriptBinding->statement->firstSourceLocation(),
2388 scriptBinding->statement->lastSourceLocation()));
2389 if (m_scopesById.existsAnywhereInDocument(name)) {
2392 breakInheritanceCycles(m_currentScope);
2393 m_scopesById.possibleScopes(
2394 name, m_currentScope, QQmlJSScopesByIdOption::Default,
2395 [&](
const QQmlJSScope::ConstPtr &otherScopeWithID,
2396 QQmlJSScopesById::Confidence confidence) {
2398 Q_UNUSED(confidence);
2400 auto otherLocation = otherScopeWithID->sourceLocation();
2404 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2405 name, QString::number(otherLocation.startLine),
2406 QString::number(otherLocation.startColumn)),
2407 qmlSyntaxDuplicateIds,
2408 scriptBinding->firstSourceLocation());
2409 return QQmlJSScopesById::CallbackResult::ContinueSearch;
2412 if (!name.isEmpty())
2413 m_scopesById.insert(name, m_currentScope);
2416void QQmlJSImportVisitor::handleLiteralBinding(
const QQmlJSMetaPropertyBinding &binding,
2417 const UiPublicMember *associatedPropertyDefinition)
2421 Q_UNUSED(associatedPropertyDefinition);
2425
2426
2427
2428
2429
2432 const QQmlJS::SourceLocation &srcLocation)
2434 const auto createBinding = [=]() {
2435 const QQmlJSScope::ScopeType type = scope->scopeType();
2442 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2443 const bool alreadyHasBinding =
std::any_of(propertyBindings.first, propertyBindings.second,
2444 [&](
const QQmlJSMetaPropertyBinding &binding) {
2445 return binding.bindingType() == bindingType;
2447 if (alreadyHasBinding)
2448 return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
2451 if (type == QQmlSA::ScopeType::GroupedPropertyScope)
2452 binding.setGroupBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2454 binding.setAttachedBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2457 return { scope->parentScope(), createBinding };
2460bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
2462 Q_ASSERT(!m_savedBindingOuterScope);
2463 Q_ASSERT(!m_thisScriptBindingIsJavaScript);
2464 m_savedBindingOuterScope = m_currentScope;
2465 const auto id = scriptBinding->qualifiedId;
2466 if (!id->next && id->name == QLatin1String(
"id")) {
2467 handleIdDeclaration(scriptBinding);
2474 for (; group->next; group = group->next) {
2475 const QString name = group->name.toString();
2479 if (group == id && isImportPrefix(name)) {
2480 prefix = name + u'.';
2484 const bool isAttachedProperty = name.front().isUpper();
2485 if (isAttachedProperty) {
2487 enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
2488 group->firstSourceLocation());
2491 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
2492 group->firstSourceLocation());
2494 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
2495 group->firstSourceLocation()));
2500 const auto name = group->name.toString();
2504 const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
2506 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2507 m_propertyBindings[m_currentScope].append(
2508 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2510 auto result = parseBindingExpression(name, scriptBinding->statement);
2511 m_thisScriptBindingIsJavaScript = (result == BindingExpressionParseResult::Script);
2513 const auto statement = scriptBinding->statement;
2514 QStringList signalParameters;
2516 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2517 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2518 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2519 signalParameters << formal->element->bindingIdentifier.toString();
2523 QQmlJSMetaMethod scopeSignal;
2524 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2525 if (!methods.isEmpty())
2526 scopeSignal = methods[0];
2528 const auto firstSourceLocation = statement->firstSourceLocation();
2529 bool hasMultilineStatementBody =
2530 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2531 m_pendingSignalHandler = firstSourceLocation;
2532 m_signalHandlers.insert(firstSourceLocation,
2533 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2537 const auto index = addFunctionOrExpression(m_currentScope, name);
2538 const auto createBinding = [
2540 scope = m_currentScope,
2541 signalName = *signal,
2544 firstSourceLocation,
2545 groupLocation = group->firstSourceLocation(),
2546 signalParameters]() {
2548 Q_ASSERT(scope->isFullyResolved());
2549 QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
2550 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2551 if (!methods.isEmpty()) {
2552 kind = QQmlSA::ScriptBindingKind::SignalHandler;
2553 checkSignal(scope, groupLocation, name, signalParameters);
2554 }
else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2555 kind = QQmlSA::ScriptBindingKind::ChangeHandler;
2556 checkSignal(scope, groupLocation, name, signalParameters);
2557 }
else if (scope->hasProperty(name)) {
2560 kind = QQmlSA::ScriptBindingKind::PropertyBinding;
2561 m_signalHandlers.remove(firstSourceLocation);
2564 checkSignal(scope, groupLocation, name, signalParameters);
2567 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2568 binding.setScriptBinding(index, kind, ScriptValue_Function);
2571 m_bindings.append(UnfinishedBinding { m_currentScope, createBinding });
2572 m_thisScriptBindingIsJavaScript =
true;
2578 while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
2579 || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
2584 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope,
2586 scriptBinding->statement->firstSourceLocation());
2588 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope,
2590 scriptBinding->statement->firstSourceLocation());
2596void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
2598 if (m_savedBindingOuterScope) {
2599 m_currentScope = m_savedBindingOuterScope;
2600 m_savedBindingOuterScope = {};
2606 if (m_thisScriptBindingIsJavaScript) {
2607 m_thisScriptBindingIsJavaScript =
false;
2608 Q_ASSERT(!m_functionStack.isEmpty());
2609 m_functionStack.pop();
2613bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
2615 createAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2616 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2617 arrayBinding->firstSourceLocation());
2618 m_currentScope->setIsArrayScope(
true);
2622void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
2629 const auto children = m_currentScope->childScopes();
2632 const int scopesEnteredCounter = openAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2633 auto guard = qScopeGuard([
this, scopesEnteredCounter]() {
2634 for (
int i = 0; i < scopesEnteredCounter; ++i)
2638 if (checkCustomParser(m_currentScope)) {
2644 auto group = arrayBinding->qualifiedId;
2645 for (; group->next; group = group->next) { }
2646 const QString propertyName = group->name.toString();
2649 for (
auto element = arrayBinding->members; element; element = element->next, ++i) {
2650 const auto &type = children[i];
2651 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2652 m_logger->log(u"Declaring an object which is not a Qml object"
2653 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2656 m_pendingPropertyObjectBindings
2657 << PendingPropertyObjectBinding { m_currentScope, type, propertyName,
2658 element->firstSourceLocation(),
false };
2659 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2660 binding.setObject(QQmlJSUtils::getScopeName(type, QQmlSA::ScopeType::QMLScope),
2661 QQmlJSScope::ConstPtr(type));
2662 m_bindings.append(UnfinishedBinding {
2664 [binding = std::move(binding)]() {
return binding; },
2665 QQmlJSScope::ListPropertyTarget
2670bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
2672 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2673 qmlEnum.setIsQml(
true);
2674 qmlEnum.setLineNumber(uied->enumToken.startLine);
2675 for (
const auto *member = uied->members; member; member = member->next) {
2676 qmlEnum.addKey(member->member.toString());
2677 qmlEnum.addValue(
int(member->value));
2679 m_currentScope->addOwnEnumeration(qmlEnum);
2683QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromHost(
2684 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2686 QFileInfo fileInfo(path);
2687 if (!fileInfo.exists()) {
2688 m_logger->log(
"File or directory you are trying to import does not exist: %1."_L1.arg(path),
2689 qmlImport, location);
2693 if (fileInfo.isFile()) {
2694 const auto scope = m_importer->importFile(path);
2695 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2696 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2697 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2698 addImportWithLocation(actualPrefix, location,
false);
2702 if (fileInfo.isDir()) {
2703 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2704 auto scopes = m_importer->importDirectory(path, precedence, prefix);
2705 const auto types = scopes.types();
2706 const auto warnings = scopes.warnings();
2707 m_rootScopeImports.add(std::move(scopes));
2708 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2709 addImportWithLocation(*it, location, !warnings.isEmpty());
2714 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2716 qmlImport, location);
2720QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromQrc(
2721 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2723 Q_ASSERT(path.startsWith(u':'));
2724 const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper();
2728 const auto pathNoColon = QStringView(path).mid(1);
2729 if (mapper->isFile(pathNoColon)) {
2730 const auto entry = m_importer->resourceFileMapper()->entry(
2731 QQmlJSResourceFileMapper::resourceFileFilter(pathNoColon.toString()));
2732 const auto scope = m_importer->importFile(entry.filePath);
2733 const QString actualPrefix =
2734 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2735 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2736 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2737 addImportWithLocation(actualPrefix, location,
false);
2741 auto scopes = m_importer->importDirectory(path, quint8(QQmlJS::PrecedenceValues::Default), prefix);
2742 const auto types = scopes.types();
2743 const auto warnings = scopes.warnings();
2744 m_rootScopeImports.add(std::move(scopes));
2745 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2746 addImportWithLocation(*it, location, !warnings.isEmpty());
2750bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
2753 QString prefix = QLatin1String(
"");
2754 if (import->asToken.isValid()) {
2755 prefix += import->importId;
2756 if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
2757 m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
2759 qmlImport, import->importIdToken,
true,
true);
2761 m_seenModuleQualifiers.append(prefix);
2764 const QString filename = import->fileName.toString();
2765 if (!filename.isEmpty()) {
2766 const QUrl url(filename);
2767 const QString scheme = url.scheme();
2768 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2769 if (scheme ==
""_L1) {
2770 QFileInfo fileInfo(url.path());
2771 QString absolute = fileInfo.isRelative()
2772 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2774 auto warnings = absolute.startsWith(u':')
2775 ? importFromQrc(absolute, prefix, importLocation)
2776 : importFromHost(absolute, prefix, importLocation);
2777 processImportWarnings(
"path \"%1\""_L1.arg(url.path()), warnings, importLocation);
2779 }
else if (scheme ==
"file"_L1) {
2780 auto warnings = importFromHost(url.path(), prefix, importLocation);
2781 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2783 }
else if (scheme ==
"qrc"_L1) {
2784 auto warnings = importFromQrc(
":"_L1 + url.path(), prefix, importLocation);
2785 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2788 m_logger->log(
"Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2789 qmlImport, import->firstSourceLocation());
2793 const QString path = buildName(import->importUri);
2795 QStringList staticModulesProvided;
2797 auto imported = m_importer->importModule(
2798 path, quint8(QQmlJS::PrecedenceValues::Default), prefix,
2799 import->version ? import->version->version : QTypeRevision(), &staticModulesProvided);
2800 const auto types = imported.types();
2801 const auto warnings = imported.warnings();
2802 m_rootScopeImports.add(std::move(imported));
2803 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2804 addImportWithLocation(*it, import->firstSourceLocation(), !warnings.isEmpty());
2806 if (prefix.isEmpty()) {
2807 for (
const QString &staticModule : std::as_const(staticModulesProvided))
2808 addStaticImportWithLocation(path, import->firstSourceLocation(), path != staticModule);
2811 processImportWarnings(
2812 QStringLiteral(
"module \"%1\"").arg(path), warnings, import->firstSourceLocation());
2816#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
2818void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2820 for (
const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2825void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2827 assign(pragma->value);
2831bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
2833 if (pragma->name == u"Strict"_s) {
2838 if (!m_logger->wasCategoryChanged(qmlCompiler))
2839 m_logger->setCategorySeverity(qmlCompiler, QQmlJS::WarningSeverity::Warning);
2840 }
else if (pragma->name == u"ComponentBehavior") {
2841 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2842 if (value == u"Bound") {
2843 m_scopesById.setComponentsAreBound(
true);
2844 }
else if (value == u"Unbound") {
2845 m_scopesById.setComponentsAreBound(
false);
2847 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2848 qmlSyntax, pragma->firstSourceLocation());
2851 }
else if (pragma->name == u"FunctionSignatureBehavior") {
2852 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2853 if (value == u"Enforced") {
2854 m_scopesById.setSignaturesAreEnforced(
true);
2855 }
else if (value == u"Ignored") {
2856 m_scopesById.setSignaturesAreEnforced(
false);
2859 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2860 qmlSyntax, pragma->firstSourceLocation());
2863 }
else if (pragma->name == u"ValueTypeBehavior") {
2864 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2865 if (value == u"Copy") {
2867 }
else if (value == u"Reference") {
2869 }
else if (value == u"Addressable") {
2870 m_scopesById.setValueTypesAreAddressable(
true);
2871 }
else if (value == u"Inaddressable") {
2872 m_scopesById.setValueTypesAreAddressable(
false);
2873 }
else if (value == u"Assertable") {
2874 m_scopesById.setValueTypesAreAssertable(
true);
2875 }
else if (value == u"Inassertable") {
2876 m_scopesById.setValueTypesAreAssertable(
false);
2878 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2879 qmlSyntax, pragma->firstSourceLocation());
2887void QQmlJSImportVisitor::throwRecursionDepthError()
2889 m_logger->log(QStringLiteral(
"Maximum statement or expression depth exceeded"),
2890 qmlRecursionDepthErrors, QQmlJS::SourceLocation());
2893bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
2895 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2896 ast->firstSourceLocation());
2900void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
2905bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
2907 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"forloop"),
2908 ast->firstSourceLocation());
2912void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
2917bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
2919 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"foreachloop"),
2920 ast->firstSourceLocation());
2924void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
2929bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
2931 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"block"),
2932 ast->firstSourceLocation());
2934 if (m_pendingSignalHandler.isValid())
2935 flushPendingSignalParameters();
2940void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
2945bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
2947 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"case"),
2948 ast->firstSourceLocation());
2952void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
2957bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
2959 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"catch"),
2960 catchStatement->firstSourceLocation());
2964void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
2969bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
2971 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"with"),
2972 ast->firstSourceLocation());
2974 m_logger->log(QStringLiteral(
"with statements are strongly discouraged in QML "
2975 "and might cause false positives when analysing unqualified "
2977 qmlWith, ast->firstSourceLocation());
2982void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
2987bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
2989 const auto &boundedNames = fpl->boundNames();
2990 for (
auto const &boundName : boundedNames) {
2992 std::optional<QString> typeName;
2993 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
2994 if (Type *type = annotation->type)
2995 typeName = type->toString();
2996 safeInsertJSIdentifier(m_currentScope, boundName.id,
2997 { QQmlJSScope::JavaScriptIdentifier::Parameter,
2998 boundName.location, typeName,
false });
3003void QQmlJSImportVisitor::createAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3005 bool needsResolution =
false;
3006 int scopesEnteredCounter = 0;
3008 for (
auto group = propertyName; group->next; group = group->next) {
3009 const QString idName = group->name.toString();
3011 if (idName.isEmpty())
3014 if (group == propertyName && isImportPrefix(idName)) {
3015 prefix = idName + u'.';
3019 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3020 : QQmlSA::ScopeType::GroupedPropertyScope;
3023 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3025 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + idName,
3026 group->firstSourceLocation()));
3028 ++scopesEnteredCounter;
3029 needsResolution = needsResolution || !exists;
3034 for (
int i=0; i < scopesEnteredCounter; ++i) {
3039 if (needsResolution) {
3040 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(),
3045bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
3049 Q_ASSERT(uiob->qualifiedTypeNameId);
3051 const QString typeName = buildName(uiob->qualifiedTypeNameId);
3052 if (typeName.front().isLower() && typeName.contains(u'.')) {
3053 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
3056 createAttachedAndGroupedScopes(uiob->qualifiedId);
3058 enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
3059 uiob->qualifiedTypeNameId->identifierToken);
3061 m_qmlTypes.append(m_currentScope);
3062 m_objectBindingScopes << m_currentScope;
3066int QQmlJSImportVisitor::openAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3069 int scopesEnteredCounter = 0;
3070 auto group = propertyName;
3071 for (; group->next; group = group->next) {
3072 const QString idName = group->name.toString();
3074 if (idName.isEmpty())
3077 if (group == propertyName && isImportPrefix(idName)) {
3078 prefix = idName + u'.';
3082 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3083 : QQmlSA::ScopeType::GroupedPropertyScope;
3085 [[maybe_unused]]
bool exists =
3086 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3088 scopesEnteredCounter++;
3092 return scopesEnteredCounter;
3095void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
3097 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), usedTypes());
3099 const QQmlJSScope::Ptr childScope = m_currentScope;
3102 const int scopesEnteredCounter = openAttachedAndGroupedScopes(uiob->qualifiedId);
3107 auto group = uiob->qualifiedId;
3108 for (; group->next; group = group->next) { }
3109 const QString propertyName = group->name.toString();
3111 if (m_currentScope->isNameDeferred(propertyName)) {
3112 bool foundIds =
false;
3113 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
3115 while (!childScopes.isEmpty()) {
3116 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
3117 m_scopesById.possibleIds(
3118 scope, scope, QQmlJSScopesByIdOption::Default,
3119 [&](
const QString &id, QQmlJSScopesById::Confidence confidence) {
3122 Q_UNUSED(confidence);
3124 return QQmlJSScopesById::CallbackResult::StopSearch;
3127 childScopes << scope->childScopes();
3132 u"Cannot defer property assignment to \"%1\". Assigning an id to an object or one of its sub-objects bound to a deferred property will make the assignment immediate."_s
3134 qmlDeferredPropertyId, uiob->firstSourceLocation());
3138 if (checkCustomParser(m_currentScope)) {
3142 m_pendingPropertyObjectBindings
3143 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
3144 uiob->firstSourceLocation(), uiob->hasOnToken };
3146 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
3147 if (uiob->hasOnToken) {
3148 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
3149 binding.setInterceptor(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3150 QQmlJSScope::ConstPtr(childScope));
3152 binding.setValueSource(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3153 QQmlJSScope::ConstPtr(childScope));
3156 binding.setObject(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3157 QQmlJSScope::ConstPtr(childScope));
3159 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
3162 for (
int i = 0; i < scopesEnteredCounter; ++i)
3166bool QQmlJSImportVisitor::visit(ExportDeclaration *)
3168 Q_ASSERT(rootScopeIsValid());
3169 Q_ASSERT(m_exportedRootScope != m_globalScope);
3170 Q_ASSERT(m_currentScope == m_globalScope);
3171 m_currentScope = m_exportedRootScope;
3175void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
3177 Q_ASSERT(rootScopeIsValid());
3178 m_currentScope = m_exportedRootScope->parentScope();
3179 Q_ASSERT(m_currentScope == m_globalScope);
3182bool QQmlJSImportVisitor::visit(ESModule *module)
3184 Q_ASSERT(!rootScopeIsValid());
3185 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"module"),
3186 module->firstSourceLocation());
3187 m_currentScope->setIsScript(
true);
3188 importBaseModules();
3193void QQmlJSImportVisitor::endVisit(ESModule *)
3195 QQmlJSScope::resolveTypes(m_exportedRootScope, m_rootScopeImports.contextualTypes(),
3199bool QQmlJSImportVisitor::visit(Program *program)
3201 Q_ASSERT(m_globalScope == m_currentScope);
3202 Q_ASSERT(!rootScopeIsValid());
3203 enterRootScope(QQmlSA::ScopeType::JSFunctionScope, u"script"_s, program->firstSourceLocation());
3204 m_exportedRootScope->setIsScript(
true);
3205 importBaseModules();
3209void QQmlJSImportVisitor::endVisit(Program *)
3211 QQmlJSScope::resolveTypes(m_exportedRootScope, m_rootScopeImports.contextualTypes(),
3215bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
3218 if (element->isVariableDeclaration()) {
3219 QQmlJS::AST::BoundNames names;
3220 element->boundNames(&names);
3221 for (
const auto &name : std::as_const(names)) {
3222 std::optional<QString> typeName;
3223 if (TypeAnnotation *annotation = name.typeAnnotation.data())
3224 if (Type *type = annotation->type)
3225 typeName = type->toString();
3226 using Kind = QQmlJSScope::JavaScriptIdentifier::Kind;
3227 const Kind kind = (element->scope == QQmlJS::AST::VariableScope::Var)
3228 ? Kind::FunctionScoped
3229 : Kind::LexicalScoped;
3230 const QString variableName = name.id;
3231 if (kind == Kind::LexicalScoped) {
3232 const QQmlJS::SourceLocation location = element->firstSourceLocation();
3233 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(variableName)) {
3234 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(variableName), qmlSyntax,
3236 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(variableName), qmlSyntax,
3237 previousDeclaration->location);
3240 const bool isConstVariable = element->scope == QQmlJS::AST::VariableScope::Const;
3241 const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
3243 { (element->scope == QQmlJS::AST::VariableScope::Var)
3244 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
3245 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
3246 name.location, typeName,