157 target->setScopeType(QQmlSA::ScopeType::QMLScope);
158 target->setBaseTypeName(
"$InProcess$"_L1);
159 target->setFilePath(m_logger->filePath());
160 target->setIsComposite(
true);
161 if (!m_importer->registerScope(target)) {
162 qCDebug(lcImportVisitor)
163 <<
"Couldn't register scope into importer: scope will be created multiple times.";
171 target->resetForReparse();
174QQmlJSImportVisitor::QQmlJSImportVisitor(
175 const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger,
176 const QString &implicitImportDirectory,
const QStringList &qmldirFiles)
177 : m_implicitImportDirectory(implicitImportDirectory),
178 m_qmldirFiles(qmldirFiles),
179 m_exportedRootScope(target),
180 m_importer(importer),
183 QQmlJS::ContextualTypes(
184 QQmlJS::ContextualTypes::QML, {}, {},
185 importer->builtinInternalNames().contextualTypes().arrayType()),
190 prepareTargetForVisit(target);
191 registerTargetIntoImporter(target);
194
195
196
197
198
199 auto globalScope = QQmlJSScope::create();
200 globalScope->setInternalName(u"global"_s);
201 globalScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
203 QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
204 QQmlJSScope::JavaScriptIdentifier::LexicalScoped, QQmlJS::SourceLocation(), std::nullopt,
208 QV4::Compiler::Codegen::forEachGlobalName([&](QLatin1StringView globalName) {
209 globalScope->insertJSIdentifier(globalName, globalJavaScript);
212 m_globalScope = globalScope;
213 m_currentScope = globalScope;
216QQmlJSImportVisitor::~QQmlJSImportVisitor() =
default;
218void QQmlJSImportVisitor::populateCurrentScope(
219 QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
221 m_currentScope->setScopeType(type);
222 m_currentScope->setIsComposite(
true);
223 m_currentScope->setFilePath(m_logger->filePath());
224 m_currentScope->setSourceLocation(location);
225 setScopeName(m_currentScope, type, name);
226 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
229void QQmlJSImportVisitor::enterRootScope(QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
231 Q_ASSERT(m_currentScope == m_globalScope);
232 QQmlJSScope::reparent(m_currentScope, m_exportedRootScope);
233 m_currentScope = m_exportedRootScope;
234 populateCurrentScope(type, name, location);
237void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type,
const QString &name,
238 const QQmlJS::SourceLocation &location)
240 QQmlJSScope::Ptr newScope = QQmlJSScope::create();
241 QQmlJSScope::reparent(m_currentScope, newScope);
242 m_currentScope = std::move(newScope);
243 populateCurrentScope(type, name, location);
246bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type,
248 const QQmlJS::SourceLocation &location)
250 Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope
251 || type == QQmlSA::ScopeType::AttachedPropertyScope);
253 const auto pred = [&](
const QQmlJSScope::ConstPtr &s) {
256 return s->internalName() == name;
258 const auto scopes = m_currentScope->childScopes();
261 auto it = std::find_if(scopes.begin(), scopes.end(), pred);
262 if (it == scopes.end()) {
264 enterEnvironment(type, name, location);
268 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, *it);
269 m_currentScope = *it;
273void QQmlJSImportVisitor::leaveEnvironment()
275 m_currentScope = m_currentScope->parentScope();
278void QQmlJSImportVisitor::warnUnresolvedType(
const QQmlJSScope::ConstPtr &type)
const
280 m_logger->log(QStringLiteral(
"Type %1 is used but it is not resolved")
281 .arg(QQmlJSUtils::getScopeName(type, type->scopeType())),
282 qmlUnresolvedType, type->sourceLocation());
285void QQmlJSImportVisitor::warnMissingPropertyForBinding(
286 const QString &property,
const QQmlJS::SourceLocation &location,
287 const std::optional<QQmlJSFixSuggestion> &fixSuggestion)
289 m_logger->log(QStringLiteral(
"Could not find property \"%1\".").arg(property),
290 qmlMissingProperty, location,
true,
true, fixSuggestion);
298void QQmlJSImportVisitor::resolveAliases()
300 QQueue<QQmlJSScope::Ptr> objects;
301 objects.enqueue(m_exportedRootScope);
303 qsizetype lastRequeueLength = std::numeric_limits<qsizetype>::max();
304 QQueue<QQmlJSScope::Ptr> requeue;
306 while (!objects.isEmpty()) {
307 const QQmlJSScope::Ptr object = objects.dequeue();
308 const auto properties = object->ownProperties();
310 bool doRequeue =
false;
311 for (
const auto &property : properties) {
312 if (!property.isAlias() || !property.type().isNull())
315 QStringList components = property.aliasExpression().split(u'.');
316 QQmlJSMetaProperty targetProperty;
318 bool foundProperty =
false;
321 QQmlJSScope::ConstPtr type = m_scopesById.scope(components.takeFirst(), object);
322 QQmlJSScope::ConstPtr typeScope;
323 if (!type.isNull()) {
324 foundProperty =
true;
331 while (type && !components.isEmpty()) {
332 const QString name = components.takeFirst();
334 if (!type->hasProperty(name)) {
335 foundProperty =
false;
340 const auto target = type->property(name);
341 if (!target.type() && target.isAlias())
344 type = target.type();
345 targetProperty = target;
353 m_logger->log(QStringLiteral(
"Cannot deduce type of alias \"%1\"")
354 .arg(property.propertyName()),
355 qmlMissingType, property.sourceLocation());
357 m_logger->log(QStringLiteral(
"Cannot resolve alias \"%1\"")
358 .arg(property.propertyName()),
359 qmlUnresolvedAlias, property.sourceLocation());
362 Q_ASSERT(property.index() >= 0);
363 object->addOwnProperty(property);
366 QQmlJSMetaProperty newProperty = property;
367 newProperty.setType(type);
369 newProperty.setIsList(targetProperty.isList());
370 newProperty.setIsWritable(targetProperty.isWritable());
371 newProperty.setIsFinal(targetProperty.isFinal());
372 newProperty.setIsPointer(targetProperty.isPointer());
374 const bool onlyId = !property.aliasExpression().contains(u'.');
376 newProperty.setAliasTargetScope(type);
377 newProperty.setAliasTargetName(QStringLiteral(
"id-only-alias"));
379 const auto &ownerScope = QQmlJSScope::ownerOfProperty(
380 typeScope, targetProperty.propertyName()).scope;
381 newProperty.setAliasTargetScope(ownerScope);
382 newProperty.setAliasTargetName(targetProperty.propertyName());
385 if (
const QString internalName = type->internalName(); !internalName.isEmpty())
386 newProperty.setTypeName(internalName);
388 Q_ASSERT(newProperty.index() >= 0);
389 object->addOwnProperty(newProperty);
390 m_aliasDefinitions.append({ object, property.propertyName() });
394 const auto childScopes = object->childScopes();
395 for (
const auto &childScope : childScopes)
396 objects.enqueue(childScope);
399 requeue.enqueue(object);
401 if (objects.isEmpty() && requeue.size() < lastRequeueLength) {
402 lastRequeueLength = requeue.size();
403 objects.swap(requeue);
407 while (!requeue.isEmpty()) {
408 const QQmlJSScope::Ptr object = requeue.dequeue();
409 const auto properties = object->ownProperties();
410 for (
const auto &property : properties) {
411 if (!property.isAlias() || property.type())
413 m_logger->log(QStringLiteral(
"Alias \"%1\" is part of an alias cycle")
414 .arg(property.propertyName()),
415 qmlAliasCycle, property.sourceLocation());
420void QQmlJSImportVisitor::resolveGroupProperties()
422 QQueue<QQmlJSScope::Ptr> objects;
423 objects.enqueue(m_exportedRootScope);
425 while (!objects.isEmpty()) {
426 const QQmlJSScope::Ptr object = objects.dequeue();
427 const auto childScopes = object->childScopes();
428 for (
const auto &childScope : childScopes) {
429 if (mayBeUnresolvedGroupedProperty(childScope)) {
430 const QString name = childScope->internalName();
431 if (object->isNameDeferred(name)) {
432 const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
433 if (!deferred.isNull()) {
434 QQmlJSScope::resolveGroup(
435 childScope, deferred, m_rootScopeImports.contextualTypes(),
438 }
else if (
const QQmlJSScope::ConstPtr propType = object->property(name).type()) {
439 QQmlJSScope::resolveGroup(
440 childScope, propType, m_rootScopeImports.contextualTypes(),
444 objects.enqueue(childScope);
449QString QQmlJSImportVisitor::implicitImportDirectory(
450 const QString &localFile, 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 = std::partition(importWarnings.begin(), importWarnings.end(),
477 [](
const QQmlJS::DiagnosticMessage &message) {
478 return message.type != QtMsgType::QtInfoMsg;
480 if (fileSelectorWarningsIt != importWarnings.end()) {
481 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImportFileSelector,
483 m_logger->processMessages(QSpan(fileSelectorWarningsIt, importWarnings.end()),
484 qmlImportFileSelector, srcLocation);
487 if (fileSelectorWarningsIt == importWarnings.begin())
490 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImport,
492 m_logger->processMessages(QSpan(importWarnings.begin(), fileSelectorWarningsIt), qmlImport,
496void QQmlJSImportVisitor::importBaseModules()
498 Q_ASSERT(m_rootScopeImports.isEmpty());
499 m_rootScopeImports = m_importer->importHardCodedBuiltins();
501 const QQmlJS::SourceLocation invalidLoc;
502 const auto types = m_rootScopeImports.types();
503 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
504 addImportWithLocation(*it, invalidLoc,
false);
506 if (!m_qmldirFiles.isEmpty())
507 m_rootScopeImports.addWarnings(m_importer->importQmldirs(m_qmldirFiles));
511 if (!m_logger->filePath().endsWith(u".qmltypes"_s)) {
512 m_rootScopeImports.add(m_importer->importDirectory(
513 m_implicitImportDirectory, QQmlJS::PrecedenceValues::ImplicitImport));
518 if (QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper()) {
519 const QStringList resourcePaths = mapper->resourcePaths(QQmlJSResourceFileMapper::Filter {
520 m_logger->filePath(), QStringList(), QQmlJSResourceFileMapper::Resource });
521 for (
const QString &path : resourcePaths) {
522 const qsizetype lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
525 m_rootScopeImports.add(m_importer->importDirectory(
526 path.first(lastSlash), QQmlJS::PrecedenceValues::ImplicitImport));
531 processImportWarnings(QStringLiteral(
"base modules"), m_rootScopeImports.warnings());
534bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiProgram *)
538 if (
auto elementName = QFileInfo(m_logger->filePath()).baseName();
539 !elementName.isEmpty() && elementName[0].isUpper()) {
540 m_rootScopeImports.setType(elementName,
541 { m_exportedRootScope, QTypeRevision{ },
542 QQmlJS::PrecedenceValues::ImplicitImport });
548void QQmlJSImportVisitor::endVisit(UiProgram *)
550 for (
const auto &scope : std::as_const(m_objectBindingScopes)) {
551 breakInheritanceCycles(scope);
552 checkDeprecation(scope);
553 checkForComponentTypeWithProperties(scope);
556 for (
const auto &scope : std::as_const(m_objectDefinitionScopes)) {
557 if (m_pendingDefaultProperties.contains(scope))
559 breakInheritanceCycles(scope);
560 checkDeprecation(scope);
561 checkForComponentTypeWithProperties(scope);
564 const auto &keys = m_pendingDefaultProperties.keys();
565 for (
const auto &scope : keys) {
566 breakInheritanceCycles(scope);
567 checkDeprecation(scope);
568 checkForComponentTypeWithProperties(scope);
572 resolveGroupProperties();
574 for (
const auto &scope : std::as_const(m_objectDefinitionScopes))
575 checkGroupedAndAttachedScopes(scope);
578 processDefaultProperties();
579 processPropertyTypes();
580 processMethodTypes();
581 processPropertyBindings();
582 processPropertyBindingObjects();
583 checkRequiredProperties();
585 auto unusedImports = m_importLocations;
586 for (
const QString &type : std::as_const(m_usedTypes)) {
587 const auto &importLocations = m_importTypeLocationMap.values(type);
588 for (
const auto &importLocation : importLocations)
589 unusedImports.remove(importLocation);
592 if (unusedImports.isEmpty())
596 const auto &imports = m_importStaticModuleLocationMap.values();
597 for (
const QQmlJS::SourceLocation &import : imports)
598 unusedImports.remove(import);
600 for (
const auto &import : unusedImports) {
601 m_logger->log(QString::fromLatin1(
"Unused import"), qmlUnusedImports, import);
604 populateRuntimeFunctionIndicesForDocument();
609 ExpressionStatement *expr = cast<ExpressionStatement *>(statement);
611 if (!statement || !expr->expression)
614 switch (expr->expression->kind) {
615 case Node::Kind_StringLiteral:
616 return cast<StringLiteral *>(expr->expression)->value.toString();
617 case Node::Kind_NumericLiteral:
618 return cast<NumericLiteral *>(expr->expression)->value;
624QList<QQmlJSAnnotation> QQmlJSImportVisitor::parseAnnotations(QQmlJS::AST::UiAnnotationList *list)
627 QList<QQmlJSAnnotation> annotationList;
629 for (UiAnnotationList *item = list; item !=
nullptr; item = item->next) {
630 UiAnnotation *annotation = item->annotation;
632 QQmlJSAnnotation qqmljsAnnotation;
633 qqmljsAnnotation.name = buildName(annotation->qualifiedTypeNameId);
635 for (UiObjectMemberList *memberItem = annotation->initializer->members; memberItem !=
nullptr; memberItem = memberItem->next) {
636 switch (memberItem->member->kind) {
637 case Node::Kind_UiScriptBinding: {
638 auto *scriptBinding = QQmlJS::AST::cast<UiScriptBinding*>(memberItem->member);
639 qqmljsAnnotation.bindings[buildName(scriptBinding->qualifiedId)]
640 = bindingToVariant(scriptBinding->statement);
649 annotationList.append(qqmljsAnnotation);
652 return annotationList;
655void QQmlJSImportVisitor::setAllBindings()
657 using Key = std::pair<QQmlJSScope::ConstPtr, QString>;
658 QHash<Key, QQmlJS::SourceLocation> foundBindings;
660 for (
auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
662 const QQmlJSScope::Ptr type = it->owner;
663 if (!checkTypeResolved(type))
672 if (!type->isFullyResolved())
674 auto binding = it->create();
675 if (!binding.isValid())
677 type->addOwnPropertyBinding(binding, it->specifier);
680 if (binding.hasInterceptor() || binding.hasValueSource())
682 const QString propertyName = binding.propertyName();
683 QQmlJSMetaProperty property = type->property(propertyName);
686
687
688
689
690
691 if (!property.isValid())
695 if (property.isList())
698 const Key key = std::make_pair(type, propertyName);
699 auto sourceLocationIt = foundBindings.constFind(key);
700 if (sourceLocationIt == foundBindings.constEnd()) {
701 foundBindings.insert(key, binding.sourceLocation());
705 const QQmlJS::SourceLocation location = binding.sourceLocation();
706 m_logger->log(
"Duplicate binding on property '%1'"_L1.arg(propertyName),
707 qmlDuplicatePropertyBinding, location);
708 m_logger->log(
"Note: previous binding on '%1' here"_L1.arg(propertyName),
709 qmlDuplicatePropertyBinding, *sourceLocationIt,
true,
true, {},
714void QQmlJSImportVisitor::processDefaultProperties()
716 for (
auto it = m_pendingDefaultProperties.constBegin();
717 it != m_pendingDefaultProperties.constEnd(); ++it) {
718 QQmlJSScope::ConstPtr parentScope = it.key();
721 if (checkCustomParser(parentScope))
725
726
727
728
729
730
731
732
733
735 parentScope = parentScope->baseType();
737 const QString defaultPropertyName =
738 parentScope ? parentScope->defaultPropertyName() : QString();
740 if (defaultPropertyName.isEmpty()) {
743 bool isComponent =
false;
744 for (QQmlJSScope::ConstPtr s = parentScope; s; s = s->baseType()) {
745 if (s->internalName() == QStringLiteral(
"QQmlComponent")) {
752 m_logger->log(QStringLiteral(
"Cannot assign to non-existent default property"),
753 qmlMissingProperty, it.value().constFirst()->sourceLocation());
759 const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
760 auto propType = defaultProp.type();
761 const auto handleUnresolvedDefaultProperty = [&](
const QQmlJSScope::ConstPtr &) {
763 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
764 "missing an import.")
765 .arg(defaultPropertyName)
766 .arg(defaultProp.typeName()),
767 qmlUnresolvedType, it.value().constFirst()->sourceLocation());
770 const auto assignToUnknownProperty = [&]() {
773 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it))
774 scope->setAssignedToUnknownProperty(
true);
777 if (propType.isNull()) {
778 handleUnresolvedDefaultProperty(propType);
779 assignToUnknownProperty();
783 if (it.value().size() > 1
784 && !defaultProp.isList()
785 && !propType->isListProperty()) {
787 QStringLiteral(
"Cannot assign multiple objects to a default non-list property"),
788 qmlNonListProperty, it.value().constFirst()->sourceLocation());
791 if (!checkTypeResolved(propType, handleUnresolvedDefaultProperty)) {
792 assignToUnknownProperty();
796 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
797 if (!checkTypeResolved(scope))
802 if (propType->canAssign(scope)) {
803 scope->setIsWrappedInImplicitComponent(
804 causesImplicitComponentWrapping(defaultProp, scope));
808 m_logger->log(QStringLiteral(
"Cannot assign to default property of incompatible type"),
809 qmlIncompatibleType, scope->sourceLocation());
814void QQmlJSImportVisitor::processPropertyTypes()
816 for (
const PendingPropertyType &type : std::as_const(m_pendingPropertyTypes)) {
817 Q_ASSERT(type.scope->hasOwnProperty(type.name));
819 auto property = type.scope->ownProperty(type.name);
821 if (
const auto propertyType = QQmlJSScope::findType(
822 property.typeName(), m_rootScopeImports.contextualTypes()).scope) {
823 property.setType(property.isList() ? propertyType->listType() : propertyType);
824 type.scope->addOwnProperty(property);
826 QString msg = property.typeName() +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports;
827 if (property.typeName() ==
"list"_L1)
828 msg +=
" list is not a type. It requires an element type argument (eg. list<int>)"_L1;
829 m_logger->log(msg, qmlImport, type.location);
834void QQmlJSImportVisitor::processMethodTypes()
836 const auto isEnumUsedAsType = [&](QStringView typeName,
const QQmlJS::SourceLocation &loc) {
837 if (typeName ==
"enum"_L1) {
841 const auto split = typeName.tokenize(u'.').toContainer<QVarLengthArray<QStringView, 4>>();
842 if (split.size() != 2)
845 const QStringView scopeName = split[0];
846 const QStringView enumName = split[1];
848 if (
auto scope = QQmlJSScope::findType(scopeName.toString(),
849 m_rootScopeImports.contextualTypes()).scope) {
850 if (scope->enumeration(enumName.toString()).isValid()) {
852 "QML enumerations are not types. Use int, or use double if the enum's underlying type does not fit into int."_L1,
853 qmlEnumsAreNotTypes, loc);
860 for (
const auto &method : std::as_const(m_pendingMethodTypeAnnotations)) {
861 for (
auto [it, end] = method.scope->mutableOwnMethodsRange(method.methodName); it != end; ++it) {
862 const auto [parameterBegin, parameterEnd] = it->mutableParametersRange();
863 for (
auto parameter = parameterBegin; parameter != parameterEnd; ++parameter) {
864 const int parameterIndex = parameter - parameterBegin;
865 if (isEnumUsedAsType(parameter->typeName(), method.locations[parameterIndex]))
867 if (
const auto parameterType = QQmlJSScope::findType(
868 parameter->typeName(), m_rootScopeImports.contextualTypes()).scope) {
869 parameter->setType({ parameterType });
872 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
873 .arg(parameter->typeName(), parameter->name(), it->methodName()),
874 qmlUnresolvedType, method.locations[parameter - parameterBegin]);
878 if (isEnumUsedAsType(it->returnTypeName(), method.locations.last()))
880 if (
const auto returnType = QQmlJSScope::findType(
881 it->returnTypeName(), m_rootScopeImports.contextualTypes()).scope) {
882 it->setReturnType({ returnType });
884 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
885 it->returnTypeName(), it->methodName()),
886 qmlUnresolvedType, method.locations.last());
894
895
896
897
898
899
900
904 for (QStringView propertyName: possiblyGroupedProperty.tokenize(u".")) {
905 property = scope->property(propertyName.toString());
906 if (property.isValid())
907 scope = property.type();
914void QQmlJSImportVisitor::processPropertyBindingObjects()
916 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundLiterals;
924 QSet<std::pair<QQmlJSScope::Ptr, QString>> visited;
925 for (
const PendingPropertyObjectBinding &objectBinding :
926 std::as_const(m_pendingPropertyObjectBindings)) {
928 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
929 if (visited.contains(uniqueBindingId))
931 visited.insert(uniqueBindingId);
933 auto [existingBindingsBegin, existingBindingsEnd] =
934 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
935 const bool hasLiteralBindings =
936 std::any_of(existingBindingsBegin, existingBindingsEnd,
937 [](
const QQmlJSMetaPropertyBinding &x) {
return x.hasLiteral(); });
938 if (hasLiteralBindings)
939 foundLiterals.insert(uniqueBindingId);
943 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundObjects;
944 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundInterceptors;
945 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundValueSources;
947 for (
const PendingPropertyObjectBinding &objectBinding :
948 std::as_const(m_pendingPropertyObjectBindings)) {
949 const QString propertyName = objectBinding.name;
950 QQmlJSScope::Ptr childScope = objectBinding.childScope;
952 const auto assignToUnknownProperty = [&]() {
955 childScope->setAssignedToUnknownProperty(
true);
959 if (!checkTypeResolved(objectBinding.scope)) {
960 assignToUnknownProperty();
964 QQmlJSMetaProperty property = resolveProperty(propertyName, objectBinding.scope);
966 if (!property.isValid()) {
967 warnMissingPropertyForBinding(propertyName, objectBinding.location);
970 const auto handleUnresolvedProperty = [&](
const QQmlJSScope::ConstPtr &) {
972 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
973 "missing an import.")
975 .arg(property.typeName()),
976 qmlUnresolvedType, objectBinding.location);
979 if (property.type().isNull()) {
980 assignToUnknownProperty();
981 handleUnresolvedProperty(property.type());
986 if (!checkTypeResolved(property.type(), handleUnresolvedProperty)) {
987 assignToUnknownProperty();
989 }
else if (!checkTypeResolved(childScope)) {
993 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
994 m_logger->log(QStringLiteral(
"Cannot assign object of type %1 to %2")
995 .arg(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope))
996 .arg(property.typeName()),
997 qmlIncompatibleType, childScope->sourceLocation());
1001 childScope->setIsWrappedInImplicitComponent(
1002 causesImplicitComponentWrapping(property, childScope));
1005 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
1006 const QString typeName = QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
1008 auto isConditionalBinding = [&]() ->
bool {
1010
1011
1012
1013
1014 return childScope->hasOwnPropertyBindings(u"enabled"_s)
1015 || childScope->hasOwnPropertyBindings(u"when"_s)
1016 || childScope->hasOwnPropertyBindings(u"running"_s);
1019 if (objectBinding.onToken) {
1020 if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueInterceptor"))) {
1021 if (foundInterceptors.contains(uniqueBindingId)) {
1022 if (!isConditionalBinding()) {
1023 m_logger->log(QStringLiteral(
"Duplicate interceptor on property \"%1\"")
1025 qmlDuplicatePropertyBinding, objectBinding.location);
1028 foundInterceptors.insert(uniqueBindingId);
1030 }
else if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueSource"))) {
1031 if (foundValueSources.contains(uniqueBindingId)) {
1032 if (!isConditionalBinding()) {
1033 m_logger->log(QStringLiteral(
"Duplicate value source on property \"%1\"")
1035 qmlDuplicatePropertyBinding, objectBinding.location);
1037 }
else if (foundObjects.contains(uniqueBindingId)
1038 || foundLiterals.contains(uniqueBindingId)) {
1039 if (!isConditionalBinding()) {
1040 m_logger->log(QStringLiteral(
"Cannot combine value source and binding on "
1043 qmlDuplicatePropertyBinding, objectBinding.location);
1046 foundValueSources.insert(uniqueBindingId);
1049 m_logger->log(QStringLiteral(
"On-binding for property \"%1\" has wrong type \"%2\"")
1052 qmlIncompatibleType, objectBinding.location);
1055 if (foundValueSources.contains(uniqueBindingId)) {
1056 if (!isConditionalBinding()) {
1058 QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
1060 qmlDuplicatePropertyBinding, objectBinding.location);
1063 foundObjects.insert(uniqueBindingId);
1071 QList<QQmlJSScope::ConstPtr> descendants;
1072 std::vector<QQmlJSScope::ConstPtr> toVisit;
1074 toVisit.push_back(scope);
1075 while (!toVisit.empty()) {
1076 const QQmlJSScope::ConstPtr s = toVisit.back();
1082 toVisit.insert(toVisit.end(), s->childScopesBegin(), s->childScopesEnd());
1089void QQmlJSImportVisitor::populatePropertyAliases()
1091 for (
const auto &alias : std::as_const(m_aliasDefinitions)) {
1092 const auto &[aliasScope, aliasName] = alias;
1093 if (aliasScope.isNull())
1096 auto property = aliasScope->ownProperty(aliasName);
1097 if (!property.isValid() || !property.aliasTargetScope())
1100 Property target(property.aliasTargetScope(), property.aliasTargetName());
1103 m_propertyAliases[target].append(alias);
1104 property = target.scope->property(target.name);
1105 target = Property(property.aliasTargetScope(), property.aliasTargetName());
1106 }
while (property.isAlias());
1110void QQmlJSImportVisitor::checkRequiredProperties()
1112 for (
const auto &required : std::as_const(m_requiredProperties)) {
1113 if (!required.scope->hasProperty(required.name)) {
1115 QStringLiteral(
"Property \"%1\" was marked as required but does not exist.")
1116 .arg(required.name),
1117 qmlRequired, required.location);
1121 const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
1122 const auto isComponentRoot = [&](
const QQmlJSScope::ConstPtr &requiredScope) {
1123 if (requiredScope->isWrappedInImplicitComponent())
1125 if (
const auto s = requiredScope->parentScope(); s && s->baseType() == compType)
1130 const auto scopeRequiresProperty = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1131 const QString &propName,
1132 const QQmlJSScope::ConstPtr &descendant) {
1133 if (!requiredScope->isPropertyLocallyRequired(propName))
1137 return QQmlJSScope::ownerOfProperty(requiredScope, propName).scope
1138 == QQmlJSScope::ownerOfProperty(descendant, propName).scope;
1141 const auto requiredHasBinding = [](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1142 const QQmlJSScope::ConstPtr &owner,
1143 const QString &propName) {
1144 for (
const auto &scope : scopesToSearch) {
1145 if (scope->property(propName).isAlias())
1147 const auto &[begin, end] = scope->ownPropertyBindings(propName);
1148 for (
auto it = begin; it != end; ++it) {
1150 const bool isRelevantBinding = QQmlSA::isRegularBindingType(it->bindingType())
1151 || it->bindingType() == QQmlSA::BindingType::Interceptor
1152 || it->bindingType() == QQmlSA::BindingType::ValueSource;
1153 if (!isRelevantBinding)
1155 if (QQmlJSScope::ownerOfProperty(scope, propName).scope == owner)
1163 const auto requiredUsedInRootAlias = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1164 const QString &propName) {
1165 const Property target(requiredScope, propName);
1168 const auto allAliasesToTargetIt = m_propertyAliases.constFind(target);
1169 if (allAliasesToTargetIt == m_propertyAliases.constEnd())
1176 allAliasesToTargetIt->constBegin(), allAliasesToTargetIt->constEnd(),
1177 [](
const Property &property) {
return property.scope->isFileRootComponent(); });
1180 const auto requiredSetThroughAlias = [&](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1181 const QQmlJSScope::ConstPtr &requiredScope,
1182 const QString &propName) {
1183 const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
1184 const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
1185 for (
const auto &alias : propertyAliases) {
1186 for (
const auto &s : scopesToSearch) {
1187 if (s->hasOwnPropertyBindings(alias.name))
1194 const auto warn = [
this](
const QQmlJSScope::ConstPtr &prevRequiredScope,
1195 const QString &propName,
const QQmlJSScope::ConstPtr &defScope,
1196 const QQmlJSScope::ConstPtr &requiredScope,
1197 const QQmlJSScope::ConstPtr &descendant) {
1198 const auto &propertyScope = QQmlJSScope::ownerOfProperty(requiredScope, propName).scope;
1199 const QString propertyScopeName = !propertyScope.isNull()
1200 ? QQmlJSUtils::getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
1203 std::optional<QQmlJSFixSuggestion> suggestion;
1205 QString message = QStringLiteral(
"Component is missing required property %1 from %2")
1207 .arg(propertyScopeName);
1208 if (requiredScope != descendant) {
1209 const QString requiredScopeName = prevRequiredScope
1210 ? QQmlJSUtils::getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
1213 if (!prevRequiredScope.isNull()) {
1214 if (
auto sourceScope = prevRequiredScope->baseType()) {
1215 suggestion = QQmlJSFixSuggestion{
1216 "%1:%2:%3: Property marked as required in %4."_L1
1217 .arg(sourceScope->filePath())
1218 .arg(sourceScope->sourceLocation().startLine)
1219 .arg(sourceScope->sourceLocation().startColumn)
1220 .arg(requiredScopeName),
1221 sourceScope->sourceLocation()
1225 if (sourceScope->isComposite())
1226 suggestion->setFilename(sourceScope->filePath());
1229 message +=
" (marked as required by %1)"_L1.arg(requiredScopeName);
1233 m_logger->log(message, qmlRequired, defScope->sourceLocation(),
true,
true, suggestion);
1236 populatePropertyAliases();
1238 for (
const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
1239 if (defScope->isFileRootComponent() || defScope->isInlineComponent()
1240 || defScope->componentRootStatus() != QQmlJSScope::IsComponentRoot::No
1241 || defScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
1245 QList<QQmlJSScope::ConstPtr> scopesToSearch;
1246 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
1247 const auto descendants = QList<QQmlJSScope::ConstPtr>()
1248 << scope << qmlScopeDescendants(scope);
1249 for (
const QQmlJSScope::ConstPtr &descendant : std::as_const(descendants)) {
1252 if (descendant != scope && descendant->isInlineComponent())
1254 scopesToSearch << descendant;
1255 const auto ownProperties = descendant->ownProperties();
1256 for (
auto propertyIt = ownProperties.constBegin();
1257 propertyIt != ownProperties.constEnd(); ++propertyIt) {
1258 const QString propName = propertyIt.key();
1259 if (descendant->hasOwnPropertyBindings(propName))
1262 QQmlJSScope::ConstPtr prevRequiredScope;
1263 for (
const QQmlJSScope::ConstPtr &requiredScope : std::as_const(scopesToSearch)) {
1266 if (isComponentRoot(requiredScope))
1269 if (!scopeRequiresProperty(requiredScope, propName, descendant)) {
1270 prevRequiredScope = requiredScope;
1274 if (requiredHasBinding(scopesToSearch, descendant, propName))
1277 if (requiredUsedInRootAlias(requiredScope, propName))
1280 if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
1283 warn(prevRequiredScope, propName, defScope, requiredScope, descendant);
1284 prevRequiredScope = requiredScope;
1292void QQmlJSImportVisitor::processPropertyBindings()
1294 for (
auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
1295 QQmlJSScope::Ptr scope = it.key();
1296 for (
auto &[visibilityScope, location, name] : it.value()) {
1297 if (!scope->hasProperty(name) && !m_logger->isDisabled()) {
1301 if (checkCustomParser(scope))
1305 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1307 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
1308 baseScope = baseScope->baseType()) {
1309 if (
auto suggestion = QQmlJSUtils::didYouMean(
1310 name, baseScope->ownProperties().keys(), location);
1311 suggestion.has_value()) {
1312 fixSuggestion = suggestion;
1317 warnMissingPropertyForBinding(name, location, fixSuggestion);
1321 const auto property = scope->property(name);
1322 if (!property.type()) {
1323 m_logger->log(QStringLiteral(
"No type found for property \"%1\". This may be due "
1324 "to a missing import statement or incomplete "
1327 qmlMissingType, location);
1330 const auto &annotations = property.annotations();
1332 const auto deprecationAnn =
1333 std::find_if(annotations.cbegin(), annotations.cend(),
1334 [](
const QQmlJSAnnotation &ann) {
return ann.isDeprecation(); });
1336 if (deprecationAnn != annotations.cend()) {
1337 const auto deprecation = deprecationAnn->deprecation();
1339 QString message = QStringLiteral(
"Binding on deprecated property \"%1\"")
1340 .arg(property.propertyName());
1342 if (!deprecation.reason.isEmpty())
1343 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1345 m_logger->log(message, qmlDeprecated, location);
1351void QQmlJSImportVisitor::checkSignal(
1352 const QQmlJSScope::ConstPtr &signalScope,
const QQmlJS::SourceLocation &location,
1353 const QString &handlerName,
const QStringList &handlerParameters)
1355 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1357 std::optional<QQmlJSMetaMethod> signalMethod;
1358 const auto setSignalMethod = [&](
const QQmlJSScope::ConstPtr &scope,
const QString &name) {
1359 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1360 if (!methods.isEmpty())
1361 signalMethod = methods[0];
1364 if (signal.has_value()) {
1365 if (signalScope->hasMethod(*signal)) {
1366 setSignalMethod(signalScope, *signal);
1367 }
else if (
auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1372 if (
auto notify = p->notify(); !notify.isEmpty()) {
1373 setSignalMethod(signalScope, notify);
1375 Q_ASSERT(!p->bindable().isEmpty());
1376 signalMethod = QQmlJSMetaMethod {};
1381 if (!signalMethod.has_value()) {
1386 if (signalScope->baseTypeName() == QStringLiteral(
"Connections")) {
1388 u"Implicitly defining \"%1\" as signal handler in Connections is deprecated. "
1389 u"Create a function instead: \"function %2(%3) { ... }\"."_s.arg(
1390 handlerName, handlerName, handlerParameters.join(u", ")),
1391 qmlUnqualified, location,
true,
true);
1395 auto baseType = QQmlJSScope::nonCompositeBaseType(signalScope);
1396 if (baseType && baseType->hasCustomParser())
1400 QStringLiteral(
"no matching signal found for handler \"%1\"").arg(handlerName),
1401 qmlUnqualified, location,
true,
true);
1405 const auto signalParameters = signalMethod->parameters();
1406 QHash<QString, qsizetype> parameterNameIndexes;
1408 for (
int i = 0, end = signalParameters.size(); i < end; i++) {
1409 auto &p = signalParameters[i];
1410 parameterNameIndexes[p.name()] = i;
1412 auto signalName = [&]() {
1414 return u" called %1"_s.arg(*signal);
1417 auto type = p.type();
1420 "Type %1 of parameter %2 in signal%3 was not found, but is required to compile "
1422 p.typeName(), p.name(), signalName(),
1423 handlerName, didYouAddAllImports),
1424 qmlSignalParameters, location);
1428 if (type->isComposite())
1436 auto parameterName = [&]() {
1437 if (p.name().isEmpty())
1439 return u" called %1"_s.arg(p.name());
1441 switch (type->accessSemantics()) {
1442 case QQmlJSScope::AccessSemantics::Reference:
1444 m_logger->log(QStringLiteral(
"Type %1 of parameter%2 in signal%3 should be "
1445 "passed by pointer to be able to compile %4. ")
1446 .arg(p.typeName(), parameterName(), signalName(),
1448 qmlSignalParameters, location);
1450 case QQmlJSScope::AccessSemantics::Value:
1451 case QQmlJSScope::AccessSemantics::Sequence:
1455 "Type %1 of parameter%2 in signal%3 should be passed by "
1456 "value or const reference to be able to compile %4. ")
1457 .arg(p.typeName(), parameterName(), signalName(),
1459 qmlSignalParameters, location);
1461 case QQmlJSScope::AccessSemantics::None:
1463 QStringLiteral(
"Type %1 of parameter%2 in signal%3 required by the "
1464 "compilation of %4 cannot be used. ")
1465 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1466 qmlSignalParameters, location);
1471 if (handlerParameters.size() > signalParameters.size()) {
1472 m_logger->log(QStringLiteral(
"Signal handler for \"%2\" has more formal"
1473 " parameters than the signal it handles.")
1475 qmlSignalParameters, location);
1479 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1480 const QStringView handlerParameter = handlerParameters.at(i);
1481 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1482 if (it == parameterNameIndexes.constEnd())
1484 const qsizetype j = *it;
1489 m_logger->log(QStringLiteral(
"Parameter %1 to signal handler for \"%2\""
1490 " is called \"%3\". The signal has a parameter"
1491 " of the same name in position %4.")
1493 .arg(handlerName, handlerParameter)
1495 qmlSignalParameters, location);
1499void QQmlJSImportVisitor::addDefaultProperties()
1501 QQmlJSScope::ConstPtr parentScope = m_currentScope->parentScope();
1502 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1503 || m_currentScope->isInlineComponent())
1506 m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
1508 if (checkCustomParser(parentScope))
1512
1513
1514
1515
1516
1517
1518
1519
1520
1522 parentScope = parentScope->baseType();
1524 const QString defaultPropertyName =
1525 parentScope ? parentScope->defaultPropertyName() : QString();
1527 if (defaultPropertyName.isEmpty())
1532 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1533 binding.setObject(QQmlJSUtils::getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
1534 QQmlJSScope::ConstPtr(m_currentScope));
1535 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() {
return binding; },
1536 QQmlJSScope::UnnamedPropertyTarget });
1539void QQmlJSImportVisitor::breakInheritanceCycles(
const QQmlJSScope::Ptr &originalScope)
1541 QList<QQmlJSScope::ConstPtr> scopes;
1542 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1543 if (scopes.contains(scope)) {
1544 QString inheritenceCycle;
1545 for (
const auto &seen : std::as_const(scopes)) {
1546 inheritenceCycle.append(seen->baseTypeName());
1547 inheritenceCycle.append(QLatin1String(
" -> "));
1549 inheritenceCycle.append(scopes.first()->baseTypeName());
1551 const QString message = QStringLiteral(
"%1 is part of an inheritance cycle: %2")
1552 .arg(scope->internalName(), inheritenceCycle);
1553 m_logger->log(message, qmlInheritanceCycle, scope->sourceLocation());
1554 originalScope->clearBaseType();
1555 originalScope->setBaseTypeError(message);
1559 scopes.append(scope);
1561 const auto newScope = scope->baseType();
1562 if (newScope.isNull()) {
1563 const QString error = scope->baseTypeError();
1564 const QString name = scope->baseTypeName();
1565 if (!error.isEmpty()) {
1566 m_logger->log(error, qmlImport, scope->sourceLocation(),
true,
true);
1567 }
else if (!name.isEmpty() && !m_unresolvedTypes.hasSeen(scope)
1568 && !m_logger->isDisabled()) {
1570 name +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports,
1571 qmlImport, scope->sourceLocation(),
true,
true,
1572 QQmlJSUtils::didYouMean(scope->baseTypeName(),
1573 m_rootScopeImports.types().keys(),
1574 scope->sourceLocation()));
1582void QQmlJSImportVisitor::checkDeprecation(
const QQmlJSScope::ConstPtr &originalScope)
1584 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1585 for (
const QQmlJSAnnotation &annotation : scope->annotations()) {
1586 if (annotation.isDeprecation()) {
1587 QQQmlJSDeprecation deprecation = annotation.deprecation();
1590 QStringLiteral(
"Type \"%1\" is deprecated").arg(scope->internalName());
1592 if (!deprecation.reason.isEmpty())
1593 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1595 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1601void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope)
1605 if (checkCustomParser(scope))
1608 auto children = scope->childScopes();
1609 while (!children.isEmpty()) {
1610 auto childScope = children.takeFirst();
1611 const auto type = childScope->scopeType();
1613 case QQmlSA::ScopeType::GroupedPropertyScope:
1614 case QQmlSA::ScopeType::AttachedPropertyScope:
1615 if (!childScope->baseType()) {
1616 m_logger->log(QStringLiteral(
"unknown %1 property scope %2.")
1617 .arg(type == QQmlSA::ScopeType::GroupedPropertyScope
1618 ? QStringLiteral(
"grouped")
1619 : QStringLiteral(
"attached"),
1620 childScope->internalName()),
1621 qmlUnqualified, childScope->sourceLocation());
1623 children.append(childScope->childScopes());
1631void QQmlJSImportVisitor::checkForComponentTypeWithProperties(
const QQmlJSScope::ConstPtr &scope)
1633 const QQmlJSScope::ConstPtr base = scope->baseType();
1640 if (base->isComposite())
1643 if (base->internalName() !=
"QQmlComponent"_L1)
1646 const auto ownProperties = scope->ownProperties();
1647 for (
const auto &property : ownProperties) {
1648 m_logger->log(
"Component objects cannot declare new properties."_L1,
1649 qmlSyntax, property.sourceLocation());
1653bool QQmlJSImportVisitor::checkCustomParser(
const QQmlJSScope::ConstPtr &scope)
1655 return scope->isInCustomParserParent();
1658void QQmlJSImportVisitor::flushPendingSignalParameters()
1660 const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
1661 for (
const QString ¶meter : handler.signalParameters) {
1662 safeInsertJSIdentifier(m_currentScope, parameter,
1663 { QQmlJSScope::JavaScriptIdentifier::Injected,
1664 m_pendingSignalHandler, std::nullopt,
false });
1666 m_pendingSignalHandler = QQmlJS::SourceLocation();
1670
1671
1672
1673
1674
1675
1676QQmlJSMetaMethod::RelativeFunctionIndex
1677QQmlJSImportVisitor::addFunctionOrExpression(
const QQmlJSScope::ConstPtr &scope,
1678 const QString &name)
1680 auto &array = m_functionsAndExpressions[scope];
1681 array.emplaceBack(name);
1688 for (
const auto &function : std::as_const(m_functionStack))
1689 m_innerFunctions[function]++;
1690 m_functionStack.push({ scope, name });
1692 return QQmlJSMetaMethod::RelativeFunctionIndex {
int(array.size() - 1) };
1696
1697
1698
1699
1700
1701
1702
1703
1704void QQmlJSImportVisitor::forgetFunctionExpression(
const QString &name)
1706 auto nameToVerify = name.isEmpty() ? u"<anon>"_s : name;
1707 Q_UNUSED(nameToVerify);
1708 Q_ASSERT(!m_functionStack.isEmpty());
1709 Q_ASSERT(m_functionStack.top().name == nameToVerify);
1710 m_functionStack.pop();
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
1726 const QQmlJSScope::Ptr &scope,
int count)
const
1728 const auto suitableScope = [](
const QQmlJSScope::Ptr &scope) {
1729 const auto type = scope->scopeType();
1730 return type == QQmlSA::ScopeType::QMLScope
1731 || type == QQmlSA::ScopeType::GroupedPropertyScope
1732 || type == QQmlSA::ScopeType::AttachedPropertyScope;
1735 if (!suitableScope(scope))
1738 auto it = m_functionsAndExpressions.constFind(scope);
1739 if (it == m_functionsAndExpressions.cend())
1742 const auto &functionsAndExpressions = *it;
1743 for (
const QString &functionOrExpression : functionsAndExpressions) {
1744 scope->addOwnRuntimeFunctionIndex(
1745 static_cast<QQmlJSMetaMethod::AbsoluteFunctionIndex>(count));
1762 count += m_innerFunctions.value({ scope, functionOrExpression }, 0);
1768void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument()
const
1771 const auto synthesize = [&](
const QQmlJSScope::Ptr ¤t) {
1772 count = synthesizeCompilationUnitRuntimeFunctionIndices(current, count);
1774 QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, synthesize);
1777bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
1779 if (m_pendingSignalHandler.isValid()) {
1780 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope, u"signalhandler"_s,
1781 ast->firstSourceLocation());
1782 flushPendingSignalParameters();
1787void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
1789 if (m_currentScope->scopeType() == QQmlSA::ScopeType::SignalHandlerFunctionScope) {
1796 const QQmlJS::SourceLocation &srcLocation);
1799 QQmlJSLogger *logger)
1801 QStringView namespaceName{ superType };
1802 namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
1803 logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
1805 qmlUncreatableType, location,
true,
true);
1808bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
1810 const QString superType = buildName(definition->qualifiedTypeNameId);
1812 const bool isRoot = !rootScopeIsValid();
1813 Q_ASSERT(!superType.isEmpty());
1818 const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
1819 const bool looksLikeGroupedProperty = superType.front().isLower();
1821 if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
1822 logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
1826 if (!looksLikeGroupedProperty) {
1828 enterEnvironment(QQmlSA::ScopeType::QMLScope, superType,
1829 definition->firstSourceLocation());
1831 enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
1832 definition->firstSourceLocation());
1833 m_currentScope->setIsRootFileComponentFlag(
true);
1834 m_currentScope->setIsSingleton(m_rootIsSingleton);
1837 const QTypeRevision revision = m_currentScope->baseTypeRevision();
1838 if (
auto base = m_currentScope->baseType(); base) {
1839 if (isRoot && base->internalName() == u"QQmlComponent") {
1840 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1841 definition->qualifiedTypeNameId->identifierToken,
true,
true);
1843 if (base->isSingleton() && m_currentScope->isComposite()) {
1844 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1845 m_currentScope->baseTypeName()),
1846 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1849 }
else if (!base->isCreatable()) {
1851 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1852 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1856 if (m_nextIsInlineComponent) {
1857 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1858 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1859 m_currentScope->setIsInlineComponent(
true);
1860 m_currentScope->setInlineComponentName(name);
1861 m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
1862 m_rootScopeImports.setType(
1863 name, { m_currentScope, revision, QQmlJS::PrecedenceValues::InlineComponent });
1864 m_nextIsInlineComponent =
false;
1867 addDefaultProperties();
1868 Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope);
1869 m_qmlTypes.append(m_currentScope);
1871 m_objectDefinitionScopes << m_currentScope;
1873 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType,
1874 definition->firstSourceLocation());
1875 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType,
1876 definition->firstSourceLocation()));
1877 QQmlJSScope::resolveTypes(
1878 m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
1881 m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
1886void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
1888 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
1892bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
1894 if (!std::holds_alternative<RootDocumentNameType>(m_currentRootName)) {
1895 m_logger->log(u"Nested inline components are not supported"_s, qmlSyntax,
1896 component->firstSourceLocation());
1900 const auto it = m_seenInlineComponents.constFind(component->name);
1901 if (it != m_seenInlineComponents.cend()) {
1902 m_logger->log(
"Duplicate inline component '%1'"_L1.arg(it.key()),
1903 qmlDuplicateInlineComponent, component->firstSourceLocation());
1904 m_logger->log(
"Note: previous component named '%1' here"_L1.arg(it.key()),
1905 qmlDuplicateInlineComponent, it.value(),
true,
true, {},
1906 component->firstSourceLocation().startLine);
1908 m_seenInlineComponents[component->name] = component->firstSourceLocation();
1911 m_nextIsInlineComponent =
true;
1912 m_currentRootName = component->name.toString();
1916void QQmlJSImportVisitor::endVisit(UiInlineComponent *component)
1918 m_currentRootName = RootDocumentNameType();
1919 if (m_nextIsInlineComponent) {
1920 m_logger->log(u"Inline component declaration must be followed by a typename"_s,
1921 qmlSyntax, component->firstSourceLocation());
1923 m_nextIsInlineComponent =
false;
1926bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
1928 switch (publicMember->type) {
1929 case UiPublicMember::Signal: {
1930 const QString signalName = publicMember->name.toString();
1931 UiParameterList *param = publicMember->parameters;
1932 QQmlJSMetaMethod method;
1933 method.setMethodType(QQmlJSMetaMethodType::Signal);
1934 method.setReturnTypeName(QStringLiteral(
"void"));
1935 method.setMethodName(signalName);
1936 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1937 publicMember->lastSourceLocation()));
1939 method.addParameter(
1940 QQmlJSMetaParameter(
1941 param->name.toString(),
1942 param->type ? param->type->toString() : QString()
1944 param = param->next;
1946 m_currentScope->addOwnMethod(method);
1949 case UiPublicMember::Property: {
1950 const QString propertyName = publicMember->name.toString();
1951 QString typeName = buildName(publicMember->memberType);
1952 if (typeName.contains(u'.') && typeName.front().isLower()) {
1953 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1957 const bool isAlias = (typeName == u"alias"_s);
1959 auto tryParseAlias = [&]() {
1961 if (!publicMember->statement) {
1962 m_logger->log(QStringLiteral(
"Invalid alias expression - an initializer is needed."),
1963 qmlSyntax, publicMember->memberType->firstSourceLocation());
1966 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1967 auto node = expression ? expression->expression :
nullptr;
1968 auto fex = cast<FieldMemberExpression *>(node);
1971 aliasExpr.prepend(u'.' + fex->name.toString());
1972 fex = cast<FieldMemberExpression *>(node);
1975 if (
const auto idExpression = cast<IdentifierExpression *>(node)) {
1976 aliasExpr.prepend(idExpression->name.toString());
1980 m_logger->log(QStringLiteral(
"Invalid alias expression. Only IDs and field "
1981 "member expressions can be aliased."),
1982 qmlSyntax, publicMember->statement->firstSourceLocation());
1987 if (m_rootScopeImports.hasType(typeName)
1988 && !m_rootScopeImports.type(typeName).scope.isNull()) {
1989 if (m_importTypeLocationMap.contains(typeName))
1990 m_usedTypes.insert(typeName);
1993 QQmlJSMetaProperty prop;
1994 prop.setPropertyName(propertyName);
1995 prop.setIsList(publicMember->typeModifier == QLatin1String(
"list"));
1996 prop.setIsWritable(!publicMember->isReadonly());
1997 prop.setIsFinal(publicMember->isFinal());
1998 prop.setIsVirtual(publicMember->isVirtual());
1999 prop.setIsOverride(publicMember->isOverride());
2000 prop.setAliasExpression(aliasExpr);
2001 prop.setSourceLocation(
2002 combine(publicMember->firstSourceLocation(), publicMember->colonToken));
2004 isAlias ? QQmlJSScope::ConstPtr() : m_rootScopeImports.type(typeName).scope;
2006 prop.setType(prop.isList() ? type->listType() : type);
2007 const QString internalName = type->internalName();
2008 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
2009 }
else if (!isAlias) {
2010 m_pendingPropertyTypes << PendingPropertyType { m_currentScope, prop.propertyName(),
2011 publicMember->firstSourceLocation() };
2012 prop.setTypeName(typeName);
2014 prop.setAnnotations(parseAnnotations(publicMember->annotations));
2015 if (publicMember->isDefaultMember())
2016 m_currentScope->setOwnDefaultPropertyName(prop.propertyName());
2017 prop.setIndex(m_currentScope->ownProperties().size());
2018 m_currentScope->insertPropertyIdentifier(prop);
2019 if (publicMember->isRequired())
2020 m_currentScope->setPropertyLocallyRequired(prop.propertyName(),
true);
2022 BindingExpressionParseResult parseResult = BindingExpressionParseResult::Invalid;
2026 parseBindingExpression(publicMember->name.toString(), publicMember->statement,
2032 if (parseResult == BindingExpressionParseResult::Script) {
2033 Q_ASSERT(!m_savedBindingOuterScope);
2034 m_savedBindingOuterScope = m_currentScope;
2035 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope, QStringLiteral(
"binding"),
2036 publicMember->statement->firstSourceLocation());
2046void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
2048 if (m_savedBindingOuterScope) {
2049 m_currentScope = m_savedBindingOuterScope;
2050 m_savedBindingOuterScope = {};
2052 forgetFunctionExpression(publicMember->name.toString());
2056bool QQmlJSImportVisitor::visit(UiRequired *required)
2058 const QString name = required->name.toString();
2060 m_requiredProperties << RequiredProperty { m_currentScope, name,
2061 required->firstSourceLocation() };
2063 m_currentScope->setPropertyLocallyRequired(name,
true);
2067void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
2069 using namespace QQmlJS::AST;
2070 auto name = fexpr->name.toString();
2071 if (!name.isEmpty()) {
2072 QQmlJSMetaMethod method(name);
2073 method.setMethodType(QQmlJSMetaMethodType::Method);
2074 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
2076 if (!m_pendingMethodAnnotations.isEmpty()) {
2077 method.setAnnotations(m_pendingMethodAnnotations);
2078 m_pendingMethodAnnotations.clear();
2082 const bool parseTypes = m_scopesById.signaturesAreEnforced();
2084 bool formalsFullyTyped = parseTypes;
2085 bool anyFormalTyped =
false;
2086 PendingMethodTypeAnnotations pending{ m_currentScope, name, {} };
2089 for (
auto formals = fexpr->formals; formals; formals = formals->next) {
2090 PatternElement *e = formals->element;
2093 if (e->typeAnnotation && (e->bindingTarget || e->initializer))
2094 m_logger->log(
"Type annotations on default parameters are not supported"_L1,
2096 combine(e->firstSourceLocation(), e->lastSourceLocation()));
2099 if (
const auto *formals = parseTypes ? fexpr->formals :
nullptr) {
2100 const auto parameters = formals->formals();
2101 for (
const auto ¶meter : parameters) {
2102 const QString type = parameter.typeAnnotation
2103 ? parameter.typeAnnotation->type->toString()
2105 if (type.isEmpty()) {
2106 formalsFullyTyped =
false;
2107 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral(
"var")));
2108 pending.locations.emplace_back();
2110 anyFormalTyped =
true;
2111 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
2112 pending.locations.append(
2113 combine(parameter.typeAnnotation->firstSourceLocation(),
2114 parameter.typeAnnotation->lastSourceLocation()));
2120 method.setIsJavaScriptFunction(!formalsFullyTyped);
2126 if (parseTypes && fexpr->typeAnnotation) {
2127 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
2128 pending.locations.append(combine(fexpr->typeAnnotation->firstSourceLocation(),
2129 fexpr->typeAnnotation->lastSourceLocation()));
2130 }
else if (anyFormalTyped) {
2131 method.setReturnTypeName(QStringLiteral(
"void"));
2133 method.setReturnTypeName(QStringLiteral(
"var"));
2136 const auto &locs = pending.locations;
2137 if (std::any_of(locs.cbegin(), locs.cend(), [](
const auto &loc) {
return loc.isValid(); }))
2138 m_pendingMethodTypeAnnotations << pending;
2140 method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
2142 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
2144 const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
2145 ? fexpr->identifierToken
2146 : fexpr->functionToken;
2147 safeInsertJSIdentifier(m_currentScope, name,
2148 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2149 functionLocation, method.returnTypeName(),
2152 m_currentScope->addOwnMethod(method);
2154 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
2156 addFunctionOrExpression(m_currentScope, QStringLiteral(
"<anon>"));
2157 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral(
"<anon>"),
2158 fexpr->firstSourceLocation());
2162bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
2164 visitFunctionExpressionHelper(fexpr);
2168void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
2170 forgetFunctionExpression(fexpr->name.toString());
2174bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
2176 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
2180bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
2182 if (!fdecl->name.isEmpty()) {
2183 const QString name = fdecl->name.toString();
2184 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
2185 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
2186 fdecl->identifierToken);
2187 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
2188 previousDeclaration->location);
2191 visitFunctionExpressionHelper(fdecl);
2195void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
2197 forgetFunctionExpression(fdecl->name.toString());
2201bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
2203 QQmlJSMetaProperty prop;
2204 prop.setPropertyName(ast->name.toString());
2205 m_currentScope->addOwnProperty(prop);
2206 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2207 ast->firstSourceLocation());
2211void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
2217 QQmlJS::AST::ArgumentList *args)
2219 QStringView contextString;
2220 QStringView mainString;
2221 QStringView commentString;
2222 auto registerContextString = [&](QStringView string) {
2223 contextString = string;
2226 auto registerMainString = [&](QStringView string) {
2227 mainString = string;
2230 auto registerCommentString = [&](QStringView string) {
2231 commentString = string;
2234 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
2235 QV4::CompiledData::TranslationData data) {
2236 if (type == QV4::CompiledData::Binding::Type_Translation) {
2237 binding.setTranslation(mainString, commentString, contextString, data.number);
2238 }
else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
2239 binding.setTranslationId(mainString, data.number);
2241 binding.setStringLiteral(mainString);
2244 QmlIR::tryGeneratingTranslationBindingBase(
2246 registerMainString, registerCommentString, registerContextString, finalizeBinding);
2249QQmlJSImportVisitor::BindingExpressionParseResult
2250QQmlJSImportVisitor::parseBindingExpression(
2251 const QString &name,
const QQmlJS::AST::Statement *statement,
2252 const UiPublicMember *associatedPropertyDefinition)
2254 if (statement ==
nullptr)
2255 return BindingExpressionParseResult::Invalid;
2257 const auto *exprStatement = cast<
const ExpressionStatement *>(statement);
2259 if (exprStatement ==
nullptr) {
2260 QQmlJS::SourceLocation location = statement->firstSourceLocation();
2262 if (
const auto *block = cast<
const Block *>(statement); block && block->statements) {
2263 location = block->statements->firstSourceLocation();
2266 QQmlJSMetaPropertyBinding binding(location, name);
2267 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2268 QQmlSA::ScriptBindingKind::PropertyBinding, ScriptValue_Function);
2269 m_bindings.append(UnfinishedBinding {
2271 [binding = std::move(binding)]() {
return binding; }
2273 return BindingExpressionParseResult::Script;
2276 auto expr = exprStatement->expression;
2277 QQmlJSMetaPropertyBinding binding(
2278 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
2281 ScriptBindingValueType scriptBindingValuetype = ScriptValue_Unknown;
2283 switch (expr->kind) {
2284 case Node::Kind_TrueLiteral:
2285 binding.setBoolLiteral(
true);
2287 case Node::Kind_FalseLiteral:
2288 binding.setBoolLiteral(
false);
2290 case Node::Kind_NullExpression:
2291 binding.setNullLiteral();
2293 case Node::Kind_IdentifierExpression: {
2294 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
2296 if (idExpr->name == u"undefined")
2297 scriptBindingValuetype = ScriptValue_Undefined;
2300 case Node::Kind_FunctionDeclaration:
2301 case Node::Kind_FunctionExpression:
2302 case Node::Kind_Block: {
2303 scriptBindingValuetype = ScriptValue_Function;
2306 case Node::Kind_NumericLiteral:
2307 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
2309 case Node::Kind_StringLiteral:
2310 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
2312 case Node::Kind_RegExpLiteral:
2313 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
2315 case Node::Kind_TemplateLiteral: {
2316 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
2317 Q_ASSERT(templateLit);
2318 if (templateLit->hasNoSubstitution) {
2319 binding.setStringLiteral(templateLit->value);
2321 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2322 QQmlSA::ScriptBindingKind::PropertyBinding);
2323 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
2324 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
2325 expression->accept(
this);
2331 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
2332 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
2333 binding.setNumberLiteral(-lit->value);
2334 }
else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
2335 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
2336 handleTranslationBinding(binding, base->name, call->arguments);
2341 if (!binding.isValid()) {
2343 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2344 QQmlSA::ScriptBindingKind::PropertyBinding,
2345 scriptBindingValuetype);
2347 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
2350 if (binding.bindingType() == QQmlSA::BindingType::Translation
2351 || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
2352 return BindingExpressionParseResult::Translation;
2354 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
2355 return BindingExpressionParseResult::Script;
2357 if (associatedPropertyDefinition)
2358 handleLiteralBinding(binding, associatedPropertyDefinition);
2360 return BindingExpressionParseResult::Literal;
2363bool QQmlJSImportVisitor::isImportPrefix(QString prefix)
const
2365 if (prefix.isEmpty() || !prefix.front().isUpper())
2368 return m_rootScopeImports.isNullType(prefix);
2371void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
2373 if (m_currentScope->scopeType() != QQmlJSScope::ScopeType::QMLScope) {
2374 m_logger->log(u"id declarations are only allowed in objects"_s, qmlSyntax,
2375 scriptBinding->statement->firstSourceLocation());
2378 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
2380 m_logger->log(u"id must be followed by an identifier"_s, qmlSyntax,
2381 scriptBinding->statement->firstSourceLocation());
2384 const QString name = [&]() {
2385 if (
const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2386 return idExpression->name.toString();
2387 else if (
const auto *idString = cast<StringLiteral *>(statement->expression)) {
2388 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2389 idString->firstSourceLocation());
2390 return idString->value.toString();
2392 m_logger->log(u"Failed to parse id"_s, qmlSyntax,
2393 statement->expression->firstSourceLocation());
2397 if (!name.isEmpty() && !name.front().isLower() && name.front() != u'_') {
2398 m_logger->log(u"Id must start with a lower case letter or an '_'"_s, qmlSyntax,
2399 statement->expression->firstSourceLocation());
2402 m_currentScope->setIdSourceLocation(combine(scriptBinding->statement->firstSourceLocation(),
2403 scriptBinding->statement->lastSourceLocation()));
2404 if (m_scopesById.existsAnywhereInDocument(name)) {
2407 breakInheritanceCycles(m_currentScope);
2408 m_scopesById.possibleScopes(
2409 name, m_currentScope, Default,
2410 [&](
const QQmlJSScope::ConstPtr &otherScopeWithID,
2411 QQmlJSScopesById::Confidence confidence) {
2413 Q_UNUSED(confidence);
2415 auto otherLocation = otherScopeWithID->sourceLocation();
2419 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2420 name, QString::number(otherLocation.startLine),
2421 QString::number(otherLocation.startColumn)),
2422 qmlSyntaxDuplicateIds,
2423 scriptBinding->firstSourceLocation());
2424 return QQmlJSScopesById::CallbackResult::ContinueSearch;
2427 if (!name.isEmpty())
2428 m_scopesById.insert(name, m_currentScope);
2431void QQmlJSImportVisitor::handleLiteralBinding(
const QQmlJSMetaPropertyBinding &binding,
2432 const UiPublicMember *associatedPropertyDefinition)
2436 Q_UNUSED(associatedPropertyDefinition);
2440
2441
2442
2443
2444
2447 const QQmlJS::SourceLocation &srcLocation)
2449 const auto createBinding = [=]() {
2450 const QQmlJSScope::ScopeType type = scope->scopeType();
2457 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2458 const bool alreadyHasBinding =
std::any_of(propertyBindings.first, propertyBindings.second,
2459 [&](
const QQmlJSMetaPropertyBinding &binding) {
2460 return binding.bindingType() == bindingType;
2462 if (alreadyHasBinding)
2463 return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
2466 if (type == QQmlSA::ScopeType::GroupedPropertyScope)
2467 binding.setGroupBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2469 binding.setAttachedBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2472 return { scope->parentScope(), createBinding };
2475bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
2477 Q_ASSERT(!m_savedBindingOuterScope);
2478 Q_ASSERT(!m_thisScriptBindingIsJavaScript);
2479 m_savedBindingOuterScope = m_currentScope;
2480 const auto id = scriptBinding->qualifiedId;
2481 if (!id->next && id->name == QLatin1String(
"id")) {
2482 handleIdDeclaration(scriptBinding);
2489 for (; group->next; group = group->next) {
2490 const QString name = group->name.toString();
2494 if (group == id && isImportPrefix(name)) {
2495 prefix = name + u'.';
2499 const bool isAttachedProperty = name.front().isUpper();
2500 if (isAttachedProperty) {
2502 enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
2503 group->firstSourceLocation());
2506 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
2507 group->firstSourceLocation());
2509 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
2510 group->firstSourceLocation()));
2515 const auto name = group->name.toString();
2519 const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
2521 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2522 m_propertyBindings[m_currentScope].append(
2523 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2525 auto result = parseBindingExpression(name, scriptBinding->statement);
2526 m_thisScriptBindingIsJavaScript = (result == BindingExpressionParseResult::Script);
2528 const auto statement = scriptBinding->statement;
2529 QStringList signalParameters;
2531 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2532 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2533 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2534 signalParameters << formal->element->bindingIdentifier.toString();
2538 QQmlJSMetaMethod scopeSignal;
2539 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2540 if (!methods.isEmpty())
2541 scopeSignal = methods[0];
2543 const auto firstSourceLocation = statement->firstSourceLocation();
2544 bool hasMultilineStatementBody =
2545 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2546 m_pendingSignalHandler = firstSourceLocation;
2547 m_signalHandlers.insert(firstSourceLocation,
2548 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2552 const auto index = addFunctionOrExpression(m_currentScope, name);
2553 const auto createBinding = [
2555 scope = m_currentScope,
2556 signalName = *signal,
2559 firstSourceLocation,
2560 groupLocation = group->firstSourceLocation(),
2561 signalParameters]() {
2563 Q_ASSERT(scope->isFullyResolved());
2564 QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
2565 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2566 if (!methods.isEmpty()) {
2567 kind = QQmlSA::ScriptBindingKind::SignalHandler;
2568 checkSignal(scope, groupLocation, name, signalParameters);
2569 }
else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2570 kind = QQmlSA::ScriptBindingKind::ChangeHandler;
2571 checkSignal(scope, groupLocation, name, signalParameters);
2572 }
else if (scope->hasProperty(name)) {
2575 kind = QQmlSA::ScriptBindingKind::PropertyBinding;
2576 m_signalHandlers.remove(firstSourceLocation);
2579 checkSignal(scope, groupLocation, name, signalParameters);
2582 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2583 binding.setScriptBinding(index, kind, ScriptValue_Function);
2586 m_bindings.append(UnfinishedBinding { m_currentScope, createBinding });
2587 m_thisScriptBindingIsJavaScript =
true;
2593 while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
2594 || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
2599 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope,
2601 scriptBinding->statement->firstSourceLocation());
2603 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope,
2605 scriptBinding->statement->firstSourceLocation());
2611void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
2613 if (m_savedBindingOuterScope) {
2614 m_currentScope = m_savedBindingOuterScope;
2615 m_savedBindingOuterScope = {};
2621 if (m_thisScriptBindingIsJavaScript) {
2622 m_thisScriptBindingIsJavaScript =
false;
2623 Q_ASSERT(!m_functionStack.isEmpty());
2624 m_functionStack.pop();
2628bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
2630 createAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2631 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2632 arrayBinding->firstSourceLocation());
2633 m_currentScope->setIsArrayScope(
true);
2637void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
2644 const auto children = m_currentScope->childScopes();
2647 const int scopesEnteredCounter = openAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2648 auto guard = qScopeGuard([
this, scopesEnteredCounter]() {
2649 for (
int i = 0; i < scopesEnteredCounter; ++i)
2653 if (checkCustomParser(m_currentScope)) {
2659 auto group = arrayBinding->qualifiedId;
2660 for (; group->next; group = group->next) { }
2661 const QString propertyName = group->name.toString();
2664 for (
auto element = arrayBinding->members; element; element = element->next, ++i) {
2665 const auto &type = children[i];
2666 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2667 m_logger->log(u"Declaring an object which is not a Qml object"
2668 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2671 m_pendingPropertyObjectBindings
2672 << PendingPropertyObjectBinding { m_currentScope, type, propertyName,
2673 element->firstSourceLocation(),
false };
2674 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2675 binding.setObject(QQmlJSUtils::getScopeName(type, QQmlSA::ScopeType::QMLScope),
2676 QQmlJSScope::ConstPtr(type));
2677 m_bindings.append(UnfinishedBinding {
2679 [binding = std::move(binding)]() {
return binding; },
2680 QQmlJSScope::ListPropertyTarget
2685bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
2687 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2688 qmlEnum.setIsQml(
true);
2689 qmlEnum.setLineNumber(uied->enumToken.startLine);
2690 for (
const auto *member = uied->members; member; member = member->next) {
2691 qmlEnum.addKey(member->member.toString());
2692 qmlEnum.addValue(
int(member->value));
2694 m_currentScope->addOwnEnumeration(qmlEnum);
2698void QQmlJSImportVisitor::addImportWithLocation(
2699 const QString &name,
const QQmlJS::SourceLocation &loc,
bool hadWarnings)
2701 if (m_importTypeLocationMap.contains(name)
2702 && m_importTypeLocationMap.values(name).contains(loc)) {
2706 m_importTypeLocationMap.insert(name, loc);
2711 if (!hadWarnings && loc.isValid())
2712 m_importLocations.insert(loc);
2715QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromHost(
2716 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2718 QFileInfo fileInfo(path);
2719 if (!fileInfo.exists()) {
2720 m_logger->log(
"File or directory you are trying to import does not exist: %1."_L1.arg(path),
2721 qmlImport, location);
2725 if (fileInfo.isFile()) {
2726 const auto scope = m_importer->importFile(path);
2727 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2728 m_rootScopeImports.setType(actualPrefix,
2729 { scope, QTypeRevision(), QQmlJS::PrecedenceValues::Default });
2730 addImportWithLocation(actualPrefix, location,
false);
2734 if (fileInfo.isDir()) {
2735 auto scopes = m_importer->importDirectory(path, QQmlJS::PrecedenceValues::Default, prefix);
2736 const auto types = scopes.types();
2737 const auto warnings = scopes.warnings();
2738 m_rootScopeImports.add(std::move(scopes));
2739 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2740 addImportWithLocation(*it, location, !warnings.isEmpty());
2745 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2747 qmlImport, location);
2751QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromQrc(
2752 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2754 Q_ASSERT(path.startsWith(u':'));
2755 const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper();
2759 const auto pathNoColon = QStringView(path).mid(1);
2760 if (mapper->isFile(pathNoColon)) {
2761 const auto entry = m_importer->resourceFileMapper()->entry(
2762 QQmlJSResourceFileMapper::resourceFileFilter(pathNoColon.toString()));
2763 const auto scope = m_importer->importFile(entry.filePath);
2764 const QString actualPrefix =
2765 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2766 m_rootScopeImports.setType(actualPrefix,
2767 { scope, QTypeRevision(), QQmlJS::PrecedenceValues::Default });
2768 addImportWithLocation(actualPrefix, location,
false);
2772 auto scopes = m_importer->importDirectory(path, QQmlJS::PrecedenceValues::Default, prefix);
2773 const auto types = scopes.types();
2774 const auto warnings = scopes.warnings();
2775 m_rootScopeImports.add(std::move(scopes));
2776 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2777 addImportWithLocation(*it, location, !warnings.isEmpty());
2781bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
2784 QString prefix = QLatin1String(
"");
2785 if (import->asToken.isValid()) {
2786 prefix += import->importId;
2787 if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
2788 m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
2790 qmlImport, import->importIdToken,
true,
true);
2792 m_seenModuleQualifiers.append(prefix);
2795 const QString filename = import->fileName.toString();
2796 if (!filename.isEmpty()) {
2797 const QUrl url(filename);
2798 const QString scheme = url.scheme();
2799 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2800 if (scheme ==
""_L1) {
2801 QFileInfo fileInfo(url.path());
2802 QString absolute = fileInfo.isRelative()
2803 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2805 auto warnings = absolute.startsWith(u':')
2806 ? importFromQrc(absolute, prefix, importLocation)
2807 : importFromHost(absolute, prefix, importLocation);
2808 processImportWarnings(
"path \"%1\""_L1.arg(url.path()), warnings, importLocation);
2810 }
else if (scheme ==
"file"_L1) {
2811 auto warnings = importFromHost(url.path(), prefix, importLocation);
2812 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2814 }
else if (scheme ==
"qrc"_L1) {
2815 auto warnings = importFromQrc(
":"_L1 + url.path(), prefix, importLocation);
2816 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2819 m_logger->log(
"Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2820 qmlImport, import->firstSourceLocation());
2824 const QString path = buildName(import->importUri);
2826 QStringList staticModulesProvided;
2828 auto imported = m_importer->importModule(
2829 path, QQmlJS::PrecedenceValues::Default, prefix,
2830 import->version ? import->version->version : QTypeRevision(), &staticModulesProvided);
2831 const auto types = imported.types();
2832 const auto warnings = imported.warnings();
2833 m_rootScopeImports.add(std::move(imported));
2834 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2835 addImportWithLocation(*it, import->firstSourceLocation(), !warnings.isEmpty());
2837 if (prefix.isEmpty()) {
2838 for (
const QString &staticModule : std::as_const(staticModulesProvided)) {
2840 if (path != staticModule && m_importStaticModuleLocationMap.contains(staticModule))
2843 m_importStaticModuleLocationMap[staticModule] = import->firstSourceLocation();
2847 processImportWarnings(
2848 QStringLiteral(
"module \"%1\"").arg(path), warnings, import->firstSourceLocation());
2852#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
2854void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2856 for (
const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2861void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2863 assign(pragma->value);
2867bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
2869 if (pragma->name == u"Strict"_s) {
2874 if (!m_logger->wasCategoryChanged(qmlCompiler))
2875 m_logger->setCategorySeverity(qmlCompiler, QQmlJS::WarningSeverity::Warning);
2876 }
else if (pragma->name == u"Singleton") {
2877 m_rootIsSingleton =
true;
2878 }
else if (pragma->name == u"ComponentBehavior") {
2879 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2880 if (value == u"Bound") {
2881 m_scopesById.setComponentsAreBound(
true);
2882 }
else if (value == u"Unbound") {
2883 m_scopesById.setComponentsAreBound(
false);
2885 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2886 qmlSyntax, pragma->firstSourceLocation());
2889 }
else if (pragma->name == u"FunctionSignatureBehavior") {
2890 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2891 if (value == u"Enforced") {
2892 m_scopesById.setSignaturesAreEnforced(
true);
2893 }
else if (value == u"Ignored") {
2894 m_scopesById.setSignaturesAreEnforced(
false);
2897 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2898 qmlSyntax, pragma->firstSourceLocation());
2901 }
else if (pragma->name == u"ValueTypeBehavior") {
2902 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2903 if (value == u"Copy") {
2905 }
else if (value == u"Reference") {
2907 }
else if (value == u"Addressable") {
2908 m_scopesById.setValueTypesAreAddressable(
true);
2909 }
else if (value == u"Inaddressable") {
2910 m_scopesById.setValueTypesAreAddressable(
false);
2911 }
else if (value == u"Assertable") {
2912 m_scopesById.setValueTypesAreAssertable(
true);
2913 }
else if (value == u"Inassertable") {
2914 m_scopesById.setValueTypesAreAssertable(
false);
2916 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2917 qmlSyntax, pragma->firstSourceLocation());
2925void QQmlJSImportVisitor::throwRecursionDepthError()
2927 m_logger->log(QStringLiteral(
"Maximum statement or expression depth exceeded"),
2928 qmlRecursionDepthErrors, QQmlJS::SourceLocation());
2931bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
2933 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2934 ast->firstSourceLocation());
2938void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
2943bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
2945 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"forloop"),
2946 ast->firstSourceLocation());
2950void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
2955bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
2957 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"foreachloop"),
2958 ast->firstSourceLocation());
2962void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
2967bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
2969 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"block"),
2970 ast->firstSourceLocation());
2972 if (m_pendingSignalHandler.isValid())
2973 flushPendingSignalParameters();
2978void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
2983bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
2985 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"case"),
2986 ast->firstSourceLocation());
2990void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
2995bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
2997 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"catch"),
2998 catchStatement->firstSourceLocation());
3002void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
3007bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
3009 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"with"),
3010 ast->firstSourceLocation());
3012 m_logger->log(QStringLiteral(
"with statements are strongly discouraged in QML "
3013 "and might cause false positives when analysing unqualified "
3015 qmlWith, ast->firstSourceLocation());
3020void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
3025bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
3027 const auto &boundedNames = fpl->boundNames();
3028 for (
auto const &boundName : boundedNames) {
3030 std::optional<QString> typeName;
3031 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
3032 if (Type *type = annotation->type)
3033 typeName = type->toString();
3034 safeInsertJSIdentifier(m_currentScope, boundName.id,
3035 { QQmlJSScope::JavaScriptIdentifier::Parameter,
3036 boundName.location, typeName,
false });
3041void QQmlJSImportVisitor::createAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3043 bool needsResolution =
false;
3044 int scopesEnteredCounter = 0;
3046 for (
auto group = propertyName; group->next; group = group->next) {
3047 const QString idName = group->name.toString();
3049 if (idName.isEmpty())
3052 if (group == propertyName && isImportPrefix(idName)) {
3053 prefix = idName + u'.';
3057 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3058 : QQmlSA::ScopeType::GroupedPropertyScope;
3061 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3063 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + idName,
3064 group->firstSourceLocation()));
3066 ++scopesEnteredCounter;
3067 needsResolution = needsResolution || !exists;
3072 for (
int i=0; i < scopesEnteredCounter; ++i) {
3077 if (needsResolution) {
3078 QQmlJSScope::resolveTypes(
3079 m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3083bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
3087 Q_ASSERT(uiob->qualifiedTypeNameId);
3089 const QString typeName = buildName(uiob->qualifiedTypeNameId);
3090 if (typeName.front().isLower() && typeName.contains(u'.')) {
3091 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
3094 createAttachedAndGroupedScopes(uiob->qualifiedId);
3096 enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
3097 uiob->qualifiedTypeNameId->identifierToken);
3099 m_qmlTypes.append(m_currentScope);
3100 m_objectBindingScopes << m_currentScope;
3104int QQmlJSImportVisitor::openAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3107 int scopesEnteredCounter = 0;
3108 auto group = propertyName;
3109 for (; group->next; group = group->next) {
3110 const QString idName = group->name.toString();
3112 if (idName.isEmpty())
3115 if (group == propertyName && isImportPrefix(idName)) {
3116 prefix = idName + u'.';
3120 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3121 : QQmlSA::ScopeType::GroupedPropertyScope;
3123 [[maybe_unused]]
bool exists =
3124 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3126 scopesEnteredCounter++;
3130 return scopesEnteredCounter;
3133void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
3135 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3137 const QQmlJSScope::Ptr childScope = m_currentScope;
3140 const int scopesEnteredCounter = openAttachedAndGroupedScopes(uiob->qualifiedId);
3145 auto group = uiob->qualifiedId;
3146 for (; group->next; group = group->next) { }
3147 const QString propertyName = group->name.toString();
3149 if (m_currentScope->isNameDeferred(propertyName)) {
3150 bool foundIds =
false;
3151 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
3153 while (!childScopes.isEmpty()) {
3154 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
3155 m_scopesById.possibleIds(
3156 scope, scope, Default,
3157 [&](
const QString &id, QQmlJSScopesById::Confidence confidence) {
3160 Q_UNUSED(confidence);
3162 return QQmlJSScopesById::CallbackResult::StopSearch;
3165 childScopes << scope->childScopes();
3170 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
3172 qmlDeferredPropertyId, uiob->firstSourceLocation());
3176 if (checkCustomParser(m_currentScope)) {
3180 m_pendingPropertyObjectBindings
3181 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
3182 uiob->firstSourceLocation(), uiob->hasOnToken };
3184 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
3185 if (uiob->hasOnToken) {
3186 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
3187 binding.setInterceptor(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3188 QQmlJSScope::ConstPtr(childScope));
3190 binding.setValueSource(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3191 QQmlJSScope::ConstPtr(childScope));
3194 binding.setObject(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3195 QQmlJSScope::ConstPtr(childScope));
3197 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
3200 for (
int i = 0; i < scopesEnteredCounter; ++i)
3204bool QQmlJSImportVisitor::visit(ExportDeclaration *)
3206 Q_ASSERT(rootScopeIsValid());
3207 Q_ASSERT(m_exportedRootScope != m_globalScope);
3208 Q_ASSERT(m_currentScope == m_globalScope);
3209 m_currentScope = m_exportedRootScope;
3213void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
3215 Q_ASSERT(rootScopeIsValid());
3216 m_currentScope = m_exportedRootScope->parentScope();
3217 Q_ASSERT(m_currentScope == m_globalScope);
3220bool QQmlJSImportVisitor::visit(ESModule *module)
3222 Q_ASSERT(!rootScopeIsValid());
3223 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"module"),
3224 module->firstSourceLocation());
3225 m_currentScope->setIsScript(
true);
3226 importBaseModules();
3231void QQmlJSImportVisitor::endVisit(ESModule *)
3233 QQmlJSScope::resolveTypes(
3234 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3237bool QQmlJSImportVisitor::visit(Program *program)
3239 Q_ASSERT(m_globalScope == m_currentScope);
3240 Q_ASSERT(!rootScopeIsValid());
3241 enterRootScope(QQmlSA::ScopeType::JSFunctionScope, u"script"_s, program->firstSourceLocation());
3242 m_exportedRootScope->setIsScript(
true);
3243 importBaseModules();
3247void QQmlJSImportVisitor::endVisit(Program *)
3249 QQmlJSScope::resolveTypes(
3250 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3253void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
3257 const QString name = fieldMember->name.toString();
3258 if (m_importTypeLocationMap.contains(name)) {
3259 const QQmlJSImportedScope type = m_rootScopeImports.type(name);
3260 if (type.scope.isNull()) {
3261 if (m_rootScopeImports.hasType(name))
3262 m_usedTypes.insert(name);
3263 }
else if (!type.scope->ownAttachedTypeName().isEmpty()) {
3264 m_usedTypes.insert(name);
3269bool QQmlJSImportVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
3271 const QString name = idexp->name.toString();
3272 if (m_importTypeLocationMap.contains(name)) {
3273 m_usedTypes.insert(name);
3279bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
3282 if (element->isVariableDeclaration()) {
3283 QQmlJS::AST::BoundNames names;
3284 element->boundNames(&names);
3285 for (
const auto &name : std::as_const(names)) {
3286 std::optional<QString> typeName;
3287 if (TypeAnnotation *annotation = name.typeAnnotation.data())
3288 if (Type *type = annotation->type)
3289 typeName = type->toString();
3290 using Kind = QQmlJSScope::JavaScriptIdentifier::Kind;
3291 const Kind kind = (element->scope == QQmlJS::AST::VariableScope::Var)
3292 ? Kind::FunctionScoped
3293 : Kind::LexicalScoped;
3294 const QString variableName = name.id;
3295 if (kind == Kind::LexicalScoped) {
3296 const QQmlJS::SourceLocation location = element->firstSourceLocation();
3297 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(variableName)) {
3298 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(variableName), qmlSyntax,
3300 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(variableName), qmlSyntax,
3301 previousDeclaration->location);
3304 const bool isConstVariable = element->scope == QQmlJS::AST::VariableScope::Const;
3305 const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
3307 { (element->scope == QQmlJS::AST::VariableScope::Var)
3308 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
3309 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
3310 name.location, typeName,
3320bool QQmlJSImportVisitor::visit(IfStatement *statement)
3322 if (BinaryExpression *binary = cast<BinaryExpression *>(statement->expression)) {
3323 if (binary->op == QSOperator::Assign) {
3325 "Assignment in condition: did you mean to use \"===\" or \"==\" instead of \"=\"?"_L1,
3326 qmlAssignmentInCondition, binary->operatorToken);