158 if (!scope->isComposite())
159 return scope->internalName();
161 if (scope->isInlineComponent() && scope->inlineComponentName().has_value())
162 return scope->inlineComponentName().value();
164 return scope->baseTypeName();
167template<
typename Node>
171 for (
const Node *segment = node; segment; segment = segment->next) {
172 if (!result.isEmpty())
174 result += segment->name;
180
181
182
183
184void QQmlJSImportVisitor::registerTargetIntoImporter(
const QQmlJSScope::Ptr &target)
186 target->setScopeType(QQmlSA::ScopeType::QMLScope);
187 target->setBaseTypeName(
"$InProcess$"_L1);
188 target->setFilePath(m_logger->filePath());
189 target->setIsComposite(
true);
190 if (!m_importer->registerScope(target)) {
191 qCDebug(lcImportVisitor)
192 <<
"Couldn't register scope into importer: scope will be created multiple times.";
200 target->resetForReparse();
203QQmlJSImportVisitor::QQmlJSImportVisitor(
204 const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger,
205 const QString &implicitImportDirectory,
const QStringList &qmldirFiles)
206 : m_implicitImportDirectory(implicitImportDirectory),
207 m_qmldirFiles(qmldirFiles),
208 m_exportedRootScope(target),
209 m_importer(importer),
212 QQmlJS::ContextualTypes(
213 QQmlJS::ContextualTypes::QML, {}, {},
214 importer->builtinInternalNames().contextualTypes().arrayType()),
219 prepareTargetForVisit(target);
220 registerTargetIntoImporter(target);
223
224
225
226
227
228 auto globalScope = QQmlJSScope::create();
229 globalScope->setInternalName(u"global"_s);
230 globalScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
232 QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
233 QQmlJSScope::JavaScriptIdentifier::LexicalScoped, QQmlJS::SourceLocation(), std::nullopt,
237 QV4::Compiler::Codegen::forEachGlobalName([&](QLatin1StringView globalName) {
238 globalScope->insertJSIdentifier(globalName, globalJavaScript);
241 m_globalScope = globalScope;
242 m_currentScope = globalScope;
245QQmlJSImportVisitor::~QQmlJSImportVisitor() =
default;
247void QQmlJSImportVisitor::populateCurrentScope(
248 QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
250 m_currentScope->setScopeType(type);
251 m_currentScope->setIsComposite(
true);
252 m_currentScope->setFilePath(m_logger->filePath());
253 m_currentScope->setSourceLocation(location);
254 setScopeName(m_currentScope, type, name);
255 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
258void QQmlJSImportVisitor::enterRootScope(QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
260 Q_ASSERT(m_currentScope == m_globalScope);
261 QQmlJSScope::reparent(m_currentScope, m_exportedRootScope);
262 m_currentScope = m_exportedRootScope;
263 populateCurrentScope(type, name, location);
266void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type,
const QString &name,
267 const QQmlJS::SourceLocation &location)
269 QQmlJSScope::Ptr newScope = QQmlJSScope::create();
270 QQmlJSScope::reparent(m_currentScope, newScope);
271 m_currentScope = std::move(newScope);
272 populateCurrentScope(type, name, location);
275bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type,
277 const QQmlJS::SourceLocation &location)
279 Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope
280 || type == QQmlSA::ScopeType::AttachedPropertyScope);
282 const auto pred = [&](
const QQmlJSScope::ConstPtr &s) {
285 return s->internalName() == name;
287 const auto scopes = m_currentScope->childScopes();
290 auto it = std::find_if(scopes.begin(), scopes.end(), pred);
291 if (it == scopes.end()) {
293 enterEnvironment(type, name, location);
297 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, *it);
298 m_currentScope = *it;
302void QQmlJSImportVisitor::leaveEnvironment()
304 m_currentScope = m_currentScope->parentScope();
307void QQmlJSImportVisitor::warnUnresolvedType(
const QQmlJSScope::ConstPtr &type)
const
309 m_logger->log(QStringLiteral(
"Type %1 is used but it is not resolved")
310 .arg(getScopeName(type, type->scopeType())),
311 qmlUnresolvedType, type->sourceLocation());
314void QQmlJSImportVisitor::warnMissingPropertyForBinding(
315 const QString &property,
const QQmlJS::SourceLocation &location,
316 const std::optional<QQmlJSFixSuggestion> &fixSuggestion)
318 m_logger->log(QStringLiteral(
"Could not find property \"%1\".").arg(property),
319 qmlMissingProperty, location,
true,
true, fixSuggestion);
327void QQmlJSImportVisitor::resolveAliases()
329 QQueue<QQmlJSScope::Ptr> objects;
330 objects.enqueue(m_exportedRootScope);
332 qsizetype lastRequeueLength = std::numeric_limits<qsizetype>::max();
333 QQueue<QQmlJSScope::Ptr> requeue;
335 while (!objects.isEmpty()) {
336 const QQmlJSScope::Ptr object = objects.dequeue();
337 const auto properties = object->ownProperties();
339 bool doRequeue =
false;
340 for (
const auto &property : properties) {
341 if (!property.isAlias() || !property.type().isNull())
344 QStringList components = property.aliasExpression().split(u'.');
345 QQmlJSMetaProperty targetProperty;
347 bool foundProperty =
false;
350 QQmlJSScope::ConstPtr type = m_scopesById.scope(components.takeFirst(), object);
351 QQmlJSScope::ConstPtr typeScope;
352 if (!type.isNull()) {
353 foundProperty =
true;
360 while (type && !components.isEmpty()) {
361 const QString name = components.takeFirst();
363 if (!type->hasProperty(name)) {
364 foundProperty =
false;
369 const auto target = type->property(name);
370 if (!target.type() && target.isAlias())
373 type = target.type();
374 targetProperty = target;
382 m_logger->log(QStringLiteral(
"Cannot deduce type of alias \"%1\"")
383 .arg(property.propertyName()),
384 qmlMissingType, property.sourceLocation());
386 m_logger->log(QStringLiteral(
"Cannot resolve alias \"%1\"")
387 .arg(property.propertyName()),
388 qmlUnresolvedAlias, property.sourceLocation());
391 Q_ASSERT(property.index() >= 0);
392 object->addOwnProperty(property);
395 QQmlJSMetaProperty newProperty = property;
396 newProperty.setType(type);
398 newProperty.setIsList(targetProperty.isList());
399 newProperty.setIsWritable(targetProperty.isWritable());
400 newProperty.setIsFinal(targetProperty.isFinal());
401 newProperty.setIsPointer(targetProperty.isPointer());
403 const bool onlyId = !property.aliasExpression().contains(u'.');
405 newProperty.setAliasTargetScope(type);
406 newProperty.setAliasTargetName(QStringLiteral(
"id-only-alias"));
408 const auto &ownerScope = QQmlJSScope::ownerOfProperty(
409 typeScope, targetProperty.propertyName()).scope;
410 newProperty.setAliasTargetScope(ownerScope);
411 newProperty.setAliasTargetName(targetProperty.propertyName());
414 if (
const QString internalName = type->internalName(); !internalName.isEmpty())
415 newProperty.setTypeName(internalName);
417 Q_ASSERT(newProperty.index() >= 0);
418 object->addOwnProperty(newProperty);
419 m_aliasDefinitions.append({ object, property.propertyName() });
423 const auto childScopes = object->childScopes();
424 for (
const auto &childScope : childScopes)
425 objects.enqueue(childScope);
428 requeue.enqueue(object);
430 if (objects.isEmpty() && requeue.size() < lastRequeueLength) {
431 lastRequeueLength = requeue.size();
432 objects.swap(requeue);
436 while (!requeue.isEmpty()) {
437 const QQmlJSScope::Ptr object = requeue.dequeue();
438 const auto properties = object->ownProperties();
439 for (
const auto &property : properties) {
440 if (!property.isAlias() || property.type())
442 m_logger->log(QStringLiteral(
"Alias \"%1\" is part of an alias cycle")
443 .arg(property.propertyName()),
444 qmlAliasCycle, property.sourceLocation());
449void QQmlJSImportVisitor::resolveGroupProperties()
451 QQueue<QQmlJSScope::Ptr> objects;
452 objects.enqueue(m_exportedRootScope);
454 while (!objects.isEmpty()) {
455 const QQmlJSScope::Ptr object = objects.dequeue();
456 const auto childScopes = object->childScopes();
457 for (
const auto &childScope : childScopes) {
458 if (mayBeUnresolvedGroupedProperty(childScope)) {
459 const QString name = childScope->internalName();
460 if (object->isNameDeferred(name)) {
461 const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
462 if (!deferred.isNull()) {
463 QQmlJSScope::resolveGroup(
464 childScope, deferred, m_rootScopeImports.contextualTypes(),
467 }
else if (
const QQmlJSScope::ConstPtr propType = object->property(name).type()) {
468 QQmlJSScope::resolveGroup(
469 childScope, propType, m_rootScopeImports.contextualTypes(),
473 objects.enqueue(childScope);
478QString QQmlJSImportVisitor::implicitImportDirectory(
479 const QString &localFile, QQmlJSResourceFileMapper *mapper)
482 const auto resource = mapper->entry(
483 QQmlJSResourceFileMapper::localFileFilter(localFile));
484 if (resource.isValid()) {
485 return resource.resourcePath.contains(u'/')
486 ? (u':' + resource.resourcePath.left(
487 resource.resourcePath.lastIndexOf(u'/') + 1))
488 : QStringLiteral(
":/");
492 return QFileInfo(localFile).canonicalPath() + u'/';
495void QQmlJSImportVisitor::processImportWarnings(
496 const QString &what,
const QList<QQmlJS::DiagnosticMessage> &warnings,
497 const QQmlJS::SourceLocation &srcLocation)
499 if (warnings.isEmpty())
502 QList<QQmlJS::DiagnosticMessage> importWarnings = warnings;
505 auto fileSelectorWarningsIt = std::remove_if(importWarnings.begin(), importWarnings.end(),
506 [](
const QQmlJS::DiagnosticMessage &message) {
507 return message.type == QtMsgType::QtInfoMsg;
509 if (fileSelectorWarningsIt != importWarnings.end()) {
510 QList<QQmlJS::DiagnosticMessage> fileSelectorImportWarnings { fileSelectorWarningsIt, importWarnings.end() };
511 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImportFileSelector,
513 m_logger->processMessages(warnings, qmlImportFileSelector, srcLocation);
514 importWarnings.erase(fileSelectorWarningsIt, importWarnings.end());
515 if (importWarnings.isEmpty())
519 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImport,
521 m_logger->processMessages(warnings, qmlImport, srcLocation);
524void QQmlJSImportVisitor::importBaseModules()
526 Q_ASSERT(m_rootScopeImports.isEmpty());
527 m_rootScopeImports = m_importer->importHardCodedBuiltins();
529 const QQmlJS::SourceLocation invalidLoc;
530 const auto types = m_rootScopeImports.types();
531 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
532 addImportWithLocation(*it, invalidLoc,
false);
534 if (!m_qmldirFiles.isEmpty())
535 m_rootScopeImports.addWarnings(m_importer->importQmldirs(m_qmldirFiles));
539 if (!m_logger->filePath().endsWith(u".qmltypes"_s)) {
540 m_rootScopeImports.add(m_importer->importDirectory(m_implicitImportDirectory));
545 if (QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper()) {
546 const QStringList resourcePaths = mapper->resourcePaths(QQmlJSResourceFileMapper::Filter {
547 m_logger->filePath(), QStringList(), QQmlJSResourceFileMapper::Resource });
548 for (
const QString &path : resourcePaths) {
549 const qsizetype lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
552 m_rootScopeImports.add(m_importer->importDirectory(path.first(lastSlash)));
557 processImportWarnings(QStringLiteral(
"base modules"), m_rootScopeImports.warnings());
560bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiProgram *)
564 if (
auto elementName = QFileInfo(m_logger->filePath()).baseName();
565 !elementName.isEmpty() && elementName[0].isUpper()) {
566 m_rootScopeImports.setType(elementName, { m_exportedRootScope, QTypeRevision {} });
572void QQmlJSImportVisitor::endVisit(UiProgram *)
574 for (
const auto &scope : std::as_const(m_objectBindingScopes)) {
575 breakInheritanceCycles(scope);
576 checkDeprecation(scope);
579 for (
const auto &scope : std::as_const(m_objectDefinitionScopes)) {
580 if (m_pendingDefaultProperties.contains(scope))
582 breakInheritanceCycles(scope);
583 checkDeprecation(scope);
586 const auto &keys = m_pendingDefaultProperties.keys();
587 for (
const auto &scope : keys) {
588 breakInheritanceCycles(scope);
589 checkDeprecation(scope);
593 resolveGroupProperties();
595 for (
const auto &scope : std::as_const(m_objectDefinitionScopes))
596 checkGroupedAndAttachedScopes(scope);
599 processDefaultProperties();
600 processPropertyTypes();
601 processMethodTypes();
602 processPropertyBindings();
603 processPropertyBindingObjects();
604 checkRequiredProperties();
606 auto unusedImports = m_importLocations;
607 for (
const QString &type : std::as_const(m_usedTypes)) {
608 const auto &importLocations = m_importTypeLocationMap.values(type);
609 for (
const auto &importLocation : importLocations)
610 unusedImports.remove(importLocation);
613 if (unusedImports.isEmpty())
617 const auto &imports = m_importStaticModuleLocationMap.values();
618 for (
const QQmlJS::SourceLocation &import : imports)
619 unusedImports.remove(import);
621 for (
const auto &import : unusedImports) {
622 m_logger->log(QString::fromLatin1(
"Unused import"), qmlUnusedImports, import);
625 populateRuntimeFunctionIndicesForDocument();
630 ExpressionStatement *expr = cast<ExpressionStatement *>(statement);
632 if (!statement || !expr->expression)
635 switch (expr->expression->kind) {
636 case Node::Kind_StringLiteral:
637 return cast<StringLiteral *>(expr->expression)->value.toString();
638 case Node::Kind_NumericLiteral:
639 return cast<NumericLiteral *>(expr->expression)->value;
645QVector<QQmlJSAnnotation> QQmlJSImportVisitor::parseAnnotations(QQmlJS::AST::UiAnnotationList *list)
648 QVector<QQmlJSAnnotation> annotationList;
650 for (UiAnnotationList *item = list; item !=
nullptr; item = item->next) {
651 UiAnnotation *annotation = item->annotation;
653 QQmlJSAnnotation qqmljsAnnotation;
654 qqmljsAnnotation.name = buildName(annotation->qualifiedTypeNameId);
656 for (UiObjectMemberList *memberItem = annotation->initializer->members; memberItem !=
nullptr; memberItem = memberItem->next) {
657 switch (memberItem->member->kind) {
658 case Node::Kind_UiScriptBinding: {
659 auto *scriptBinding = QQmlJS::AST::cast<UiScriptBinding*>(memberItem->member);
660 qqmljsAnnotation.bindings[buildName(scriptBinding->qualifiedId)]
661 = bindingToVariant(scriptBinding->statement);
670 annotationList.append(qqmljsAnnotation);
673 return annotationList;
676void QQmlJSImportVisitor::setAllBindings()
678 using Key = std::pair<QQmlJSScope::ConstPtr, QString>;
679 QHash<Key, QQmlJS::SourceLocation> foundBindings;
681 for (
auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
683 const QQmlJSScope::Ptr type = it->owner;
684 if (!checkTypeResolved(type))
687 auto binding = it->create();
688 if (!binding.isValid())
690 type->addOwnPropertyBinding(binding, it->specifier);
693 if (binding.hasInterceptor() || binding.hasValueSource())
695 const QString propertyName = binding.propertyName();
696 QQmlJSMetaProperty property = type->property(propertyName);
699
700
701
702
703
704 if (!property.isValid())
708 if (property.isList())
711 const Key key = std::make_pair(type, propertyName);
712 auto sourceLocationIt = foundBindings.constFind(key);
713 if (sourceLocationIt == foundBindings.constEnd()) {
714 foundBindings.insert(key, binding.sourceLocation());
718 const QQmlJS::SourceLocation location = binding.sourceLocation();
719 m_logger->log(
"Duplicate binding on property '%1'"_L1.arg(propertyName),
720 qmlDuplicatePropertyBinding, location);
721 m_logger->log(
"Note: previous binding on '%1' here"_L1.arg(propertyName),
722 qmlDuplicatePropertyBinding, *sourceLocationIt,
true,
true, {}, {},
727void QQmlJSImportVisitor::processDefaultProperties()
729 for (
auto it = m_pendingDefaultProperties.constBegin();
730 it != m_pendingDefaultProperties.constEnd(); ++it) {
731 QQmlJSScope::ConstPtr parentScope = it.key();
734 if (checkCustomParser(parentScope))
738
739
740
741
742
743
744
745
746
748 parentScope = parentScope->baseType();
750 const QString defaultPropertyName =
751 parentScope ? parentScope->defaultPropertyName() : QString();
753 if (defaultPropertyName.isEmpty()) {
756 bool isComponent =
false;
757 for (QQmlJSScope::ConstPtr s = parentScope; s; s = s->baseType()) {
758 if (s->internalName() == QStringLiteral(
"QQmlComponent")) {
765 m_logger->log(QStringLiteral(
"Cannot assign to non-existent default property"),
766 qmlMissingProperty, it.value().constFirst()->sourceLocation());
772 const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
773 auto propType = defaultProp.type();
774 const auto handleUnresolvedDefaultProperty = [&](
const QQmlJSScope::ConstPtr &) {
776 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
777 "missing an import.")
778 .arg(defaultPropertyName)
779 .arg(defaultProp.typeName()),
780 qmlUnresolvedType, it.value().constFirst()->sourceLocation());
783 const auto assignToUnknownProperty = [&]() {
786 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it))
787 scope->setAssignedToUnknownProperty(
true);
790 if (propType.isNull()) {
791 handleUnresolvedDefaultProperty(propType);
792 assignToUnknownProperty();
796 if (it.value().size() > 1
797 && !defaultProp.isList()
798 && !propType->isListProperty()) {
800 QStringLiteral(
"Cannot assign multiple objects to a default non-list property"),
801 qmlNonListProperty, it.value().constFirst()->sourceLocation());
804 if (!checkTypeResolved(propType, handleUnresolvedDefaultProperty)) {
805 assignToUnknownProperty();
809 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
810 if (!checkTypeResolved(scope))
815 if (propType->canAssign(scope)) {
816 scope->setIsWrappedInImplicitComponent(
817 causesImplicitComponentWrapping(defaultProp, scope));
821 m_logger->log(QStringLiteral(
"Cannot assign to default property of incompatible type"),
822 qmlIncompatibleType, scope->sourceLocation());
827void QQmlJSImportVisitor::processPropertyTypes()
829 for (
const PendingPropertyType &type : std::as_const(m_pendingPropertyTypes)) {
830 Q_ASSERT(type.scope->hasOwnProperty(type.name));
832 auto property = type.scope->ownProperty(type.name);
834 if (
const auto propertyType = QQmlJSScope::findType(
835 property.typeName(), m_rootScopeImports.contextualTypes()).scope) {
836 property.setType(propertyType);
837 type.scope->addOwnProperty(property);
839 m_logger->log(property.typeName() +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports,
840 qmlImport, type.location);
845void QQmlJSImportVisitor::processMethodTypes()
847 const auto isEnumUsedAsType = [&](QStringView typeName,
const QQmlJS::SourceLocation &loc) {
848 if (typeName ==
"enum"_L1) {
849 m_logger->log(
"QML does not have an `enum` type. Use the enum's underlying type "
850 "(int or double)."_L1,
851 qmlEnumsAreNotTypes, loc);
855 const auto split = typeName.tokenize(u'.').toContainer<QVarLengthArray<QStringView, 4>>();
856 if (split.size() != 2)
859 const QStringView scopeName = split[0];
860 const QStringView enumName = split[1];
862 if (
auto scope = QQmlJSScope::findType(scopeName.toString(),
863 m_rootScopeImports.contextualTypes()).scope) {
864 if (scope->enumeration(enumName.toString()).isValid()) {
865 m_logger->log(
"QML enumerations are not types. Use underlying type "
866 "(int or double) instead."_L1,
867 qmlEnumsAreNotTypes, loc);
874 for (
const auto &method : std::as_const(m_pendingMethodTypeAnnotations)) {
875 for (
auto [it, end] = method.scope->mutableOwnMethodsRange(method.methodName); it != end; ++it) {
876 const auto [parameterBegin, parameterEnd] = it->mutableParametersRange();
877 for (
auto parameter = parameterBegin; parameter != parameterEnd; ++parameter) {
878 const int parameterIndex = parameter - parameterBegin;
879 if (isEnumUsedAsType(parameter->typeName(), method.locations[parameterIndex]))
881 if (
const auto parameterType = QQmlJSScope::findType(
882 parameter->typeName(), m_rootScopeImports.contextualTypes()).scope) {
883 parameter->setType({ parameterType });
886 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
887 .arg(parameter->typeName(), parameter->name(), it->methodName()),
888 qmlUnresolvedType, method.locations[parameter - parameterBegin]);
892 if (isEnumUsedAsType(it->returnTypeName(), method.locations.last()))
894 if (
const auto returnType = QQmlJSScope::findType(
895 it->returnTypeName(), m_rootScopeImports.contextualTypes()).scope) {
896 it->setReturnType({ returnType });
898 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
899 it->returnTypeName(), it->methodName()),
900 qmlUnresolvedType, method.locations.last());
908
909
910
911
912
913
914
918 for (QStringView propertyName: possiblyGroupedProperty.tokenize(u".")) {
919 property = scope->property(propertyName.toString());
920 if (property.isValid())
921 scope = property.type();
928void QQmlJSImportVisitor::processPropertyBindingObjects()
930 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundLiterals;
938 QSet<std::pair<QQmlJSScope::Ptr, QString>> visited;
939 for (
const PendingPropertyObjectBinding &objectBinding :
940 std::as_const(m_pendingPropertyObjectBindings)) {
942 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
943 if (visited.contains(uniqueBindingId))
945 visited.insert(uniqueBindingId);
947 auto [existingBindingsBegin, existingBindingsEnd] =
948 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
949 const bool hasLiteralBindings =
950 std::any_of(existingBindingsBegin, existingBindingsEnd,
951 [](
const QQmlJSMetaPropertyBinding &x) {
return x.hasLiteral(); });
952 if (hasLiteralBindings)
953 foundLiterals.insert(uniqueBindingId);
957 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundObjects;
958 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundInterceptors;
959 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundValueSources;
961 for (
const PendingPropertyObjectBinding &objectBinding :
962 std::as_const(m_pendingPropertyObjectBindings)) {
963 const QString propertyName = objectBinding.name;
964 QQmlJSScope::Ptr childScope = objectBinding.childScope;
966 const auto assignToUnknownProperty = [&]() {
969 childScope->setAssignedToUnknownProperty(
true);
973 if (!checkTypeResolved(objectBinding.scope)) {
974 assignToUnknownProperty();
978 QQmlJSMetaProperty property = resolveProperty(propertyName, objectBinding.scope);
980 if (!property.isValid()) {
981 warnMissingPropertyForBinding(propertyName, objectBinding.location);
984 const auto handleUnresolvedProperty = [&](
const QQmlJSScope::ConstPtr &) {
986 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
987 "missing an import.")
989 .arg(property.typeName()),
990 qmlUnresolvedType, objectBinding.location);
993 if (property.type().isNull()) {
994 assignToUnknownProperty();
995 handleUnresolvedProperty(property.type());
1000 if (!checkTypeResolved(property.type(), handleUnresolvedProperty)) {
1001 assignToUnknownProperty();
1003 }
else if (!checkTypeResolved(childScope)) {
1007 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
1008 m_logger->log(QStringLiteral(
"Cannot assign object of type %1 to %2")
1009 .arg(getScopeName(childScope, QQmlSA::ScopeType::QMLScope))
1010 .arg(property.typeName()),
1011 qmlIncompatibleType, childScope->sourceLocation());
1015 childScope->setIsWrappedInImplicitComponent(
1016 causesImplicitComponentWrapping(property, childScope));
1019 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
1020 const QString typeName = getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
1022 auto isConditionalBinding = [&]() ->
bool {
1024
1025
1026
1027
1028 return childScope->hasOwnPropertyBindings(u"enabled"_s)
1029 || childScope->hasOwnPropertyBindings(u"when"_s)
1030 || childScope->hasOwnPropertyBindings(u"running"_s);
1033 if (objectBinding.onToken) {
1034 if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueInterceptor"))) {
1035 if (foundInterceptors.contains(uniqueBindingId)) {
1036 if (!isConditionalBinding()) {
1037 m_logger->log(QStringLiteral(
"Duplicate interceptor on property \"%1\"")
1039 qmlDuplicatePropertyBinding, objectBinding.location);
1042 foundInterceptors.insert(uniqueBindingId);
1044 }
else if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueSource"))) {
1045 if (foundValueSources.contains(uniqueBindingId)) {
1046 if (!isConditionalBinding()) {
1047 m_logger->log(QStringLiteral(
"Duplicate value source on property \"%1\"")
1049 qmlDuplicatePropertyBinding, objectBinding.location);
1051 }
else if (foundObjects.contains(uniqueBindingId)
1052 || foundLiterals.contains(uniqueBindingId)) {
1053 if (!isConditionalBinding()) {
1054 m_logger->log(QStringLiteral(
"Cannot combine value source and binding on "
1057 qmlDuplicatePropertyBinding, objectBinding.location);
1060 foundValueSources.insert(uniqueBindingId);
1063 m_logger->log(QStringLiteral(
"On-binding for property \"%1\" has wrong type \"%2\"")
1066 qmlIncompatibleType, objectBinding.location);
1069 if (foundValueSources.contains(uniqueBindingId)) {
1070 if (!isConditionalBinding()) {
1072 QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
1074 qmlDuplicatePropertyBinding, objectBinding.location);
1077 foundObjects.insert(uniqueBindingId);
1085 QList<QQmlJSScope::ConstPtr> descendants;
1086 std::vector<QQmlJSScope::ConstPtr> toVisit;
1088 toVisit.push_back(scope);
1089 while (!toVisit.empty()) {
1090 const QQmlJSScope::ConstPtr s = toVisit.back();
1096 toVisit.insert(toVisit.end(), s->childScopesBegin(), s->childScopesEnd());
1103void QQmlJSImportVisitor::populatePropertyAliases()
1105 for (
const auto &alias : std::as_const(m_aliasDefinitions)) {
1106 const auto &[aliasScope, aliasName] = alias;
1107 if (aliasScope.isNull())
1110 auto property = aliasScope->ownProperty(aliasName);
1111 if (!property.isValid() || !property.aliasTargetScope())
1114 Property target(property.aliasTargetScope(), property.aliasTargetName());
1117 m_propertyAliases[target].append(alias);
1118 property = target.scope->property(target.name);
1119 target = Property(property.aliasTargetScope(), property.aliasTargetName());
1120 }
while (property.isAlias());
1124void QQmlJSImportVisitor::checkRequiredProperties()
1126 for (
const auto &required : std::as_const(m_requiredProperties)) {
1127 if (!required.scope->hasProperty(required.name)) {
1129 QStringLiteral(
"Property \"%1\" was marked as required but does not exist.")
1130 .arg(required.name),
1131 qmlRequired, required.location);
1135 const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
1136 const auto isInComponent = [&](
const QQmlJSScope::ConstPtr &requiredScope) {
1137 for (
auto s = requiredScope; s; s = s->parentScope()) {
1138 if (s->isWrappedInImplicitComponent() || s->baseType() == compType)
1144 const auto requiredHasBinding = [](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1145 const QQmlJSScope::ConstPtr &owner,
1146 const QString &propName) {
1147 for (
const auto &scope : scopesToSearch) {
1148 if (scope->property(propName).isAlias())
1150 const auto &[begin, end] = scope->ownPropertyBindings(propName);
1151 for (
auto it = begin; it != end; ++it) {
1153 const bool isRelevantBinding = QQmlSA::isRegularBindingType(it->bindingType())
1154 || it->bindingType() == QQmlSA::BindingType::Interceptor
1155 || it->bindingType() == QQmlSA::BindingType::ValueSource;
1156 if (!isRelevantBinding)
1158 if (QQmlJSScope::ownerOfProperty(scope, propName).scope == owner)
1166 const auto requiredUsedInRootAlias = [&](
const QQmlJSScope::ConstPtr &defScope,
1167 const QQmlJSScope::ConstPtr &requiredScope,
1168 const QString &propName) {
1169 if (defScope->filePath() == requiredScope->filePath()) {
1170 QQmlJSScope::ConstPtr fileRootScope = requiredScope;
1171 while (fileRootScope->parentScope() != m_globalScope)
1172 fileRootScope = fileRootScope->parentScope();
1174 const auto &rootProperties = fileRootScope->ownProperties();
1175 for (
const auto &p : rootProperties) {
1176 if (p.isAlias() && p.aliasTargetScope() == requiredScope
1177 && p.aliasTargetName() == propName) {
1186 const auto requiredSetThroughAlias = [&](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1187 const QQmlJSScope::ConstPtr &requiredScope,
1188 const QString &propName) {
1189 const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
1190 const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
1191 for (
const auto &alias : propertyAliases) {
1192 for (
const auto &s : scopesToSearch) {
1193 if (s->hasOwnPropertyBindings(alias.name))
1200 const auto warn = [
this](
const QQmlJSScope::ConstPtr &prevRequiredScope,
1201 const QString &propName,
const QQmlJSScope::ConstPtr &defScope,
1202 const QQmlJSScope::ConstPtr &requiredScope,
1203 const QQmlJSScope::ConstPtr &descendant) {
1204 const auto &propertyScope = QQmlJSScope::ownerOfProperty(requiredScope, propName).scope;
1205 const QString propertyScopeName = !propertyScope.isNull()
1206 ? getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
1209 std::optional<QQmlJSFixSuggestion> suggestion;
1211 QString message = QStringLiteral(
"Component is missing required property %1 from %2")
1213 .arg(propertyScopeName);
1214 if (requiredScope != descendant) {
1215 const QString requiredScopeName = prevRequiredScope
1216 ? getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
1219 if (!prevRequiredScope.isNull()) {
1220 if (
auto sourceScope = prevRequiredScope->baseType()) {
1221 suggestion = QQmlJSFixSuggestion{
1222 "%1:%2:%3: Property marked as required in %4."_L1
1223 .arg(sourceScope->filePath())
1224 .arg(sourceScope->sourceLocation().startLine)
1225 .arg(sourceScope->sourceLocation().startColumn)
1226 .arg(requiredScopeName),
1227 sourceScope->sourceLocation()
1231 if (sourceScope->isComposite())
1232 suggestion->setFilename(sourceScope->filePath());
1235 message +=
" (marked as required by %1)"_L1.arg(requiredScopeName);
1239 m_logger->log(message, qmlRequired, defScope->sourceLocation(),
true,
true, suggestion);
1242 populatePropertyAliases();
1244 for (
const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
1245 if (defScope->isFileRootComponent() || defScope->isInlineComponent()
1246 || defScope->componentRootStatus() != QQmlJSScope::IsComponentRoot::No
1247 || defScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
1251 QVector<QQmlJSScope::ConstPtr> scopesToSearch;
1252 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
1253 const auto descendants = QList<QQmlJSScope::ConstPtr>()
1254 << scope << qmlScopeDescendants(scope);
1255 for (
const QQmlJSScope::ConstPtr &descendant : std::as_const(descendants)) {
1258 if (descendant != scope && descendant->isInlineComponent())
1260 scopesToSearch << descendant;
1261 const auto ownProperties = descendant->ownProperties();
1262 for (
auto propertyIt = ownProperties.constBegin();
1263 propertyIt != ownProperties.constEnd(); ++propertyIt) {
1264 const QString propName = propertyIt.key();
1265 if (descendant->hasOwnPropertyBindings(propName))
1268 QQmlJSScope::ConstPtr prevRequiredScope;
1269 for (
const QQmlJSScope::ConstPtr &requiredScope : std::as_const(scopesToSearch)) {
1270 if (isInComponent(requiredScope))
1273 if (!requiredScope->isPropertyLocallyRequired(propName)) {
1274 prevRequiredScope = requiredScope;
1278 if (requiredHasBinding(scopesToSearch, descendant, propName))
1281 if (requiredUsedInRootAlias(defScope, requiredScope, propName))
1284 if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
1287 warn(prevRequiredScope, propName, defScope, requiredScope, descendant);
1288 prevRequiredScope = requiredScope;
1296void QQmlJSImportVisitor::processPropertyBindings()
1298 for (
auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
1299 QQmlJSScope::Ptr scope = it.key();
1300 for (
auto &[visibilityScope, location, name] : it.value()) {
1301 if (!scope->hasProperty(name) && !m_logger->isDisabled()) {
1305 if (checkCustomParser(scope))
1309 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1311 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
1312 baseScope = baseScope->baseType()) {
1313 if (
auto suggestion = QQmlJSUtils::didYouMean(
1314 name, baseScope->ownProperties().keys(), location);
1315 suggestion.has_value()) {
1316 fixSuggestion = suggestion;
1321 warnMissingPropertyForBinding(name, location, fixSuggestion);
1325 const auto property = scope->property(name);
1326 if (!property.type()) {
1327 m_logger->log(QStringLiteral(
"No type found for property \"%1\". This may be due "
1328 "to a missing import statement or incomplete "
1331 qmlMissingType, location);
1334 const auto &annotations = property.annotations();
1336 const auto deprecationAnn =
1337 std::find_if(annotations.cbegin(), annotations.cend(),
1338 [](
const QQmlJSAnnotation &ann) {
return ann.isDeprecation(); });
1340 if (deprecationAnn != annotations.cend()) {
1341 const auto deprecation = deprecationAnn->deprecation();
1343 QString message = QStringLiteral(
"Binding on deprecated property \"%1\"")
1344 .arg(property.propertyName());
1346 if (!deprecation.reason.isEmpty())
1347 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1349 m_logger->log(message, qmlDeprecated, location);
1355void QQmlJSImportVisitor::checkSignal(
1356 const QQmlJSScope::ConstPtr &signalScope,
const QQmlJS::SourceLocation &location,
1357 const QString &handlerName,
const QStringList &handlerParameters)
1359 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1361 std::optional<QQmlJSMetaMethod> signalMethod;
1362 const auto setSignalMethod = [&](
const QQmlJSScope::ConstPtr &scope,
const QString &name) {
1363 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1364 if (!methods.isEmpty())
1365 signalMethod = methods[0];
1368 if (signal.has_value()) {
1369 if (signalScope->hasMethod(*signal)) {
1370 setSignalMethod(signalScope, *signal);
1371 }
else if (
auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1376 if (
auto notify = p->notify(); !notify.isEmpty()) {
1377 setSignalMethod(signalScope, notify);
1379 Q_ASSERT(!p->bindable().isEmpty());
1380 signalMethod = QQmlJSMetaMethod {};
1385 if (!signalMethod.has_value()) {
1390 if (signalScope->baseTypeName() == QStringLiteral(
"Connections")) {
1392 u"Implicitly defining \"%1\" as signal handler in Connections is deprecated. "
1393 u"Create a function instead: \"function %2(%3) { ... }\"."_s.arg(
1394 handlerName, handlerName, handlerParameters.join(u", ")),
1395 qmlUnqualified, location,
true,
true);
1399 auto baseType = QQmlJSScope::nonCompositeBaseType(signalScope);
1400 if (baseType && baseType->hasCustomParser())
1404 QStringLiteral(
"no matching signal found for handler \"%1\"").arg(handlerName),
1405 qmlUnqualified, location,
true,
true);
1409 const auto signalParameters = signalMethod->parameters();
1410 QHash<QString, qsizetype> parameterNameIndexes;
1412 for (
int i = 0, end = signalParameters.size(); i < end; i++) {
1413 auto &p = signalParameters[i];
1414 parameterNameIndexes[p.name()] = i;
1416 auto signalName = [&]() {
1418 return u" called %1"_s.arg(*signal);
1421 auto type = p.type();
1424 "Type %1 of parameter %2 in signal%3 was not found, but is required to compile "
1426 p.typeName(), p.name(), signalName(),
1427 handlerName, didYouAddAllImports),
1428 qmlSignalParameters, location);
1432 if (type->isComposite())
1440 auto parameterName = [&]() {
1441 if (p.name().isEmpty())
1443 return u" called %1"_s.arg(p.name());
1445 switch (type->accessSemantics()) {
1446 case QQmlJSScope::AccessSemantics::Reference:
1448 m_logger->log(QStringLiteral(
"Type %1 of parameter%2 in signal%3 should be "
1449 "passed by pointer to be able to compile %4. ")
1450 .arg(p.typeName(), parameterName(), signalName(),
1452 qmlSignalParameters, location);
1454 case QQmlJSScope::AccessSemantics::Value:
1455 case QQmlJSScope::AccessSemantics::Sequence:
1459 "Type %1 of parameter%2 in signal%3 should be passed by "
1460 "value or const reference to be able to compile %4. ")
1461 .arg(p.typeName(), parameterName(), signalName(),
1463 qmlSignalParameters, location);
1465 case QQmlJSScope::AccessSemantics::None:
1467 QStringLiteral(
"Type %1 of parameter%2 in signal%3 required by the "
1468 "compilation of %4 cannot be used. ")
1469 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1470 qmlSignalParameters, location);
1475 if (handlerParameters.size() > signalParameters.size()) {
1476 m_logger->log(QStringLiteral(
"Signal handler for \"%2\" has more formal"
1477 " parameters than the signal it handles.")
1479 qmlSignalParameters, location);
1483 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1484 const QStringView handlerParameter = handlerParameters.at(i);
1485 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1486 if (it == parameterNameIndexes.constEnd())
1488 const qsizetype j = *it;
1493 m_logger->log(QStringLiteral(
"Parameter %1 to signal handler for \"%2\""
1494 " is called \"%3\". The signal has a parameter"
1495 " of the same name in position %4.")
1497 .arg(handlerName, handlerParameter)
1499 qmlSignalParameters, location);
1503void QQmlJSImportVisitor::addDefaultProperties()
1505 QQmlJSScope::ConstPtr parentScope = m_currentScope->parentScope();
1506 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1507 || m_currentScope->isInlineComponent())
1510 m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
1512 if (checkCustomParser(parentScope))
1516
1517
1518
1519
1520
1521
1522
1523
1524
1526 parentScope = parentScope->baseType();
1528 const QString defaultPropertyName =
1529 parentScope ? parentScope->defaultPropertyName() : QString();
1531 if (defaultPropertyName.isEmpty())
1536 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1537 binding.setObject(getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
1538 QQmlJSScope::ConstPtr(m_currentScope));
1539 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() {
return binding; },
1540 QQmlJSScope::UnnamedPropertyTarget });
1543void QQmlJSImportVisitor::breakInheritanceCycles(
const QQmlJSScope::Ptr &originalScope)
1545 QList<QQmlJSScope::ConstPtr> scopes;
1546 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1547 if (scopes.contains(scope)) {
1548 QString inheritenceCycle;
1549 for (
const auto &seen : std::as_const(scopes)) {
1550 inheritenceCycle.append(seen->baseTypeName());
1551 inheritenceCycle.append(QLatin1String(
" -> "));
1553 inheritenceCycle.append(scopes.first()->baseTypeName());
1555 const QString message = QStringLiteral(
"%1 is part of an inheritance cycle: %2")
1556 .arg(scope->internalName(), inheritenceCycle);
1557 m_logger->log(message, qmlInheritanceCycle, scope->sourceLocation());
1558 originalScope->clearBaseType();
1559 originalScope->setBaseTypeError(message);
1563 scopes.append(scope);
1565 const auto newScope = scope->baseType();
1566 if (newScope.isNull()) {
1567 const QString error = scope->baseTypeError();
1568 const QString name = scope->baseTypeName();
1569 if (!error.isEmpty()) {
1570 m_logger->log(error, qmlImport, scope->sourceLocation(),
true,
true);
1571 }
else if (!name.isEmpty() && !m_unresolvedTypes.hasSeen(scope)
1572 && !m_logger->isDisabled()) {
1574 name +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports,
1575 qmlImport, scope->sourceLocation(),
true,
true,
1576 QQmlJSUtils::didYouMean(scope->baseTypeName(),
1577 m_rootScopeImports.types().keys(),
1578 scope->sourceLocation()));
1586void QQmlJSImportVisitor::checkDeprecation(
const QQmlJSScope::ConstPtr &originalScope)
1588 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1589 for (
const QQmlJSAnnotation &annotation : scope->annotations()) {
1590 if (annotation.isDeprecation()) {
1591 QQQmlJSDeprecation deprecation = annotation.deprecation();
1594 QStringLiteral(
"Type \"%1\" is deprecated").arg(scope->internalName());
1596 if (!deprecation.reason.isEmpty())
1597 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1599 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1605void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope)
1609 if (checkCustomParser(scope))
1612 auto children = scope->childScopes();
1613 while (!children.isEmpty()) {
1614 auto childScope = children.takeFirst();
1615 const auto type = childScope->scopeType();
1617 case QQmlSA::ScopeType::GroupedPropertyScope:
1618 case QQmlSA::ScopeType::AttachedPropertyScope:
1619 if (!childScope->baseType()) {
1620 m_logger->log(QStringLiteral(
"unknown %1 property scope %2.")
1621 .arg(type == QQmlSA::ScopeType::GroupedPropertyScope
1622 ? QStringLiteral(
"grouped")
1623 : QStringLiteral(
"attached"),
1624 childScope->internalName()),
1625 qmlUnqualified, childScope->sourceLocation());
1627 children.append(childScope->childScopes());
1635bool QQmlJSImportVisitor::checkCustomParser(
const QQmlJSScope::ConstPtr &scope)
1637 return scope->isInCustomParserParent();
1640void QQmlJSImportVisitor::flushPendingSignalParameters()
1642 const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
1643 for (
const QString ¶meter : handler.signalParameters) {
1644 safeInsertJSIdentifier(m_currentScope, parameter,
1645 { QQmlJSScope::JavaScriptIdentifier::Injected,
1646 m_pendingSignalHandler, std::nullopt,
false });
1648 m_pendingSignalHandler = QQmlJS::SourceLocation();
1652
1653
1654
1655
1656
1657
1658QQmlJSMetaMethod::RelativeFunctionIndex
1659QQmlJSImportVisitor::addFunctionOrExpression(
const QQmlJSScope::ConstPtr &scope,
1660 const QString &name)
1662 auto &array = m_functionsAndExpressions[scope];
1663 array.emplaceBack(name);
1670 for (
const auto &function : std::as_const(m_functionStack))
1671 m_innerFunctions[function]++;
1672 m_functionStack.push({ scope, name });
1674 return QQmlJSMetaMethod::RelativeFunctionIndex {
int(array.size() - 1) };
1678
1679
1680
1681
1682
1683
1684
1685
1686void QQmlJSImportVisitor::forgetFunctionExpression(
const QString &name)
1688 auto nameToVerify = name.isEmpty() ? u"<anon>"_s : name;
1689 Q_UNUSED(nameToVerify);
1690 Q_ASSERT(!m_functionStack.isEmpty());
1691 Q_ASSERT(m_functionStack.top().name == nameToVerify);
1692 m_functionStack.pop();
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
1708 const QQmlJSScope::Ptr &scope,
int count)
const
1710 const auto suitableScope = [](
const QQmlJSScope::Ptr &scope) {
1711 const auto type = scope->scopeType();
1712 return type == QQmlSA::ScopeType::QMLScope
1713 || type == QQmlSA::ScopeType::GroupedPropertyScope
1714 || type == QQmlSA::ScopeType::AttachedPropertyScope;
1717 if (!suitableScope(scope))
1720 auto it = m_functionsAndExpressions.constFind(scope);
1721 if (it == m_functionsAndExpressions.cend())
1724 const auto &functionsAndExpressions = *it;
1725 for (
const QString &functionOrExpression : functionsAndExpressions) {
1726 scope->addOwnRuntimeFunctionIndex(
1727 static_cast<QQmlJSMetaMethod::AbsoluteFunctionIndex>(count));
1744 count += m_innerFunctions.value({ scope, functionOrExpression }, 0);
1750void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument()
const
1753 const auto synthesize = [&](
const QQmlJSScope::Ptr ¤t) {
1754 count = synthesizeCompilationUnitRuntimeFunctionIndices(current, count);
1756 QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, synthesize);
1759bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
1761 if (m_pendingSignalHandler.isValid()) {
1762 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope, u"signalhandler"_s,
1763 ast->firstSourceLocation());
1764 flushPendingSignalParameters();
1769void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
1771 if (m_currentScope->scopeType() == QQmlSA::ScopeType::SignalHandlerFunctionScope) {
1778 const QQmlJS::SourceLocation &srcLocation);
1781 QQmlJSLogger *logger)
1783 QStringView namespaceName{ superType };
1784 namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
1785 logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
1787 qmlUncreatableType, location,
true,
true);
1790bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
1792 const QString superType = buildName(definition->qualifiedTypeNameId);
1794 const bool isRoot = !rootScopeIsValid();
1795 Q_ASSERT(!superType.isEmpty());
1800 const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
1801 const bool looksLikeGroupedProperty = superType.front().isLower();
1803 if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
1804 logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
1808 if (!looksLikeGroupedProperty) {
1810 enterEnvironment(QQmlSA::ScopeType::QMLScope, superType,
1811 definition->firstSourceLocation());
1813 enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
1814 definition->firstSourceLocation());
1815 m_currentScope->setIsRootFileComponentFlag(
true);
1816 m_currentScope->setIsSingleton(m_rootIsSingleton);
1819 const QTypeRevision revision = m_currentScope->baseTypeRevision();
1820 if (
auto base = m_currentScope->baseType(); base) {
1821 if (isRoot && base->internalName() == u"QQmlComponent") {
1822 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1823 definition->qualifiedTypeNameId->identifierToken,
true,
true);
1825 if (base->isSingleton() && m_currentScope->isComposite()) {
1826 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1827 m_currentScope->baseTypeName()),
1828 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1831 }
else if (!base->isCreatable()) {
1833 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1834 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1838 if (m_nextIsInlineComponent) {
1839 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1840 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1841 m_currentScope->setIsInlineComponent(
true);
1842 m_currentScope->setInlineComponentName(name);
1843 m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
1844 m_rootScopeImports.setType(name, { m_currentScope, revision });
1845 m_nextIsInlineComponent =
false;
1848 addDefaultProperties();
1849 Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope);
1850 m_qmlTypes.append(m_currentScope);
1852 m_objectDefinitionScopes << m_currentScope;
1854 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType,
1855 definition->firstSourceLocation());
1856 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType,
1857 definition->firstSourceLocation()));
1858 QQmlJSScope::resolveTypes(
1859 m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
1862 m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
1867void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
1869 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
1873bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
1875 if (!std::holds_alternative<RootDocumentNameType>(m_currentRootName)) {
1876 m_logger->log(u"Nested inline components are not supported"_s, qmlSyntax,
1877 component->firstSourceLocation());
1881 const auto it = m_seenInlineComponents.constFind(component->name);
1882 if (it != m_seenInlineComponents.cend()) {
1883 m_logger->log(
"Duplicate inline component '%1'"_L1.arg(it.key()),
1884 qmlDuplicateInlineComponent, component->firstSourceLocation());
1885 m_logger->log(
"Note: previous component named '%1' here"_L1.arg(it.key()),
1886 qmlDuplicateInlineComponent, it.value(),
true,
true, {}, {},
1887 component->firstSourceLocation().startLine);
1889 m_seenInlineComponents[component->name] = component->firstSourceLocation();
1892 m_nextIsInlineComponent =
true;
1893 m_currentRootName = component->name.toString();
1897void QQmlJSImportVisitor::endVisit(UiInlineComponent *component)
1899 m_currentRootName = RootDocumentNameType();
1900 if (m_nextIsInlineComponent) {
1901 m_logger->log(u"Inline component declaration must be followed by a typename"_s,
1902 qmlSyntax, component->firstSourceLocation());
1904 m_nextIsInlineComponent =
false;
1907bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
1909 switch (publicMember->type) {
1910 case UiPublicMember::Signal: {
1911 if (m_currentScope->ownMethods().contains(publicMember->name.toString())) {
1912 m_logger->log(QStringLiteral(
"Duplicated signal name \"%1\".").arg(
1913 publicMember->name.toString()), qmlDuplicatedName,
1914 publicMember->firstSourceLocation());
1916 UiParameterList *param = publicMember->parameters;
1917 QQmlJSMetaMethod method;
1918 method.setMethodType(QQmlJSMetaMethodType::Signal);
1919 method.setReturnTypeName(QStringLiteral(
"void"));
1920 method.setMethodName(publicMember->name.toString());
1921 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1922 publicMember->lastSourceLocation()));
1924 method.addParameter(
1925 QQmlJSMetaParameter(
1926 param->name.toString(),
1927 param->type ? param->type->toString() : QString()
1929 param = param->next;
1931 m_currentScope->addOwnMethod(method);
1934 case UiPublicMember::Property: {
1935 if (m_currentScope->ownProperties().contains(publicMember->name.toString())) {
1936 m_logger->log(QStringLiteral(
"Duplicated property name \"%1\".").arg(
1937 publicMember->name.toString()), qmlDuplicatedName,
1938 publicMember->firstSourceLocation());
1940 QString typeName = buildName(publicMember->memberType);
1941 if (typeName.contains(u'.') && typeName.front().isLower()) {
1942 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1946 const bool isAlias = (typeName == u"alias"_s);
1948 auto tryParseAlias = [&]() {
1950 if (!publicMember->statement) {
1951 m_logger->log(QStringLiteral(
"Invalid alias expression - an initalizer is needed."),
1952 qmlSyntax, publicMember->memberType->firstSourceLocation());
1955 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1956 auto node = expression ? expression->expression :
nullptr;
1957 auto fex = cast<FieldMemberExpression *>(node);
1960 aliasExpr.prepend(u'.' + fex->name.toString());
1961 fex = cast<FieldMemberExpression *>(node);
1964 if (
const auto idExpression = cast<IdentifierExpression *>(node)) {
1965 aliasExpr.prepend(idExpression->name.toString());
1969 m_logger->log(QStringLiteral(
"Invalid alias expression. Only IDs and field "
1970 "member expressions can be aliased."),
1971 qmlSyntax, publicMember->statement->firstSourceLocation());
1976 if (m_rootScopeImports.hasType(typeName)
1977 && !m_rootScopeImports.type(typeName).scope.isNull()) {
1978 if (m_importTypeLocationMap.contains(typeName))
1979 m_usedTypes.insert(typeName);
1982 QQmlJSMetaProperty prop;
1983 prop.setPropertyName(publicMember->name.toString());
1984 prop.setIsList(publicMember->typeModifier == QLatin1String(
"list"));
1985 prop.setIsWritable(!publicMember->isReadonly());
1986 prop.setIsFinal(publicMember->isFinal());
1987 prop.setAliasExpression(aliasExpr);
1988 prop.setSourceLocation(
1989 combine(publicMember->firstSourceLocation(), publicMember->colonToken));
1991 isAlias ? QQmlJSScope::ConstPtr() : m_rootScopeImports.type(typeName).scope;
1993 prop.setType(prop.isList() ? type->listType() : type);
1994 const QString internalName = type->internalName();
1995 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
1996 }
else if (!isAlias) {
1997 m_pendingPropertyTypes << PendingPropertyType { m_currentScope, prop.propertyName(),
1998 publicMember->firstSourceLocation() };
1999 prop.setTypeName(typeName);
2001 prop.setAnnotations(parseAnnotations(publicMember->annotations));
2002 if (publicMember->isDefaultMember())
2003 m_currentScope->setOwnDefaultPropertyName(prop.propertyName());
2004 prop.setIndex(m_currentScope->ownProperties().size());
2005 m_currentScope->insertPropertyIdentifier(prop);
2006 if (publicMember->isRequired())
2007 m_currentScope->setPropertyLocallyRequired(prop.propertyName(),
true);
2009 BindingExpressionParseResult parseResult = BindingExpressionParseResult::Invalid;
2013 parseBindingExpression(publicMember->name.toString(), publicMember->statement,
2019 if (parseResult == BindingExpressionParseResult::Script) {
2020 Q_ASSERT(!m_savedBindingOuterScope);
2021 m_savedBindingOuterScope = m_currentScope;
2022 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope, QStringLiteral(
"binding"),
2023 publicMember->statement->firstSourceLocation());
2033void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
2035 if (m_savedBindingOuterScope) {
2036 m_currentScope = m_savedBindingOuterScope;
2037 m_savedBindingOuterScope = {};
2039 forgetFunctionExpression(publicMember->name.toString());
2043bool QQmlJSImportVisitor::visit(UiRequired *required)
2045 const QString name = required->name.toString();
2047 m_requiredProperties << RequiredProperty { m_currentScope, name,
2048 required->firstSourceLocation() };
2050 m_currentScope->setPropertyLocallyRequired(name,
true);
2054void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
2056 using namespace QQmlJS::AST;
2057 auto name = fexpr->name.toString();
2058 if (!name.isEmpty()) {
2059 QQmlJSMetaMethod method(name);
2060 method.setMethodType(QQmlJSMetaMethodType::Method);
2061 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
2063 if (!m_pendingMethodAnnotations.isEmpty()) {
2064 method.setAnnotations(m_pendingMethodAnnotations);
2065 m_pendingMethodAnnotations.clear();
2069 const bool parseTypes = m_scopesById.signaturesAreEnforced();
2071 bool formalsFullyTyped = parseTypes;
2072 bool anyFormalTyped =
false;
2073 PendingMethodTypeAnnotations pending{ m_currentScope, name, {} };
2076 for (
auto formals = fexpr->formals; formals; formals = formals->next) {
2077 PatternElement *e = formals->element;
2080 if (e->typeAnnotation && (e->bindingTarget || e->initializer))
2081 m_logger->log(
"Type annotations on default parameters are not supported"_L1,
2083 combine(e->firstSourceLocation(), e->lastSourceLocation()));
2086 if (
const auto *formals = parseTypes ? fexpr->formals :
nullptr) {
2087 const auto parameters = formals->formals();
2088 for (
const auto ¶meter : parameters) {
2089 const QString type = parameter.typeAnnotation
2090 ? parameter.typeAnnotation->type->toString()
2092 if (type.isEmpty()) {
2093 formalsFullyTyped =
false;
2094 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral(
"var")));
2095 pending.locations.emplace_back();
2097 anyFormalTyped =
true;
2098 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
2099 pending.locations.append(
2100 combine(parameter.typeAnnotation->firstSourceLocation(),
2101 parameter.typeAnnotation->lastSourceLocation()));
2107 method.setIsJavaScriptFunction(!formalsFullyTyped);
2113 if (parseTypes && fexpr->typeAnnotation) {
2114 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
2115 pending.locations.append(combine(fexpr->typeAnnotation->firstSourceLocation(),
2116 fexpr->typeAnnotation->lastSourceLocation()));
2117 }
else if (anyFormalTyped) {
2118 method.setReturnTypeName(QStringLiteral(
"void"));
2120 method.setReturnTypeName(QStringLiteral(
"var"));
2123 const auto &locs = pending.locations;
2124 if (std::any_of(locs.cbegin(), locs.cend(), [](
const auto &loc) {
return loc.isValid(); }))
2125 m_pendingMethodTypeAnnotations << pending;
2127 method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
2128 m_currentScope->addOwnMethod(method);
2130 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
2132 const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
2133 ? fexpr->identifierToken
2134 : fexpr->functionToken;
2135 safeInsertJSIdentifier(m_currentScope, name,
2136 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2137 functionLocation, method.returnTypeName(),
2140 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
2142 addFunctionOrExpression(m_currentScope, QStringLiteral(
"<anon>"));
2143 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral(
"<anon>"),
2144 fexpr->firstSourceLocation());
2148bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
2150 visitFunctionExpressionHelper(fexpr);
2154void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
2156 forgetFunctionExpression(fexpr->name.toString());
2160bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
2162 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
2166bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
2168 if (!fdecl->name.isEmpty()) {
2169 const QString name = fdecl->name.toString();
2170 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
2171 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
2172 fdecl->identifierToken);
2173 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
2174 previousDeclaration->location);
2177 visitFunctionExpressionHelper(fdecl);
2181void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
2183 forgetFunctionExpression(fdecl->name.toString());
2187bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
2189 QQmlJSMetaProperty prop;
2190 prop.setPropertyName(ast->name.toString());
2191 m_currentScope->addOwnProperty(prop);
2192 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2193 ast->firstSourceLocation());
2197void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
2203 QQmlJS::AST::ArgumentList *args)
2205 QStringView contextString;
2206 QStringView mainString;
2207 QStringView commentString;
2208 auto registerContextString = [&](QStringView string) {
2209 contextString = string;
2212 auto registerMainString = [&](QStringView string) {
2213 mainString = string;
2216 auto registerCommentString = [&](QStringView string) {
2217 commentString = string;
2220 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
2221 QV4::CompiledData::TranslationData data) {
2222 if (type == QV4::CompiledData::Binding::Type_Translation) {
2223 binding.setTranslation(mainString, commentString, contextString, data.number);
2224 }
else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
2225 binding.setTranslationId(mainString, data.number);
2227 binding.setStringLiteral(mainString);
2230 QmlIR::tryGeneratingTranslationBindingBase(
2232 registerMainString, registerCommentString, registerContextString, finalizeBinding);
2235QQmlJSImportVisitor::BindingExpressionParseResult
2236QQmlJSImportVisitor::parseBindingExpression(
2237 const QString &name,
const QQmlJS::AST::Statement *statement,
2238 const UiPublicMember *associatedPropertyDefinition)
2240 if (statement ==
nullptr)
2241 return BindingExpressionParseResult::Invalid;
2243 const auto *exprStatement = cast<
const ExpressionStatement *>(statement);
2245 if (exprStatement ==
nullptr) {
2246 QQmlJS::SourceLocation location = statement->firstSourceLocation();
2248 if (
const auto *block = cast<
const Block *>(statement); block && block->statements) {
2249 location = block->statements->firstSourceLocation();
2252 QQmlJSMetaPropertyBinding binding(location, name);
2253 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2254 QQmlSA::ScriptBindingKind::PropertyBinding, ScriptValue_Function);
2255 m_bindings.append(UnfinishedBinding {
2257 [binding = std::move(binding)]() {
return binding; }
2259 return BindingExpressionParseResult::Script;
2262 auto expr = exprStatement->expression;
2263 QQmlJSMetaPropertyBinding binding(
2264 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
2267 ScriptBindingValueType scriptBindingValuetype = ScriptValue_Unknown;
2269 switch (expr->kind) {
2270 case Node::Kind_TrueLiteral:
2271 binding.setBoolLiteral(
true);
2273 case Node::Kind_FalseLiteral:
2274 binding.setBoolLiteral(
false);
2276 case Node::Kind_NullExpression:
2277 binding.setNullLiteral();
2279 case Node::Kind_IdentifierExpression: {
2280 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
2282 if (idExpr->name == u"undefined")
2283 scriptBindingValuetype = ScriptValue_Undefined;
2286 case Node::Kind_FunctionDeclaration:
2287 case Node::Kind_FunctionExpression:
2288 case Node::Kind_Block: {
2289 scriptBindingValuetype = ScriptValue_Function;
2292 case Node::Kind_NumericLiteral:
2293 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
2295 case Node::Kind_StringLiteral:
2296 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
2298 case Node::Kind_RegExpLiteral:
2299 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
2301 case Node::Kind_TemplateLiteral: {
2302 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
2303 Q_ASSERT(templateLit);
2304 if (templateLit->hasNoSubstitution) {
2305 binding.setStringLiteral(templateLit->value);
2307 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2308 QQmlSA::ScriptBindingKind::PropertyBinding);
2309 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
2310 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
2311 expression->accept(
this);
2317 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
2318 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
2319 binding.setNumberLiteral(-lit->value);
2320 }
else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
2321 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
2322 handleTranslationBinding(binding, base->name, call->arguments);
2327 if (!binding.isValid()) {
2329 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2330 QQmlSA::ScriptBindingKind::PropertyBinding,
2331 scriptBindingValuetype);
2333 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
2336 if (binding.bindingType() == QQmlSA::BindingType::Translation
2337 || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
2338 return BindingExpressionParseResult::Translation;
2340 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
2341 return BindingExpressionParseResult::Script;
2343 if (associatedPropertyDefinition)
2344 handleLiteralBinding(binding, associatedPropertyDefinition);
2346 return BindingExpressionParseResult::Literal;
2349bool QQmlJSImportVisitor::isImportPrefix(QString prefix)
const
2351 if (prefix.isEmpty() || !prefix.front().isUpper())
2354 return m_rootScopeImports.isNullType(prefix);
2357void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
2359 if (m_currentScope->scopeType() != QQmlJSScope::ScopeType::QMLScope) {
2360 m_logger->log(u"id declarations are only allowed in objects"_s, qmlSyntax,
2361 scriptBinding->statement->firstSourceLocation());
2364 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
2366 m_logger->log(u"id must be followed by an identifier"_s, qmlSyntax,
2367 scriptBinding->statement->firstSourceLocation());
2370 const QString name = [&]() {
2371 if (
const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2372 return idExpression->name.toString();
2373 else if (
const auto *idString = cast<StringLiteral *>(statement->expression)) {
2374 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2375 idString->firstSourceLocation());
2376 return idString->value.toString();
2378 m_logger->log(u"Failed to parse id"_s, qmlSyntax,
2379 statement->expression->firstSourceLocation());
2383 if (!name.isEmpty() && !name.front().isLower() && name.front() != u'_') {
2384 m_logger->log(u"Id must start with a lower case letter or an '_'"_s, qmlSyntax,
2385 statement->expression->firstSourceLocation());
2388 m_currentScope->setIdSourceLocation(combine(scriptBinding->statement->firstSourceLocation(),
2389 scriptBinding->statement->lastSourceLocation()));
2390 if (m_scopesById.existsAnywhereInDocument(name)) {
2393 breakInheritanceCycles(m_currentScope);
2394 m_scopesById.possibleScopes(
2395 name, m_currentScope, Default,
2396 [&](
const QQmlJSScope::ConstPtr &otherScopeWithID,
2397 QQmlJSScopesById::Confidence confidence) {
2399 Q_UNUSED(confidence);
2401 auto otherLocation = otherScopeWithID->sourceLocation();
2405 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2406 name, QString::number(otherLocation.startLine),
2407 QString::number(otherLocation.startColumn)),
2408 qmlSyntaxDuplicateIds,
2409 scriptBinding->firstSourceLocation());
2410 return QQmlJSScopesById::CallbackResult::ContinueSearch;
2413 if (!name.isEmpty())
2414 m_scopesById.insert(name, m_currentScope);
2417void QQmlJSImportVisitor::handleLiteralBinding(
const QQmlJSMetaPropertyBinding &binding,
2418 const UiPublicMember *associatedPropertyDefinition)
2422 Q_UNUSED(associatedPropertyDefinition);
2426
2427
2428
2429
2430
2433 const QQmlJS::SourceLocation &srcLocation)
2435 const auto createBinding = [=]() {
2436 const QQmlJSScope::ScopeType type = scope->scopeType();
2443 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2444 const bool alreadyHasBinding =
std::any_of(propertyBindings.first, propertyBindings.second,
2445 [&](
const QQmlJSMetaPropertyBinding &binding) {
2446 return binding.bindingType() == bindingType;
2448 if (alreadyHasBinding)
2449 return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
2452 if (type == QQmlSA::ScopeType::GroupedPropertyScope)
2453 binding.setGroupBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2455 binding.setAttachedBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2458 return { scope->parentScope(), createBinding };
2461bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
2463 Q_ASSERT(!m_savedBindingOuterScope);
2464 Q_ASSERT(!m_thisScriptBindingIsJavaScript);
2465 m_savedBindingOuterScope = m_currentScope;
2466 const auto id = scriptBinding->qualifiedId;
2467 if (!id->next && id->name == QLatin1String(
"id")) {
2468 handleIdDeclaration(scriptBinding);
2475 for (; group->next; group = group->next) {
2476 const QString name = group->name.toString();
2480 if (group == id && isImportPrefix(name)) {
2481 prefix = name + u'.';
2485 const bool isAttachedProperty = name.front().isUpper();
2486 if (isAttachedProperty) {
2488 enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
2489 group->firstSourceLocation());
2492 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
2493 group->firstSourceLocation());
2495 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
2496 group->firstSourceLocation()));
2501 const auto name = group->name.toString();
2505 const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
2507 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2508 m_propertyBindings[m_currentScope].append(
2509 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2511 auto result = parseBindingExpression(name, scriptBinding->statement);
2512 m_thisScriptBindingIsJavaScript = (result == BindingExpressionParseResult::Script);
2514 const auto statement = scriptBinding->statement;
2515 QStringList signalParameters;
2517 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2518 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2519 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2520 signalParameters << formal->element->bindingIdentifier.toString();
2524 QQmlJSMetaMethod scopeSignal;
2525 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2526 if (!methods.isEmpty())
2527 scopeSignal = methods[0];
2529 const auto firstSourceLocation = statement->firstSourceLocation();
2530 bool hasMultilineStatementBody =
2531 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2532 m_pendingSignalHandler = firstSourceLocation;
2533 m_signalHandlers.insert(firstSourceLocation,
2534 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2538 const auto index = addFunctionOrExpression(m_currentScope, name);
2539 const auto createBinding = [
2541 scope = m_currentScope,
2542 signalName = *signal,
2545 firstSourceLocation,
2546 groupLocation = group->firstSourceLocation(),
2547 signalParameters]() {
2549 Q_ASSERT(scope->isFullyResolved());
2550 QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
2551 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2552 if (!methods.isEmpty()) {
2553 kind = QQmlSA::ScriptBindingKind::SignalHandler;
2554 checkSignal(scope, groupLocation, name, signalParameters);
2555 }
else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2556 kind = QQmlSA::ScriptBindingKind::ChangeHandler;
2557 checkSignal(scope, groupLocation, name, signalParameters);
2558 }
else if (scope->hasProperty(name)) {
2561 kind = QQmlSA::ScriptBindingKind::PropertyBinding;
2562 m_signalHandlers.remove(firstSourceLocation);
2565 checkSignal(scope, groupLocation, name, signalParameters);
2568 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2569 binding.setScriptBinding(index, kind, ScriptValue_Function);
2572 m_bindings.append(UnfinishedBinding { m_currentScope, createBinding });
2573 m_thisScriptBindingIsJavaScript =
true;
2579 while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
2580 || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
2585 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope,
2587 scriptBinding->statement->firstSourceLocation());
2589 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope,
2591 scriptBinding->statement->firstSourceLocation());
2597void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
2599 if (m_savedBindingOuterScope) {
2600 m_currentScope = m_savedBindingOuterScope;
2601 m_savedBindingOuterScope = {};
2607 if (m_thisScriptBindingIsJavaScript) {
2608 m_thisScriptBindingIsJavaScript =
false;
2609 Q_ASSERT(!m_functionStack.isEmpty());
2610 m_functionStack.pop();
2614bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
2616 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2617 arrayBinding->firstSourceLocation());
2618 m_currentScope->setIsArrayScope(
true);
2625void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
2632 const auto children = m_currentScope->childScopes();
2633 const auto propertyName = getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope);
2636 if (checkCustomParser(m_currentScope)) {
2643 for (
auto element = arrayBinding->members; element; element = element->next, ++i) {
2644 const auto &type = children[i];
2645 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2646 m_logger->log(u"Declaring an object which is not an Qml object"
2647 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2650 m_pendingPropertyObjectBindings
2651 << PendingPropertyObjectBinding { m_currentScope, type, propertyName,
2652 element->firstSourceLocation(),
false };
2653 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2654 binding.setObject(getScopeName(type, QQmlSA::ScopeType::QMLScope),
2655 QQmlJSScope::ConstPtr(type));
2656 m_bindings.append(UnfinishedBinding {
2658 [binding = std::move(binding)]() {
return binding; },
2659 QQmlJSScope::ListPropertyTarget
2664bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
2666 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2667 qmlEnum.setIsQml(
true);
2668 qmlEnum.setLineNumber(uied->enumToken.startLine);
2669 for (
const auto *member = uied->members; member; member = member->next) {
2670 qmlEnum.addKey(member->member.toString());
2671 qmlEnum.addValue(
int(member->value));
2673 m_currentScope->addOwnEnumeration(qmlEnum);
2677void QQmlJSImportVisitor::addImportWithLocation(
2678 const QString &name,
const QQmlJS::SourceLocation &loc,
bool hadWarnings)
2680 if (m_importTypeLocationMap.contains(name)
2681 && m_importTypeLocationMap.values(name).contains(loc)) {
2685 m_importTypeLocationMap.insert(name, loc);
2690 if (!hadWarnings && loc.isValid())
2691 m_importLocations.insert(loc);
2694QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromHost(
2695 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2697 QFileInfo fileInfo(path);
2698 if (!fileInfo.exists()) {
2699 m_logger->log(
"File or directory you are trying to import does not exist: %1."_L1.arg(path),
2700 qmlImport, location);
2704 if (fileInfo.isFile()) {
2705 const auto scope = m_importer->importFile(path);
2706 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2707 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
2708 addImportWithLocation(actualPrefix, location,
false);
2712 if (fileInfo.isDir()) {
2713 auto scopes = m_importer->importDirectory(path, prefix);
2714 const auto types = scopes.types();
2715 const auto warnings = scopes.warnings();
2716 m_rootScopeImports.add(std::move(scopes));
2717 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2718 addImportWithLocation(*it, location, !warnings.isEmpty());
2723 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2725 qmlImport, location);
2729QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromQrc(
2730 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2732 Q_ASSERT(path.startsWith(u':'));
2733 const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper();
2737 const auto pathNoColon = QStringView(path).mid(1);
2738 if (mapper->isFile(pathNoColon)) {
2739 const auto entry = m_importer->resourceFileMapper()->entry(
2740 QQmlJSResourceFileMapper::resourceFileFilter(pathNoColon.toString()));
2741 const auto scope = m_importer->importFile(entry.filePath);
2742 const QString actualPrefix =
2743 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2744 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
2745 addImportWithLocation(actualPrefix, location,
false);
2749 auto scopes = m_importer->importDirectory(path, prefix);
2750 const auto types = scopes.types();
2751 const auto warnings = scopes.warnings();
2752 m_rootScopeImports.add(std::move(scopes));
2753 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2754 addImportWithLocation(*it, location, !warnings.isEmpty());
2758bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
2761 QString prefix = QLatin1String(
"");
2762 if (import->asToken.isValid()) {
2763 prefix += import->importId;
2764 if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
2765 m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
2767 qmlImport, import->importIdToken,
true,
true);
2769 m_seenModuleQualifiers.append(prefix);
2772 const QString filename = import->fileName.toString();
2773 if (!filename.isEmpty()) {
2774 const QUrl url(filename);
2775 const QString scheme = url.scheme();
2776 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2777 if (scheme ==
""_L1) {
2778 QFileInfo fileInfo(url.path());
2779 QString absolute = fileInfo.isRelative()
2780 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2782 auto warnings = absolute.startsWith(u':')
2783 ? importFromQrc(absolute, prefix, importLocation)
2784 : importFromHost(absolute, prefix, importLocation);
2785 processImportWarnings(
"path \"%1\""_L1.arg(url.path()), warnings, importLocation);
2787 }
else if (scheme ==
"file"_L1) {
2788 auto warnings = importFromHost(url.path(), prefix, importLocation);
2789 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2791 }
else if (scheme ==
"qrc"_L1) {
2792 auto warnings = importFromQrc(
":"_L1 + url.path(), prefix, importLocation);
2793 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2796 m_logger->log(
"Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2797 qmlImport, import->firstSourceLocation());
2801 const QString path = buildName(import->importUri);
2803 QStringList staticModulesProvided;
2805 auto imported = m_importer->importModule(
2806 path, prefix, import->version ? import->version->version : QTypeRevision(),
2807 &staticModulesProvided);
2808 const auto types = imported.types();
2809 const auto warnings = imported.warnings();
2810 m_rootScopeImports.add(std::move(imported));
2811 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2812 addImportWithLocation(*it, import->firstSourceLocation(), !warnings.isEmpty());
2814 if (prefix.isEmpty()) {
2815 for (
const QString &staticModule : std::as_const(staticModulesProvided)) {
2817 if (path != staticModule && m_importStaticModuleLocationMap.contains(staticModule))
2820 m_importStaticModuleLocationMap[staticModule] = import->firstSourceLocation();
2824 processImportWarnings(
2825 QStringLiteral(
"module \"%1\"").arg(path), warnings, import->firstSourceLocation());
2829#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
2831void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2833 for (
const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2838void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2840 assign(pragma->value);
2844bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
2846 if (pragma->name == u"Strict"_s) {
2851 if (!m_logger->wasCategoryChanged(qmlCompiler)) {
2853 m_logger->setCategoryLevel(qmlCompiler, QtWarningMsg);
2854 m_logger->setCategoryIgnored(qmlCompiler,
false);
2856 }
else if (pragma->name == u"Singleton") {
2857 m_rootIsSingleton =
true;
2858 }
else if (pragma->name == u"ComponentBehavior") {
2859 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2860 if (value == u"Bound") {
2861 m_scopesById.setComponentsAreBound(
true);
2862 }
else if (value == u"Unbound") {
2863 m_scopesById.setComponentsAreBound(
false);
2865 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2866 qmlSyntax, pragma->firstSourceLocation());
2869 }
else if (pragma->name == u"FunctionSignatureBehavior") {
2870 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2871 if (value == u"Enforced") {
2872 m_scopesById.setSignaturesAreEnforced(
true);
2873 }
else if (value == u"Ignored") {
2874 m_scopesById.setSignaturesAreEnforced(
false);
2877 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2878 qmlSyntax, pragma->firstSourceLocation());
2881 }
else if (pragma->name == u"ValueTypeBehavior") {
2882 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2883 if (value == u"Copy") {
2885 }
else if (value == u"Reference") {
2887 }
else if (value == u"Addressable") {
2888 m_scopesById.setValueTypesAreAddressable(
true);
2889 }
else if (value == u"Inaddressable") {
2890 m_scopesById.setValueTypesAreAddressable(
false);
2892 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2893 qmlSyntax, pragma->firstSourceLocation());
2901void QQmlJSImportVisitor::throwRecursionDepthError()
2903 m_logger->log(QStringLiteral(
"Maximum statement or expression depth exceeded"),
2904 qmlRecursionDepthErrors, QQmlJS::SourceLocation());
2907bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
2909 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2910 ast->firstSourceLocation());
2914void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
2919bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
2921 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"forloop"),
2922 ast->firstSourceLocation());
2926void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
2931bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
2933 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"foreachloop"),
2934 ast->firstSourceLocation());
2938void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
2943bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
2945 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"block"),
2946 ast->firstSourceLocation());
2948 if (m_pendingSignalHandler.isValid())
2949 flushPendingSignalParameters();
2954void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
2959bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
2961 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"case"),
2962 ast->firstSourceLocation());
2966void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
2971bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
2973 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"catch"),
2974 catchStatement->firstSourceLocation());
2975 safeInsertJSIdentifier(m_currentScope,
2976 catchStatement->patternElement->bindingIdentifier.toString(),
2977 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2978 catchStatement->patternElement->firstSourceLocation(), std::nullopt,
2979 catchStatement->patternElement->scope == QQmlJS::AST::VariableScope::Const });
2983void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
2988bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
2990 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"with"),
2991 ast->firstSourceLocation());
2993 m_logger->log(QStringLiteral(
"with statements are strongly discouraged in QML "
2994 "and might cause false positives when analysing unqualified "
2996 qmlWith, ast->firstSourceLocation());
3001void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
3006bool QQmlJSImportVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
3009 std::optional<QString> typeName;
3010 if (TypeAnnotation *annotation = vdl->declaration->typeAnnotation)
3011 if (Type *type = annotation->type)
3012 typeName = type->toString();
3014 using Kind = QQmlJSScope::JavaScriptIdentifier::Kind;
3015 const Kind kind = (vdl->declaration->scope == QQmlJS::AST::VariableScope::Var)
3016 ? Kind::FunctionScoped
3017 : Kind::LexicalScoped;
3018 const QString name = vdl->declaration->bindingIdentifier.toString();
3019 const QQmlJS::SourceLocation location = vdl->declaration->firstSourceLocation();
3020 if (kind == Kind::LexicalScoped) {
3021 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
3022 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
3024 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
3025 previousDeclaration->location);
3029 const bool isConst = vdl->declaration->scope == QQmlJS::AST::VariableScope::Const;
3031 if (!safeInsertJSIdentifier(m_currentScope, name, { kind, location, typeName, isConst }))
3038bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
3040 const auto &boundedNames = fpl->boundNames();
3041 for (
auto const &boundName : boundedNames) {
3043 std::optional<QString> typeName;
3044 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
3045 if (Type *type = annotation->type)
3046 typeName = type->toString();
3047 safeInsertJSIdentifier(m_currentScope, boundName.id,
3048 { QQmlJSScope::JavaScriptIdentifier::Parameter,
3049 boundName.location, typeName,
false });
3054bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
3058 Q_ASSERT(uiob->qualifiedTypeNameId);
3060 bool needsResolution =
false;
3061 int scopesEnteredCounter = 0;
3063 const QString typeName = buildName(uiob->qualifiedTypeNameId);
3064 if (typeName.front().isLower() && typeName.contains(u'.')) {
3065 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
3069 for (
auto group = uiob->qualifiedId; group->next; group = group->next) {
3070 const QString idName = group->name.toString();
3072 if (idName.isEmpty())
3075 if (group == uiob->qualifiedId && isImportPrefix(idName)) {
3076 prefix = idName + u'.';
3080 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3081 : QQmlSA::ScopeType::GroupedPropertyScope;
3084 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3086 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + idName,
3087 group->firstSourceLocation()));
3089 ++scopesEnteredCounter;
3090 needsResolution = needsResolution || !exists;
3095 for (
int i=0; i < scopesEnteredCounter; ++i) {
3100 if (needsResolution) {
3101 QQmlJSScope::resolveTypes(
3102 m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3105 enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
3106 uiob->qualifiedTypeNameId->identifierToken);
3108 m_qmlTypes.append(m_currentScope);
3109 m_objectBindingScopes << m_currentScope;
3113void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
3115 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3117 const QQmlJSScope::Ptr childScope = m_currentScope;
3120 auto group = uiob->qualifiedId;
3121 int scopesEnteredCounter = 0;
3124 for (; group->next; group = group->next) {
3125 const QString idName = group->name.toString();
3127 if (idName.isEmpty())
3130 if (group == uiob->qualifiedId && isImportPrefix(idName)) {
3131 prefix = idName + u'.';
3135 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3136 : QQmlSA::ScopeType::GroupedPropertyScope;
3138 [[maybe_unused]]
bool exists =
3139 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3141 scopesEnteredCounter++;
3149 const QString propertyName = group->name.toString();
3151 if (m_currentScope->isNameDeferred(propertyName)) {
3152 bool foundIds =
false;
3153 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
3155 while (!childScopes.isEmpty()) {
3156 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
3157 m_scopesById.possibleIds(
3158 scope, scope, Default,
3159 [&](
const QString &id, QQmlJSScopesById::Confidence confidence) {
3162 Q_UNUSED(confidence);
3164 return QQmlJSScopesById::CallbackResult::StopSearch;
3167 childScopes << scope->childScopes();
3172 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
3174 qmlDeferredPropertyId, uiob->firstSourceLocation());
3178 if (checkCustomParser(m_currentScope)) {
3182 m_pendingPropertyObjectBindings
3183 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
3184 uiob->firstSourceLocation(), uiob->hasOnToken };
3186 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
3187 if (uiob->hasOnToken) {
3188 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
3189 binding.setInterceptor(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3190 QQmlJSScope::ConstPtr(childScope));
3192 binding.setValueSource(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3193 QQmlJSScope::ConstPtr(childScope));
3196 binding.setObject(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3197 QQmlJSScope::ConstPtr(childScope));
3199 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
3202 for (
int i = 0; i < scopesEnteredCounter; ++i)
3206bool QQmlJSImportVisitor::visit(ExportDeclaration *)
3208 Q_ASSERT(rootScopeIsValid());
3209 Q_ASSERT(m_exportedRootScope != m_globalScope);
3210 Q_ASSERT(m_currentScope == m_globalScope);
3211 m_currentScope = m_exportedRootScope;
3215void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
3217 Q_ASSERT(rootScopeIsValid());
3218 m_currentScope = m_exportedRootScope->parentScope();
3219 Q_ASSERT(m_currentScope == m_globalScope);
3222bool QQmlJSImportVisitor::visit(ESModule *module)
3224 Q_ASSERT(!rootScopeIsValid());
3225 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"module"),
3226 module->firstSourceLocation());
3227 m_currentScope->setIsScript(
true);
3228 importBaseModules();
3233void QQmlJSImportVisitor::endVisit(ESModule *)
3235 QQmlJSScope::resolveTypes(
3236 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3239bool QQmlJSImportVisitor::visit(Program *program)
3241 Q_ASSERT(m_globalScope == m_currentScope);
3242 Q_ASSERT(!rootScopeIsValid());
3243 enterRootScope(QQmlSA::ScopeType::JSFunctionScope, u"script"_s, program->firstSourceLocation());
3244 m_exportedRootScope->setIsScript(
true);
3245 importBaseModules();
3249void QQmlJSImportVisitor::endVisit(Program *)
3251 QQmlJSScope::resolveTypes(
3252 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3255void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
3259 const QString name = fieldMember->name.toString();
3260 if (m_importTypeLocationMap.contains(name)) {
3261 const QQmlJSImportedScope type = m_rootScopeImports.type(name);
3262 if (type.scope.isNull()) {
3263 if (m_rootScopeImports.hasType(name))
3264 m_usedTypes.insert(name);
3265 }
else if (!type.scope->ownAttachedTypeName().isEmpty()) {
3266 m_usedTypes.insert(name);
3271bool QQmlJSImportVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
3273 const QString name = idexp->name.toString();
3274 if (m_importTypeLocationMap.contains(name)) {
3275 m_usedTypes.insert(name);
3281bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
3284 if (element->isVariableDeclaration()) {
3285 QQmlJS::AST::BoundNames names;
3286 element->boundNames(&names);
3287 for (
const auto &name : std::as_const(names)) {
3288 std::optional<QString> typeName;
3289 if (TypeAnnotation *annotation = name.typeAnnotation.data())
3290 if (Type *type = annotation->type)
3291 typeName = type->toString();
3292 const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
3294 { (element->scope == QQmlJS::AST::VariableScope::Var)
3295 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
3296 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
3297 name.location, typeName,
3298 element->scope == QQmlJS::AST::VariableScope::Const });
3307bool QQmlJSImportVisitor::visit(IfStatement *statement)
3309 if (BinaryExpression *binary = cast<BinaryExpression *>(statement->expression)) {
3310 if (binary->op == QSOperator::Assign) {
3312 "Assignment in condition: did you meant to use \"===\" or \"==\" instead of \"=\"?"_L1,
3313 qmlAssignmentInCondition, binary->operatorToken);