150QQmlJSImportVisitor::QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
151 const QString &implicitImportDirectory,
152 const QStringList &qmldirFiles)
153 : m_implicitImportDirectory(implicitImportDirectory),
154 m_qmldirFiles(qmldirFiles),
155 m_exportedRootScope(QQmlJSScope::resetForReparse(importer->importFile(logger->filePath()))),
156 m_importer(importer),
158 m_rootScopeImports(QQmlJS::ContextualTypes(
159 QQmlJS::ContextualTypes::QML, { }, { },
160 importer->builtinInternalNames().contextualTypes().arrayType()),
166 m_exportedRootScope->setScopeType(QQmlSA::ScopeType::QMLScope);
167 m_exportedRootScope->setBaseTypeName(QQmlJSImporter::s_inProcessMarker);
168 m_exportedRootScope->setFilePath(m_logger->filePath());
169 m_exportedRootScope->setIsComposite(
true);
172
173
174
175
176
177 auto globalScope = QQmlJSScope::create();
178 globalScope->setInternalName(u"global"_s);
179 globalScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
181 QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
182 QQmlJSScope::JavaScriptIdentifier::LexicalScoped, QQmlJS::SourceLocation(), std::nullopt,
186 QV4::Compiler::Codegen::forEachGlobalName([&](QLatin1StringView globalName) {
187 globalScope->insertJSIdentifier(globalName, globalJavaScript);
190 m_globalScope = globalScope;
191 m_currentScope = globalScope;
194QQmlJSImportVisitor::~QQmlJSImportVisitor() =
default;
196void QQmlJSImportVisitor::populateCurrentScope(
197 QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
199 m_currentScope->setScopeType(type);
200 m_currentScope->setIsComposite(
true);
201 m_currentScope->setFilePath(m_logger->filePath());
202 m_currentScope->setSourceLocation(location);
203 setScopeName(m_currentScope, type, name);
204 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
207void QQmlJSImportVisitor::enterRootScope(QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
209 Q_ASSERT(m_currentScope == m_globalScope);
210 QQmlJSScope::reparent(m_currentScope, m_exportedRootScope);
211 m_currentScope = m_exportedRootScope;
212 populateCurrentScope(type, name, location);
215void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type,
const QString &name,
216 const QQmlJS::SourceLocation &location)
218 QQmlJSScope::Ptr newScope = QQmlJSScope::create();
219 QQmlJSScope::reparent(m_currentScope, newScope);
220 m_currentScope = std::move(newScope);
221 populateCurrentScope(type, name, location);
224bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type,
226 const QQmlJS::SourceLocation &location)
228 Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope
229 || type == QQmlSA::ScopeType::AttachedPropertyScope);
231 const auto pred = [&](
const QQmlJSScope::ConstPtr &s) {
234 return s->internalName() == name;
236 const auto scopes = m_currentScope->childScopes();
239 auto it = std::find_if(scopes.begin(), scopes.end(), pred);
240 if (it == scopes.end()) {
242 enterEnvironment(type, name, location);
246 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, *it);
247 m_currentScope = *it;
251void QQmlJSImportVisitor::leaveEnvironment()
253 m_currentScope = m_currentScope->parentScope();
256void QQmlJSImportVisitor::warnUnresolvedType(
const QQmlJSScope::ConstPtr &type)
const
258 m_logger->log(QStringLiteral(
"Type %1 is used but it is not resolved")
259 .arg(QQmlJSUtils::getScopeName(type, type->scopeType())),
260 qmlUnresolvedType, type->sourceLocation());
263void QQmlJSImportVisitor::warnMissingPropertyForBinding(
264 const QString &property,
const QQmlJS::SourceLocation &location,
265 const std::optional<QQmlJSFixSuggestion> &fixSuggestion)
267 m_logger->log(QStringLiteral(
"Could not find property \"%1\".").arg(property),
268 qmlMissingProperty, location,
true,
true, fixSuggestion);
276bool QQmlJSImportVisitor::resolveAliasProperty(
const QQmlJSScope::Ptr &object,
277 const QQmlJSMetaProperty &property)
279 bool doRequeue =
false;
280 QStringList components = property.aliasExpression().split(u'.');
281 QQmlJSMetaProperty targetProperty;
283 bool foundProperty =
false;
284 bool hasWarnedAlready =
false;
287 QQmlJSScope::ConstPtr type = m_scopesById.scope(components.takeFirst(), object);
288 QQmlJSScope::ConstPtr typeScope;
289 if (!type.isNull()) {
290 foundProperty =
true;
297 while (type && !components.isEmpty()) {
298 const QString name = components.takeFirst();
300 if (!checkTypeResolved(type)) {
301 hasWarnedAlready =
true;
306 if (!type->hasProperty(name)) {
307 foundProperty =
false;
312 const auto target = type->property(name);
313 if (!target.type()) {
314 if (target.isAlias()) {
319 hasWarnedAlready = QQmlJSScope::ownerOfProperty(type, name).scope->filePath()
320 == m_exportedRootScope->filePath();
324 type = target.type();
325 targetProperty = target;
332 if (!hasWarnedAlready) {
334 m_logger->log(QStringLiteral(
"Cannot deduce type of alias \"%1\"")
335 .arg(property.propertyName()),
336 qmlMissingType, property.sourceLocation());
339 QStringLiteral(
"Cannot resolve alias \"%1\"").arg(property.propertyName()),
340 qmlUnresolvedAlias, property.sourceLocation());
344 Q_ASSERT(property.index() >= 0);
345 object->addOwnProperty(property);
348 QQmlJSMetaProperty newProperty = property;
349 newProperty.setType(type);
351 newProperty.setIsList(targetProperty.isList());
352 newProperty.setIsWritable(targetProperty.isWritable());
353 newProperty.setIsFinal(targetProperty.isFinal());
354 newProperty.setIsPointer(targetProperty.isPointer());
356 const bool onlyId = !property.aliasExpression().contains(u'.');
358 newProperty.setAliasTargetScope(type);
359 newProperty.setAliasTargetName(QStringLiteral(
"id-only-alias"));
361 const auto &ownerScope =
362 QQmlJSScope::ownerOfProperty(typeScope, targetProperty.propertyName()).scope;
363 newProperty.setAliasTargetScope(ownerScope);
364 newProperty.setAliasTargetName(targetProperty.propertyName());
367 if (
const QString internalName = type->internalName(); !internalName.isEmpty())
368 newProperty.setTypeName(internalName);
370 Q_ASSERT(newProperty.index() >= 0);
371 object->addOwnProperty(newProperty);
372 m_aliasDefinitions.append({ object, property.propertyName() });
377void QQmlJSImportVisitor::resolveAliases()
379 QQueue<QQmlJSScope::Ptr> objects;
380 objects.enqueue(m_exportedRootScope);
382 qsizetype lastRequeueLength = std::numeric_limits<qsizetype>::max();
383 QQueue<QQmlJSScope::Ptr> requeue;
385 while (!objects.isEmpty()) {
386 const QQmlJSScope::Ptr object = objects.dequeue();
387 const auto properties = object->ownProperties();
389 bool doRequeue =
false;
390 for (
const auto &property : properties) {
391 if (!property.isAlias() || !property.type().isNull())
393 doRequeue |= resolveAliasProperty(object, property);
396 const auto childScopes = object->childScopes();
397 for (
const auto &childScope : childScopes)
398 objects.enqueue(childScope);
401 requeue.enqueue(object);
403 if (objects.isEmpty() && requeue.size() < lastRequeueLength) {
404 lastRequeueLength = requeue.size();
405 objects.swap(requeue);
409 while (!requeue.isEmpty()) {
410 const QQmlJSScope::Ptr object = requeue.dequeue();
411 const auto properties = object->ownProperties();
412 for (
const auto &property : properties) {
413 if (!property.isAlias() || property.type())
415 m_logger->log(QStringLiteral(
"Alias \"%1\" is part of an alias cycle")
416 .arg(property.propertyName()),
417 qmlAliasCycle, property.sourceLocation());
422void QQmlJSImportVisitor::resolveGroupProperties()
424 QQueue<QQmlJSScope::Ptr> objects;
425 objects.enqueue(m_exportedRootScope);
427 while (!objects.isEmpty()) {
428 const QQmlJSScope::Ptr object = objects.dequeue();
429 const auto childScopes = object->childScopes();
430 for (
const auto &childScope : childScopes) {
431 if (mayBeUnresolvedGroupedProperty(childScope)) {
432 const QString name = childScope->internalName();
433 if (object->isNameDeferred(name)) {
434 const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
435 if (!deferred.isNull()) {
436 QQmlJSScope::resolveGroup(childScope, deferred,
437 m_rootScopeImports.contextualTypes(),
440 }
else if (
const QQmlJSScope::ConstPtr propType = object->property(name).type()) {
441 QQmlJSScope::resolveGroup(childScope, propType,
442 m_rootScopeImports.contextualTypes(), usedTypes());
445 objects.enqueue(childScope);
450QString QQmlJSImportVisitor::implicitImportDirectory(
const QString &localFile,
451 const QQmlJSResourceFileMapper *mapper)
454 const auto resource = mapper->entry(
455 QQmlJSResourceFileMapper::localFileFilter(localFile));
456 if (resource.isValid()) {
457 return resource.resourcePath.contains(u'/')
458 ? (u':' + resource.resourcePath.left(
459 resource.resourcePath.lastIndexOf(u'/') + 1))
460 : QStringLiteral(
":/");
464 return QFileInfo(localFile).canonicalPath() + u'/';
467void QQmlJSImportVisitor::processImportWarnings(
468 const QString &what,
const QList<QQmlJS::DiagnosticMessage> &warnings,
469 const QQmlJS::SourceLocation &srcLocation)
471 if (warnings.isEmpty())
474 QList<QQmlJS::DiagnosticMessage> importWarnings = warnings;
477 auto fileSelectorWarningsIt = std::partition(importWarnings.begin(), importWarnings.end(),
478 [](
const QQmlJS::DiagnosticMessage &message) {
479 return message.type != QtMsgType::QtInfoMsg;
481 if (fileSelectorWarningsIt != importWarnings.end()) {
482 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImportFileSelector,
484 m_logger->processMessages(QSpan(fileSelectorWarningsIt, importWarnings.end()),
485 qmlImportFileSelector, srcLocation);
488 if (fileSelectorWarningsIt == importWarnings.begin())
491 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImport,
493 m_logger->processMessages(QSpan(importWarnings.begin(), fileSelectorWarningsIt), qmlImport,
497void QQmlJSImportVisitor::importBaseModules()
499 Q_ASSERT(m_rootScopeImports.isEmpty());
500 m_rootScopeImports = m_importer->importHardCodedBuiltins();
502
503
504
505
506
507 m_rootScopeImports.setCurrentFileSelector(
508 QQmlJSUtils::fileSelectorFor(m_exportedRootScope));
510 const QQmlJS::SourceLocation invalidLoc;
511 const auto types = m_rootScopeImports.types();
512 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
513 addImportWithLocation(*it, invalidLoc,
false);
515 if (!m_qmldirFiles.isEmpty())
516 m_rootScopeImports.addWarnings(m_importer->importQmldirs(m_qmldirFiles));
520 if (!m_logger->filePath().endsWith(u".qmltypes"_s)) {
521 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
522 m_rootScopeImports.add(m_importer->importDirectory(m_implicitImportDirectory, precedence));
527 if (
const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper()) {
528 const QStringList resourcePaths = mapper->resourcePaths(QQmlJSResourceFileMapper::Filter {
529 m_logger->filePath(), QStringList(), QQmlJSResourceFileMapper::Resource });
530 for (
const QString &path : resourcePaths) {
531 const qsizetype lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
534 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
535 m_rootScopeImports.add(m_importer->importDirectory(path.first(lastSlash),
541 processImportWarnings(QStringLiteral(
"base modules"), m_rootScopeImports.warnings());
544bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiProgram *)
548 if (
auto elementName = QFileInfo(m_logger->filePath()).baseName();
549 !elementName.isEmpty() && elementName[0].isUpper()) {
550 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
551 m_rootScopeImports.setType(elementName,
552 { m_exportedRootScope, QTypeRevision{ }, precedence });
558void QQmlJSImportVisitor::endVisit(UiProgram *)
560 for (
const auto &scope : std::as_const(m_objectBindingScopes)) {
561 breakInheritanceCycles(scope);
562 checkDeprecation(scope);
563 checkForComponentTypeWithProperties(scope);
566 for (
const auto &scope : std::as_const(m_objectDefinitionScopes)) {
567 if (m_pendingDefaultProperties.contains(scope))
569 breakInheritanceCycles(scope);
570 checkDeprecation(scope);
571 checkForComponentTypeWithProperties(scope);
574 const auto &keys = m_pendingDefaultProperties.keys();
575 for (
const auto &scope : keys) {
576 breakInheritanceCycles(scope);
577 checkDeprecation(scope);
578 checkForComponentTypeWithProperties(scope);
582 resolveGroupProperties();
584 checkGroupedAndAttachedScopes();
587 processDefaultProperties();
588 processPropertyTypes();
589 processMethodTypes();
590 processPropertyBindings();
591 processPropertyBindingObjects();
592 checkRequiredProperties();
594 populateRuntimeFunctionIndicesForDocument();
599 ExpressionStatement *expr = cast<ExpressionStatement *>(statement);
601 if (!statement || !expr->expression)
604 switch (expr->expression->kind) {
605 case Node::Kind_StringLiteral:
606 return cast<StringLiteral *>(expr->expression)->value.toString();
607 case Node::Kind_NumericLiteral:
608 return cast<NumericLiteral *>(expr->expression)->value;
614QList<QQmlJSAnnotation> QQmlJSImportVisitor::parseAnnotations(QQmlJS::AST::UiAnnotationList *list)
617 QList<QQmlJSAnnotation> annotationList;
619 for (UiAnnotationList *item = list; item !=
nullptr; item = item->next) {
620 UiAnnotation *annotation = item->annotation;
622 QQmlJSAnnotation qqmljsAnnotation;
623 qqmljsAnnotation.name = buildName(annotation->qualifiedTypeNameId);
625 for (UiObjectMemberList *memberItem = annotation->initializer->members; memberItem !=
nullptr; memberItem = memberItem->next) {
626 switch (memberItem->member->kind) {
627 case Node::Kind_UiScriptBinding: {
628 auto *scriptBinding = QQmlJS::AST::cast<UiScriptBinding*>(memberItem->member);
629 qqmljsAnnotation.bindings[buildName(scriptBinding->qualifiedId)]
630 = bindingToVariant(scriptBinding->statement);
639 annotationList.append(qqmljsAnnotation);
642 return annotationList;
645void QQmlJSImportVisitor::setAllBindings()
647 using Key = std::pair<QQmlJSScope::ConstPtr, QString>;
648 QHash<Key, QQmlJS::SourceLocation> foundBindings;
650 for (
auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
652 const QQmlJSScope::Ptr type = it->owner;
653 if (!checkTypeResolved(type))
662 if (!type->isFullyResolved())
664 auto binding = it->create();
665 if (!binding.isValid())
667 type->addOwnPropertyBinding(binding, it->specifier);
670 if (binding.hasInterceptor() || binding.hasValueSource())
672 const QString propertyName = binding.propertyName();
673 QQmlJSMetaProperty property = type->property(propertyName);
676
677
678
679
680
681 if (!property.isValid())
685 if (property.isList())
688 const Key key = std::make_pair(type, propertyName);
689 auto sourceLocationIt = foundBindings.constFind(key);
690 if (sourceLocationIt == foundBindings.constEnd()) {
691 foundBindings.insert(key, binding.sourceLocation());
695 const QQmlJS::SourceLocation location = binding.sourceLocation();
696 m_logger->log(
"Duplicate binding on property '%1'"_L1.arg(propertyName),
697 qmlDuplicatePropertyBinding, location);
698 m_logger->log(
"Note: previous binding on '%1' here"_L1.arg(propertyName),
699 qmlDuplicatePropertyBinding, *sourceLocationIt,
true,
true, {},
704void QQmlJSImportVisitor::processDefaultProperties()
706 for (
auto it = m_pendingDefaultProperties.constBegin();
707 it != m_pendingDefaultProperties.constEnd(); ++it) {
708 QQmlJSScope::ConstPtr parentScope = it.key();
711 if (checkCustomParser(parentScope))
714 if (!checkTypeResolved(parentScope))
718
719
720
721
722
723
724
725
726
728 parentScope = parentScope->baseType();
730 const QString defaultPropertyName =
731 parentScope ? parentScope->defaultPropertyName() : QString();
733 if (defaultPropertyName.isEmpty()) {
736 bool isComponent =
false;
737 for (QQmlJSScope::ConstPtr s = parentScope; s; s = s->baseType()) {
738 if (s->internalName() == QStringLiteral(
"QQmlComponent")) {
744 if (!isComponent && checkTypeResolved(parentScope)) {
745 m_logger->log(QStringLiteral(
"Cannot assign to non-existent default property"),
746 qmlMissingProperty, it.value().constFirst()->sourceLocation());
752 const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
753 auto propType = defaultProp.type();
754 const auto handleUnresolvedDefaultProperty = [&](
const QQmlJSScope::ConstPtr &) {
756 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
757 "missing an import.")
758 .arg(defaultPropertyName)
759 .arg(defaultProp.typeName()),
760 qmlUnresolvedType, it.value().constFirst()->sourceLocation());
763 const auto assignToUnknownProperty = [&]() {
766 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it))
767 scope->setAssignedToUnknownProperty(
true);
770 if (propType.isNull()) {
771 if (checkTypeResolved(parentScope)
772 && QQmlJSScope::ownerOfProperty(parentScope, defaultPropertyName).scope->filePath()
773 != m_exportedRootScope->filePath()) {
774 handleUnresolvedDefaultProperty(propType);
776 assignToUnknownProperty();
780 if (it.value().size() > 1
781 && !defaultProp.isList()
782 && !propType->isListProperty()) {
784 QStringLiteral(
"Cannot assign multiple objects to a default non-list property"),
785 qmlNonListProperty, it.value().constFirst()->sourceLocation());
788 if (!checkTypeResolved(propType, handleUnresolvedDefaultProperty)) {
789 assignToUnknownProperty();
793 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
794 if (!checkTypeResolved(scope))
799 if (propType->canAssign(scope)) {
800 scope->setIsWrappedInImplicitComponent(
801 causesImplicitComponentWrapping(defaultProp, scope));
805 m_logger->log(QStringLiteral(
"Cannot assign to default property of incompatible type"),
806 qmlIncompatibleType, scope->sourceLocation());
811void QQmlJSImportVisitor::processPropertyTypes()
813 for (
const PendingPropertyType &type : std::as_const(m_pendingPropertyTypes)) {
814 Q_ASSERT(type.scope->hasOwnProperty(type.name));
816 auto property = type.scope->ownProperty(type.name);
818 if (
const auto propertyType = QQmlJSScope::findType(
819 property.typeName(), m_rootScopeImports.contextualTypes()).scope) {
820 property.setType(property.isList() ? propertyType->listType() : propertyType);
821 type.scope->addOwnProperty(property);
823 QString msg = property.typeName() +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports;
824 if (property.typeName() ==
"list"_L1)
825 msg +=
" list is not a type. It requires an element type argument (eg. list<int>)"_L1;
826 m_logger->log(msg, qmlImport, type.location);
831void QQmlJSImportVisitor::processMethodTypes()
833 const auto isEnumUsedAsType = [&](QStringView typeName,
const QQmlJS::SourceLocation &loc) {
834 if (typeName ==
"enum"_L1) {
838 const auto split = typeName.tokenize(u'.').toContainer<QVarLengthArray<QStringView, 4>>();
839 if (split.size() != 2)
842 const QStringView scopeName = split[0];
843 const QStringView enumName = split[1];
845 if (
auto scope = QQmlJSScope::findType(scopeName.toString(),
846 m_rootScopeImports.contextualTypes()).scope) {
847 if (scope->enumeration(enumName.toString()).isValid()) {
849 "QML enumerations are not types. Use int, or use double if the enum's underlying type does not fit into int."_L1,
850 qmlEnumsAreNotTypes, loc);
857 for (
const auto &method : std::as_const(m_pendingMethodTypeAnnotations)) {
858 for (
auto [it, end] = method.scope->mutableOwnMethodsRange(method.methodName); it != end; ++it) {
859 const auto [parameterBegin, parameterEnd] = it->mutableParametersRange();
860 for (
auto parameter = parameterBegin; parameter != parameterEnd; ++parameter) {
861 const int parameterIndex = parameter - parameterBegin;
862 if (isEnumUsedAsType(parameter->typeName(), method.locations[parameterIndex]))
864 if (
const auto parameterType = QQmlJSScope::findType(
865 parameter->typeName(), m_rootScopeImports.contextualTypes()).scope) {
866 parameter->setType({ parameterType });
869 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
870 .arg(parameter->typeName(), parameter->name(), it->methodName()),
871 qmlUnresolvedType, method.locations[parameter - parameterBegin]);
875 if (isEnumUsedAsType(it->returnTypeName(), method.locations.last()))
877 if (
const auto returnType = QQmlJSScope::findType(
878 it->returnTypeName(), m_rootScopeImports.contextualTypes()).scope) {
879 it->setReturnType({ returnType });
881 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
882 it->returnTypeName(), it->methodName()),
883 qmlUnresolvedType, method.locations.last());
891
892
893
894
895
896
897
901 for (QStringView propertyName: possiblyGroupedProperty.tokenize(u".")) {
902 property = scope->property(propertyName.toString());
903 if (property.isValid())
904 scope = property.type();
911void QQmlJSImportVisitor::processPropertyBindingObjects()
913 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundLiterals;
921 QSet<std::pair<QQmlJSScope::Ptr, QString>> visited;
922 for (
const PendingPropertyObjectBinding &objectBinding :
923 std::as_const(m_pendingPropertyObjectBindings)) {
925 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
926 if (visited.contains(uniqueBindingId))
928 visited.insert(uniqueBindingId);
930 auto [existingBindingsBegin, existingBindingsEnd] =
931 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
932 const bool hasLiteralBindings =
933 std::any_of(existingBindingsBegin, existingBindingsEnd,
934 [](
const QQmlJSMetaPropertyBinding &x) {
return x.hasLiteral(); });
935 if (hasLiteralBindings)
936 foundLiterals.insert(uniqueBindingId);
940 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundObjects;
941 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundInterceptors;
942 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundValueSources;
944 for (
const PendingPropertyObjectBinding &objectBinding :
945 std::as_const(m_pendingPropertyObjectBindings)) {
946 const QString propertyName = objectBinding.name;
947 QQmlJSScope::Ptr childScope = objectBinding.childScope;
949 const auto assignToUnknownProperty = [&]() {
952 childScope->setAssignedToUnknownProperty(
true);
956 if (!checkTypeResolved(objectBinding.scope)) {
957 assignToUnknownProperty();
961 QQmlJSMetaProperty property = resolveProperty(propertyName, objectBinding.scope);
963 if (!property.isValid()) {
964 warnMissingPropertyForBinding(propertyName, objectBinding.location);
967 const auto handleUnresolvedProperty = [&](
const QQmlJSScope::ConstPtr &) {
969 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
970 "missing an import.")
972 .arg(property.typeName()),
973 qmlUnresolvedType, objectBinding.location);
976 if (property.type().isNull()) {
977 assignToUnknownProperty();
978 if (checkTypeResolved(objectBinding.scope)
979 && QQmlJSScope::ownerOfProperty(objectBinding.scope, propertyName).scope->filePath()
980 != m_exportedRootScope->filePath()) {
983 handleUnresolvedProperty(property.type());
989 if (!checkTypeResolved(property.type(), handleUnresolvedProperty)) {
990 assignToUnknownProperty();
992 }
else if (!checkTypeResolved(childScope)) {
996 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
997 m_logger->log(QStringLiteral(
"Cannot assign object of type %1 to %2")
998 .arg(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope))
999 .arg(property.typeName()),
1000 qmlIncompatibleType, childScope->sourceLocation());
1004 childScope->setIsWrappedInImplicitComponent(
1005 causesImplicitComponentWrapping(property, childScope));
1008 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
1009 const QString typeName = QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
1011 auto isConditionalBinding = [&]() ->
bool {
1013
1014
1015
1016
1017 return childScope->hasOwnPropertyBindings(u"enabled"_s)
1018 || childScope->hasOwnPropertyBindings(u"when"_s)
1019 || childScope->hasOwnPropertyBindings(u"running"_s);
1022 if (objectBinding.onToken) {
1023 if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueInterceptor"))) {
1024 if (foundInterceptors.contains(uniqueBindingId)) {
1025 if (!isConditionalBinding()) {
1026 m_logger->log(QStringLiteral(
"Duplicate interceptor on property \"%1\"")
1028 qmlDuplicatePropertyBinding, objectBinding.location);
1031 foundInterceptors.insert(uniqueBindingId);
1033 }
else if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueSource"))) {
1034 if (foundValueSources.contains(uniqueBindingId)) {
1035 if (!isConditionalBinding()) {
1036 m_logger->log(QStringLiteral(
"Duplicate value source on property \"%1\"")
1038 qmlDuplicatePropertyBinding, objectBinding.location);
1040 }
else if (foundObjects.contains(uniqueBindingId)
1041 || foundLiterals.contains(uniqueBindingId)) {
1042 if (!isConditionalBinding()) {
1043 m_logger->log(QStringLiteral(
"Cannot combine value source and binding on "
1046 qmlDuplicatePropertyBinding, objectBinding.location);
1049 foundValueSources.insert(uniqueBindingId);
1052 m_logger->log(QStringLiteral(
"On-binding for property \"%1\" has wrong type \"%2\"")
1055 qmlIncompatibleType, objectBinding.location);
1058 if (foundValueSources.contains(uniqueBindingId)) {
1059 if (!isConditionalBinding()) {
1061 QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
1063 qmlDuplicatePropertyBinding, objectBinding.location);
1066 foundObjects.insert(uniqueBindingId);
1074 QList<QQmlJSScope::ConstPtr> descendants;
1075 std::vector<QQmlJSScope::ConstPtr> toVisit;
1077 toVisit.push_back(scope);
1078 while (!toVisit.empty()) {
1079 const QQmlJSScope::ConstPtr s = toVisit.back();
1085 toVisit.insert(toVisit.end(), s->childScopesBegin(), s->childScopesEnd());
1092void QQmlJSImportVisitor::populatePropertyAliases()
1094 for (
const auto &alias : std::as_const(m_aliasDefinitions)) {
1095 const auto &[aliasScope, aliasName] = alias;
1096 if (aliasScope.isNull())
1099 auto property = aliasScope->ownProperty(aliasName);
1100 if (!property.isValid() || !property.aliasTargetScope())
1103 Property target(property.aliasTargetScope(), property.aliasTargetName());
1106 m_propertyAliases[target].append(alias);
1107 property = target.scope->property(target.name);
1108 target = Property(property.aliasTargetScope(), property.aliasTargetName());
1109 }
while (property.isAlias() && target.scope);
1113void QQmlJSImportVisitor::checkRequiredProperties()
1115 for (
const auto &required : std::as_const(m_requiredProperties)) {
1116 if (!required.scope->hasProperty(required.name)) {
1118 QStringLiteral(
"Property \"%1\" was marked as required but does not exist.")
1119 .arg(required.name),
1120 qmlRequired, required.location);
1124 const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
1125 const auto isComponentRoot = [&](
const QQmlJSScope::ConstPtr &requiredScope) {
1126 if (requiredScope->isWrappedInImplicitComponent())
1128 if (
const auto s = requiredScope->parentScope(); s && s->baseType() == compType)
1133 const auto scopeRequiresProperty = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1134 const QString &propName,
1135 const QQmlJSScope::ConstPtr &descendant) {
1136 if (!requiredScope->isPropertyLocallyRequired(propName))
1140 return QQmlJSScope::ownerOfProperty(requiredScope, propName).scope
1141 == QQmlJSScope::ownerOfProperty(descendant, propName).scope;
1144 const auto requiredHasBinding = [](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1145 const QQmlJSScope::ConstPtr &owner,
1146 const QString &propName) {
1147 for (
const auto &scope : scopesToSearch) {
1148 if (scope->property(propName).isAlias())
1150 const auto &[begin, end] = scope->ownPropertyBindings(propName);
1151 for (
auto it = begin; it != end; ++it) {
1153 const bool isRelevantBinding = QQmlSA::isRegularBindingType(it->bindingType())
1154 || it->bindingType() == QQmlSA::BindingType::Interceptor
1155 || it->bindingType() == QQmlSA::BindingType::ValueSource;
1156 if (!isRelevantBinding)
1158 if (QQmlJSScope::ownerOfProperty(scope, propName).scope == owner)
1166 const auto requiredUsedInRootAlias = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1167 const QString &propName) {
1168 const Property target(requiredScope, propName);
1171 const auto allAliasesToTargetIt = m_propertyAliases.constFind(target);
1172 if (allAliasesToTargetIt == m_propertyAliases.constEnd())
1179 allAliasesToTargetIt->constBegin(), allAliasesToTargetIt->constEnd(),
1180 [](
const Property &property) {
return property.scope->isFileRootComponent(); });
1183 const auto requiredSetThroughAlias = [&](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1184 const QQmlJSScope::ConstPtr &requiredScope,
1185 const QString &propName) {
1186 const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
1187 const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
1188 for (
const auto &alias : propertyAliases) {
1189 for (
const auto &s : scopesToSearch) {
1190 if (s->hasOwnPropertyBindings(alias.name))
1197 const auto warn = [
this](
const QQmlJSScope::ConstPtr &prevRequiredScope,
1198 const QString &propName,
const QQmlJSScope::ConstPtr &defScope,
1199 const QQmlJSScope::ConstPtr &requiredScope,
1200 const QQmlJSScope::ConstPtr &descendant) {
1201 const auto &propertyScope = QQmlJSScope::ownerOfProperty(requiredScope, propName).scope;
1202 const QString propertyScopeName = !propertyScope.isNull()
1203 ? QQmlJSUtils::getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
1206 std::optional<QQmlJSFixSuggestion> suggestion;
1208 QString message = QStringLiteral(
"Component is missing required property %1 from %2")
1210 .arg(propertyScopeName);
1211 if (requiredScope != descendant) {
1212 const QString requiredScopeName = prevRequiredScope
1213 ? QQmlJSUtils::getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
1216 if (!prevRequiredScope.isNull()) {
1217 if (
auto sourceScope = prevRequiredScope->baseType()) {
1218 suggestion = QQmlJSFixSuggestion{
1219 "%1:%2:%3: Property marked as required in %4."_L1
1220 .arg(sourceScope->filePath())
1221 .arg(sourceScope->sourceLocation().startLine)
1222 .arg(sourceScope->sourceLocation().startColumn)
1223 .arg(requiredScopeName),
1224 sourceScope->sourceLocation()
1228 if (sourceScope->isComposite())
1229 suggestion->setFilename(sourceScope->filePath());
1232 message +=
" (marked as required by %1)"_L1.arg(requiredScopeName);
1236 m_logger->log(message, qmlRequired, defScope->sourceLocation(),
true,
true, suggestion);
1239 populatePropertyAliases();
1241 for (
const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
1242 if (defScope->isFileRootComponent() || defScope->isInlineComponent()
1243 || defScope->componentRootStatus() != QQmlJSScope::IsComponentRoot::No
1244 || defScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
1248 QList<QQmlJSScope::ConstPtr> scopesToSearch;
1249 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
1250 const auto descendants = QList<QQmlJSScope::ConstPtr>()
1251 << scope << qmlScopeDescendants(scope);
1252 for (
const QQmlJSScope::ConstPtr &descendant : std::as_const(descendants)) {
1255 if (descendant != scope && descendant->isInlineComponent())
1257 scopesToSearch << descendant;
1258 const auto ownProperties = descendant->ownProperties();
1259 for (
auto propertyIt = ownProperties.constBegin();
1260 propertyIt != ownProperties.constEnd(); ++propertyIt) {
1261 const QString propName = propertyIt.key();
1262 if (descendant->hasOwnPropertyBindings(propName))
1265 QQmlJSScope::ConstPtr prevRequiredScope;
1266 for (
const QQmlJSScope::ConstPtr &requiredScope : std::as_const(scopesToSearch)) {
1269 if (isComponentRoot(requiredScope))
1272 if (!scopeRequiresProperty(requiredScope, propName, descendant)) {
1273 prevRequiredScope = requiredScope;
1277 if (requiredHasBinding(scopesToSearch, descendant, propName))
1280 if (requiredUsedInRootAlias(requiredScope, propName))
1283 if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
1286 warn(prevRequiredScope, propName, defScope, requiredScope, descendant);
1287 prevRequiredScope = requiredScope;
1295void QQmlJSImportVisitor::processPropertyBindings()
1297 for (
auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
1298 QQmlJSScope::Ptr scope = it.key();
1299 for (
auto &[visibilityScope, location, name] : it.value()) {
1300 if (!scope->hasProperty(name) && !m_logger->isDisabled()) {
1304 if (checkCustomParser(scope))
1308 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1310 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
1311 baseScope = baseScope->baseType()) {
1312 if (
auto suggestion = QQmlJSUtils::didYouMean(
1313 name, baseScope->ownProperties().keys(), m_logger->filePath(), location);
1314 suggestion.has_value()) {
1315 fixSuggestion = suggestion;
1320 if (checkTypeResolved(scope))
1321 warnMissingPropertyForBinding(name, location, fixSuggestion);
1325 const auto property = scope->property(name);
1326 if (!property.type()) {
1327 m_logger->log(QStringLiteral(
"No type found for property \"%1\". This may be due "
1328 "to a missing import statement or incomplete "
1331 qmlMissingType, location);
1334 const auto &annotations = property.annotations();
1336 const auto deprecationAnn =
1337 std::find_if(annotations.cbegin(), annotations.cend(),
1338 [](
const QQmlJSAnnotation &ann) {
return ann.isDeprecation(); });
1340 if (deprecationAnn != annotations.cend()) {
1341 const auto deprecation = deprecationAnn->deprecation();
1343 QString message = QStringLiteral(
"Binding on deprecated property \"%1\"")
1344 .arg(property.propertyName());
1346 if (!deprecation.reason.isEmpty())
1347 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1349 m_logger->log(message, qmlDeprecated, location);
1355void QQmlJSImportVisitor::checkSignal(
1356 const QQmlJSScope::ConstPtr &signalScope,
const QQmlJS::SourceLocation &location,
1357 const QString &handlerName,
const QStringList &handlerParameters)
1359 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1361 std::optional<QQmlJSMetaMethod> signalMethod;
1362 const auto setSignalMethod = [&](
const QQmlJSScope::ConstPtr &scope,
const QString &name) {
1363 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1364 if (!methods.isEmpty())
1365 signalMethod = methods[0];
1368 if (signal.has_value()) {
1369 if (signalScope->hasMethod(*signal)) {
1370 setSignalMethod(signalScope, *signal);
1371 }
else if (
auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1376 if (
auto notify = p->notify(); !notify.isEmpty()) {
1377 setSignalMethod(signalScope, notify);
1379 Q_ASSERT(!p->bindable().isEmpty());
1380 signalMethod = QQmlJSMetaMethod {};
1385 if (!signalMethod.has_value()) {
1390 if (signalScope->baseTypeName() == QStringLiteral(
"Connections")) {
1392 u"Implicitly defining \"%1\" as signal handler in Connections is deprecated. "
1393 u"Create a function instead: \"function %2(%3) { ... }\"."_s.arg(
1394 handlerName, handlerName, handlerParameters.join(u", ")),
1395 qmlUnqualified, location,
true,
true);
1399 auto baseType = QQmlJSScope::nonCompositeBaseType(signalScope);
1400 if (baseType && baseType->hasCustomParser())
1404 QStringLiteral(
"no matching signal found for handler \"%1\"").arg(handlerName),
1405 qmlUnqualified, location,
true,
true);
1409 const auto signalParameters = signalMethod->parameters();
1410 QHash<QString, qsizetype> parameterNameIndexes;
1412 for (
int i = 0, end = signalParameters.size(); i < end; i++) {
1413 auto &p = signalParameters[i];
1414 parameterNameIndexes[p.name()] = i;
1416 auto signalName = [&]() {
1418 return u" called %1"_s.arg(*signal);
1421 auto type = p.type();
1424 "Type %1 of parameter %2 in signal%3 was not found, but is required to compile "
1426 p.typeName(), p.name(), signalName(),
1427 handlerName, didYouAddAllImports),
1428 qmlSignalParameters, location);
1432 if (type->isComposite())
1440 auto parameterName = [&]() {
1441 if (p.name().isEmpty())
1443 return u" called %1"_s.arg(p.name());
1445 switch (type->accessSemantics()) {
1446 case QQmlJSScope::AccessSemantics::Reference:
1448 m_logger->log(QStringLiteral(
"Type %1 of parameter%2 in signal%3 should be "
1449 "passed by pointer to be able to compile %4. ")
1450 .arg(p.typeName(), parameterName(), signalName(),
1452 qmlSignalParameters, location);
1454 case QQmlJSScope::AccessSemantics::Value:
1455 case QQmlJSScope::AccessSemantics::Sequence:
1459 "Type %1 of parameter%2 in signal%3 should be passed by "
1460 "value or const reference to be able to compile %4. ")
1461 .arg(p.typeName(), parameterName(), signalName(),
1463 qmlSignalParameters, location);
1465 case QQmlJSScope::AccessSemantics::None:
1467 QStringLiteral(
"Type %1 of parameter%2 in signal%3 required by the "
1468 "compilation of %4 cannot be used. ")
1469 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1470 qmlSignalParameters, location);
1475 if (handlerParameters.size() > signalParameters.size()) {
1476 m_logger->log(QStringLiteral(
"Signal handler for \"%2\" has more formal"
1477 " parameters than the signal it handles.")
1479 qmlSignalParameters, location);
1483 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1484 const QStringView handlerParameter = handlerParameters.at(i);
1485 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1486 if (it == parameterNameIndexes.constEnd())
1488 const qsizetype j = *it;
1493 m_logger->log(QStringLiteral(
"Parameter %1 to signal handler for \"%2\""
1494 " is called \"%3\". The signal has a parameter"
1495 " of the same name in position %4.")
1497 .arg(handlerName, handlerParameter)
1499 qmlSignalParameters, location);
1503void QQmlJSImportVisitor::addDefaultProperties()
1505 QQmlJSScope::ConstPtr parentScope = m_currentScope->parentScope();
1506 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1507 || m_currentScope->isInlineComponent())
1510 m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
1512 if (checkCustomParser(parentScope))
1516
1517
1518
1519
1520
1521
1522
1523
1524
1526 parentScope = parentScope->baseType();
1528 const QString defaultPropertyName =
1529 parentScope ? parentScope->defaultPropertyName() : QString();
1531 if (defaultPropertyName.isEmpty())
1536 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1537 binding.setObject(QQmlJSUtils::getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
1538 QQmlJSScope::ConstPtr(m_currentScope));
1539 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() {
return binding; },
1540 QQmlJSScope::UnnamedPropertyTarget });
1543void QQmlJSImportVisitor::breakInheritanceCycles(
const QQmlJSScope::Ptr &originalScope)
1545 QList<QQmlJSScope::ConstPtr> scopes;
1546 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1547 if (scopes.contains(scope)) {
1548 QString inheritenceCycle;
1549 for (
const auto &seen : std::as_const(scopes)) {
1550 inheritenceCycle.append(seen->baseTypeName());
1551 inheritenceCycle.append(QLatin1String(
" -> "));
1553 inheritenceCycle.append(scopes.first()->baseTypeName());
1555 const QString message = QStringLiteral(
"%1 is part of an inheritance cycle: %2")
1556 .arg(originalScope->baseTypeName(), inheritenceCycle);
1557 m_logger->log(message, qmlInheritanceCycle, scope->sourceLocation());
1558 originalScope->clearBaseType();
1559 originalScope->setBaseTypeError(message);
1563 scopes.append(scope);
1565 const auto newScope = scope->baseType();
1566 if (newScope.isNull()) {
1567 const QString error = scope->baseTypeError();
1568 const QString name = scope->baseTypeName();
1569 if (!error.isEmpty()) {
1570 m_logger->log(error, qmlImport, scope->sourceLocation(),
true,
true);
1571 }
else if (!name.isEmpty() && !m_unresolvedTypes.hasSeen(scope)
1572 && !m_logger->isDisabled()) {
1574 name +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports,
1575 qmlImport, scope->sourceLocation(),
true,
true,
1576 QQmlJSUtils::didYouMean(scope->baseTypeName(),
1577 m_rootScopeImports.types().keys(),
1578 m_logger->filePath(),
1579 scope->sourceLocation()));
1587void QQmlJSImportVisitor::checkDeprecation(
const QQmlJSScope::ConstPtr &originalScope)
1589 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1590 for (
const QQmlJSAnnotation &annotation : scope->annotations()) {
1591 if (annotation.isDeprecation()) {
1592 QQQmlJSDeprecation deprecation = annotation.deprecation();
1595 QStringLiteral(
"Type \"%1\" is deprecated").arg(scope->internalName());
1597 if (!deprecation.reason.isEmpty())
1598 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1600 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1606void QQmlJSImportVisitor::checkForComponentTypeWithProperties(
const QQmlJSScope::ConstPtr &scope)
1608 const QQmlJSScope::ConstPtr base = scope->baseType();
1615 if (base->isComposite())
1618 if (base->internalName() !=
"QQmlComponent"_L1)
1621 const auto ownProperties = scope->ownProperties();
1622 for (
const auto &property : ownProperties) {
1623 m_logger->log(
"Component objects cannot declare new properties."_L1,
1624 qmlSyntax, property.sourceLocation());
1628bool QQmlJSImportVisitor::checkCustomParser(
const QQmlJSScope::ConstPtr &scope)
1630 return scope->isInCustomParserParent();
1633void QQmlJSImportVisitor::flushPendingSignalParameters()
1635 const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
1636 for (
const QString ¶meter : handler.signalParameters) {
1637 safeInsertJSIdentifier(m_currentScope, parameter,
1638 { QQmlJSScope::JavaScriptIdentifier::Injected,
1639 m_pendingSignalHandler, std::nullopt,
false });
1641 m_pendingSignalHandler = QQmlJS::SourceLocation();
1645
1646
1647
1648
1649
1650
1651QQmlJSMetaMethod::RelativeFunctionIndex
1652QQmlJSImportVisitor::addFunctionOrExpression(
const QQmlJSScope::ConstPtr &scope,
1653 const QString &name)
1655 auto &array = m_functionsAndExpressions[scope];
1656 array.emplaceBack(name);
1663 for (
const auto &function : std::as_const(m_functionStack))
1664 m_innerFunctions[function]++;
1665 m_functionStack.push({ scope, name });
1667 return QQmlJSMetaMethod::RelativeFunctionIndex {
int(array.size() - 1) };
1671
1672
1673
1674
1675
1676
1677
1678
1679void QQmlJSImportVisitor::forgetFunctionExpression(
const QString &name)
1681 auto nameToVerify = name.isEmpty() ? u"<anon>"_s : name;
1682 Q_UNUSED(nameToVerify);
1683 Q_ASSERT(!m_functionStack.isEmpty());
1684 Q_ASSERT(m_functionStack.top().name == nameToVerify);
1685 m_functionStack.pop();
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
1701 const QQmlJSScope::Ptr &scope,
int count)
const
1703 const auto suitableScope = [](
const QQmlJSScope::Ptr &scope) {
1704 const auto type = scope->scopeType();
1705 return type == QQmlSA::ScopeType::QMLScope
1706 || type == QQmlSA::ScopeType::GroupedPropertyScope
1707 || type == QQmlSA::ScopeType::AttachedPropertyScope;
1710 if (!suitableScope(scope))
1713 auto it = m_functionsAndExpressions.constFind(scope);
1714 if (it == m_functionsAndExpressions.cend())
1717 const auto &functionsAndExpressions = *it;
1718 for (
const QString &functionOrExpression : functionsAndExpressions) {
1719 scope->addOwnRuntimeFunctionIndex(
1720 static_cast<QQmlJSMetaMethod::AbsoluteFunctionIndex>(count));
1737 count += m_innerFunctions.value({ scope, functionOrExpression }, 0);
1743void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument()
const
1746 const auto synthesize = [&](
const QQmlJSScope::Ptr ¤t) {
1747 count = synthesizeCompilationUnitRuntimeFunctionIndices(current, count);
1749 QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, synthesize);
1752bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
1754 if (m_pendingSignalHandler.isValid()) {
1755 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope, u"signalhandler"_s,
1756 ast->firstSourceLocation());
1757 flushPendingSignalParameters();
1762void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
1764 if (m_currentScope->scopeType() == QQmlSA::ScopeType::SignalHandlerFunctionScope) {
1771 const QQmlJS::SourceLocation &srcLocation);
1774 QQmlJSLogger *logger)
1776 QStringView namespaceName{ superType };
1777 namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
1778 logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
1780 qmlUncreatableType, location,
true,
true);
1783bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
1785 const QString superType = buildName(definition->qualifiedTypeNameId);
1787 const bool isRoot = !rootScopeIsValid();
1788 Q_ASSERT(!superType.isEmpty());
1795 const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
1796 const bool looksLikeGroupedProperty = !superType.front().isUpper();
1798 if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
1799 logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
1803 if (!looksLikeGroupedProperty) {
1805 enterEnvironment(QQmlSA::ScopeType::QMLScope, superType,
1806 definition->firstSourceLocation());
1808 enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
1809 definition->firstSourceLocation());
1810 m_currentScope->setIsRootFileComponentFlag(
true);
1813 const QTypeRevision revision = m_currentScope->baseTypeRevision();
1814 if (
auto base = m_currentScope->baseType(); base) {
1815 if (isRoot && base->internalName() == u"QQmlComponent") {
1816 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1817 definition->qualifiedTypeNameId->identifierToken,
true,
true);
1819 if (base->isSingleton() && m_currentScope->isComposite()) {
1820 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1821 m_currentScope->baseTypeName()),
1822 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1825 }
else if (!base->isCreatable()) {
1827 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1828 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1832 if (m_nextIsInlineComponent) {
1833 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1834 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1835 m_currentScope->setIsInlineComponent(
true);
1836 m_currentScope->setInlineComponentName(name);
1837 m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
1838 auto precedence = quint8(QQmlJS::PrecedenceValues::InlineComponent);
1839 m_rootScopeImports.setType(name, { m_currentScope, revision, precedence });
1840 m_nextIsInlineComponent =
false;
1843 addDefaultProperties();
1844 Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope);
1845 m_qmlTypes.append(m_currentScope);
1847 m_objectDefinitionScopes << m_currentScope;
1849 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType,
1850 definition->firstSourceLocation());
1851 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType,
1852 definition->firstSourceLocation()));
1853 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(),
1857 m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
1862void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
1864 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), usedTypes());
1868bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
1870 if (!std::holds_alternative<RootDocumentNameType>(m_currentRootName)) {
1871 m_logger->log(u"Nested inline components are not supported"_s, qmlSyntax,
1872 component->firstSourceLocation());
1876 const auto it = m_seenInlineComponents.constFind(component->name);
1877 if (it != m_seenInlineComponents.cend()) {
1878 m_logger->log(
"Duplicate inline component '%1'"_L1.arg(it.key()),
1879 qmlDuplicateInlineComponent, component->firstSourceLocation());
1880 m_logger->log(
"Note: previous component named '%1' here"_L1.arg(it.key()),
1881 qmlDuplicateInlineComponent, it.value(),
true,
true, {},
1882 component->firstSourceLocation().startLine);
1884 m_seenInlineComponents[component->name] = component->firstSourceLocation();
1887 m_nextIsInlineComponent =
true;
1888 m_currentRootName = component->name.toString();
1892void QQmlJSImportVisitor::endVisit(UiInlineComponent *component)
1894 m_currentRootName = RootDocumentNameType();
1895 if (m_nextIsInlineComponent) {
1896 m_logger->log(u"Inline component declaration must be followed by a typename"_s,
1897 qmlSyntax, component->firstSourceLocation());
1899 m_nextIsInlineComponent =
false;
1902bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
1904 switch (publicMember->type) {
1905 case UiPublicMember::Signal: {
1906 const QString signalName = publicMember->name.toString();
1907 UiParameterList *param = publicMember->parameters;
1908 QQmlJSMetaMethod method;
1909 method.setMethodType(QQmlJSMetaMethodType::Signal);
1910 method.setReturnTypeName(QStringLiteral(
"void"));
1911 method.setMethodName(signalName);
1912 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1913 publicMember->lastSourceLocation()));
1914 method.setOtherMethodIndex(
1915 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
1917 method.addParameter(
1918 QQmlJSMetaParameter(
1919 param->name.toString(),
1920 param->type ? param->type->toString() : QString()
1922 param = param->next;
1924 m_currentScope->addOwnMethod(method);
1927 case UiPublicMember::Property: {
1928 const QString propertyName = publicMember->name.toString();
1929 QString typeName = buildName(publicMember->memberType);
1930 if (typeName.contains(u'.') && typeName.front().isLower()) {
1931 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1935 const bool isAlias = (typeName == u"alias"_s);
1937 auto tryParseAlias = [&]() {
1939 if (!publicMember->statement) {
1940 m_logger->log(QStringLiteral(
"Invalid alias expression - an initializer is needed."),
1941 qmlSyntax, publicMember->memberType->firstSourceLocation());
1944 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1945 auto node = expression ? expression->expression :
nullptr;
1946 auto fex = cast<FieldMemberExpression *>(node);
1949 aliasExpr.prepend(u'.' + fex->name.toString());
1950 fex = cast<FieldMemberExpression *>(node);
1953 if (
const auto idExpression = cast<IdentifierExpression *>(node)) {
1954 aliasExpr.prepend(idExpression->name.toString());
1958 m_logger->log(QStringLiteral(
"Invalid alias expression. Only IDs and field "
1959 "member expressions can be aliased."),
1960 qmlSyntax, publicMember->statement->firstSourceLocation());
1965 QQmlJSMetaProperty prop;
1966 prop.setPropertyName(propertyName);
1967 prop.setIsList(publicMember->typeModifier == QLatin1String(
"list"));
1968 prop.setIsWritable(!publicMember->isReadonly());
1969 prop.setIsFinal(publicMember->isFinal());
1970 prop.setIsVirtual(publicMember->isVirtual());
1971 prop.setIsOverride(publicMember->isOverride());
1972 prop.setAliasExpression(aliasExpr);
1973 prop.setSourceLocation(
1974 combine(publicMember->firstSourceLocation(), publicMember->colonToken));
1976 isAlias ? QQmlJSScope::ConstPtr() : m_rootScopeImports.type(typeName).scope;
1978 const auto factory = type.factory();
1980 prop.setType(prop.isList() ? (factory ? QQmlJSScope::Ptr{ } : type->listType()) : type);
1981 const QString internalName = factory ? factory->internalName() : type->internalName();
1982 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
1988 Q_ASSERT(type.factory() == factory);
1989 }
else if (!isAlias) {
1990 m_pendingPropertyTypes << PendingPropertyType { m_currentScope, prop.propertyName(),
1991 publicMember->firstSourceLocation() };
1992 prop.setTypeName(typeName);
1994 prop.setAnnotations(parseAnnotations(publicMember->annotations));
1995 if (publicMember->isDefaultMember())
1996 m_currentScope->setOwnDefaultPropertyName(propertyName);
1997 prop.setIndex(m_currentScope->ownProperties().size());
1998 m_currentScope->addOwnProperty(prop);
2000 QQmlJSMetaMethod method(
2001 QQmlSignalNames::propertyNameToChangedSignalName(propertyName), u"void"_s);
2002 method.setMethodType(QQmlJSMetaMethodType::Signal);
2003 method.setIsImplicitQmlPropertyChangeSignal(
true);
2004 method.setOtherMethodIndex(
2005 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2006 m_currentScope->addOwnMethod(method);
2008 if (publicMember->isRequired())
2009 m_currentScope->setPropertyLocallyRequired(prop.propertyName(),
true);
2011 BindingExpressionParseResult parseResult = BindingExpressionParseResult::Invalid;
2015 parseBindingExpression(publicMember->name.toString(), publicMember->statement,
2021 if (parseResult == BindingExpressionParseResult::Script) {
2022 Q_ASSERT(!m_savedBindingOuterScope);
2023 m_savedBindingOuterScope = m_currentScope;
2024 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope, QStringLiteral(
"binding"),
2025 publicMember->statement->firstSourceLocation());
2035void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
2037 if (m_savedBindingOuterScope) {
2038 m_currentScope = m_savedBindingOuterScope;
2039 m_savedBindingOuterScope = {};
2041 forgetFunctionExpression(publicMember->name.toString());
2045bool QQmlJSImportVisitor::visit(UiRequired *required)
2047 const QString name = required->name.toString();
2049 m_requiredProperties << RequiredProperty { m_currentScope, name,
2050 required->firstSourceLocation() };
2052 m_currentScope->setPropertyLocallyRequired(name,
true);
2056void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
2058 using namespace QQmlJS::AST;
2059 auto name = fexpr->name.toString();
2060 if (!name.isEmpty()) {
2061 QQmlJSMetaMethod method(name);
2062 method.setMethodType(QQmlJSMetaMethodType::Method);
2063 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
2065 if (!m_pendingMethodAnnotations.isEmpty()) {
2066 method.setAnnotations(m_pendingMethodAnnotations);
2067 m_pendingMethodAnnotations.clear();
2071 const bool parseTypes = m_scopesById.signaturesAreEnforced();
2073 bool formalsFullyTyped = parseTypes;
2074 bool anyFormalTyped =
false;
2075 PendingMethodTypeAnnotations pending{ m_currentScope, name, {} };
2078 for (
auto formals = fexpr->formals; formals; formals = formals->next) {
2079 PatternElement *e = formals->element;
2082 if (e->typeAnnotation && (e->bindingTarget || e->initializer))
2083 m_logger->log(
"Type annotations on default parameters are not supported"_L1,
2085 combine(e->firstSourceLocation(), e->lastSourceLocation()));
2088 if (
const auto *formals = parseTypes ? fexpr->formals :
nullptr) {
2089 const auto parameters = formals->formals();
2090 for (
const auto ¶meter : parameters) {
2091 const QString type = parameter.typeAnnotation
2092 ? parameter.typeAnnotation->type->toString()
2094 if (type.isEmpty()) {
2095 formalsFullyTyped =
false;
2096 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral(
"var")));
2097 pending.locations.emplace_back();
2099 anyFormalTyped =
true;
2100 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
2101 pending.locations.append(
2102 combine(parameter.typeAnnotation->firstSourceLocation(),
2103 parameter.typeAnnotation->lastSourceLocation()));
2109 method.setIsJavaScriptFunction(!formalsFullyTyped);
2115 if (parseTypes && fexpr->typeAnnotation) {
2116 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
2117 pending.locations.append(combine(fexpr->typeAnnotation->firstSourceLocation(),
2118 fexpr->typeAnnotation->lastSourceLocation()));
2119 }
else if (anyFormalTyped) {
2120 method.setReturnTypeName(QStringLiteral(
"void"));
2122 method.setReturnTypeName(QStringLiteral(
"var"));
2125 const auto &locs = pending.locations;
2126 if (std::any_of(locs.cbegin(), locs.cend(), [](
const auto &loc) {
return loc.isValid(); }))
2127 m_pendingMethodTypeAnnotations << pending;
2129 method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
2130 method.setOtherMethodIndex(
2131 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2133 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
2135 const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
2136 ? fexpr->identifierToken
2137 : fexpr->functionToken;
2138 safeInsertJSIdentifier(m_currentScope, name,
2139 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2140 functionLocation, method.returnTypeName(),
2143 m_currentScope->addOwnMethod(method);
2145 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
2147 addFunctionOrExpression(m_currentScope, QStringLiteral(
"<anon>"));
2148 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral(
"<anon>"),
2149 fexpr->firstSourceLocation());
2153bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
2155 visitFunctionExpressionHelper(fexpr);
2159void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
2161 forgetFunctionExpression(fexpr->name.toString());
2165bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
2167 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
2171bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
2173 if (!fdecl->name.isEmpty()) {
2174 const QString name = fdecl->name.toString();
2175 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
2176 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
2177 fdecl->identifierToken);
2178 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
2179 previousDeclaration->location);
2182 visitFunctionExpressionHelper(fdecl);
2186void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
2188 forgetFunctionExpression(fdecl->name.toString());
2192bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
2194 QQmlJSMetaProperty prop;
2195 prop.setPropertyName(ast->name.toString());
2196 m_currentScope->addOwnProperty(prop);
2197 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2198 ast->firstSourceLocation());
2202void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
2208 QQmlJS::AST::ArgumentList *args)
2210 QStringView contextString;
2211 QStringView mainString;
2212 QStringView commentString;
2213 auto registerContextString = [&](QStringView string) {
2214 contextString = string;
2217 auto registerMainString = [&](QStringView string) {
2218 mainString = string;
2221 auto registerCommentString = [&](QStringView string) {
2222 commentString = string;
2225 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
2226 QV4::CompiledData::TranslationData data) {
2227 if (type == QV4::CompiledData::Binding::Type_Translation) {
2228 binding.setTranslation(mainString, commentString, contextString, data.number);
2229 }
else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
2230 binding.setTranslationId(mainString, data.number);
2232 binding.setStringLiteral(mainString);
2235 QmlIR::tryGeneratingTranslationBindingBase(
2237 registerMainString, registerCommentString, registerContextString, finalizeBinding);
2240QQmlJSImportVisitor::BindingExpressionParseResult
2241QQmlJSImportVisitor::parseBindingExpression(
2242 const QString &name,
const QQmlJS::AST::Statement *statement,
2243 const UiPublicMember *associatedPropertyDefinition)
2245 if (statement ==
nullptr)
2246 return BindingExpressionParseResult::Invalid;
2248 const auto *exprStatement = cast<
const ExpressionStatement *>(statement);
2250 if (exprStatement ==
nullptr) {
2251 QQmlJS::SourceLocation location = statement->firstSourceLocation();
2253 if (
const auto *block = cast<
const Block *>(statement); block && block->statements) {
2254 location = block->statements->firstSourceLocation();
2257 QQmlJSMetaPropertyBinding binding(location, name);
2258 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2259 QQmlSA::ScriptBindingKind::PropertyBinding, ScriptValue_Function);
2260 m_bindings.append(UnfinishedBinding {
2262 [binding = std::move(binding)]() {
return binding; }
2264 return BindingExpressionParseResult::Script;
2267 auto expr = exprStatement->expression;
2268 QQmlJSMetaPropertyBinding binding(
2269 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
2272 ScriptBindingValueType scriptBindingValuetype = ScriptValue_Unknown;
2274 switch (expr->kind) {
2275 case Node::Kind_TrueLiteral:
2276 binding.setBoolLiteral(
true);
2278 case Node::Kind_FalseLiteral:
2279 binding.setBoolLiteral(
false);
2281 case Node::Kind_NullExpression:
2282 binding.setNullLiteral();
2284 case Node::Kind_IdentifierExpression: {
2285 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
2287 if (idExpr->name == u"undefined")
2288 scriptBindingValuetype = ScriptValue_Undefined;
2291 case Node::Kind_FunctionDeclaration:
2292 case Node::Kind_FunctionExpression:
2293 case Node::Kind_Block: {
2294 scriptBindingValuetype = ScriptValue_Function;
2297 case Node::Kind_NumericLiteral:
2298 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
2300 case Node::Kind_StringLiteral:
2301 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
2303 case Node::Kind_RegExpLiteral:
2304 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
2306 case Node::Kind_TemplateLiteral: {
2307 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
2308 Q_ASSERT(templateLit);
2309 if (templateLit->hasNoSubstitution) {
2310 binding.setStringLiteral(templateLit->value);
2312 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2313 QQmlSA::ScriptBindingKind::PropertyBinding);
2314 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
2315 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
2316 expression->accept(
this);
2322 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
2323 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
2324 binding.setNumberLiteral(-lit->value);
2325 }
else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
2326 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
2327 handleTranslationBinding(binding, base->name, call->arguments);
2332 if (!binding.isValid()) {
2334 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2335 QQmlSA::ScriptBindingKind::PropertyBinding,
2336 scriptBindingValuetype);
2338 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
2341 if (binding.bindingType() == QQmlSA::BindingType::Translation
2342 || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
2343 return BindingExpressionParseResult::Translation;
2345 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
2346 return BindingExpressionParseResult::Script;
2348 if (associatedPropertyDefinition)
2349 handleLiteralBinding(binding, associatedPropertyDefinition);
2351 return BindingExpressionParseResult::Literal;
2354bool QQmlJSImportVisitor::isImportPrefix(QString prefix)
const
2356 if (prefix.isEmpty() || !prefix.front().isUpper())
2359 return m_rootScopeImports.isNullType(prefix);
2362void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
2364 if (m_currentScope->scopeType() != QQmlJSScope::ScopeType::QMLScope) {
2365 m_logger->log(u"id declarations are only allowed in objects"_s, qmlSyntax,
2366 scriptBinding->statement->firstSourceLocation());
2369 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
2371 m_logger->log(u"id must be followed by an identifier"_s, qmlSyntax,
2372 scriptBinding->statement->firstSourceLocation());
2375 const QString name = [&]() {
2376 if (
const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2377 return idExpression->name.toString();
2378 else if (
const auto *idString = cast<StringLiteral *>(statement->expression)) {
2379 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2380 idString->firstSourceLocation());
2381 return idString->value.toString();
2383 m_logger->log(u"Failed to parse id"_s, qmlSyntax,
2384 statement->expression->firstSourceLocation());
2388 if (!name.isEmpty() && !name.front().isLower() && name.front() != u'_') {
2389 m_logger->log(u"Id must start with a lower case letter or an '_'"_s, qmlSyntax,
2390 statement->expression->firstSourceLocation());
2393 m_currentScope->setIdSourceLocation(combine(scriptBinding->statement->firstSourceLocation(),
2394 scriptBinding->statement->lastSourceLocation()));
2395 if (m_scopesById.existsAnywhereInDocument(name)) {
2398 breakInheritanceCycles(m_currentScope);
2399 m_scopesById.possibleScopes(
2400 name, m_currentScope, QQmlJSScopesByIdOption::Default,
2401 [&](
const QQmlJSScope::ConstPtr &otherScopeWithID,
2402 QQmlJSScopesById::Confidence confidence) {
2404 Q_UNUSED(confidence);
2406 auto otherLocation = otherScopeWithID->sourceLocation();
2410 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2411 name, QString::number(otherLocation.startLine),
2412 QString::number(otherLocation.startColumn)),
2413 qmlSyntaxDuplicateIds,
2414 scriptBinding->firstSourceLocation());
2415 return QQmlJSScopesById::CallbackResult::ContinueSearch;
2418 if (!name.isEmpty())
2419 m_scopesById.insert(name, m_currentScope);
2422void QQmlJSImportVisitor::handleLiteralBinding(
const QQmlJSMetaPropertyBinding &binding,
2423 const UiPublicMember *associatedPropertyDefinition)
2427 Q_UNUSED(associatedPropertyDefinition);
2431
2432
2433
2434
2435
2438 const QQmlJS::SourceLocation &srcLocation)
2440 const auto createBinding = [=]() {
2441 const QQmlJSScope::ScopeType type = scope->scopeType();
2448 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2449 const bool alreadyHasBinding =
std::any_of(propertyBindings.first, propertyBindings.second,
2450 [&](
const QQmlJSMetaPropertyBinding &binding) {
2451 return binding.bindingType() == bindingType;
2453 if (alreadyHasBinding)
2454 return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
2457 if (type == QQmlSA::ScopeType::GroupedPropertyScope)
2458 binding.setGroupBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2460 binding.setAttachedBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2463 return { scope->parentScope(), createBinding };
2466bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
2468 Q_ASSERT(!m_savedBindingOuterScope);
2469 Q_ASSERT(!m_thisScriptBindingIsJavaScript);
2470 m_savedBindingOuterScope = m_currentScope;
2471 const auto id = scriptBinding->qualifiedId;
2472 if (!id->next && id->name == QLatin1String(
"id")) {
2473 handleIdDeclaration(scriptBinding);
2480 for (; group->next; group = group->next) {
2481 const QString name = group->name.toString();
2485 if (group == id && isImportPrefix(name)) {
2486 prefix = name + u'.';
2490 const bool isAttachedProperty = name.front().isUpper();
2491 if (isAttachedProperty) {
2493 enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
2494 group->firstSourceLocation());
2497 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
2498 group->firstSourceLocation());
2500 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
2501 group->firstSourceLocation()));
2506 const auto name = group->name.toString();
2510 const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
2512 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2513 m_propertyBindings[m_currentScope].append(
2514 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2516 auto result = parseBindingExpression(name, scriptBinding->statement);
2517 m_thisScriptBindingIsJavaScript = (result == BindingExpressionParseResult::Script);
2519 const auto statement = scriptBinding->statement;
2520 QStringList signalParameters;
2522 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2523 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2524 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2525 signalParameters << formal->element->bindingIdentifier.toString();
2529 QQmlJSMetaMethod scopeSignal;
2530 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2531 if (!methods.isEmpty())
2532 scopeSignal = methods[0];
2534 const auto firstSourceLocation = statement->firstSourceLocation();
2535 bool hasMultilineStatementBody =
2536 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2537 m_pendingSignalHandler = firstSourceLocation;
2538 m_signalHandlers.insert(firstSourceLocation,
2539 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2543 const auto index = addFunctionOrExpression(m_currentScope, name);
2544 const auto createBinding = [
2546 scope = m_currentScope,
2547 signalName = *signal,
2550 firstSourceLocation,
2551 groupLocation = group->firstSourceLocation(),
2552 signalParameters]() {
2554 Q_ASSERT(scope->isFullyResolved());
2555 QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
2556 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2557 if (!methods.isEmpty()) {
2558 kind = QQmlSA::ScriptBindingKind::SignalHandler;
2559 checkSignal(scope, groupLocation, name, signalParameters);
2560 }
else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2561 kind = QQmlSA::ScriptBindingKind::ChangeHandler;
2562 checkSignal(scope, groupLocation, name, signalParameters);
2563 }
else if (scope->hasProperty(name)) {
2566 kind = QQmlSA::ScriptBindingKind::PropertyBinding;
2567 m_signalHandlers.remove(firstSourceLocation);
2570 checkSignal(scope, groupLocation, name, signalParameters);
2573 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2574 binding.setScriptBinding(index, kind, ScriptValue_Function);
2577 m_bindings.append(UnfinishedBinding { m_currentScope, createBinding });
2578 m_thisScriptBindingIsJavaScript =
true;
2584 while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
2585 || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
2590 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope,
2592 scriptBinding->statement->firstSourceLocation());
2594 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope,
2596 scriptBinding->statement->firstSourceLocation());
2602void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
2604 if (m_savedBindingOuterScope) {
2605 m_currentScope = m_savedBindingOuterScope;
2606 m_savedBindingOuterScope = {};
2612 if (m_thisScriptBindingIsJavaScript) {
2613 m_thisScriptBindingIsJavaScript =
false;
2614 Q_ASSERT(!m_functionStack.isEmpty());
2615 m_functionStack.pop();
2619bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
2621 createAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2622 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2623 arrayBinding->firstSourceLocation());
2624 m_currentScope->setIsArrayScope(
true);
2628void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
2635 const auto children = m_currentScope->childScopes();
2638 const int scopesEnteredCounter = openAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2639 auto guard = qScopeGuard([
this, scopesEnteredCounter]() {
2640 for (
int i = 0; i < scopesEnteredCounter; ++i)
2644 if (checkCustomParser(m_currentScope)) {
2650 auto group = arrayBinding->qualifiedId;
2651 for (; group->next; group = group->next) { }
2652 const QString propertyName = group->name.toString();
2655 for (
auto element = arrayBinding->members; element; element = element->next, ++i) {
2656 const auto &type = children[i];
2657 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2658 m_logger->log(u"Declaring an object which is not a Qml object"
2659 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2662 m_pendingPropertyObjectBindings
2663 << PendingPropertyObjectBinding { m_currentScope, type, propertyName,
2664 element->firstSourceLocation(),
false };
2665 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2666 binding.setObject(QQmlJSUtils::getScopeName(type, QQmlSA::ScopeType::QMLScope),
2667 QQmlJSScope::ConstPtr(type));
2668 m_bindings.append(UnfinishedBinding {
2670 [binding = std::move(binding)]() {
return binding; },
2671 QQmlJSScope::ListPropertyTarget
2676bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
2678 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2679 qmlEnum.setIsQml(
true);
2680 qmlEnum.setLineNumber(uied->enumToken.startLine);
2681 for (
const auto *member = uied->members; member; member = member->next) {
2682 qmlEnum.addKey(member->member.toString());
2683 qmlEnum.addValue(
int(member->value));
2685 m_currentScope->addOwnEnumeration(qmlEnum);
2689QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromHost(
2690 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2692 QFileInfo fileInfo(path);
2693 if (!fileInfo.exists()) {
2694 m_logger->log(
"File or directory you are trying to import does not exist: %1."_L1.arg(path),
2695 qmlImport, location);
2699 if (fileInfo.isFile()) {
2700 const auto scope = m_importer->importFile(path);
2701 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2702 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2703 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2704 addImportWithLocation(actualPrefix, location,
false);
2708 if (fileInfo.isDir()) {
2709 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2710 auto scopes = m_importer->importDirectory(path, precedence, prefix);
2711 const auto types = scopes.types();
2712 const auto warnings = scopes.warnings();
2713 m_rootScopeImports.add(std::move(scopes));
2714 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2715 addImportWithLocation(*it, location, !warnings.isEmpty());
2720 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2722 qmlImport, location);
2726QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromQrc(
2727 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2729 Q_ASSERT(path.startsWith(u':'));
2730 const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper();
2734 const auto pathNoColon = QStringView(path).mid(1);
2735 if (mapper->isFile(pathNoColon)) {
2736 const auto entry = m_importer->resourceFileMapper()->entry(
2737 QQmlJSResourceFileMapper::resourceFileFilter(pathNoColon.toString()));
2738 const auto scope = m_importer->importFile(entry.filePath);
2739 const QString actualPrefix =
2740 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2741 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2742 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2743 addImportWithLocation(actualPrefix, location,
false);
2747 auto scopes = m_importer->importDirectory(path, quint8(QQmlJS::PrecedenceValues::Default), prefix);
2748 const auto types = scopes.types();
2749 const auto warnings = scopes.warnings();
2750 m_rootScopeImports.add(std::move(scopes));
2751 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2752 addImportWithLocation(*it, location, !warnings.isEmpty());
2756bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
2759 QString prefix = QLatin1String(
"");
2760 if (import->asToken.isValid()) {
2761 prefix += import->importId;
2762 if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
2763 m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
2765 qmlImport, import->importIdToken,
true,
true);
2767 m_seenModuleQualifiers.append(prefix);
2770 const QString filename = import->fileName.toString();
2771 if (!filename.isEmpty()) {
2772 const QUrl url(filename);
2773 const QString scheme = url.scheme();
2774 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2775 if (scheme ==
""_L1) {
2776 QFileInfo fileInfo(url.path());
2777 QString absolute = fileInfo.isRelative()
2778 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2780 auto warnings = absolute.startsWith(u':')
2781 ? importFromQrc(absolute, prefix, importLocation)
2782 : importFromHost(absolute, prefix, importLocation);
2783 processImportWarnings(
"path \"%1\""_L1.arg(url.path()), warnings, importLocation);
2785 }
else if (scheme ==
"file"_L1) {
2786 auto warnings = importFromHost(url.path(), prefix, importLocation);
2787 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2789 }
else if (scheme ==
"qrc"_L1) {
2790 auto warnings = importFromQrc(
":"_L1 + url.path(), prefix, importLocation);
2791 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2794 m_logger->log(
"Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2795 qmlImport, import->firstSourceLocation());
2799 const QString path = buildName(import->importUri);
2801 QStringList staticModulesProvided;
2803 auto imported = m_importer->importModule(
2804 path, quint8(QQmlJS::PrecedenceValues::Default), prefix,
2805 import->version ? import->version->version : QTypeRevision(), &staticModulesProvided);
2806 const auto types = imported.types();
2807 const auto warnings = imported.warnings();
2808 m_rootScopeImports.add(std::move(imported));
2809 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2810 addImportWithLocation(*it, import->firstSourceLocation(), !warnings.isEmpty());
2812 if (prefix.isEmpty()) {
2813 for (
const QString &staticModule : std::as_const(staticModulesProvided))
2814 addStaticImportWithLocation(path, import->firstSourceLocation(), path != staticModule);
2817 processImportWarnings(
2818 QStringLiteral(
"module \"%1\"").arg(path), warnings, import->firstSourceLocation());
2822#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
2824void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2826 for (
const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2831void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2833 assign(pragma->value);
2837bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
2839 if (pragma->name == u"Strict"_s) {
2844 if (!m_logger->wasCategoryChanged(qmlCompiler))
2845 m_logger->setCategorySeverity(qmlCompiler, QQmlJS::WarningSeverity::Warning);
2846 }
else if (pragma->name == u"ComponentBehavior") {
2847 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2848 if (value == u"Bound") {
2849 m_scopesById.setComponentsAreBound(
true);
2850 }
else if (value == u"Unbound") {
2851 m_scopesById.setComponentsAreBound(
false);
2853 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2854 qmlSyntax, pragma->firstSourceLocation());
2857 }
else if (pragma->name == u"FunctionSignatureBehavior") {
2858 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2859 if (value == u"Enforced") {
2860 m_scopesById.setSignaturesAreEnforced(
true);
2861 }
else if (value == u"Ignored") {
2862 m_scopesById.setSignaturesAreEnforced(
false);
2865 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2866 qmlSyntax, pragma->firstSourceLocation());
2869 }
else if (pragma->name == u"ValueTypeBehavior") {
2870 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2871 if (value == u"Copy") {
2873 }
else if (value == u"Reference") {
2875 }
else if (value == u"Addressable") {
2876 m_scopesById.setValueTypesAreAddressable(
true);
2877 }
else if (value == u"Inaddressable") {
2878 m_scopesById.setValueTypesAreAddressable(
false);
2879 }
else if (value == u"Assertable") {
2880 m_scopesById.setValueTypesAreAssertable(
true);
2881 }
else if (value == u"Inassertable") {
2882 m_scopesById.setValueTypesAreAssertable(
false);
2884 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2885 qmlSyntax, pragma->firstSourceLocation());
2893void QQmlJSImportVisitor::throwRecursionDepthError()
2895 m_logger->log(QStringLiteral(
"Maximum statement or expression depth exceeded"),
2896 qmlRecursionDepthErrors, QQmlJS::SourceLocation());
2899bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
2901 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2902 ast->firstSourceLocation());
2906void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
2911bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
2913 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"forloop"),
2914 ast->firstSourceLocation());
2918void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
2923bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
2925 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"foreachloop"),
2926 ast->firstSourceLocation());
2930void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
2935bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
2937 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"block"),
2938 ast->firstSourceLocation());
2940 if (m_pendingSignalHandler.isValid())
2941 flushPendingSignalParameters();
2946void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
2951bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
2953 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"case"),
2954 ast->firstSourceLocation());
2958void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
2963bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
2965 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"catch"),
2966 catchStatement->firstSourceLocation());
2970void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
2975bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
2977 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"with"),
2978 ast->firstSourceLocation());
2980 m_logger->log(QStringLiteral(
"with statements are strongly discouraged in QML "
2981 "and might cause false positives when analysing unqualified "
2983 qmlWith, ast->firstSourceLocation());
2988void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
2993bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
2995 const auto &boundedNames = fpl->boundNames();
2996 for (
auto const &boundName : boundedNames) {
2998 std::optional<QString> typeName;
2999 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
3000 if (Type *type = annotation->type)
3001 typeName = type->toString();
3002 safeInsertJSIdentifier(m_currentScope, boundName.id,
3003 { QQmlJSScope::JavaScriptIdentifier::Parameter,
3004 boundName.location, typeName,
false });
3009void QQmlJSImportVisitor::createAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3011 bool needsResolution =
false;
3012 int scopesEnteredCounter = 0;
3014 for (
auto group = propertyName; group->next; group = group->next) {
3015 const QString idName = group->name.toString();
3017 if (idName.isEmpty())
3020 if (group == propertyName && isImportPrefix(idName)) {
3021 prefix = idName + u'.';
3025 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3026 : QQmlSA::ScopeType::GroupedPropertyScope;
3029 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3031 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + idName,
3032 group->firstSourceLocation()));
3034 ++scopesEnteredCounter;
3035 needsResolution = needsResolution || !exists;
3040 for (
int i=0; i < scopesEnteredCounter; ++i) {
3045 if (needsResolution) {
3046 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(),
3051bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
3055 Q_ASSERT(uiob->qualifiedTypeNameId);
3057 const QString typeName = buildName(uiob->qualifiedTypeNameId);
3058 if (typeName.front().isLower() && typeName.contains(u'.')) {
3059 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
3062 createAttachedAndGroupedScopes(uiob->qualifiedId);
3064 enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
3065 uiob->qualifiedTypeNameId->identifierToken);
3067 m_qmlTypes.append(m_currentScope);
3068 m_objectBindingScopes << m_currentScope;
3072int QQmlJSImportVisitor::openAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3075 int scopesEnteredCounter = 0;
3076 auto group = propertyName;
3077 for (; group->next; group = group->next) {
3078 const QString idName = group->name.toString();
3080 if (idName.isEmpty())
3083 if (group == propertyName && isImportPrefix(idName)) {
3084 prefix = idName + u'.';
3088 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3089 : QQmlSA::ScopeType::GroupedPropertyScope;
3091 [[maybe_unused]]
bool exists =
3092 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3094 scopesEnteredCounter++;
3098 return scopesEnteredCounter;
3101void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
3103 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), usedTypes());
3105 const QQmlJSScope::Ptr childScope = m_currentScope;
3108 const int scopesEnteredCounter = openAttachedAndGroupedScopes(uiob->qualifiedId);
3113 auto group = uiob->qualifiedId;
3114 for (; group->next; group = group->next) { }
3115 const QString propertyName = group->name.toString();
3117 if (m_currentScope->isNameDeferred(propertyName)) {
3118 bool foundIds =
false;
3119 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
3121 while (!childScopes.isEmpty()) {
3122 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
3123 m_scopesById.possibleIds(
3124 scope, scope, QQmlJSScopesByIdOption::Default,
3125 [&](
const QString &id, QQmlJSScopesById::Confidence confidence) {
3128 Q_UNUSED(confidence);
3130 return QQmlJSScopesById::CallbackResult::StopSearch;
3133 childScopes << scope->childScopes();
3138 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
3140 qmlDeferredPropertyId, uiob->firstSourceLocation());
3144 if (checkCustomParser(m_currentScope)) {
3148 m_pendingPropertyObjectBindings
3149 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
3150 uiob->firstSourceLocation(), uiob->hasOnToken };
3152 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
3153 if (uiob->hasOnToken) {
3154 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
3155 binding.setInterceptor(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3156 QQmlJSScope::ConstPtr(childScope));
3158 binding.setValueSource(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3159 QQmlJSScope::ConstPtr(childScope));
3162 binding.setObject(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3163 QQmlJSScope::ConstPtr(childScope));
3165 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
3168 for (
int i = 0; i < scopesEnteredCounter; ++i)
3172bool QQmlJSImportVisitor::visit(ExportDeclaration *)
3174 Q_ASSERT(rootScopeIsValid());
3175 Q_ASSERT(m_exportedRootScope != m_globalScope);
3176 Q_ASSERT(m_currentScope == m_globalScope);
3177 m_currentScope = m_exportedRootScope;
3181void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
3183 Q_ASSERT(rootScopeIsValid());
3184 m_currentScope = m_exportedRootScope->parentScope();
3185 Q_ASSERT(m_currentScope == m_globalScope);
3188bool QQmlJSImportVisitor::visit(ESModule *module)
3190 Q_ASSERT(!rootScopeIsValid());
3191 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"module"),
3192 module->firstSourceLocation());
3193 m_currentScope->setIsScript(
true);
3194 importBaseModules();
3199void QQmlJSImportVisitor::endVisit(ESModule *)
3201 QQmlJSScope::resolveTypes(m_exportedRootScope, m_rootScopeImports.contextualTypes(),
3205bool QQmlJSImportVisitor::visit(Program *program)
3207 Q_ASSERT(m_globalScope == m_currentScope);
3208 Q_ASSERT(!rootScopeIsValid());
3209 enterRootScope(QQmlSA::ScopeType::JSFunctionScope, u"script"_s, program->firstSourceLocation());
3210 m_exportedRootScope->setIsScript(
true);
3211 importBaseModules();
3215void QQmlJSImportVisitor::endVisit(Program *)
3217 QQmlJSScope::resolveTypes(m_exportedRootScope, m_rootScopeImports.contextualTypes(),
3221bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
3224 if (element->isVariableDeclaration()) {
3225 QQmlJS::AST::BoundNames names;
3226 element->boundNames(&names);
3227 for (
const auto &name : std::as_const(names)) {
3228 std::optional<QString> typeName;
3229 if (TypeAnnotation *annotation = name.typeAnnotation.data())
3230 if (Type *type = annotation->type)
3231 typeName = type->toString();
3232 using Kind = QQmlJSScope::JavaScriptIdentifier::Kind;
3233 const Kind kind = (element->scope == QQmlJS::AST::VariableScope::Var)
3234 ? Kind::FunctionScoped
3235 : Kind::LexicalScoped;
3236 const QString variableName = name.id;
3237 if (kind == Kind::LexicalScoped) {
3238 const QQmlJS::SourceLocation location = element->firstSourceLocation();
3239 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(variableName)) {
3240 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(variableName), qmlSyntax,
3242 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(variableName), qmlSyntax,
3243 previousDeclaration->location);
3246 const bool isConstVariable = element->scope == QQmlJS::AST::VariableScope::Const;
3247 const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
3249 { (element->scope == QQmlJS::AST::VariableScope::Var)
3250 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
3251 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
3252 name.location, typeName,
3262bool QQmlJSImportVisitor::visit(IfStatement *statement)
3264 if (BinaryExpression *binary = cast<BinaryExpression *>(statement->expression)) {
3265 if (binary->op == QSOperator::Assign) {
3267 "Assignment in condition: did you mean to use \"===\" or \"==\" instead of \"=\"?"_L1,
3268 qmlAssignmentInCondition, binary->operatorToken);