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);
274QQmlJSImportVisitor::AliasPropertyResolution
275QQmlJSImportVisitor::resolveAliasProperty(
const QQmlJSScope::Ptr &object,
276 const QQmlJSMetaProperty &property)
278 AliasPropertyResolution result = Resolved;
279 QStringList components = property.aliasExpression().split(u'.');
280 QQmlJSMetaProperty targetProperty;
282 bool foundProperty =
false;
283 bool hasWarnedAlready =
false;
286 QQmlJSScope::ConstPtr type = m_scopesById.scope(components.takeFirst(), object);
287 QQmlJSScope::ConstPtr typeScope;
288 if (!type.isNull()) {
289 foundProperty =
true;
296 while (type && !components.isEmpty()) {
297 const QString name = components.takeFirst();
299 if (!checkTypeResolved(type)) {
300 hasWarnedAlready =
true;
305 if (!type->hasProperty(name)) {
306 foundProperty =
false;
311 const auto target = type->property(name);
312 if (!target.type()) {
313 if (target.isAlias()) {
314 result = NeedsRequeue;
318 hasWarnedAlready = QQmlJSScope::ownerOfProperty(type, name).scope->filePath()
319 == m_exportedRootScope->filePath();
323 type = target.type();
324 targetProperty = target;
329 if (result == NeedsRequeue)
331 if (!hasWarnedAlready) {
333 m_logger->log(QStringLiteral(
"Cannot deduce type of alias \"%1\"")
334 .arg(property.propertyName()),
335 qmlMissingType, property.sourceLocation());
338 QStringLiteral(
"Cannot resolve alias \"%1\"").arg(property.propertyName()),
339 qmlUnresolvedAlias, property.sourceLocation());
343 Q_ASSERT(property.index() >= 0);
344 object->addOwnProperty(property);
347 QQmlJSMetaProperty newProperty = property;
348 newProperty.setType(type);
350 newProperty.setIsList(targetProperty.isList());
351 newProperty.setIsWritable(targetProperty.isWritable());
352 newProperty.setIsFinal(targetProperty.isFinal());
353 newProperty.setIsPointer(targetProperty.isPointer());
355 const bool onlyId = !property.aliasExpression().contains(u'.');
357 newProperty.setAliasTargetScope(type);
358 newProperty.setAliasTargetName(QStringLiteral(
"id-only-alias"));
360 const auto &ownerScope =
361 QQmlJSScope::ownerOfProperty(typeScope, targetProperty.propertyName()).scope;
362 newProperty.setAliasTargetScope(ownerScope);
363 newProperty.setAliasTargetName(targetProperty.propertyName());
366 if (
const QString internalName = type->internalName(); !internalName.isEmpty())
367 newProperty.setTypeName(internalName);
369 Q_ASSERT(newProperty.index() >= 0);
370 object->addOwnProperty(newProperty);
371 m_aliasDefinitions.append({ object, property.propertyName() });
376void QQmlJSImportVisitor::resolveAliases()
378 QQueue<QQmlJSScope::Ptr> objects;
379 objects.enqueue(m_exportedRootScope);
381 qsizetype lastRequeueLength = std::numeric_limits<qsizetype>::max();
382 QQueue<QQmlJSScope::Ptr> requeue;
383 QDuplicateTracker<std::pair<QQmlJSScope::Ptr, QQmlJSMetaProperty>> alreadyResolvedAliases;
385 bool processingRequeue =
false;
387 while (!objects.isEmpty()) {
388 const QQmlJSScope::Ptr object = objects.dequeue();
389 const auto properties = object->ownProperties();
391 bool doRequeue =
false;
392 for (
const auto &property : properties) {
393 if (!property.isAlias() || !property.type().isNull()
394 || alreadyResolvedAliases.contains({ object, property })) {
397 if (resolveAliasProperty(object, property) == NeedsRequeue) {
400 Q_UNUSED(alreadyResolvedAliases.hasSeen({ object, property }));
404 if (!processingRequeue) {
405 const auto childScopes = object->childScopes();
406 for (
const auto &childScope : childScopes)
407 objects.enqueue(childScope);
411 requeue.enqueue(object);
413 if (objects.isEmpty() && requeue.size() < lastRequeueLength) {
414 processingRequeue =
true;
415 lastRequeueLength = requeue.size();
416 objects.swap(requeue);
421void QQmlJSImportVisitor::resolveGroupedProperties()
423 QQueue<QQmlJSScope::Ptr> objects;
424 objects.enqueue(m_exportedRootScope);
426 while (!objects.isEmpty()) {
427 const QQmlJSScope::Ptr object = objects.dequeue();
428 const auto childScopes = object->childScopes();
429 for (
const auto &childScope : childScopes) {
430 if (mayBeUnresolvedGroupedProperty(childScope)) {
431 const QString name = childScope->internalName();
432 if (object->isNameDeferred(name)) {
433 const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
434 if (!deferred.isNull()) {
435 QQmlJSScope::resolveGroup(childScope, deferred,
436 m_rootScopeImports.contextualTypes(),
439 }
else if (
const QQmlJSScope::ConstPtr propType = object->property(name).type()) {
440 QQmlJSScope::resolveGroup(childScope, propType,
441 m_rootScopeImports.contextualTypes(), usedTypes());
444 objects.enqueue(childScope);
449QString QQmlJSImportVisitor::implicitImportDirectory(
const QString &localFile,
450 const QQmlJSResourceFileMapper *mapper)
453 const auto resource = mapper->entry(
454 QQmlJSResourceFileMapper::localFileFilter(localFile));
455 if (resource.isValid()) {
456 return resource.resourcePath.contains(u'/')
457 ? (u':' + resource.resourcePath.left(
458 resource.resourcePath.lastIndexOf(u'/') + 1))
459 : QStringLiteral(
":/");
463 return QFileInfo(localFile).canonicalPath() + u'/';
466void QQmlJSImportVisitor::processImportWarnings(
467 const QString &what,
const QList<QQmlJS::DiagnosticMessage> &warnings,
468 const QQmlJS::SourceLocation &srcLocation)
470 if (warnings.isEmpty())
473 QList<QQmlJS::DiagnosticMessage> importWarnings = warnings;
476 auto fileSelectorWarningsIt =
477 std::partition(importWarnings.begin(), importWarnings.end(),
478 [](
const QQmlJS::DiagnosticMessage &message) {
479 return message.msgType() != 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 auto implicitImport = m_importer->importDirectory(m_implicitImportDirectory, precedence);
523 m_importer->registerVersionedImports(implicitImport);
524 m_rootScopeImports.add(std::move(implicitImport));
529 if (
const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper()) {
530 const QStringList resourcePaths = mapper->resourcePaths(QQmlJSResourceFileMapper::Filter {
531 m_logger->filePath(), QStringList(), QQmlJSResourceFileMapper::Resource });
532 for (
const QString &path : resourcePaths) {
533 const qsizetype lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
536 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
537 auto resourceImport =
538 m_importer->importDirectory(path.first(lastSlash), precedence);
539 m_importer->registerVersionedImports(resourceImport);
540 m_rootScopeImports.add(std::move(resourceImport));
545 processImportWarnings(QStringLiteral(
"base modules"), m_rootScopeImports.warnings());
548bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiProgram *)
552 if (
auto elementName = QFileInfo(m_logger->filePath()).baseName();
553 !elementName.isEmpty() && elementName[0].isUpper()) {
554 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
555 m_rootScopeImports.setType(elementName,
556 { m_exportedRootScope, QTypeRevision{ }, precedence });
562void QQmlJSImportVisitor::endVisit(UiProgram *)
564 for (
const auto &scope : std::as_const(m_objectBindingScopes)) {
565 breakInheritanceCycles(scope);
566 checkDeprecation(scope);
567 checkForComponentTypeWithProperties(scope);
570 for (
const auto &scope : std::as_const(m_objectDefinitionScopes)) {
571 if (m_pendingDefaultProperties.contains(scope))
573 breakInheritanceCycles(scope);
574 checkDeprecation(scope);
575 checkForComponentTypeWithProperties(scope);
578 const auto &keys = m_pendingDefaultProperties.keys();
579 for (
const auto &scope : keys) {
580 breakInheritanceCycles(scope);
581 checkDeprecation(scope);
582 checkForComponentTypeWithProperties(scope);
586 resolveGroupedProperties();
588 checkGroupedAndAttachedScopes();
591 processDefaultProperties();
592 processPropertyTypes();
593 processMethodTypes();
594 processPropertyBindings();
595 processPropertyBindingObjects();
596 checkRequiredProperties();
598 populateRuntimeFunctionIndicesForDocument();
603 ExpressionStatement *expr = cast<ExpressionStatement *>(statement);
605 if (!statement || !expr->expression)
608 switch (expr->expression->kind) {
609 case Node::Kind_StringLiteral:
610 return cast<StringLiteral *>(expr->expression)->value.toString();
611 case Node::Kind_NumericLiteral:
612 return cast<NumericLiteral *>(expr->expression)->value;
618QList<QQmlJSAnnotation> QQmlJSImportVisitor::parseAnnotations(QQmlJS::AST::UiAnnotationList *list)
621 QList<QQmlJSAnnotation> annotationList;
623 for (UiAnnotationList *item = list; item !=
nullptr; item = item->next) {
624 UiAnnotation *annotation = item->annotation;
626 QQmlJSAnnotation qqmljsAnnotation;
627 qqmljsAnnotation.name = buildName(annotation->qualifiedTypeNameId);
629 for (UiObjectMemberList *memberItem = annotation->initializer->members; memberItem !=
nullptr; memberItem = memberItem->next) {
630 switch (memberItem->member->kind) {
631 case Node::Kind_UiScriptBinding: {
632 auto *scriptBinding = QQmlJS::AST::cast<UiScriptBinding*>(memberItem->member);
633 qqmljsAnnotation.bindings[buildName(scriptBinding->qualifiedId)]
634 = bindingToVariant(scriptBinding->statement);
643 annotationList.append(qqmljsAnnotation);
646 return annotationList;
650 const QQmlJS::SourceLocation &first,
const QQmlJS::SourceLocation &second,
651 const QList<QQmlJS::SourceLocation> &groupedObjectDefinitions)
653 const auto innermostDefinitionOffset =
654 [groupedObjectDefinitions](
const QQmlJS::SourceLocation &location) {
655 quint32 innermostOffset = 0;
656 for (
const QQmlJS::SourceLocation &definition : groupedObjectDefinitions) {
657 const bool contains = definition.offset <= location.offset
658 && location.offset < definition.offset + definition.length;
659 if (contains && definition.offset > innermostOffset)
660 innermostOffset = definition.offset;
662 return innermostOffset;
664 return innermostDefinitionOffset(first) == innermostDefinitionOffset(second);
667void QQmlJSImportVisitor::setAllBindings()
669 using Key = std::pair<QQmlJSScope::ConstPtr, QString>;
670 QHash<Key, QQmlJS::SourceLocation> foundBindings;
672 for (
auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
674 const QQmlJSScope::Ptr type = it->owner;
675 if (!checkTypeResolved(type))
684 if (!type->isFullyResolved())
686 auto binding = it->create();
687 if (!binding.isValid())
689 type->addOwnPropertyBinding(binding, it->specifier);
692 if (binding.hasInterceptor() || binding.hasValueSource())
694 const QString propertyName = binding.propertyName();
695 QQmlJSMetaProperty property = type->property(propertyName);
698
699
700
701
702
703 if (!property.isValid())
707 if (property.isList())
710 const Key key = std::make_pair(type, propertyName);
711 auto sourceLocationIt = foundBindings.constFind(key);
712 if (sourceLocationIt == foundBindings.constEnd()) {
713 foundBindings.insert(key, binding.sourceLocation());
717 const QQmlJS::SourceLocation location = binding.sourceLocation();
724 const bool irBuilderAlreadyWarned = groupedPropertiesInSameGroupedObjectDefinition(
725 *sourceLocationIt, location, m_groupedObjectDefinitions);
726 if (!irBuilderAlreadyWarned) {
727 m_logger->log(
"Duplicate binding on property '%1'"_L1.arg(propertyName),
728 qmlDuplicatePropertyBinding, location);
730 m_logger->log(
"Note: previous binding on '%1' here"_L1.arg(propertyName),
731 qmlDuplicatePropertyBinding, *sourceLocationIt,
true,
true, {},
736void QQmlJSImportVisitor::processDefaultProperties()
738 for (
auto it = m_pendingDefaultProperties.constBegin();
739 it != m_pendingDefaultProperties.constEnd(); ++it) {
740 QQmlJSScope::ConstPtr parentScope = it.key();
743 if (checkCustomParser(parentScope))
746 if (!checkTypeResolved(parentScope))
750
751
752
753
754
755
756
757
758
760 parentScope = parentScope->baseType();
762 const QString defaultPropertyName =
763 parentScope ? parentScope->defaultPropertyName() : QString();
765 if (defaultPropertyName.isEmpty()) {
768 bool isComponent =
false;
769 for (QQmlJSScope::ConstPtr s = parentScope; s; s = s->baseType()) {
770 if (s->internalName() == QStringLiteral(
"QQmlComponent")) {
776 if (!isComponent && checkTypeResolved(parentScope)) {
777 m_logger->log(QStringLiteral(
"Cannot assign to non-existent default property"),
778 qmlMissingProperty, it.value().constFirst()->sourceLocation());
784 const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
785 auto propType = defaultProp.type();
786 const auto handleUnresolvedDefaultProperty = [&](
const QQmlJSScope::ConstPtr &) {
788 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
789 "missing an import.")
790 .arg(defaultPropertyName)
791 .arg(defaultProp.typeName()),
792 qmlUnresolvedType, it.value().constFirst()->sourceLocation());
795 const auto assignToUnknownProperty = [&]() {
798 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it))
799 scope->setAssignedToUnknownProperty(
true);
802 if (propType.isNull()) {
803 if (checkTypeResolved(parentScope)
804 && QQmlJSScope::ownerOfProperty(parentScope, defaultPropertyName).scope->filePath()
805 != m_exportedRootScope->filePath()) {
806 handleUnresolvedDefaultProperty(propType);
808 assignToUnknownProperty();
812 if (it.value().size() > 1
813 && !defaultProp.isList()
814 && !propType->isListProperty()) {
816 QStringLiteral(
"Cannot assign multiple objects to a default non-list property"),
817 qmlNonListProperty, it.value().constFirst()->sourceLocation());
820 if (!checkTypeResolved(propType, handleUnresolvedDefaultProperty)) {
821 assignToUnknownProperty();
825 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
826 if (!checkTypeResolved(scope))
831 if (propType->canAssign(scope)) {
832 scope->setIsWrappedInImplicitComponent(
833 causesImplicitComponentWrapping(defaultProp, scope));
837 m_logger->log(QStringLiteral(
"Cannot assign to default property of incompatible type"),
838 qmlIncompatibleType, scope->sourceLocation());
843void QQmlJSImportVisitor::processPropertyTypes()
845 for (
const PendingPropertyType &type : std::as_const(m_pendingPropertyTypes)) {
846 Q_ASSERT(type.scope->hasOwnProperty(type.name));
848 auto property = type.scope->ownProperty(type.name);
850 if (
const auto propertyType = QQmlJSScope::findType(
851 property.typeName(), m_rootScopeImports.contextualTypes()).scope) {
852 property.setType(property.isList() ? propertyType->listType() : propertyType);
853 type.scope->addOwnProperty(property);
855 QString msg = property.typeName() +
' '_L1 + wasNotFound;
856 if (property.typeName() ==
"list"_L1)
857 msg +=
" list is not a type. It requires an element type argument (eg. list<int>)"_L1;
858 m_logger->log(msg, qmlImport, type.location);
863void QQmlJSImportVisitor::processMethodTypes()
865 const auto isEnumUsedAsType = [&](QStringView typeName,
const QQmlJS::SourceLocation &loc) {
866 if (typeName ==
"enum"_L1) {
870 const auto split = typeName.tokenize(u'.').toContainer<QVarLengthArray<QStringView, 4>>();
871 if (split.size() != 2)
874 const QStringView scopeName = split[0];
875 const QStringView enumName = split[1];
877 if (
auto scope = QQmlJSScope::findType(scopeName.toString(),
878 m_rootScopeImports.contextualTypes()).scope) {
879 if (scope->enumeration(enumName.toString()).isValid()) {
881 "QML enumerations are not types. Use int, or use double if the enum's underlying type does not fit into int."_L1,
882 qmlEnumsAreNotTypes, loc);
889 for (
const auto &method : std::as_const(m_pendingMethodTypeAnnotations)) {
890 for (
auto [it, end] = method.scope->mutableOwnMethodsRange(method.methodName); it != end; ++it) {
891 const auto [parameterBegin, parameterEnd] = it->mutableParametersRange();
892 for (
auto parameter = parameterBegin; parameter != parameterEnd; ++parameter) {
893 const int parameterIndex = parameter - parameterBegin;
894 if (isEnumUsedAsType(parameter->typeName(), method.locations[parameterIndex]))
896 if (
const auto parameterType = QQmlJSScope::findType(
897 parameter->typeName(), m_rootScopeImports.contextualTypes()).scope) {
898 parameter->setType({ parameterType });
901 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
902 .arg(parameter->typeName(), parameter->name(), it->methodName()),
903 qmlUnresolvedType, method.locations[parameter - parameterBegin]);
907 if (isEnumUsedAsType(it->returnTypeName(), method.locations.last()))
909 if (
const auto returnType = QQmlJSScope::findType(
910 it->returnTypeName(), m_rootScopeImports.contextualTypes()).scope) {
911 it->setReturnType({ returnType });
913 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
914 it->returnTypeName(), it->methodName()),
915 qmlUnresolvedType, method.locations.last());
923
924
925
926
927
928
929
933 for (QStringView propertyName: possiblyGroupedProperty.tokenize(u".")) {
934 property = scope->property(propertyName.toString());
935 if (property.isValid())
936 scope = property.type();
943void QQmlJSImportVisitor::processPropertyBindingObjects()
945 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundLiterals;
953 QSet<std::pair<QQmlJSScope::Ptr, QString>> visited;
954 for (
const PendingPropertyObjectBinding &objectBinding :
955 std::as_const(m_pendingPropertyObjectBindings)) {
957 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
958 if (visited.contains(uniqueBindingId))
960 visited.insert(uniqueBindingId);
962 auto [existingBindingsBegin, existingBindingsEnd] =
963 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
964 const bool hasLiteralBindings =
965 std::any_of(existingBindingsBegin, existingBindingsEnd,
966 [](
const QQmlJSMetaPropertyBinding &x) {
return x.hasLiteral(); });
967 if (hasLiteralBindings)
968 foundLiterals.insert(uniqueBindingId);
972 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundObjects;
973 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundInterceptors;
974 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundValueSources;
976 for (
const PendingPropertyObjectBinding &objectBinding :
977 std::as_const(m_pendingPropertyObjectBindings)) {
978 const QString propertyName = objectBinding.name;
979 QQmlJSScope::Ptr childScope = objectBinding.childScope;
981 const auto assignToUnknownProperty = [&]() {
984 childScope->setAssignedToUnknownProperty(
true);
988 if (!checkTypeResolved(objectBinding.scope)) {
989 assignToUnknownProperty();
993 QQmlJSMetaProperty property = resolveProperty(propertyName, objectBinding.scope);
995 if (!property.isValid()) {
996 warnMissingPropertyForBinding(propertyName, objectBinding.location);
999 const auto handleUnresolvedProperty = [&](
const QQmlJSScope::ConstPtr &) {
1001 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
1002 "missing an import.")
1004 .arg(property.typeName()),
1005 qmlUnresolvedType, objectBinding.location);
1008 if (property.type().isNull()) {
1009 assignToUnknownProperty();
1010 if (checkTypeResolved(objectBinding.scope)
1011 && QQmlJSScope::ownerOfProperty(objectBinding.scope, propertyName).scope->filePath()
1012 != m_exportedRootScope->filePath()) {
1015 handleUnresolvedProperty(property.type());
1021 if (!checkTypeResolved(property.type(), handleUnresolvedProperty)) {
1022 assignToUnknownProperty();
1024 }
else if (!checkTypeResolved(childScope)) {
1028 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
1029 m_logger->log(QStringLiteral(
"Cannot assign object of type %1 to %2")
1030 .arg(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope))
1031 .arg(property.typeName()),
1032 qmlIncompatibleType, childScope->sourceLocation());
1036 childScope->setIsWrappedInImplicitComponent(
1037 causesImplicitComponentWrapping(property, childScope));
1040 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
1041 const QString typeName = QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
1043 auto isConditionalBinding = [&]() ->
bool {
1045
1046
1047
1048
1049 return childScope->hasOwnPropertyBindings(u"enabled"_s)
1050 || childScope->hasOwnPropertyBindings(u"when"_s)
1051 || childScope->hasOwnPropertyBindings(u"running"_s);
1054 if (objectBinding.onToken) {
1055 if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueInterceptor"))) {
1056 if (foundInterceptors.contains(uniqueBindingId)) {
1057 if (!isConditionalBinding()) {
1058 m_logger->log(QStringLiteral(
"Duplicate interceptor on property \"%1\"")
1060 qmlDuplicatePropertyBinding, objectBinding.location);
1063 foundInterceptors.insert(uniqueBindingId);
1065 }
else if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueSource"))) {
1066 if (foundValueSources.contains(uniqueBindingId)) {
1067 if (!isConditionalBinding()) {
1068 m_logger->log(QStringLiteral(
"Duplicate value source on property \"%1\"")
1070 qmlDuplicatePropertyBinding, objectBinding.location);
1072 }
else if (foundObjects.contains(uniqueBindingId)
1073 || foundLiterals.contains(uniqueBindingId)) {
1074 if (!isConditionalBinding()) {
1075 m_logger->log(QStringLiteral(
"Cannot combine value source and binding on "
1078 qmlDuplicatePropertyBinding, objectBinding.location);
1081 foundValueSources.insert(uniqueBindingId);
1084 m_logger->log(QStringLiteral(
"On-binding for property \"%1\" has wrong type \"%2\"")
1087 qmlIncompatibleType, objectBinding.location);
1090 if (foundValueSources.contains(uniqueBindingId)) {
1091 if (!isConditionalBinding()) {
1093 QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
1095 qmlDuplicatePropertyBinding, objectBinding.location);
1098 foundObjects.insert(uniqueBindingId);
1106 QList<QQmlJSScope::ConstPtr> descendants;
1107 std::vector<QQmlJSScope::ConstPtr> toVisit;
1109 toVisit.push_back(scope);
1110 while (!toVisit.empty()) {
1111 const QQmlJSScope::ConstPtr s = toVisit.back();
1117 toVisit.insert(toVisit.end(), s->childScopesBegin(), s->childScopesEnd());
1124void QQmlJSImportVisitor::populatePropertyAliases()
1126 for (
const auto &alias : std::as_const(m_aliasDefinitions)) {
1127 const auto &[aliasScope, aliasName] = alias;
1128 if (aliasScope.isNull())
1131 auto property = aliasScope->ownProperty(aliasName);
1132 if (!property.isValid() || !property.aliasTargetScope())
1135 Property target(property.aliasTargetScope(), property.aliasTargetName());
1138 m_propertyAliases[target].append(alias);
1139 property = target.scope->property(target.name);
1140 target = Property(property.aliasTargetScope(), property.aliasTargetName());
1141 }
while (property.isAlias() && target.scope);
1145void QQmlJSImportVisitor::checkRequiredProperties()
1147 for (
const auto &required : std::as_const(m_requiredProperties)) {
1148 if (!required.scope->hasProperty(required.name)) {
1150 QStringLiteral(
"Property \"%1\" was marked as required but does not exist.")
1151 .arg(required.name),
1152 qmlRequired, required.location);
1156 const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
1157 const auto isComponentRoot = [&](
const QQmlJSScope::ConstPtr &requiredScope) {
1158 if (requiredScope->isWrappedInImplicitComponent())
1160 if (
const auto s = requiredScope->parentScope(); s && s->baseType() == compType)
1165 const auto scopeRequiresProperty = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1166 const QString &propName,
1167 const QQmlJSScope::ConstPtr &descendant) {
1168 if (!requiredScope->isPropertyLocallyRequired(propName))
1172 return QQmlJSScope::ownerOfProperty(requiredScope, propName).scope
1173 == QQmlJSScope::ownerOfProperty(descendant, propName).scope;
1176 const auto requiredHasBinding = [](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1177 const QQmlJSScope::ConstPtr &owner,
1178 const QString &propName) {
1179 for (
const auto &scope : scopesToSearch) {
1180 if (scope->property(propName).isAlias())
1182 const auto &[begin, end] = scope->ownPropertyBindings(propName);
1183 for (
auto it = begin; it != end; ++it) {
1185 const bool isRelevantBinding = QQmlSA::isRegularBindingType(it->bindingType())
1186 || it->bindingType() == QQmlSA::BindingType::Interceptor
1187 || it->bindingType() == QQmlSA::BindingType::ValueSource;
1188 if (!isRelevantBinding)
1190 if (QQmlJSScope::ownerOfProperty(scope, propName).scope == owner)
1198 const auto requiredUsedInRootAlias = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1199 const QString &propName) {
1200 const Property target(requiredScope, propName);
1203 const auto allAliasesToTargetIt = m_propertyAliases.constFind(target);
1204 if (allAliasesToTargetIt == m_propertyAliases.constEnd())
1211 allAliasesToTargetIt->constBegin(), allAliasesToTargetIt->constEnd(),
1212 [](
const Property &property) {
return property.scope->isFileRootComponent(); });
1215 const auto requiredSetThroughAlias = [&](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1216 const QQmlJSScope::ConstPtr &requiredScope,
1217 const QString &propName) {
1218 const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
1219 const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
1220 for (
const auto &alias : propertyAliases) {
1221 for (
const auto &s : scopesToSearch) {
1222 if (s->hasOwnPropertyBindings(alias.name))
1229 const auto warn = [
this](
const QQmlJSScope::ConstPtr &prevRequiredScope,
1230 const QString &propName,
const QQmlJSScope::ConstPtr &defScope,
1231 const QQmlJSScope::ConstPtr &requiredScope,
1232 const QQmlJSScope::ConstPtr &descendant) {
1233 const auto &propertyScope = QQmlJSScope::ownerOfProperty(requiredScope, propName).scope;
1234 const QString propertyScopeName = !propertyScope.isNull()
1235 ? QQmlJSUtils::getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
1238 std::optional<QQmlJSFixSuggestion> suggestion;
1240 QString message = QStringLiteral(
"Component is missing required property %1 from %2")
1242 .arg(propertyScopeName);
1243 if (requiredScope != descendant) {
1244 const QString requiredScopeName = prevRequiredScope
1245 ? QQmlJSUtils::getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
1248 if (!prevRequiredScope.isNull()) {
1249 if (
auto sourceScope = prevRequiredScope->baseType()) {
1250 suggestion = QQmlJSFixSuggestion{
1251 "%1:%2:%3: Property marked as required in %4."_L1
1252 .arg(sourceScope->filePath())
1253 .arg(sourceScope->sourceLocation().startLine)
1254 .arg(sourceScope->sourceLocation().startColumn)
1255 .arg(requiredScopeName),
1256 sourceScope->sourceLocation()
1260 if (sourceScope->isComposite())
1261 suggestion->setFilename(sourceScope->filePath());
1264 message +=
" (marked as required by %1)"_L1.arg(requiredScopeName);
1268 m_logger->log(message, qmlRequired, defScope->sourceLocation(),
true,
true, suggestion);
1271 populatePropertyAliases();
1273 for (
const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
1274 if (defScope->isFileRootComponent() || defScope->isInlineComponent()
1275 || defScope->componentRootStatus() != QQmlJSScope::IsComponentRoot::No
1276 || defScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
1280 QList<QQmlJSScope::ConstPtr> scopesToSearch;
1281 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
1282 const auto descendants = QList<QQmlJSScope::ConstPtr>()
1283 << scope << qmlScopeDescendants(scope);
1284 for (
const QQmlJSScope::ConstPtr &descendant : std::as_const(descendants)) {
1287 if (descendant != scope && descendant->isInlineComponent())
1289 scopesToSearch << descendant;
1290 const auto ownProperties = descendant->ownProperties();
1291 for (
auto propertyIt = ownProperties.constBegin();
1292 propertyIt != ownProperties.constEnd(); ++propertyIt) {
1293 const QString propName = propertyIt.key();
1294 if (descendant->hasOwnPropertyBindings(propName))
1297 QQmlJSScope::ConstPtr prevRequiredScope;
1298 for (
const QQmlJSScope::ConstPtr &requiredScope : std::as_const(scopesToSearch)) {
1301 if (isComponentRoot(requiredScope))
1304 if (!scopeRequiresProperty(requiredScope, propName, descendant)) {
1305 prevRequiredScope = requiredScope;
1309 if (requiredHasBinding(scopesToSearch, descendant, propName))
1312 if (requiredUsedInRootAlias(requiredScope, propName))
1315 if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
1318 warn(prevRequiredScope, propName, defScope, requiredScope, descendant);
1319 prevRequiredScope = requiredScope;
1327void QQmlJSImportVisitor::processPropertyBindings()
1329 for (
auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
1330 QQmlJSScope::Ptr scope = it.key();
1331 for (
auto &[visibilityScope, location, name] : it.value()) {
1332 if (!scope->hasProperty(name) && !m_logger->isDisabled()) {
1336 if (checkCustomParser(scope))
1340 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1342 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
1343 baseScope = baseScope->baseType()) {
1344 if (
auto suggestion = QQmlJSUtils::didYouMean(
1345 name, baseScope->ownProperties().keys(), m_logger->filePath(), location);
1346 suggestion.has_value()) {
1347 fixSuggestion = suggestion;
1352 if (checkTypeResolved(scope))
1353 warnMissingPropertyForBinding(name, location, fixSuggestion);
1357 const auto property = scope->property(name);
1358 if (!property.type()) {
1359 m_logger->log(QStringLiteral(
"No type found for property \"%1\". This may be due "
1360 "to a missing import statement or incomplete "
1363 qmlMissingType, location);
1366 const auto &annotations = property.annotations();
1368 const auto deprecationAnn =
1369 std::find_if(annotations.cbegin(), annotations.cend(),
1370 [](
const QQmlJSAnnotation &ann) {
return ann.isDeprecation(); });
1372 if (deprecationAnn != annotations.cend()) {
1373 const auto deprecation = deprecationAnn->deprecation();
1375 QString message = QStringLiteral(
"Binding on deprecated property \"%1\"")
1376 .arg(property.propertyName());
1378 if (!deprecation.reason.isEmpty())
1379 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1381 m_logger->log(message, qmlDeprecated, location);
1387void QQmlJSImportVisitor::checkSignal(
1388 const QQmlJSScope::ConstPtr &signalScope,
const QQmlJS::SourceLocation &location,
1389 const QString &handlerName,
const QStringList &handlerParameters)
1391 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1393 std::optional<QQmlJSMetaMethod> signalMethod;
1394 const auto setSignalMethod = [&](
const QQmlJSScope::ConstPtr &scope,
const QString &name) {
1395 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1396 if (!methods.isEmpty())
1397 signalMethod = methods[0];
1400 if (signal.has_value()) {
1401 if (signalScope->hasMethod(*signal)) {
1402 setSignalMethod(signalScope, *signal);
1403 }
else if (
auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1408 if (
auto notify = p->notify(); !notify.isEmpty()) {
1409 setSignalMethod(signalScope, notify);
1411 Q_ASSERT(!p->bindable().isEmpty());
1412 signalMethod = QQmlJSMetaMethod {};
1417 if (!signalMethod.has_value()) {
1422 if (signalScope->baseTypeName() == QStringLiteral(
"Connections")) {
1424 u"Implicitly defining \"%1\" as signal handler in Connections is deprecated. "
1425 u"Create a function instead: \"function %2(%3) { ... }\"."_s.arg(
1426 handlerName, handlerName, handlerParameters.join(u", ")),
1427 qmlUnqualified, location,
true,
true);
1431 auto baseType = QQmlJSScope::nonCompositeBaseType(signalScope);
1432 if (baseType && baseType->hasCustomParser())
1436 QStringLiteral(
"no matching signal found for handler \"%1\"").arg(handlerName),
1437 qmlUnqualified, location,
true,
true);
1441 const auto signalParameters = signalMethod->parameters();
1442 QHash<QString, qsizetype> parameterNameIndexes;
1444 for (
int i = 0, end = signalParameters.size(); i < end; i++) {
1445 auto &p = signalParameters[i];
1446 parameterNameIndexes[p.name()] = i;
1448 auto signalName = [&]() {
1450 return u" called %1"_s.arg(*signal);
1453 auto type = p.type();
1456 "Type %1 of parameter %2 in signal%3 was not found, but is required to compile "
1457 "%4."_L1.arg(p.typeName(), p.name(), signalName(), handlerName),
1458 qmlSignalParameters, location);
1462 if (type->isComposite())
1470 auto parameterName = [&]() {
1471 if (p.name().isEmpty())
1473 return u" called %1"_s.arg(p.name());
1475 switch (type->accessSemantics()) {
1476 case QQmlJSScope::AccessSemantics::Reference:
1478 m_logger->log(QStringLiteral(
"Type %1 of parameter%2 in signal%3 should be "
1479 "passed by pointer to be able to compile %4. ")
1480 .arg(p.typeName(), parameterName(), signalName(),
1482 qmlSignalParameters, location);
1484 case QQmlJSScope::AccessSemantics::Value:
1485 case QQmlJSScope::AccessSemantics::Sequence:
1489 "Type %1 of parameter%2 in signal%3 should be passed by "
1490 "value or const reference to be able to compile %4. ")
1491 .arg(p.typeName(), parameterName(), signalName(),
1493 qmlSignalParameters, location);
1495 case QQmlJSScope::AccessSemantics::None:
1497 QStringLiteral(
"Type %1 of parameter%2 in signal%3 required by the "
1498 "compilation of %4 cannot be used. ")
1499 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1500 qmlSignalParameters, location);
1505 if (handlerParameters.size() > signalParameters.size()) {
1506 m_logger->log(QStringLiteral(
"Signal handler for \"%2\" has more formal"
1507 " parameters than the signal it handles.")
1509 qmlSignalParameters, location);
1513 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1514 const QStringView handlerParameter = handlerParameters.at(i);
1515 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1516 if (it == parameterNameIndexes.constEnd())
1518 const qsizetype j = *it;
1523 m_logger->log(QStringLiteral(
"Parameter %1 to signal handler for \"%2\""
1524 " is called \"%3\". The signal has a parameter"
1525 " of the same name in position %4.")
1527 .arg(handlerName, handlerParameter)
1529 qmlSignalParameters, location);
1533void QQmlJSImportVisitor::addDefaultProperties()
1535 QQmlJSScope::ConstPtr parentScope = m_currentScope->parentScope();
1536 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1537 || m_currentScope->isInlineComponent())
1540 m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
1542 if (checkCustomParser(parentScope))
1546
1547
1548
1549
1550
1551
1552
1553
1554
1556 parentScope = parentScope->baseType();
1558 const QString defaultPropertyName =
1559 parentScope ? parentScope->defaultPropertyName() : QString();
1561 if (defaultPropertyName.isEmpty())
1566 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1567 binding.setObject(QQmlJSUtils::getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
1568 QQmlJSScope::ConstPtr(m_currentScope));
1569 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() {
return binding; },
1570 QQmlJSScope::UnnamedPropertyTarget });
1573void QQmlJSImportVisitor::breakInheritanceCycles(
const QQmlJSScope::Ptr &originalScope)
1575 QList<QQmlJSScope::ConstPtr> scopes;
1576 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1577 if (scopes.contains(scope)) {
1578 QString inheritenceCycle;
1579 for (
const auto &seen : std::as_const(scopes)) {
1580 inheritenceCycle.append(seen->baseTypeName());
1581 inheritenceCycle.append(QLatin1String(
" -> "));
1583 inheritenceCycle.append(scopes.first()->baseTypeName());
1585 const QString message = QStringLiteral(
"%1 is part of an inheritance cycle: %2")
1586 .arg(originalScope->baseTypeName(), inheritenceCycle);
1587 m_logger->log(message, qmlInheritanceCycle, scope->sourceLocation());
1588 originalScope->clearBaseType();
1589 originalScope->setBaseTypeError(message);
1593 scopes.append(scope);
1595 const auto newScope = scope->baseType();
1596 if (newScope.isNull()) {
1597 const QString error = scope->baseTypeError();
1598 const QString name = scope->baseTypeName();
1599 if (!error.isEmpty()) {
1600 m_logger->log(error, qmlImport, scope->sourceLocation(),
true,
true);
1601 }
else if (!name.isEmpty() && !m_unresolvedTypes.hasSeen(scope)
1602 && !m_logger->isDisabled()) {
1603 m_logger->log(name +
' '_L1 + wasNotFound +
' '_L1, qmlImport,
1604 scope->sourceLocation(),
true,
true,
1605 QQmlJSUtils::didYouMean(
1606 scope->baseTypeName(), m_rootScopeImports.types().keys(),
1607 m_logger->filePath(), scope->sourceLocation()));
1615void QQmlJSImportVisitor::checkDeprecation(
const QQmlJSScope::ConstPtr &originalScope)
1617 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1618 for (
const QQmlJSAnnotation &annotation : scope->annotations()) {
1619 if (annotation.isDeprecation()) {
1620 QQQmlJSDeprecation deprecation = annotation.deprecation();
1623 QStringLiteral(
"Type \"%1\" is deprecated").arg(scope->internalName());
1625 if (!deprecation.reason.isEmpty())
1626 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1628 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1634void QQmlJSImportVisitor::checkForComponentTypeWithProperties(
const QQmlJSScope::ConstPtr &scope)
1636 const QQmlJSScope::ConstPtr base = scope->baseType();
1643 if (base->isComposite())
1646 if (base->internalName() !=
"QQmlComponent"_L1)
1649 const auto ownProperties = scope->ownProperties();
1650 for (
const auto &property : ownProperties) {
1651 m_logger->log(
"Component objects cannot declare new properties."_L1,
1652 qmlSyntax, property.sourceLocation());
1656bool QQmlJSImportVisitor::checkCustomParser(
const QQmlJSScope::ConstPtr &scope)
1658 return scope->isInCustomParserParent();
1661void QQmlJSImportVisitor::flushPendingSignalParameters()
1663 const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
1664 for (
const QString ¶meter : handler.signalParameters) {
1665 safeInsertJSIdentifier(m_currentScope, parameter,
1666 { QQmlJSScope::JavaScriptIdentifier::Injected,
1667 m_pendingSignalHandler, std::nullopt,
false });
1669 m_pendingSignalHandler = QQmlJS::SourceLocation();
1673
1674
1675
1676
1677
1678
1679QQmlJSMetaMethod::RelativeFunctionIndex
1680QQmlJSImportVisitor::addFunctionOrExpression(
const QQmlJSScope::ConstPtr &scope,
1681 const QString &name)
1683 auto &array = m_functionsAndExpressions[scope];
1684 array.emplaceBack(name);
1691 for (
const auto &function : std::as_const(m_functionStack))
1692 m_innerFunctions[function]++;
1693 m_functionStack.push({ scope, name });
1695 return QQmlJSMetaMethod::RelativeFunctionIndex {
int(array.size() - 1) };
1699
1700
1701
1702
1703
1704
1705
1706
1707void QQmlJSImportVisitor::forgetFunctionExpression(
const QString &name)
1709 auto nameToVerify = name.isEmpty() ? u"<anon>"_s : name;
1710 Q_UNUSED(nameToVerify);
1711 Q_ASSERT(!m_functionStack.isEmpty());
1712 Q_ASSERT(m_functionStack.top().name == nameToVerify);
1713 m_functionStack.pop();
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
1729 const QQmlJSScope::Ptr &scope,
int count)
const
1731 const auto suitableScope = [](
const QQmlJSScope::Ptr &scope) {
1732 const auto type = scope->scopeType();
1733 return type == QQmlSA::ScopeType::QMLScope
1734 || type == QQmlSA::ScopeType::GroupedPropertyScope
1735 || type == QQmlSA::ScopeType::AttachedPropertyScope;
1738 if (!suitableScope(scope))
1741 auto it = m_functionsAndExpressions.constFind(scope);
1742 if (it == m_functionsAndExpressions.cend())
1745 const auto &functionsAndExpressions = *it;
1746 for (
const QString &functionOrExpression : functionsAndExpressions) {
1747 scope->addOwnRuntimeFunctionIndex(
1748 static_cast<QQmlJSMetaMethod::AbsoluteFunctionIndex>(count));
1765 count += m_innerFunctions.value({ scope, functionOrExpression }, 0);
1771void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument()
const
1774 const auto synthesize = [&](
const QQmlJSScope::Ptr ¤t) {
1775 count = synthesizeCompilationUnitRuntimeFunctionIndices(current, count);
1777 QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, synthesize);
1780bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
1782 if (m_pendingSignalHandler.isValid()) {
1783 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope, u"signalhandler"_s,
1784 ast->firstSourceLocation());
1785 flushPendingSignalParameters();
1790void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
1792 if (m_currentScope->scopeType() == QQmlSA::ScopeType::SignalHandlerFunctionScope) {
1799 const QQmlJS::SourceLocation &srcLocation);
1802 QQmlJSLogger *logger)
1804 QStringView namespaceName{ superType };
1805 namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
1806 logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
1808 qmlUncreatableType, location,
true,
true);
1811bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
1813 const QString superType = buildName(definition->qualifiedTypeNameId);
1815 const bool isRoot = !rootScopeIsValid();
1816 Q_ASSERT(!superType.isEmpty());
1823 const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
1824 const bool looksLikeGroupedProperty = !superType.front().isUpper();
1826 if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
1827 logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
1831 if (!looksLikeGroupedProperty) {
1833 enterEnvironment(QQmlSA::ScopeType::QMLScope, superType,
1834 definition->firstSourceLocation());
1836 enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
1837 definition->firstSourceLocation());
1838 m_currentScope->setIsRootFileComponentFlag(
true);
1841 const QTypeRevision revision = m_currentScope->baseTypeRevision();
1842 if (
auto base = m_currentScope->baseType(); base) {
1843 if (isRoot && base->internalName() == u"QQmlComponent") {
1844 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1845 definition->qualifiedTypeNameId->identifierToken,
true,
true);
1847 if (base->isSingleton() && m_currentScope->isComposite()) {
1848 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1849 m_currentScope->baseTypeName()),
1850 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1853 }
else if (!base->isCreatable()) {
1855 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1856 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1860 if (m_nextIsInlineComponent) {
1861 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1862 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1863 m_currentScope->setIsInlineComponent(
true);
1864 m_currentScope->setInlineComponentName(name);
1865 m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
1866 auto precedence = quint8(QQmlJS::PrecedenceValues::InlineComponent);
1867 m_rootScopeImports.setType(name, { m_currentScope, revision, precedence });
1868 m_nextIsInlineComponent =
false;
1871 addDefaultProperties();
1872 Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope);
1873 m_qmlTypes.append(m_currentScope);
1875 m_objectDefinitionScopes << m_currentScope;
1877 m_groupedObjectDefinitions.append(
1878 combine(definition->firstSourceLocation(), definition->lastSourceLocation()));
1879 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType,
1880 definition->firstSourceLocation());
1881 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType,
1882 definition->firstSourceLocation()));
1883 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(),
1887 m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
1892void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
1894 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), usedTypes());
1898bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
1900 if (!std::holds_alternative<RootDocumentNameType>(m_currentRootName)) {
1905 const auto it = m_seenInlineComponents.constFind(component->name);
1906 if (it != m_seenInlineComponents.cend()) {
1907 m_logger->log(
"Note: previous component named '%1' here"_L1.arg(it.key()),
1908 qmlDuplicateInlineComponent, it.value(),
true,
true, {},
1909 component->firstSourceLocation().startLine);
1911 m_seenInlineComponents[component->name] = component->firstSourceLocation();
1914 m_nextIsInlineComponent =
true;
1915 m_currentRootName = component->name.toString();
1919void QQmlJSImportVisitor::endVisit(UiInlineComponent *component)
1921 m_currentRootName = RootDocumentNameType();
1922 if (m_nextIsInlineComponent) {
1923 m_logger->log(u"Inline component declaration must be followed by a typename"_s,
1924 qmlSyntax, component->firstSourceLocation());
1926 m_nextIsInlineComponent =
false;
1929bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
1931 switch (publicMember->type) {
1932 case UiPublicMember::Signal: {
1933 const QString signalName = publicMember->name.toString();
1934 UiParameterList *param = publicMember->parameters;
1935 QQmlJSMetaMethod method;
1936 method.setMethodType(QQmlJSMetaMethodType::Signal);
1937 method.setReturnTypeName(QStringLiteral(
"void"));
1938 method.setMethodName(signalName);
1939 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1940 publicMember->lastSourceLocation()));
1941 method.setMethodIndex(
1942 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
1944 method.addParameter(
1945 QQmlJSMetaParameter(
1946 param->name.toString(),
1947 param->type ? param->type->toString() : QString()
1949 param = param->next;
1951 m_currentScope->addOwnMethod(method);
1954 case UiPublicMember::Property: {
1955 const QString propertyName = publicMember->name.toString();
1956 QString typeName = buildName(publicMember->memberType);
1957 if (typeName.contains(u'.') && typeName.front().isLower()) {
1958 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1962 const bool isAlias = (typeName == u"alias"_s);
1964 auto tryParseAlias = [&]() {
1966 if (!publicMember->statement) {
1967 m_logger->log(QStringLiteral(
"Invalid alias expression - an initializer is needed."),
1968 qmlSyntax, publicMember->memberType->firstSourceLocation());
1971 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1972 auto node = expression ? expression->expression :
nullptr;
1973 auto fex = cast<FieldMemberExpression *>(node);
1976 aliasExpr.prepend(u'.' + fex->name.toString());
1977 fex = cast<FieldMemberExpression *>(node);
1980 if (
const auto idExpression = cast<IdentifierExpression *>(node)) {
1981 aliasExpr.prepend(idExpression->name.toString());
1986 QQmlJSMetaProperty prop;
1987 prop.setPropertyName(propertyName);
1988 prop.setIsList(publicMember->typeModifier == QLatin1String(
"list"));
1989 prop.setIsWritable(!publicMember->isReadonly());
1990 prop.setIsFinal(publicMember->isFinal());
1991 prop.setIsVirtual(publicMember->isVirtual());
1992 prop.setIsOverride(publicMember->isOverride());
1993 prop.setAliasExpression(aliasExpr);
1994 prop.setSourceLocation(
1995 combine(publicMember->firstSourceLocation(), publicMember->colonToken));
1997 isAlias ? QQmlJSScope::ConstPtr() : m_rootScopeImports.type(typeName).scope;
1999 const auto factory = type.factory();
2001 prop.setType(prop.isList() ? (factory ? QQmlJSScope::Ptr{ } : type->listType()) : type);
2002 const QString internalName = factory ? factory->internalName() : type->internalName();
2003 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
2009 Q_ASSERT(type.factory() == factory);
2010 }
else if (!isAlias) {
2011 m_pendingPropertyTypes << PendingPropertyType { m_currentScope, prop.propertyName(),
2012 publicMember->firstSourceLocation() };
2013 prop.setTypeName(typeName);
2015 prop.setAnnotations(parseAnnotations(publicMember->annotations));
2016 if (publicMember->isDefaultMember())
2017 m_currentScope->setOwnDefaultPropertyName(propertyName);
2018 prop.setIndex(m_currentScope->ownProperties().size());
2019 m_currentScope->addOwnProperty(prop);
2021 QQmlJSMetaMethod method(
2022 QQmlSignalNames::propertyNameToChangedSignalName(propertyName), u"void"_s);
2023 method.setMethodType(QQmlJSMetaMethodType::Signal);
2024 method.setIsImplicitQmlPropertyChangeSignal(
true);
2025 method.setMethodIndex(
2026 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2027 m_currentScope->addOwnMethod(method);
2029 if (publicMember->isRequired())
2030 m_currentScope->setPropertyLocallyRequired(prop.propertyName(),
true);
2032 BindingExpressionParseResult parseResult = BindingExpressionParseResult::Invalid;
2036 parseBindingExpression(publicMember->name.toString(), publicMember->statement,
2042 if (parseResult == BindingExpressionParseResult::Script) {
2043 Q_ASSERT(!m_savedBindingOuterScope);
2044 m_savedBindingOuterScope = m_currentScope;
2045 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope, QStringLiteral(
"binding"),
2046 publicMember->statement->firstSourceLocation());
2056void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
2058 if (m_savedBindingOuterScope) {
2059 m_currentScope = m_savedBindingOuterScope;
2060 m_savedBindingOuterScope = {};
2062 forgetFunctionExpression(publicMember->name.toString());
2066bool QQmlJSImportVisitor::visit(UiRequired *required)
2068 const QString name = required->name.toString();
2070 m_requiredProperties << RequiredProperty { m_currentScope, name,
2071 required->firstSourceLocation() };
2073 m_currentScope->setPropertyLocallyRequired(name,
true);
2077void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
2079 using namespace QQmlJS::AST;
2080 auto name = fexpr->name.toString();
2081 if (!name.isEmpty()) {
2082 QQmlJSMetaMethod method(name);
2083 method.setMethodType(QQmlJSMetaMethodType::Method);
2084 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
2086 if (!m_pendingMethodAnnotations.isEmpty()) {
2087 method.setAnnotations(m_pendingMethodAnnotations);
2088 m_pendingMethodAnnotations.clear();
2092 const bool parseTypes = m_scopesById.signaturesAreEnforced();
2094 bool formalsFullyTyped = parseTypes;
2095 bool anyFormalTyped =
false;
2096 PendingMethodTypeAnnotations pending{ m_currentScope, name, {} };
2099 for (
auto formals = fexpr->formals; formals; formals = formals->next) {
2100 PatternElement *e = formals->element;
2103 if (e->typeAnnotation && (e->bindingTarget || e->initializer))
2104 m_logger->log(
"Type annotations on default parameters are not supported"_L1,
2106 combine(e->firstSourceLocation(), e->lastSourceLocation()));
2109 if (
const auto *formals = parseTypes ? fexpr->formals :
nullptr) {
2110 const auto parameters = formals->formals();
2111 for (
const auto ¶meter : parameters) {
2112 const QString type = parameter.typeAnnotation
2113 ? parameter.typeAnnotation->type->toString()
2115 if (type.isEmpty()) {
2116 formalsFullyTyped =
false;
2117 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral(
"var")));
2118 pending.locations.emplace_back();
2120 anyFormalTyped =
true;
2121 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
2122 pending.locations.append(
2123 combine(parameter.typeAnnotation->firstSourceLocation(),
2124 parameter.typeAnnotation->lastSourceLocation()));
2130 method.setIsJavaScriptFunction(!formalsFullyTyped);
2136 if (parseTypes && fexpr->typeAnnotation) {
2137 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
2138 pending.locations.append(combine(fexpr->typeAnnotation->firstSourceLocation(),
2139 fexpr->typeAnnotation->lastSourceLocation()));
2140 }
else if (anyFormalTyped) {
2141 method.setReturnTypeName(QStringLiteral(
"void"));
2143 method.setReturnTypeName(QStringLiteral(
"var"));
2146 const auto &locs = pending.locations;
2147 if (std::any_of(locs.cbegin(), locs.cend(), [](
const auto &loc) {
return loc.isValid(); }))
2148 m_pendingMethodTypeAnnotations << pending;
2150 method.setCompiledFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
2151 method.setMethodIndex(
2152 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2154 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
2156 const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
2157 ? fexpr->identifierToken
2158 : fexpr->functionToken;
2159 safeInsertJSIdentifier(m_currentScope, name,
2160 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2161 functionLocation, method.returnTypeName(),
2164 m_currentScope->addOwnMethod(method);
2166 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
2168 addFunctionOrExpression(m_currentScope, QStringLiteral(
"<anon>"));
2169 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral(
"<anon>"),
2170 fexpr->firstSourceLocation());
2174bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
2176 visitFunctionExpressionHelper(fexpr);
2180void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
2182 forgetFunctionExpression(fexpr->name.toString());
2186bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
2188 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
2192bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
2194 if (!fdecl->name.isEmpty()) {
2195 const QString name = fdecl->name.toString();
2196 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
2197 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
2198 fdecl->identifierToken);
2199 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
2200 previousDeclaration->location);
2203 visitFunctionExpressionHelper(fdecl);
2207void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
2209 forgetFunctionExpression(fdecl->name.toString());
2213bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
2215 QQmlJSMetaProperty prop;
2216 prop.setPropertyName(ast->name.toString());
2217 m_currentScope->addOwnProperty(prop);
2218 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2219 ast->firstSourceLocation());
2223void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
2229 QQmlJS::AST::ArgumentList *args)
2231 QStringView contextString;
2232 QStringView mainString;
2233 QStringView commentString;
2234 auto registerContextString = [&](QStringView string) {
2235 contextString = string;
2238 auto registerMainString = [&](QStringView string) {
2239 mainString = string;
2242 auto registerCommentString = [&](QStringView string) {
2243 commentString = string;
2246 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
2247 QV4::CompiledData::TranslationData data) {
2248 if (type == QV4::CompiledData::Binding::Type_Translation) {
2249 binding.setTranslation(mainString, commentString, contextString, data.number);
2250 }
else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
2251 binding.setTranslationId(mainString, data.number);
2253 binding.setStringLiteral(mainString);
2256 QmlIR::tryGeneratingTranslationBindingBase(
2258 registerMainString, registerCommentString, registerContextString, finalizeBinding);
2261QQmlJSImportVisitor::BindingExpressionParseResult
2262QQmlJSImportVisitor::parseBindingExpression(
2263 const QString &name,
const QQmlJS::AST::Statement *statement,
2264 const UiPublicMember *associatedPropertyDefinition)
2266 if (statement ==
nullptr)
2267 return BindingExpressionParseResult::Invalid;
2269 const auto *exprStatement = cast<
const ExpressionStatement *>(statement);
2271 if (exprStatement ==
nullptr) {
2272 QQmlJS::SourceLocation location = statement->firstSourceLocation();
2274 if (
const auto *block = cast<
const Block *>(statement); block && block->statements) {
2275 location = block->statements->firstSourceLocation();
2278 QQmlJSMetaPropertyBinding binding(location, name);
2279 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2280 QQmlSA::ScriptBindingKind::PropertyBinding, ScriptValue_Function);
2281 m_bindings.append(UnfinishedBinding {
2283 [binding = std::move(binding)]() {
return binding; }
2285 return BindingExpressionParseResult::Script;
2288 auto expr = exprStatement->expression;
2289 QQmlJSMetaPropertyBinding binding(
2290 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
2293 ScriptBindingValueType scriptBindingValuetype = ScriptValue_Unknown;
2295 switch (expr->kind) {
2296 case Node::Kind_TrueLiteral:
2297 binding.setBoolLiteral(
true);
2299 case Node::Kind_FalseLiteral:
2300 binding.setBoolLiteral(
false);
2302 case Node::Kind_NullExpression:
2303 binding.setNullLiteral();
2305 case Node::Kind_IdentifierExpression: {
2306 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
2308 if (idExpr->name == u"undefined")
2309 scriptBindingValuetype = ScriptValue_Undefined;
2312 case Node::Kind_FunctionDeclaration:
2313 case Node::Kind_FunctionExpression:
2314 case Node::Kind_Block: {
2315 scriptBindingValuetype = ScriptValue_Function;
2318 case Node::Kind_NumericLiteral:
2319 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
2321 case Node::Kind_StringLiteral:
2322 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
2324 case Node::Kind_RegExpLiteral:
2325 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
2327 case Node::Kind_TemplateLiteral: {
2328 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
2329 Q_ASSERT(templateLit);
2330 if (templateLit->hasNoSubstitution) {
2331 binding.setStringLiteral(templateLit->value);
2333 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2334 QQmlSA::ScriptBindingKind::PropertyBinding);
2335 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
2336 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
2337 expression->accept(
this);
2343 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
2344 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
2345 binding.setNumberLiteral(-lit->value);
2346 }
else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
2347 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
2348 handleTranslationBinding(binding, base->name, call->arguments);
2353 if (!binding.isValid()) {
2355 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2356 QQmlSA::ScriptBindingKind::PropertyBinding,
2357 scriptBindingValuetype);
2359 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
2362 if (binding.bindingType() == QQmlSA::BindingType::Translation
2363 || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
2364 return BindingExpressionParseResult::Translation;
2366 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
2367 return BindingExpressionParseResult::Script;
2369 if (associatedPropertyDefinition)
2370 handleLiteralBinding(binding, associatedPropertyDefinition);
2372 return BindingExpressionParseResult::Literal;
2375bool QQmlJSImportVisitor::isImportPrefix(QString prefix)
const
2377 if (prefix.isEmpty() || !prefix.front().isUpper())
2380 return m_rootScopeImports.isNullType(prefix);
2383void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
2385 if (m_currentScope->scopeType() != QQmlJSScope::ScopeType::QMLScope) {
2386 m_logger->log(u"id declarations are only allowed in objects"_s, qmlSyntax,
2387 scriptBinding->statement->firstSourceLocation());
2390 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
2395 const QString name = [&]() {
2396 if (
const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2397 return idExpression->name.toString();
2398 else if (
const auto *idString = cast<StringLiteral *>(statement->expression)) {
2399 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2400 idString->firstSourceLocation());
2401 return idString->value.toString();
2407 m_currentScope->setIdSourceLocation(combine(scriptBinding->statement->firstSourceLocation(),
2408 scriptBinding->statement->lastSourceLocation()));
2409 if (m_scopesById.existsAnywhereInDocument(name)) {
2412 breakInheritanceCycles(m_currentScope);
2413 m_scopesById.possibleScopes(
2414 name, m_currentScope, QQmlJSScopesByIdOption::Default,
2415 [&](
const QQmlJSScope::ConstPtr &otherScopeWithID,
2416 QQmlJSScopesById::Confidence confidence) {
2418 Q_UNUSED(confidence);
2420 auto otherLocation = otherScopeWithID->sourceLocation();
2424 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2425 name, QString::number(otherLocation.startLine),
2426 QString::number(otherLocation.startColumn)),
2427 qmlSyntaxDuplicateIds,
2428 scriptBinding->firstSourceLocation());
2429 return QQmlJSScopesById::CallbackResult::ContinueSearch;
2432 if (!name.isEmpty())
2433 m_scopesById.insert(name, m_currentScope);
2436void QQmlJSImportVisitor::handleLiteralBinding(
const QQmlJSMetaPropertyBinding &binding,
2437 const UiPublicMember *associatedPropertyDefinition)
2441 Q_UNUSED(associatedPropertyDefinition);
2445
2446
2447
2448
2449
2452 const QQmlJS::SourceLocation &srcLocation)
2454 const auto createBinding = [=]() {
2455 const QQmlJSScope::ScopeType type = scope->scopeType();
2462 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2463 const bool alreadyHasBinding =
std::any_of(propertyBindings.first, propertyBindings.second,
2464 [&](
const QQmlJSMetaPropertyBinding &binding) {
2465 return binding.bindingType() == bindingType;
2467 if (alreadyHasBinding)
2468 return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
2471 if (type == QQmlSA::ScopeType::GroupedPropertyScope)
2472 binding.setGroupBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2474 binding.setAttachedBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2477 return { scope->parentScope(), createBinding };
2480bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
2482 Q_ASSERT(!m_savedBindingOuterScope);
2483 Q_ASSERT(!m_thisScriptBindingIsJavaScript);
2484 m_savedBindingOuterScope = m_currentScope;
2485 const auto id = scriptBinding->qualifiedId;
2486 if (!id->next && id->name == QLatin1String(
"id")) {
2487 handleIdDeclaration(scriptBinding);
2494 for (; group->next; group = group->next) {
2495 const QString name = group->name.toString();
2499 if (group == id && isImportPrefix(name)) {
2500 prefix = name + u'.';
2504 const bool isAttachedProperty = name.front().isUpper();
2505 if (isAttachedProperty) {
2507 enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
2508 group->firstSourceLocation());
2511 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
2512 group->firstSourceLocation());
2514 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
2515 group->firstSourceLocation()));
2520 const auto name = group->name.toString();
2524 const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
2526 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2527 m_propertyBindings[m_currentScope].append(
2528 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2530 auto result = parseBindingExpression(name, scriptBinding->statement);
2531 m_thisScriptBindingIsJavaScript = (result == BindingExpressionParseResult::Script);
2533 const auto statement = scriptBinding->statement;
2534 QStringList signalParameters;
2536 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2537 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2538 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2539 signalParameters << formal->element->bindingIdentifier.toString();
2543 QQmlJSMetaMethod scopeSignal;
2544 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2545 if (!methods.isEmpty())
2546 scopeSignal = methods[0];
2548 const auto firstSourceLocation = statement->firstSourceLocation();
2549 bool hasMultilineStatementBody =
2550 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2551 m_pendingSignalHandler = firstSourceLocation;
2552 m_signalHandlers.insert(firstSourceLocation,
2553 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2557 const auto index = addFunctionOrExpression(m_currentScope, name);
2558 const auto createBinding = [
2560 scope = m_currentScope,
2561 signalName = *signal,
2564 firstSourceLocation,
2565 groupLocation = group->firstSourceLocation(),
2566 signalParameters]() {
2568 Q_ASSERT(scope->isFullyResolved());
2569 QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
2570 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2571 if (!methods.isEmpty()) {
2572 kind = QQmlSA::ScriptBindingKind::SignalHandler;
2573 checkSignal(scope, groupLocation, name, signalParameters);
2574 }
else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2575 kind = QQmlSA::ScriptBindingKind::ChangeHandler;
2576 checkSignal(scope, groupLocation, name, signalParameters);
2577 }
else if (scope->hasProperty(name)) {
2580 kind = QQmlSA::ScriptBindingKind::PropertyBinding;
2581 m_signalHandlers.remove(firstSourceLocation);
2584 checkSignal(scope, groupLocation, name, signalParameters);
2587 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2588 binding.setScriptBinding(index, kind, ScriptValue_Function);
2591 m_bindings.append(UnfinishedBinding { m_currentScope, createBinding });
2592 m_thisScriptBindingIsJavaScript =
true;
2598 while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
2599 || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
2604 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope,
2606 scriptBinding->statement->firstSourceLocation());
2608 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope,
2610 scriptBinding->statement->firstSourceLocation());
2616void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
2618 if (m_savedBindingOuterScope) {
2619 m_currentScope = m_savedBindingOuterScope;
2620 m_savedBindingOuterScope = {};
2626 if (m_thisScriptBindingIsJavaScript) {
2627 m_thisScriptBindingIsJavaScript =
false;
2628 Q_ASSERT(!m_functionStack.isEmpty());
2629 m_functionStack.pop();
2633bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
2635 createAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2636 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2637 arrayBinding->firstSourceLocation());
2638 m_currentScope->setIsArrayScope(
true);
2642void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
2649 const auto children = m_currentScope->childScopes();
2652 const int scopesEnteredCounter = openAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2653 auto guard = qScopeGuard([
this, scopesEnteredCounter]() {
2654 for (
int i = 0; i < scopesEnteredCounter; ++i)
2658 if (checkCustomParser(m_currentScope)) {
2664 auto group = arrayBinding->qualifiedId;
2665 for (; group->next; group = group->next) { }
2666 const QString propertyName = group->name.toString();
2669 for (
auto element = arrayBinding->members; element; element = element->next, ++i) {
2670 const auto &type = children[i];
2671 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2672 m_logger->log(u"Declaring an object which is not a Qml object"
2673 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2676 m_pendingPropertyObjectBindings
2677 << PendingPropertyObjectBinding { m_currentScope, type, propertyName,
2678 element->firstSourceLocation(),
false };
2679 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2680 binding.setObject(QQmlJSUtils::getScopeName(type, QQmlSA::ScopeType::QMLScope),
2681 QQmlJSScope::ConstPtr(type));
2682 m_bindings.append(UnfinishedBinding {
2684 [binding = std::move(binding)]() {
return binding; },
2685 QQmlJSScope::ListPropertyTarget
2690bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
2692 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2693 qmlEnum.setIsQml(
true);
2694 qmlEnum.setLineNumber(uied->enumToken.startLine);
2695 for (
const auto *member = uied->members; member; member = member->next) {
2696 qmlEnum.addKey(member->member.toString());
2697 qmlEnum.addValue(
int(member->value));
2699 m_currentScope->addOwnEnumeration(qmlEnum);
2703QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromHost(
2704 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2706 QFileInfo fileInfo(path);
2707 if (!fileInfo.exists()) {
2708 m_logger->log(
"File or directory you are trying to import does not exist: %1."_L1.arg(path),
2709 qmlImport, location);
2713 if (fileInfo.isFile()) {
2714 const auto scope = m_importer->importFile(path);
2715 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2716 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2717 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2718 addImportWithLocation(actualPrefix, location,
false);
2722 if (fileInfo.isDir()) {
2723 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2724 auto scopes = m_importer->importDirectory(path, precedence, prefix);
2725 m_importer->registerVersionedImports(scopes);
2726 const auto types = scopes.types();
2727 const auto warnings = scopes.warnings();
2728 m_rootScopeImports.add(std::move(scopes));
2729 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2730 addImportWithLocation(*it, location, !warnings.isEmpty());
2735 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2737 qmlImport, location);
2741QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromQrc(
2742 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2744 Q_ASSERT(path.startsWith(u':'));
2745 const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper();
2749 const auto pathNoColon = QStringView(path).mid(1);
2750 if (mapper->isFile(pathNoColon)) {
2751 const auto entry = m_importer->resourceFileMapper()->entry(
2752 QQmlJSResourceFileMapper::resourceFileFilter(pathNoColon.toString()));
2753 const auto scope = m_importer->importFile(entry.filePath);
2754 const QString actualPrefix =
2755 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2756 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2757 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2758 addImportWithLocation(actualPrefix, location,
false);
2762 auto scopes = m_importer->importDirectory(path, quint8(QQmlJS::PrecedenceValues::Default), prefix);
2763 m_importer->registerVersionedImports(scopes);
2764 const auto types = scopes.types();
2765 const auto warnings = scopes.warnings();
2766 m_rootScopeImports.add(std::move(scopes));
2767 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2768 addImportWithLocation(*it, location, !warnings.isEmpty());
2772bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
2775 QString prefix = QLatin1String(
"");
2776 if (import->asToken.isValid()) {
2777 prefix += import->importId;
2778 m_seenModuleQualifiers.append(prefix);
2781 const QString filename = import->fileName.toString();
2782 if (!filename.isEmpty()) {
2783 const QUrl url(filename);
2784 const QString scheme = url.scheme();
2785 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2786 if (scheme ==
""_L1) {
2787 QFileInfo fileInfo(url.path());
2788 QString absolute = fileInfo.isRelative()
2789 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2791 auto warnings = absolute.startsWith(u':')
2792 ? importFromQrc(absolute, prefix, importLocation)
2793 : importFromHost(absolute, prefix, importLocation);
2794 processImportWarnings(
"path \"%1\""_L1.arg(url.path()), warnings, importLocation);
2796 }
else if (scheme ==
"file"_L1) {
2797 auto warnings = importFromHost(url.path(), prefix, importLocation);
2798 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2800 }
else if (scheme ==
"qrc"_L1) {
2801 auto warnings = importFromQrc(
":"_L1 + url.path(), prefix, importLocation);
2802 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2805 m_logger->log(
"Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2806 qmlImport, import->firstSourceLocation());
2810 const QString path = buildName(import->importUri);
2812 QStringList staticModulesProvided;
2814 auto imported = m_importer->importModule(
2815 path, quint8(QQmlJS::PrecedenceValues::Default), prefix,
2816 import->version ? import->version->version : QTypeRevision(), &staticModulesProvided);
2817 m_importer->registerVersionedImports(imported);
2818 const auto types = imported.types();
2819 const auto warnings = imported.warnings();
2820 m_rootScopeImports.add(std::move(imported));
2821 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2822 addImportWithLocation(*it, import->firstSourceLocation(), !warnings.isEmpty());
2824 if (prefix.isEmpty()) {
2825 for (
const QString &staticModule : std::as_const(staticModulesProvided))
2826 addStaticImportWithLocation(path, import->firstSourceLocation(), path != staticModule);
2829 processImportWarnings(
2830 QStringLiteral(
"module \"%1\"").arg(path), warnings, import->firstSourceLocation());
2834#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
2836void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2838 for (
const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2843void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2845 assign(pragma->value);
2849bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
2851 if (pragma->name == u"Strict"_s) {
2856 if (!m_logger->wasCategoryChanged(qmlCompiler))
2857 m_logger->setCategorySeverity(qmlCompiler, QQmlJS::WarningSeverity::Warning);
2858 }
else if (pragma->name == u"ComponentBehavior") {
2859 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2860 if (value == u"Bound") {
2861 m_scopesById.setComponentsAreBound(
true);
2862 }
else if (value == u"Unbound") {
2863 m_scopesById.setComponentsAreBound(
false);
2865 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2866 qmlSyntax, pragma->firstSourceLocation());
2869 }
else if (pragma->name == u"FunctionSignatureBehavior") {
2870 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2871 if (value == u"Enforced") {
2872 m_scopesById.setSignaturesAreEnforced(
true);
2873 }
else if (value == u"Ignored") {
2874 m_scopesById.setSignaturesAreEnforced(
false);
2877 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2878 qmlSyntax, pragma->firstSourceLocation());
2881 }
else if (pragma->name == u"ValueTypeBehavior") {
2882 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2883 if (value == u"Copy") {
2885 }
else if (value == u"Reference") {
2887 }
else if (value == u"Addressable") {
2888 m_scopesById.setValueTypesAreAddressable(
true);
2889 }
else if (value == u"Inaddressable") {
2890 m_scopesById.setValueTypesAreAddressable(
false);
2891 }
else if (value == u"Assertable") {
2892 m_scopesById.setValueTypesAreAssertable(
true);
2893 }
else if (value == u"Inassertable") {
2894 m_scopesById.setValueTypesAreAssertable(
false);
2896 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2897 qmlSyntax, pragma->firstSourceLocation());
2905void QQmlJSImportVisitor::throwRecursionDepthError()
2907 m_logger->log(QStringLiteral(
"Maximum statement or expression depth exceeded"),
2908 qmlRecursionDepthErrors, QQmlJS::SourceLocation());
2911bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
2913 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2914 ast->firstSourceLocation());
2918void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
2923bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
2925 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"forloop"),
2926 ast->firstSourceLocation());
2930void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
2935bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
2937 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"foreachloop"),
2938 ast->firstSourceLocation());
2942void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
2947bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
2949 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"block"),
2950 ast->firstSourceLocation());
2952 if (m_pendingSignalHandler.isValid())
2953 flushPendingSignalParameters();
2958void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
2963bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
2965 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"case"),
2966 ast->firstSourceLocation());
2970void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
2975bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
2977 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"catch"),
2978 catchStatement->firstSourceLocation());
2982void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
2987bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
2989 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"with"),
2990 ast->firstSourceLocation());
2992 m_logger->log(QStringLiteral(
"with statements are strongly discouraged in QML "
2993 "and might cause false positives when analysing unqualified "
2995 qmlWith, ast->firstSourceLocation());
3000void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
3005bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
3007 const auto &boundedNames = fpl->boundNames();
3008 for (
auto const &boundName : boundedNames) {
3010 std::optional<QString> typeName;
3011 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
3012 if (Type *type = annotation->type)
3013 typeName = type->toString();
3014 safeInsertJSIdentifier(m_currentScope, boundName.id,
3015 { QQmlJSScope::JavaScriptIdentifier::Parameter,
3016 boundName.location, typeName,
false });
3021void QQmlJSImportVisitor::createAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3023 bool needsResolution =
false;
3024 int scopesEnteredCounter = 0;
3026 for (
auto group = propertyName; group->next; group = group->next) {
3027 const QString idName = group->name.toString();
3029 if (idName.isEmpty())
3032 if (group == propertyName && isImportPrefix(idName)) {
3033 prefix = idName + u'.';
3037 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3038 : QQmlSA::ScopeType::GroupedPropertyScope;
3041 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3043 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + idName,
3044 group->firstSourceLocation()));
3046 ++scopesEnteredCounter;
3047 needsResolution = needsResolution || !exists;
3052 for (
int i=0; i < scopesEnteredCounter; ++i) {
3057 if (needsResolution) {
3058 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(),
3063bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
3067 Q_ASSERT(uiob->qualifiedTypeNameId);
3069 const QString typeName = buildName(uiob->qualifiedTypeNameId);
3070 if (typeName.front().isLower() && typeName.contains(u'.')) {
3071 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
3074 createAttachedAndGroupedScopes(uiob->qualifiedId);
3076 enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
3077 uiob->qualifiedTypeNameId->identifierToken);
3079 m_qmlTypes.append(m_currentScope);
3080 m_objectBindingScopes << m_currentScope;
3084int QQmlJSImportVisitor::openAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3087 int scopesEnteredCounter = 0;
3088 auto group = propertyName;
3089 for (; group->next; group = group->next) {
3090 const QString idName = group->name.toString();
3092 if (idName.isEmpty())
3095 if (group == propertyName && isImportPrefix(idName)) {
3096 prefix = idName + u'.';
3100 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3101 : QQmlSA::ScopeType::GroupedPropertyScope;
3103 [[maybe_unused]]
bool exists =
3104 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3106 scopesEnteredCounter++;
3110 return scopesEnteredCounter;
3113void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
3115 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), usedTypes());
3117 const QQmlJSScope::Ptr childScope = m_currentScope;
3120 const int scopesEnteredCounter = openAttachedAndGroupedScopes(uiob->qualifiedId);
3125 auto group = uiob->qualifiedId;
3126 for (; group->next; group = group->next) { }
3127 const QString propertyName = group->name.toString();
3129 if (m_currentScope->isNameDeferred(propertyName)) {
3130 bool foundIds =
false;
3131 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
3133 while (!childScopes.isEmpty()) {
3134 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
3135 m_scopesById.possibleIds(
3136 scope, scope, QQmlJSScopesByIdOption::Default,
3137 [&](
const QString &id, QQmlJSScopesById::Confidence confidence) {
3140 Q_UNUSED(confidence);
3142 return QQmlJSScopesById::CallbackResult::StopSearch;
3145 childScopes << scope->childScopes();
3150 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
3152 qmlDeferredPropertyId, uiob->firstSourceLocation());
3156 if (checkCustomParser(m_currentScope)) {
3160 m_pendingPropertyObjectBindings
3161 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
3162 uiob->firstSourceLocation(), uiob->hasOnToken };
3164 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
3165 if (uiob->hasOnToken) {
3166 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
3167 binding.setInterceptor(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3168 QQmlJSScope::ConstPtr(childScope));
3170 binding.setValueSource(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3171 QQmlJSScope::ConstPtr(childScope));
3174 binding.setObject(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3175 QQmlJSScope::ConstPtr(childScope));
3177 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
3180 for (
int i = 0; i < scopesEnteredCounter; ++i)
3184bool QQmlJSImportVisitor::visit(ExportDeclaration *)
3186 Q_ASSERT(rootScopeIsValid());
3187 Q_ASSERT(m_exportedRootScope != m_globalScope);
3188 Q_ASSERT(m_currentScope == m_globalScope);
3189 m_currentScope = m_exportedRootScope;
3193void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
3195 Q_ASSERT(rootScopeIsValid());
3196 m_currentScope = m_exportedRootScope->parentScope();
3197 Q_ASSERT(m_currentScope == m_globalScope);
3200bool QQmlJSImportVisitor::visit(ESModule *module)
3202 Q_ASSERT(!rootScopeIsValid());
3203 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"module"),
3204 module->firstSourceLocation());
3205 m_currentScope->setIsScript(
true);
3206 importBaseModules();
3211void QQmlJSImportVisitor::endVisit(ESModule *)
3213 QQmlJSScope::resolveTypes(m_exportedRootScope, m_rootScopeImports.contextualTypes(),
3217bool QQmlJSImportVisitor::visit(Program *program)
3219 Q_ASSERT(m_globalScope == m_currentScope);
3220 Q_ASSERT(!rootScopeIsValid());
3221 enterRootScope(QQmlSA::ScopeType::JSFunctionScope, u"script"_s, program->firstSourceLocation());
3222 m_exportedRootScope->setIsScript(
true);
3223 importBaseModules();
3227void QQmlJSImportVisitor::endVisit(Program *)
3229 QQmlJSScope::resolveTypes(m_exportedRootScope, m_rootScopeImports.contextualTypes(),
3233bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
3236 if (element->isVariableDeclaration()) {
3237 QQmlJS::AST::BoundNames names;
3238 element->boundNames(&names);
3239 for (
const auto &name : std::as_const(names)) {
3240 std::optional<QString> typeName;
3241 if (TypeAnnotation *annotation = name.typeAnnotation.data())
3242 if (Type *type = annotation->type)
3243 typeName = type->toString();
3244 using Kind = QQmlJSScope::JavaScriptIdentifier::Kind;
3245 const Kind kind = (element->scope == QQmlJS::AST::VariableScope::Var)
3246 ? Kind::FunctionScoped
3247 : Kind::LexicalScoped;
3248 const QString variableName = name.id;
3249 if (kind == Kind::LexicalScoped) {
3250 const QQmlJS::SourceLocation location = element->firstSourceLocation();
3251 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(variableName)) {
3252 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(variableName), qmlSyntax,
3254 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(variableName), qmlSyntax,
3255 previousDeclaration->location);
3258 const bool isConstVariable = element->scope == QQmlJS::AST::VariableScope::Const;
3259 const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
3261 { (element->scope == QQmlJS::AST::VariableScope::Var)
3262 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
3263 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
3264 name.location, typeName,