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 QString msg = property.typeName() +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports;
840 if (property.typeName() ==
"list"_L1)
841 msg +=
" list is not a type. It requires an element type argument (eg. list<int>)"_L1;
842 m_logger->log(msg, qmlImport, type.location);
847void QQmlJSImportVisitor::processMethodTypes()
849 const auto isEnumUsedAsType = [&](QStringView typeName,
const QQmlJS::SourceLocation &loc) {
850 if (typeName ==
"enum"_L1) {
851 m_logger->log(
"QML does not have an `enum` type. Use the enum's underlying type "
852 "(int or double)."_L1,
853 qmlEnumsAreNotTypes, loc);
857 const auto split = typeName.tokenize(u'.').toContainer<QVarLengthArray<QStringView, 4>>();
858 if (split.size() != 2)
861 const QStringView scopeName = split[0];
862 const QStringView enumName = split[1];
864 if (
auto scope = QQmlJSScope::findType(scopeName.toString(),
865 m_rootScopeImports.contextualTypes()).scope) {
866 if (scope->enumeration(enumName.toString()).isValid()) {
867 m_logger->log(
"QML enumerations are not types. Use underlying type "
868 "(int or double) instead."_L1,
869 qmlEnumsAreNotTypes, loc);
876 for (
const auto &method : std::as_const(m_pendingMethodTypeAnnotations)) {
877 for (
auto [it, end] = method.scope->mutableOwnMethodsRange(method.methodName); it != end; ++it) {
878 const auto [parameterBegin, parameterEnd] = it->mutableParametersRange();
879 for (
auto parameter = parameterBegin; parameter != parameterEnd; ++parameter) {
880 const int parameterIndex = parameter - parameterBegin;
881 if (isEnumUsedAsType(parameter->typeName(), method.locations[parameterIndex]))
883 if (
const auto parameterType = QQmlJSScope::findType(
884 parameter->typeName(), m_rootScopeImports.contextualTypes()).scope) {
885 parameter->setType({ parameterType });
888 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
889 .arg(parameter->typeName(), parameter->name(), it->methodName()),
890 qmlUnresolvedType, method.locations[parameter - parameterBegin]);
894 if (isEnumUsedAsType(it->returnTypeName(), method.locations.last()))
896 if (
const auto returnType = QQmlJSScope::findType(
897 it->returnTypeName(), m_rootScopeImports.contextualTypes()).scope) {
898 it->setReturnType({ returnType });
900 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
901 it->returnTypeName(), it->methodName()),
902 qmlUnresolvedType, method.locations.last());
910
911
912
913
914
915
916
920 for (QStringView propertyName: possiblyGroupedProperty.tokenize(u".")) {
921 property = scope->property(propertyName.toString());
922 if (property.isValid())
923 scope = property.type();
930void QQmlJSImportVisitor::processPropertyBindingObjects()
932 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundLiterals;
940 QSet<std::pair<QQmlJSScope::Ptr, QString>> visited;
941 for (
const PendingPropertyObjectBinding &objectBinding :
942 std::as_const(m_pendingPropertyObjectBindings)) {
944 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
945 if (visited.contains(uniqueBindingId))
947 visited.insert(uniqueBindingId);
949 auto [existingBindingsBegin, existingBindingsEnd] =
950 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
951 const bool hasLiteralBindings =
952 std::any_of(existingBindingsBegin, existingBindingsEnd,
953 [](
const QQmlJSMetaPropertyBinding &x) {
return x.hasLiteral(); });
954 if (hasLiteralBindings)
955 foundLiterals.insert(uniqueBindingId);
959 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundObjects;
960 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundInterceptors;
961 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundValueSources;
963 for (
const PendingPropertyObjectBinding &objectBinding :
964 std::as_const(m_pendingPropertyObjectBindings)) {
965 const QString propertyName = objectBinding.name;
966 QQmlJSScope::Ptr childScope = objectBinding.childScope;
968 const auto assignToUnknownProperty = [&]() {
971 childScope->setAssignedToUnknownProperty(
true);
975 if (!checkTypeResolved(objectBinding.scope)) {
976 assignToUnknownProperty();
980 QQmlJSMetaProperty property = resolveProperty(propertyName, objectBinding.scope);
982 if (!property.isValid()) {
983 warnMissingPropertyForBinding(propertyName, objectBinding.location);
986 const auto handleUnresolvedProperty = [&](
const QQmlJSScope::ConstPtr &) {
988 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
989 "missing an import.")
991 .arg(property.typeName()),
992 qmlUnresolvedType, objectBinding.location);
995 if (property.type().isNull()) {
996 assignToUnknownProperty();
997 handleUnresolvedProperty(property.type());
1002 if (!checkTypeResolved(property.type(), handleUnresolvedProperty)) {
1003 assignToUnknownProperty();
1005 }
else if (!checkTypeResolved(childScope)) {
1009 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
1010 m_logger->log(QStringLiteral(
"Cannot assign object of type %1 to %2")
1011 .arg(getScopeName(childScope, QQmlSA::ScopeType::QMLScope))
1012 .arg(property.typeName()),
1013 qmlIncompatibleType, childScope->sourceLocation());
1017 childScope->setIsWrappedInImplicitComponent(
1018 causesImplicitComponentWrapping(property, childScope));
1021 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
1022 const QString typeName = getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
1024 auto isConditionalBinding = [&]() ->
bool {
1026
1027
1028
1029
1030 return childScope->hasOwnPropertyBindings(u"enabled"_s)
1031 || childScope->hasOwnPropertyBindings(u"when"_s)
1032 || childScope->hasOwnPropertyBindings(u"running"_s);
1035 if (objectBinding.onToken) {
1036 if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueInterceptor"))) {
1037 if (foundInterceptors.contains(uniqueBindingId)) {
1038 if (!isConditionalBinding()) {
1039 m_logger->log(QStringLiteral(
"Duplicate interceptor on property \"%1\"")
1041 qmlDuplicatePropertyBinding, objectBinding.location);
1044 foundInterceptors.insert(uniqueBindingId);
1046 }
else if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueSource"))) {
1047 if (foundValueSources.contains(uniqueBindingId)) {
1048 if (!isConditionalBinding()) {
1049 m_logger->log(QStringLiteral(
"Duplicate value source on property \"%1\"")
1051 qmlDuplicatePropertyBinding, objectBinding.location);
1053 }
else if (foundObjects.contains(uniqueBindingId)
1054 || foundLiterals.contains(uniqueBindingId)) {
1055 if (!isConditionalBinding()) {
1056 m_logger->log(QStringLiteral(
"Cannot combine value source and binding on "
1059 qmlDuplicatePropertyBinding, objectBinding.location);
1062 foundValueSources.insert(uniqueBindingId);
1065 m_logger->log(QStringLiteral(
"On-binding for property \"%1\" has wrong type \"%2\"")
1068 qmlIncompatibleType, objectBinding.location);
1071 if (foundValueSources.contains(uniqueBindingId)) {
1072 if (!isConditionalBinding()) {
1074 QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
1076 qmlDuplicatePropertyBinding, objectBinding.location);
1079 foundObjects.insert(uniqueBindingId);
1087 QList<QQmlJSScope::ConstPtr> descendants;
1088 std::vector<QQmlJSScope::ConstPtr> toVisit;
1090 toVisit.push_back(scope);
1091 while (!toVisit.empty()) {
1092 const QQmlJSScope::ConstPtr s = toVisit.back();
1098 toVisit.insert(toVisit.end(), s->childScopesBegin(), s->childScopesEnd());
1105void QQmlJSImportVisitor::populatePropertyAliases()
1107 for (
const auto &alias : std::as_const(m_aliasDefinitions)) {
1108 const auto &[aliasScope, aliasName] = alias;
1109 if (aliasScope.isNull())
1112 auto property = aliasScope->ownProperty(aliasName);
1113 if (!property.isValid() || !property.aliasTargetScope())
1116 Property target(property.aliasTargetScope(), property.aliasTargetName());
1119 m_propertyAliases[target].append(alias);
1120 property = target.scope->property(target.name);
1121 target = Property(property.aliasTargetScope(), property.aliasTargetName());
1122 }
while (property.isAlias());
1126void QQmlJSImportVisitor::checkRequiredProperties()
1128 for (
const auto &required : std::as_const(m_requiredProperties)) {
1129 if (!required.scope->hasProperty(required.name)) {
1131 QStringLiteral(
"Property \"%1\" was marked as required but does not exist.")
1132 .arg(required.name),
1133 qmlRequired, required.location);
1137 const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
1138 const auto isInComponent = [&](
const QQmlJSScope::ConstPtr &requiredScope) {
1139 for (
auto s = requiredScope; s; s = s->parentScope()) {
1140 if (s->isWrappedInImplicitComponent() || s->baseType() == compType)
1146 const auto requiredHasBinding = [](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1147 const QQmlJSScope::ConstPtr &owner,
1148 const QString &propName) {
1149 for (
const auto &scope : scopesToSearch) {
1150 if (scope->property(propName).isAlias())
1152 const auto &[begin, end] = scope->ownPropertyBindings(propName);
1153 for (
auto it = begin; it != end; ++it) {
1155 const bool isRelevantBinding = QQmlSA::isRegularBindingType(it->bindingType())
1156 || it->bindingType() == QQmlSA::BindingType::Interceptor
1157 || it->bindingType() == QQmlSA::BindingType::ValueSource;
1158 if (!isRelevantBinding)
1160 if (QQmlJSScope::ownerOfProperty(scope, propName).scope == owner)
1168 const auto requiredUsedInRootAlias = [&](
const QQmlJSScope::ConstPtr &defScope,
1169 const QQmlJSScope::ConstPtr &requiredScope,
1170 const QString &propName) {
1171 if (defScope->filePath() == requiredScope->filePath()) {
1172 QQmlJSScope::ConstPtr fileRootScope = requiredScope;
1173 while (fileRootScope->parentScope() != m_globalScope)
1174 fileRootScope = fileRootScope->parentScope();
1176 const auto &rootProperties = fileRootScope->ownProperties();
1177 for (
const auto &p : rootProperties) {
1178 if (p.isAlias() && p.aliasTargetScope() == requiredScope
1179 && p.aliasTargetName() == propName) {
1188 const auto requiredSetThroughAlias = [&](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1189 const QQmlJSScope::ConstPtr &requiredScope,
1190 const QString &propName) {
1191 const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
1192 const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
1193 for (
const auto &alias : propertyAliases) {
1194 for (
const auto &s : scopesToSearch) {
1195 if (s->hasOwnPropertyBindings(alias.name))
1202 const auto warn = [
this](
const QQmlJSScope::ConstPtr &prevRequiredScope,
1203 const QString &propName,
const QQmlJSScope::ConstPtr &defScope,
1204 const QQmlJSScope::ConstPtr &requiredScope,
1205 const QQmlJSScope::ConstPtr &descendant) {
1206 const auto &propertyScope = QQmlJSScope::ownerOfProperty(requiredScope, propName).scope;
1207 const QString propertyScopeName = !propertyScope.isNull()
1208 ? getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
1211 std::optional<QQmlJSFixSuggestion> suggestion;
1213 QString message = QStringLiteral(
"Component is missing required property %1 from %2")
1215 .arg(propertyScopeName);
1216 if (requiredScope != descendant) {
1217 const QString requiredScopeName = prevRequiredScope
1218 ? getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
1221 if (!prevRequiredScope.isNull()) {
1222 if (
auto sourceScope = prevRequiredScope->baseType()) {
1223 suggestion = QQmlJSFixSuggestion{
1224 "%1:%2:%3: Property marked as required in %4."_L1
1225 .arg(sourceScope->filePath())
1226 .arg(sourceScope->sourceLocation().startLine)
1227 .arg(sourceScope->sourceLocation().startColumn)
1228 .arg(requiredScopeName),
1229 sourceScope->sourceLocation()
1233 if (sourceScope->isComposite())
1234 suggestion->setFilename(sourceScope->filePath());
1237 message +=
" (marked as required by %1)"_L1.arg(requiredScopeName);
1241 m_logger->log(message, qmlRequired, defScope->sourceLocation(),
true,
true, suggestion);
1244 populatePropertyAliases();
1246 for (
const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
1247 if (defScope->isFileRootComponent() || defScope->isInlineComponent()
1248 || defScope->componentRootStatus() != QQmlJSScope::IsComponentRoot::No
1249 || defScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
1253 QVector<QQmlJSScope::ConstPtr> scopesToSearch;
1254 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
1255 const auto descendants = QList<QQmlJSScope::ConstPtr>()
1256 << scope << qmlScopeDescendants(scope);
1257 for (
const QQmlJSScope::ConstPtr &descendant : std::as_const(descendants)) {
1260 if (descendant != scope && descendant->isInlineComponent())
1262 scopesToSearch << descendant;
1263 const auto ownProperties = descendant->ownProperties();
1264 for (
auto propertyIt = ownProperties.constBegin();
1265 propertyIt != ownProperties.constEnd(); ++propertyIt) {
1266 const QString propName = propertyIt.key();
1267 if (descendant->hasOwnPropertyBindings(propName))
1270 QQmlJSScope::ConstPtr prevRequiredScope;
1271 for (
const QQmlJSScope::ConstPtr &requiredScope : std::as_const(scopesToSearch)) {
1272 if (isInComponent(requiredScope))
1275 if (!requiredScope->isPropertyLocallyRequired(propName)) {
1276 prevRequiredScope = requiredScope;
1280 if (requiredHasBinding(scopesToSearch, descendant, propName))
1283 if (requiredUsedInRootAlias(defScope, requiredScope, propName))
1286 if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
1289 warn(prevRequiredScope, propName, defScope, requiredScope, descendant);
1290 prevRequiredScope = requiredScope;
1298void QQmlJSImportVisitor::processPropertyBindings()
1300 for (
auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
1301 QQmlJSScope::Ptr scope = it.key();
1302 for (
auto &[visibilityScope, location, name] : it.value()) {
1303 if (!scope->hasProperty(name) && !m_logger->isDisabled()) {
1307 if (checkCustomParser(scope))
1311 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1313 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
1314 baseScope = baseScope->baseType()) {
1315 if (
auto suggestion = QQmlJSUtils::didYouMean(
1316 name, baseScope->ownProperties().keys(), location);
1317 suggestion.has_value()) {
1318 fixSuggestion = suggestion;
1323 warnMissingPropertyForBinding(name, location, fixSuggestion);
1327 const auto property = scope->property(name);
1328 if (!property.type()) {
1329 m_logger->log(QStringLiteral(
"No type found for property \"%1\". This may be due "
1330 "to a missing import statement or incomplete "
1333 qmlMissingType, location);
1336 const auto &annotations = property.annotations();
1338 const auto deprecationAnn =
1339 std::find_if(annotations.cbegin(), annotations.cend(),
1340 [](
const QQmlJSAnnotation &ann) {
return ann.isDeprecation(); });
1342 if (deprecationAnn != annotations.cend()) {
1343 const auto deprecation = deprecationAnn->deprecation();
1345 QString message = QStringLiteral(
"Binding on deprecated property \"%1\"")
1346 .arg(property.propertyName());
1348 if (!deprecation.reason.isEmpty())
1349 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1351 m_logger->log(message, qmlDeprecated, location);
1357void QQmlJSImportVisitor::checkSignal(
1358 const QQmlJSScope::ConstPtr &signalScope,
const QQmlJS::SourceLocation &location,
1359 const QString &handlerName,
const QStringList &handlerParameters)
1361 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1363 std::optional<QQmlJSMetaMethod> signalMethod;
1364 const auto setSignalMethod = [&](
const QQmlJSScope::ConstPtr &scope,
const QString &name) {
1365 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1366 if (!methods.isEmpty())
1367 signalMethod = methods[0];
1370 if (signal.has_value()) {
1371 if (signalScope->hasMethod(*signal)) {
1372 setSignalMethod(signalScope, *signal);
1373 }
else if (
auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1378 if (
auto notify = p->notify(); !notify.isEmpty()) {
1379 setSignalMethod(signalScope, notify);
1381 Q_ASSERT(!p->bindable().isEmpty());
1382 signalMethod = QQmlJSMetaMethod {};
1387 if (!signalMethod.has_value()) {
1392 if (signalScope->baseTypeName() == QStringLiteral(
"Connections")) {
1394 u"Implicitly defining \"%1\" as signal handler in Connections is deprecated. "
1395 u"Create a function instead: \"function %2(%3) { ... }\"."_s.arg(
1396 handlerName, handlerName, handlerParameters.join(u", ")),
1397 qmlUnqualified, location,
true,
true);
1401 auto baseType = QQmlJSScope::nonCompositeBaseType(signalScope);
1402 if (baseType && baseType->hasCustomParser())
1406 QStringLiteral(
"no matching signal found for handler \"%1\"").arg(handlerName),
1407 qmlUnqualified, location,
true,
true);
1411 const auto signalParameters = signalMethod->parameters();
1412 QHash<QString, qsizetype> parameterNameIndexes;
1414 for (
int i = 0, end = signalParameters.size(); i < end; i++) {
1415 auto &p = signalParameters[i];
1416 parameterNameIndexes[p.name()] = i;
1418 auto signalName = [&]() {
1420 return u" called %1"_s.arg(*signal);
1423 auto type = p.type();
1426 "Type %1 of parameter %2 in signal%3 was not found, but is required to compile "
1428 p.typeName(), p.name(), signalName(),
1429 handlerName, didYouAddAllImports),
1430 qmlSignalParameters, location);
1434 if (type->isComposite())
1442 auto parameterName = [&]() {
1443 if (p.name().isEmpty())
1445 return u" called %1"_s.arg(p.name());
1447 switch (type->accessSemantics()) {
1448 case QQmlJSScope::AccessSemantics::Reference:
1450 m_logger->log(QStringLiteral(
"Type %1 of parameter%2 in signal%3 should be "
1451 "passed by pointer to be able to compile %4. ")
1452 .arg(p.typeName(), parameterName(), signalName(),
1454 qmlSignalParameters, location);
1456 case QQmlJSScope::AccessSemantics::Value:
1457 case QQmlJSScope::AccessSemantics::Sequence:
1461 "Type %1 of parameter%2 in signal%3 should be passed by "
1462 "value or const reference to be able to compile %4. ")
1463 .arg(p.typeName(), parameterName(), signalName(),
1465 qmlSignalParameters, location);
1467 case QQmlJSScope::AccessSemantics::None:
1469 QStringLiteral(
"Type %1 of parameter%2 in signal%3 required by the "
1470 "compilation of %4 cannot be used. ")
1471 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1472 qmlSignalParameters, location);
1477 if (handlerParameters.size() > signalParameters.size()) {
1478 m_logger->log(QStringLiteral(
"Signal handler for \"%2\" has more formal"
1479 " parameters than the signal it handles.")
1481 qmlSignalParameters, location);
1485 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1486 const QStringView handlerParameter = handlerParameters.at(i);
1487 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1488 if (it == parameterNameIndexes.constEnd())
1490 const qsizetype j = *it;
1495 m_logger->log(QStringLiteral(
"Parameter %1 to signal handler for \"%2\""
1496 " is called \"%3\". The signal has a parameter"
1497 " of the same name in position %4.")
1499 .arg(handlerName, handlerParameter)
1501 qmlSignalParameters, location);
1505void QQmlJSImportVisitor::addDefaultProperties()
1507 QQmlJSScope::ConstPtr parentScope = m_currentScope->parentScope();
1508 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1509 || m_currentScope->isInlineComponent())
1512 m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
1514 if (checkCustomParser(parentScope))
1518
1519
1520
1521
1522
1523
1524
1525
1526
1528 parentScope = parentScope->baseType();
1530 const QString defaultPropertyName =
1531 parentScope ? parentScope->defaultPropertyName() : QString();
1533 if (defaultPropertyName.isEmpty())
1538 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1539 binding.setObject(getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
1540 QQmlJSScope::ConstPtr(m_currentScope));
1541 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() {
return binding; },
1542 QQmlJSScope::UnnamedPropertyTarget });
1545void QQmlJSImportVisitor::breakInheritanceCycles(
const QQmlJSScope::Ptr &originalScope)
1547 QList<QQmlJSScope::ConstPtr> scopes;
1548 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1549 if (scopes.contains(scope)) {
1550 QString inheritenceCycle;
1551 for (
const auto &seen : std::as_const(scopes)) {
1552 inheritenceCycle.append(seen->baseTypeName());
1553 inheritenceCycle.append(QLatin1String(
" -> "));
1555 inheritenceCycle.append(scopes.first()->baseTypeName());
1557 const QString message = QStringLiteral(
"%1 is part of an inheritance cycle: %2")
1558 .arg(scope->internalName(), inheritenceCycle);
1559 m_logger->log(message, qmlInheritanceCycle, scope->sourceLocation());
1560 originalScope->clearBaseType();
1561 originalScope->setBaseTypeError(message);
1565 scopes.append(scope);
1567 const auto newScope = scope->baseType();
1568 if (newScope.isNull()) {
1569 const QString error = scope->baseTypeError();
1570 const QString name = scope->baseTypeName();
1571 if (!error.isEmpty()) {
1572 m_logger->log(error, qmlImport, scope->sourceLocation(),
true,
true);
1573 }
else if (!name.isEmpty() && !m_unresolvedTypes.hasSeen(scope)
1574 && !m_logger->isDisabled()) {
1576 name +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports,
1577 qmlImport, scope->sourceLocation(),
true,
true,
1578 QQmlJSUtils::didYouMean(scope->baseTypeName(),
1579 m_rootScopeImports.types().keys(),
1580 scope->sourceLocation()));
1588void QQmlJSImportVisitor::checkDeprecation(
const QQmlJSScope::ConstPtr &originalScope)
1590 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1591 for (
const QQmlJSAnnotation &annotation : scope->annotations()) {
1592 if (annotation.isDeprecation()) {
1593 QQQmlJSDeprecation deprecation = annotation.deprecation();
1596 QStringLiteral(
"Type \"%1\" is deprecated").arg(scope->internalName());
1598 if (!deprecation.reason.isEmpty())
1599 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1601 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1607void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope)
1611 if (checkCustomParser(scope))
1614 auto children = scope->childScopes();
1615 while (!children.isEmpty()) {
1616 auto childScope = children.takeFirst();
1617 const auto type = childScope->scopeType();
1619 case QQmlSA::ScopeType::GroupedPropertyScope:
1620 case QQmlSA::ScopeType::AttachedPropertyScope:
1621 if (!childScope->baseType()) {
1622 m_logger->log(QStringLiteral(
"unknown %1 property scope %2.")
1623 .arg(type == QQmlSA::ScopeType::GroupedPropertyScope
1624 ? QStringLiteral(
"grouped")
1625 : QStringLiteral(
"attached"),
1626 childScope->internalName()),
1627 qmlUnqualified, childScope->sourceLocation());
1629 children.append(childScope->childScopes());
1637bool QQmlJSImportVisitor::checkCustomParser(
const QQmlJSScope::ConstPtr &scope)
1639 return scope->isInCustomParserParent();
1642void QQmlJSImportVisitor::flushPendingSignalParameters()
1644 const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
1645 for (
const QString ¶meter : handler.signalParameters) {
1646 safeInsertJSIdentifier(m_currentScope, parameter,
1647 { QQmlJSScope::JavaScriptIdentifier::Injected,
1648 m_pendingSignalHandler, std::nullopt,
false });
1650 m_pendingSignalHandler = QQmlJS::SourceLocation();
1654
1655
1656
1657
1658
1659
1660QQmlJSMetaMethod::RelativeFunctionIndex
1661QQmlJSImportVisitor::addFunctionOrExpression(
const QQmlJSScope::ConstPtr &scope,
1662 const QString &name)
1664 auto &array = m_functionsAndExpressions[scope];
1665 array.emplaceBack(name);
1672 for (
const auto &function : std::as_const(m_functionStack))
1673 m_innerFunctions[function]++;
1674 m_functionStack.push({ scope, name });
1676 return QQmlJSMetaMethod::RelativeFunctionIndex {
int(array.size() - 1) };
1680
1681
1682
1683
1684
1685
1686
1687
1688void QQmlJSImportVisitor::forgetFunctionExpression(
const QString &name)
1690 auto nameToVerify = name.isEmpty() ? u"<anon>"_s : name;
1691 Q_UNUSED(nameToVerify);
1692 Q_ASSERT(!m_functionStack.isEmpty());
1693 Q_ASSERT(m_functionStack.top().name == nameToVerify);
1694 m_functionStack.pop();
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices(
1710 const QQmlJSScope::Ptr &scope,
int count)
const
1712 const auto suitableScope = [](
const QQmlJSScope::Ptr &scope) {
1713 const auto type = scope->scopeType();
1714 return type == QQmlSA::ScopeType::QMLScope
1715 || type == QQmlSA::ScopeType::GroupedPropertyScope
1716 || type == QQmlSA::ScopeType::AttachedPropertyScope;
1719 if (!suitableScope(scope))
1722 auto it = m_functionsAndExpressions.constFind(scope);
1723 if (it == m_functionsAndExpressions.cend())
1726 const auto &functionsAndExpressions = *it;
1727 for (
const QString &functionOrExpression : functionsAndExpressions) {
1728 scope->addOwnRuntimeFunctionIndex(
1729 static_cast<QQmlJSMetaMethod::AbsoluteFunctionIndex>(count));
1746 count += m_innerFunctions.value({ scope, functionOrExpression }, 0);
1752void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument()
const
1755 const auto synthesize = [&](
const QQmlJSScope::Ptr ¤t) {
1756 count = synthesizeCompilationUnitRuntimeFunctionIndices(current, count);
1758 QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, synthesize);
1761bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
1763 if (m_pendingSignalHandler.isValid()) {
1764 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope, u"signalhandler"_s,
1765 ast->firstSourceLocation());
1766 flushPendingSignalParameters();
1771void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
1773 if (m_currentScope->scopeType() == QQmlSA::ScopeType::SignalHandlerFunctionScope) {
1780 const QQmlJS::SourceLocation &srcLocation);
1783 QQmlJSLogger *logger)
1785 QStringView namespaceName{ superType };
1786 namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
1787 logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
1789 qmlUncreatableType, location,
true,
true);
1792bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
1794 const QString superType = buildName(definition->qualifiedTypeNameId);
1796 const bool isRoot = !rootScopeIsValid();
1797 Q_ASSERT(!superType.isEmpty());
1802 const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
1803 const bool looksLikeGroupedProperty = superType.front().isLower();
1805 if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
1806 logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
1810 if (!looksLikeGroupedProperty) {
1812 enterEnvironment(QQmlSA::ScopeType::QMLScope, superType,
1813 definition->firstSourceLocation());
1815 enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
1816 definition->firstSourceLocation());
1817 m_currentScope->setIsRootFileComponentFlag(
true);
1818 m_currentScope->setIsSingleton(m_rootIsSingleton);
1821 const QTypeRevision revision = m_currentScope->baseTypeRevision();
1822 if (
auto base = m_currentScope->baseType(); base) {
1823 if (isRoot && base->internalName() == u"QQmlComponent") {
1824 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1825 definition->qualifiedTypeNameId->identifierToken,
true,
true);
1827 if (base->isSingleton() && m_currentScope->isComposite()) {
1828 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1829 m_currentScope->baseTypeName()),
1830 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1833 }
else if (!base->isCreatable()) {
1835 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1836 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1840 if (m_nextIsInlineComponent) {
1841 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1842 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1843 m_currentScope->setIsInlineComponent(
true);
1844 m_currentScope->setInlineComponentName(name);
1845 m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
1846 m_rootScopeImports.setType(name, { m_currentScope, revision });
1847 m_nextIsInlineComponent =
false;
1850 addDefaultProperties();
1851 Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope);
1852 m_qmlTypes.append(m_currentScope);
1854 m_objectDefinitionScopes << m_currentScope;
1856 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType,
1857 definition->firstSourceLocation());
1858 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType,
1859 definition->firstSourceLocation()));
1860 QQmlJSScope::resolveTypes(
1861 m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
1864 m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
1869void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
1871 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
1875bool QQmlJSImportVisitor::visit(UiInlineComponent *component)
1877 if (!std::holds_alternative<RootDocumentNameType>(m_currentRootName)) {
1878 m_logger->log(u"Nested inline components are not supported"_s, qmlSyntax,
1879 component->firstSourceLocation());
1883 const auto it = m_seenInlineComponents.constFind(component->name);
1884 if (it != m_seenInlineComponents.cend()) {
1885 m_logger->log(
"Duplicate inline component '%1'"_L1.arg(it.key()),
1886 qmlDuplicateInlineComponent, component->firstSourceLocation());
1887 m_logger->log(
"Note: previous component named '%1' here"_L1.arg(it.key()),
1888 qmlDuplicateInlineComponent, it.value(),
true,
true, {}, {},
1889 component->firstSourceLocation().startLine);
1891 m_seenInlineComponents[component->name] = component->firstSourceLocation();
1894 m_nextIsInlineComponent =
true;
1895 m_currentRootName = component->name.toString();
1899void QQmlJSImportVisitor::endVisit(UiInlineComponent *component)
1901 m_currentRootName = RootDocumentNameType();
1902 if (m_nextIsInlineComponent) {
1903 m_logger->log(u"Inline component declaration must be followed by a typename"_s,
1904 qmlSyntax, component->firstSourceLocation());
1906 m_nextIsInlineComponent =
false;
1909bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
1911 switch (publicMember->type) {
1912 case UiPublicMember::Signal: {
1913 if (m_currentScope->ownMethods().contains(publicMember->name.toString())) {
1914 m_logger->log(QStringLiteral(
"Duplicated signal name \"%1\".").arg(
1915 publicMember->name.toString()), qmlDuplicatedName,
1916 publicMember->firstSourceLocation());
1918 UiParameterList *param = publicMember->parameters;
1919 QQmlJSMetaMethod method;
1920 method.setMethodType(QQmlJSMetaMethodType::Signal);
1921 method.setReturnTypeName(QStringLiteral(
"void"));
1922 method.setMethodName(publicMember->name.toString());
1923 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1924 publicMember->lastSourceLocation()));
1926 method.addParameter(
1927 QQmlJSMetaParameter(
1928 param->name.toString(),
1929 param->type ? param->type->toString() : QString()
1931 param = param->next;
1933 m_currentScope->addOwnMethod(method);
1936 case UiPublicMember::Property: {
1937 if (m_currentScope->ownProperties().contains(publicMember->name.toString())) {
1938 m_logger->log(QStringLiteral(
"Duplicated property name \"%1\".").arg(
1939 publicMember->name.toString()), qmlDuplicatedName,
1940 publicMember->firstSourceLocation());
1942 QString typeName = buildName(publicMember->memberType);
1943 if (typeName.contains(u'.') && typeName.front().isLower()) {
1944 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1948 const bool isAlias = (typeName == u"alias"_s);
1950 auto tryParseAlias = [&]() {
1952 if (!publicMember->statement) {
1953 m_logger->log(QStringLiteral(
"Invalid alias expression - an initalizer is needed."),
1954 qmlSyntax, publicMember->memberType->firstSourceLocation());
1957 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1958 auto node = expression ? expression->expression :
nullptr;
1959 auto fex = cast<FieldMemberExpression *>(node);
1962 aliasExpr.prepend(u'.' + fex->name.toString());
1963 fex = cast<FieldMemberExpression *>(node);
1966 if (
const auto idExpression = cast<IdentifierExpression *>(node)) {
1967 aliasExpr.prepend(idExpression->name.toString());
1971 m_logger->log(QStringLiteral(
"Invalid alias expression. Only IDs and field "
1972 "member expressions can be aliased."),
1973 qmlSyntax, publicMember->statement->firstSourceLocation());
1978 if (m_rootScopeImports.hasType(typeName)
1979 && !m_rootScopeImports.type(typeName).scope.isNull()) {
1980 if (m_importTypeLocationMap.contains(typeName))
1981 m_usedTypes.insert(typeName);
1984 QQmlJSMetaProperty prop;
1985 prop.setPropertyName(publicMember->name.toString());
1986 prop.setIsList(publicMember->typeModifier == QLatin1String(
"list"));
1987 prop.setIsWritable(!publicMember->isReadonly());
1988 prop.setIsFinal(publicMember->isFinal());
1989 prop.setAliasExpression(aliasExpr);
1990 prop.setSourceLocation(
1991 combine(publicMember->firstSourceLocation(), publicMember->colonToken));
1993 isAlias ? QQmlJSScope::ConstPtr() : m_rootScopeImports.type(typeName).scope;
1995 prop.setType(prop.isList() ? type->listType() : type);
1996 const QString internalName = type->internalName();
1997 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
1998 }
else if (!isAlias) {
1999 m_pendingPropertyTypes << PendingPropertyType { m_currentScope, prop.propertyName(),
2000 publicMember->firstSourceLocation() };
2001 prop.setTypeName(typeName);
2003 prop.setAnnotations(parseAnnotations(publicMember->annotations));
2004 if (publicMember->isDefaultMember())
2005 m_currentScope->setOwnDefaultPropertyName(prop.propertyName());
2006 prop.setIndex(m_currentScope->ownProperties().size());
2007 m_currentScope->insertPropertyIdentifier(prop);
2008 if (publicMember->isRequired())
2009 m_currentScope->setPropertyLocallyRequired(prop.propertyName(),
true);
2011 BindingExpressionParseResult parseResult = BindingExpressionParseResult::Invalid;
2015 parseBindingExpression(publicMember->name.toString(), publicMember->statement,
2021 if (parseResult == BindingExpressionParseResult::Script) {
2022 Q_ASSERT(!m_savedBindingOuterScope);
2023 m_savedBindingOuterScope = m_currentScope;
2024 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope, QStringLiteral(
"binding"),
2025 publicMember->statement->firstSourceLocation());
2035void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
2037 if (m_savedBindingOuterScope) {
2038 m_currentScope = m_savedBindingOuterScope;
2039 m_savedBindingOuterScope = {};
2041 forgetFunctionExpression(publicMember->name.toString());
2045bool QQmlJSImportVisitor::visit(UiRequired *required)
2047 const QString name = required->name.toString();
2049 m_requiredProperties << RequiredProperty { m_currentScope, name,
2050 required->firstSourceLocation() };
2052 m_currentScope->setPropertyLocallyRequired(name,
true);
2056void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
2058 using namespace QQmlJS::AST;
2059 auto name = fexpr->name.toString();
2060 if (!name.isEmpty()) {
2061 QQmlJSMetaMethod method(name);
2062 method.setMethodType(QQmlJSMetaMethodType::Method);
2063 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
2065 if (!m_pendingMethodAnnotations.isEmpty()) {
2066 method.setAnnotations(m_pendingMethodAnnotations);
2067 m_pendingMethodAnnotations.clear();
2071 const bool parseTypes = m_scopesById.signaturesAreEnforced();
2073 bool formalsFullyTyped = parseTypes;
2074 bool anyFormalTyped =
false;
2075 PendingMethodTypeAnnotations pending{ m_currentScope, name, {} };
2078 for (
auto formals = fexpr->formals; formals; formals = formals->next) {
2079 PatternElement *e = formals->element;
2082 if (e->typeAnnotation && (e->bindingTarget || e->initializer))
2083 m_logger->log(
"Type annotations on default parameters are not supported"_L1,
2085 combine(e->firstSourceLocation(), e->lastSourceLocation()));
2088 if (
const auto *formals = parseTypes ? fexpr->formals :
nullptr) {
2089 const auto parameters = formals->formals();
2090 for (
const auto ¶meter : parameters) {
2091 const QString type = parameter.typeAnnotation
2092 ? parameter.typeAnnotation->type->toString()
2094 if (type.isEmpty()) {
2095 formalsFullyTyped =
false;
2096 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral(
"var")));
2097 pending.locations.emplace_back();
2099 anyFormalTyped =
true;
2100 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
2101 pending.locations.append(
2102 combine(parameter.typeAnnotation->firstSourceLocation(),
2103 parameter.typeAnnotation->lastSourceLocation()));
2109 method.setIsJavaScriptFunction(!formalsFullyTyped);
2115 if (parseTypes && fexpr->typeAnnotation) {
2116 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
2117 pending.locations.append(combine(fexpr->typeAnnotation->firstSourceLocation(),
2118 fexpr->typeAnnotation->lastSourceLocation()));
2119 }
else if (anyFormalTyped) {
2120 method.setReturnTypeName(QStringLiteral(
"void"));
2122 method.setReturnTypeName(QStringLiteral(
"var"));
2125 const auto &locs = pending.locations;
2126 if (std::any_of(locs.cbegin(), locs.cend(), [](
const auto &loc) {
return loc.isValid(); }))
2127 m_pendingMethodTypeAnnotations << pending;
2129 method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
2130 m_currentScope->addOwnMethod(method);
2132 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
2134 const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
2135 ? fexpr->identifierToken
2136 : fexpr->functionToken;
2137 safeInsertJSIdentifier(m_currentScope, name,
2138 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2139 functionLocation, method.returnTypeName(),
2142 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
2144 addFunctionOrExpression(m_currentScope, QStringLiteral(
"<anon>"));
2145 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral(
"<anon>"),
2146 fexpr->firstSourceLocation());
2150bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
2152 visitFunctionExpressionHelper(fexpr);
2156void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
2158 forgetFunctionExpression(fexpr->name.toString());
2162bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
2164 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
2168bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
2170 if (!fdecl->name.isEmpty()) {
2171 const QString name = fdecl->name.toString();
2172 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
2173 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
2174 fdecl->identifierToken);
2175 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
2176 previousDeclaration->location);
2179 visitFunctionExpressionHelper(fdecl);
2183void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
2185 forgetFunctionExpression(fdecl->name.toString());
2189bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
2191 QQmlJSMetaProperty prop;
2192 prop.setPropertyName(ast->name.toString());
2193 m_currentScope->addOwnProperty(prop);
2194 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2195 ast->firstSourceLocation());
2199void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
2205 QQmlJS::AST::ArgumentList *args)
2207 QStringView contextString;
2208 QStringView mainString;
2209 QStringView commentString;
2210 auto registerContextString = [&](QStringView string) {
2211 contextString = string;
2214 auto registerMainString = [&](QStringView string) {
2215 mainString = string;
2218 auto registerCommentString = [&](QStringView string) {
2219 commentString = string;
2222 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
2223 QV4::CompiledData::TranslationData data) {
2224 if (type == QV4::CompiledData::Binding::Type_Translation) {
2225 binding.setTranslation(mainString, commentString, contextString, data.number);
2226 }
else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
2227 binding.setTranslationId(mainString, data.number);
2229 binding.setStringLiteral(mainString);
2232 QmlIR::tryGeneratingTranslationBindingBase(
2234 registerMainString, registerCommentString, registerContextString, finalizeBinding);
2237QQmlJSImportVisitor::BindingExpressionParseResult
2238QQmlJSImportVisitor::parseBindingExpression(
2239 const QString &name,
const QQmlJS::AST::Statement *statement,
2240 const UiPublicMember *associatedPropertyDefinition)
2242 if (statement ==
nullptr)
2243 return BindingExpressionParseResult::Invalid;
2245 const auto *exprStatement = cast<
const ExpressionStatement *>(statement);
2247 if (exprStatement ==
nullptr) {
2248 QQmlJS::SourceLocation location = statement->firstSourceLocation();
2250 if (
const auto *block = cast<
const Block *>(statement); block && block->statements) {
2251 location = block->statements->firstSourceLocation();
2254 QQmlJSMetaPropertyBinding binding(location, name);
2255 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2256 QQmlSA::ScriptBindingKind::PropertyBinding, ScriptValue_Function);
2257 m_bindings.append(UnfinishedBinding {
2259 [binding = std::move(binding)]() {
return binding; }
2261 return BindingExpressionParseResult::Script;
2264 auto expr = exprStatement->expression;
2265 QQmlJSMetaPropertyBinding binding(
2266 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
2269 ScriptBindingValueType scriptBindingValuetype = ScriptValue_Unknown;
2271 switch (expr->kind) {
2272 case Node::Kind_TrueLiteral:
2273 binding.setBoolLiteral(
true);
2275 case Node::Kind_FalseLiteral:
2276 binding.setBoolLiteral(
false);
2278 case Node::Kind_NullExpression:
2279 binding.setNullLiteral();
2281 case Node::Kind_IdentifierExpression: {
2282 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
2284 if (idExpr->name == u"undefined")
2285 scriptBindingValuetype = ScriptValue_Undefined;
2288 case Node::Kind_FunctionDeclaration:
2289 case Node::Kind_FunctionExpression:
2290 case Node::Kind_Block: {
2291 scriptBindingValuetype = ScriptValue_Function;
2294 case Node::Kind_NumericLiteral:
2295 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
2297 case Node::Kind_StringLiteral:
2298 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
2300 case Node::Kind_RegExpLiteral:
2301 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
2303 case Node::Kind_TemplateLiteral: {
2304 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
2305 Q_ASSERT(templateLit);
2306 if (templateLit->hasNoSubstitution) {
2307 binding.setStringLiteral(templateLit->value);
2309 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2310 QQmlSA::ScriptBindingKind::PropertyBinding);
2311 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
2312 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
2313 expression->accept(
this);
2319 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
2320 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
2321 binding.setNumberLiteral(-lit->value);
2322 }
else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
2323 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
2324 handleTranslationBinding(binding, base->name, call->arguments);
2329 if (!binding.isValid()) {
2331 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2332 QQmlSA::ScriptBindingKind::PropertyBinding,
2333 scriptBindingValuetype);
2335 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
2338 if (binding.bindingType() == QQmlSA::BindingType::Translation
2339 || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
2340 return BindingExpressionParseResult::Translation;
2342 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
2343 return BindingExpressionParseResult::Script;
2345 if (associatedPropertyDefinition)
2346 handleLiteralBinding(binding, associatedPropertyDefinition);
2348 return BindingExpressionParseResult::Literal;
2351bool QQmlJSImportVisitor::isImportPrefix(QString prefix)
const
2353 if (prefix.isEmpty() || !prefix.front().isUpper())
2356 return m_rootScopeImports.isNullType(prefix);
2359void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
2361 if (m_currentScope->scopeType() != QQmlJSScope::ScopeType::QMLScope) {
2362 m_logger->log(u"id declarations are only allowed in objects"_s, qmlSyntax,
2363 scriptBinding->statement->firstSourceLocation());
2366 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
2368 m_logger->log(u"id must be followed by an identifier"_s, qmlSyntax,
2369 scriptBinding->statement->firstSourceLocation());
2372 const QString name = [&]() {
2373 if (
const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2374 return idExpression->name.toString();
2375 else if (
const auto *idString = cast<StringLiteral *>(statement->expression)) {
2376 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2377 idString->firstSourceLocation());
2378 return idString->value.toString();
2380 m_logger->log(u"Failed to parse id"_s, qmlSyntax,
2381 statement->expression->firstSourceLocation());
2385 if (!name.isEmpty() && !name.front().isLower() && name.front() != u'_') {
2386 m_logger->log(u"Id must start with a lower case letter or an '_'"_s, qmlSyntax,
2387 statement->expression->firstSourceLocation());
2390 m_currentScope->setIdSourceLocation(combine(scriptBinding->statement->firstSourceLocation(),
2391 scriptBinding->statement->lastSourceLocation()));
2392 if (m_scopesById.existsAnywhereInDocument(name)) {
2395 breakInheritanceCycles(m_currentScope);
2396 m_scopesById.possibleScopes(
2397 name, m_currentScope, Default,
2398 [&](
const QQmlJSScope::ConstPtr &otherScopeWithID,
2399 QQmlJSScopesById::Confidence confidence) {
2401 Q_UNUSED(confidence);
2403 auto otherLocation = otherScopeWithID->sourceLocation();
2407 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2408 name, QString::number(otherLocation.startLine),
2409 QString::number(otherLocation.startColumn)),
2410 qmlSyntaxDuplicateIds,
2411 scriptBinding->firstSourceLocation());
2412 return QQmlJSScopesById::CallbackResult::ContinueSearch;
2415 if (!name.isEmpty())
2416 m_scopesById.insert(name, m_currentScope);
2419void QQmlJSImportVisitor::handleLiteralBinding(
const QQmlJSMetaPropertyBinding &binding,
2420 const UiPublicMember *associatedPropertyDefinition)
2424 Q_UNUSED(associatedPropertyDefinition);
2428
2429
2430
2431
2432
2435 const QQmlJS::SourceLocation &srcLocation)
2437 const auto createBinding = [=]() {
2438 const QQmlJSScope::ScopeType type = scope->scopeType();
2445 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2446 const bool alreadyHasBinding =
std::any_of(propertyBindings.first, propertyBindings.second,
2447 [&](
const QQmlJSMetaPropertyBinding &binding) {
2448 return binding.bindingType() == bindingType;
2450 if (alreadyHasBinding)
2451 return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
2454 if (type == QQmlSA::ScopeType::GroupedPropertyScope)
2455 binding.setGroupBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2457 binding.setAttachedBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2460 return { scope->parentScope(), createBinding };
2463bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
2465 Q_ASSERT(!m_savedBindingOuterScope);
2466 Q_ASSERT(!m_thisScriptBindingIsJavaScript);
2467 m_savedBindingOuterScope = m_currentScope;
2468 const auto id = scriptBinding->qualifiedId;
2469 if (!id->next && id->name == QLatin1String(
"id")) {
2470 handleIdDeclaration(scriptBinding);
2477 for (; group->next; group = group->next) {
2478 const QString name = group->name.toString();
2482 if (group == id && isImportPrefix(name)) {
2483 prefix = name + u'.';
2487 const bool isAttachedProperty = name.front().isUpper();
2488 if (isAttachedProperty) {
2490 enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
2491 group->firstSourceLocation());
2494 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
2495 group->firstSourceLocation());
2497 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
2498 group->firstSourceLocation()));
2503 const auto name = group->name.toString();
2507 const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
2509 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2510 m_propertyBindings[m_currentScope].append(
2511 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2513 auto result = parseBindingExpression(name, scriptBinding->statement);
2514 m_thisScriptBindingIsJavaScript = (result == BindingExpressionParseResult::Script);
2516 const auto statement = scriptBinding->statement;
2517 QStringList signalParameters;
2519 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2520 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2521 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2522 signalParameters << formal->element->bindingIdentifier.toString();
2526 QQmlJSMetaMethod scopeSignal;
2527 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2528 if (!methods.isEmpty())
2529 scopeSignal = methods[0];
2531 const auto firstSourceLocation = statement->firstSourceLocation();
2532 bool hasMultilineStatementBody =
2533 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2534 m_pendingSignalHandler = firstSourceLocation;
2535 m_signalHandlers.insert(firstSourceLocation,
2536 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2540 const auto index = addFunctionOrExpression(m_currentScope, name);
2541 const auto createBinding = [
2543 scope = m_currentScope,
2544 signalName = *signal,
2547 firstSourceLocation,
2548 groupLocation = group->firstSourceLocation(),
2549 signalParameters]() {
2551 Q_ASSERT(scope->isFullyResolved());
2552 QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
2553 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2554 if (!methods.isEmpty()) {
2555 kind = QQmlSA::ScriptBindingKind::SignalHandler;
2556 checkSignal(scope, groupLocation, name, signalParameters);
2557 }
else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2558 kind = QQmlSA::ScriptBindingKind::ChangeHandler;
2559 checkSignal(scope, groupLocation, name, signalParameters);
2560 }
else if (scope->hasProperty(name)) {
2563 kind = QQmlSA::ScriptBindingKind::PropertyBinding;
2564 m_signalHandlers.remove(firstSourceLocation);
2567 checkSignal(scope, groupLocation, name, signalParameters);
2570 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2571 binding.setScriptBinding(index, kind, ScriptValue_Function);
2574 m_bindings.append(UnfinishedBinding { m_currentScope, createBinding });
2575 m_thisScriptBindingIsJavaScript =
true;
2581 while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
2582 || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
2587 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope,
2589 scriptBinding->statement->firstSourceLocation());
2591 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope,
2593 scriptBinding->statement->firstSourceLocation());
2599void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
2601 if (m_savedBindingOuterScope) {
2602 m_currentScope = m_savedBindingOuterScope;
2603 m_savedBindingOuterScope = {};
2609 if (m_thisScriptBindingIsJavaScript) {
2610 m_thisScriptBindingIsJavaScript =
false;
2611 Q_ASSERT(!m_functionStack.isEmpty());
2612 m_functionStack.pop();
2616bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
2618 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2619 arrayBinding->firstSourceLocation());
2620 m_currentScope->setIsArrayScope(
true);
2627void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
2634 const auto children = m_currentScope->childScopes();
2635 const auto propertyName = getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope);
2638 if (checkCustomParser(m_currentScope)) {
2645 for (
auto element = arrayBinding->members; element; element = element->next, ++i) {
2646 const auto &type = children[i];
2647 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2648 m_logger->log(u"Declaring an object which is not an Qml object"
2649 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2652 m_pendingPropertyObjectBindings
2653 << PendingPropertyObjectBinding { m_currentScope, type, propertyName,
2654 element->firstSourceLocation(),
false };
2655 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2656 binding.setObject(getScopeName(type, QQmlSA::ScopeType::QMLScope),
2657 QQmlJSScope::ConstPtr(type));
2658 m_bindings.append(UnfinishedBinding {
2660 [binding = std::move(binding)]() {
return binding; },
2661 QQmlJSScope::ListPropertyTarget
2666bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
2668 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2669 qmlEnum.setIsQml(
true);
2670 qmlEnum.setLineNumber(uied->enumToken.startLine);
2671 for (
const auto *member = uied->members; member; member = member->next) {
2672 qmlEnum.addKey(member->member.toString());
2673 qmlEnum.addValue(
int(member->value));
2675 m_currentScope->addOwnEnumeration(qmlEnum);
2679void QQmlJSImportVisitor::addImportWithLocation(
2680 const QString &name,
const QQmlJS::SourceLocation &loc,
bool hadWarnings)
2682 if (m_importTypeLocationMap.contains(name)
2683 && m_importTypeLocationMap.values(name).contains(loc)) {
2687 m_importTypeLocationMap.insert(name, loc);
2692 if (!hadWarnings && loc.isValid())
2693 m_importLocations.insert(loc);
2696QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromHost(
2697 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2699 QFileInfo fileInfo(path);
2700 if (!fileInfo.exists()) {
2701 m_logger->log(
"File or directory you are trying to import does not exist: %1."_L1.arg(path),
2702 qmlImport, location);
2706 if (fileInfo.isFile()) {
2707 const auto scope = m_importer->importFile(path);
2708 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2709 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
2710 addImportWithLocation(actualPrefix, location,
false);
2714 if (fileInfo.isDir()) {
2715 auto scopes = m_importer->importDirectory(path, prefix);
2716 const auto types = scopes.types();
2717 const auto warnings = scopes.warnings();
2718 m_rootScopeImports.add(std::move(scopes));
2719 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2720 addImportWithLocation(*it, location, !warnings.isEmpty());
2725 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2727 qmlImport, location);
2731QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromQrc(
2732 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2734 Q_ASSERT(path.startsWith(u':'));
2735 const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper();
2739 const auto pathNoColon = QStringView(path).mid(1);
2740 if (mapper->isFile(pathNoColon)) {
2741 const auto entry = m_importer->resourceFileMapper()->entry(
2742 QQmlJSResourceFileMapper::resourceFileFilter(pathNoColon.toString()));
2743 const auto scope = m_importer->importFile(entry.filePath);
2744 const QString actualPrefix =
2745 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2746 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
2747 addImportWithLocation(actualPrefix, location,
false);
2751 auto scopes = m_importer->importDirectory(path, prefix);
2752 const auto types = scopes.types();
2753 const auto warnings = scopes.warnings();
2754 m_rootScopeImports.add(std::move(scopes));
2755 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2756 addImportWithLocation(*it, location, !warnings.isEmpty());
2760bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
2763 QString prefix = QLatin1String(
"");
2764 if (import->asToken.isValid()) {
2765 prefix += import->importId;
2766 if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
2767 m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
2769 qmlImport, import->importIdToken,
true,
true);
2771 m_seenModuleQualifiers.append(prefix);
2774 const QString filename = import->fileName.toString();
2775 if (!filename.isEmpty()) {
2776 const QUrl url(filename);
2777 const QString scheme = url.scheme();
2778 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2779 if (scheme ==
""_L1) {
2780 QFileInfo fileInfo(url.path());
2781 QString absolute = fileInfo.isRelative()
2782 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2784 auto warnings = absolute.startsWith(u':')
2785 ? importFromQrc(absolute, prefix, importLocation)
2786 : importFromHost(absolute, prefix, importLocation);
2787 processImportWarnings(
"path \"%1\""_L1.arg(url.path()), warnings, importLocation);
2789 }
else if (scheme ==
"file"_L1) {
2790 auto warnings = importFromHost(url.path(), prefix, importLocation);
2791 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2793 }
else if (scheme ==
"qrc"_L1) {
2794 auto warnings = importFromQrc(
":"_L1 + url.path(), prefix, importLocation);
2795 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2798 m_logger->log(
"Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2799 qmlImport, import->firstSourceLocation());
2803 const QString path = buildName(import->importUri);
2805 QStringList staticModulesProvided;
2807 auto imported = m_importer->importModule(
2808 path, prefix, import->version ? import->version->version : QTypeRevision(),
2809 &staticModulesProvided);
2810 const auto types = imported.types();
2811 const auto warnings = imported.warnings();
2812 m_rootScopeImports.add(std::move(imported));
2813 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2814 addImportWithLocation(*it, import->firstSourceLocation(), !warnings.isEmpty());
2816 if (prefix.isEmpty()) {
2817 for (
const QString &staticModule : std::as_const(staticModulesProvided)) {
2819 if (path != staticModule && m_importStaticModuleLocationMap.contains(staticModule))
2822 m_importStaticModuleLocationMap[staticModule] = import->firstSourceLocation();
2826 processImportWarnings(
2827 QStringLiteral(
"module \"%1\"").arg(path), warnings, import->firstSourceLocation());
2831#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
2833void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2835 for (
const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2840void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2842 assign(pragma->value);
2846bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
2848 if (pragma->name == u"Strict"_s) {
2853 if (!m_logger->wasCategoryChanged(qmlCompiler)) {
2855 m_logger->setCategoryLevel(qmlCompiler, QtWarningMsg);
2856 m_logger->setCategoryIgnored(qmlCompiler,
false);
2858 }
else if (pragma->name == u"Singleton") {
2859 m_rootIsSingleton =
true;
2860 }
else if (pragma->name == u"ComponentBehavior") {
2861 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2862 if (value == u"Bound") {
2863 m_scopesById.setComponentsAreBound(
true);
2864 }
else if (value == u"Unbound") {
2865 m_scopesById.setComponentsAreBound(
false);
2867 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2868 qmlSyntax, pragma->firstSourceLocation());
2871 }
else if (pragma->name == u"FunctionSignatureBehavior") {
2872 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2873 if (value == u"Enforced") {
2874 m_scopesById.setSignaturesAreEnforced(
true);
2875 }
else if (value == u"Ignored") {
2876 m_scopesById.setSignaturesAreEnforced(
false);
2879 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2880 qmlSyntax, pragma->firstSourceLocation());
2883 }
else if (pragma->name == u"ValueTypeBehavior") {
2884 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2885 if (value == u"Copy") {
2887 }
else if (value == u"Reference") {
2889 }
else if (value == u"Addressable") {
2890 m_scopesById.setValueTypesAreAddressable(
true);
2891 }
else if (value == u"Inaddressable") {
2892 m_scopesById.setValueTypesAreAddressable(
false);
2894 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2895 qmlSyntax, pragma->firstSourceLocation());
2903void QQmlJSImportVisitor::throwRecursionDepthError()
2905 m_logger->log(QStringLiteral(
"Maximum statement or expression depth exceeded"),
2906 qmlRecursionDepthErrors, QQmlJS::SourceLocation());
2909bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
2911 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2912 ast->firstSourceLocation());
2916void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
2921bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
2923 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"forloop"),
2924 ast->firstSourceLocation());
2928void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
2933bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
2935 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"foreachloop"),
2936 ast->firstSourceLocation());
2940void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
2945bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
2947 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"block"),
2948 ast->firstSourceLocation());
2950 if (m_pendingSignalHandler.isValid())
2951 flushPendingSignalParameters();
2956void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
2961bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
2963 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"case"),
2964 ast->firstSourceLocation());
2968void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
2973bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
2975 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"catch"),
2976 catchStatement->firstSourceLocation());
2977 safeInsertJSIdentifier(m_currentScope,
2978 catchStatement->patternElement->bindingIdentifier.toString(),
2979 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2980 catchStatement->patternElement->firstSourceLocation(), std::nullopt,
2981 catchStatement->patternElement->scope == QQmlJS::AST::VariableScope::Const });
2985void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
2990bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
2992 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"with"),
2993 ast->firstSourceLocation());
2995 m_logger->log(QStringLiteral(
"with statements are strongly discouraged in QML "
2996 "and might cause false positives when analysing unqualified "
2998 qmlWith, ast->firstSourceLocation());
3003void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
3008bool QQmlJSImportVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
3011 std::optional<QString> typeName;
3012 if (TypeAnnotation *annotation = vdl->declaration->typeAnnotation)
3013 if (Type *type = annotation->type)
3014 typeName = type->toString();
3016 using Kind = QQmlJSScope::JavaScriptIdentifier::Kind;
3017 const Kind kind = (vdl->declaration->scope == QQmlJS::AST::VariableScope::Var)
3018 ? Kind::FunctionScoped
3019 : Kind::LexicalScoped;
3020 const QString name = vdl->declaration->bindingIdentifier.toString();
3021 const QQmlJS::SourceLocation location = vdl->declaration->firstSourceLocation();
3022 if (kind == Kind::LexicalScoped) {
3023 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
3024 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
3026 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
3027 previousDeclaration->location);
3031 const bool isConst = vdl->declaration->scope == QQmlJS::AST::VariableScope::Const;
3033 if (!safeInsertJSIdentifier(m_currentScope, name, { kind, location, typeName, isConst }))
3040bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
3042 const auto &boundedNames = fpl->boundNames();
3043 for (
auto const &boundName : boundedNames) {
3045 std::optional<QString> typeName;
3046 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
3047 if (Type *type = annotation->type)
3048 typeName = type->toString();
3049 safeInsertJSIdentifier(m_currentScope, boundName.id,
3050 { QQmlJSScope::JavaScriptIdentifier::Parameter,
3051 boundName.location, typeName,
false });
3056bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
3060 Q_ASSERT(uiob->qualifiedTypeNameId);
3062 bool needsResolution =
false;
3063 int scopesEnteredCounter = 0;
3065 const QString typeName = buildName(uiob->qualifiedTypeNameId);
3066 if (typeName.front().isLower() && typeName.contains(u'.')) {
3067 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
3071 for (
auto group = uiob->qualifiedId; group->next; group = group->next) {
3072 const QString idName = group->name.toString();
3074 if (idName.isEmpty())
3077 if (group == uiob->qualifiedId && isImportPrefix(idName)) {
3078 prefix = idName + u'.';
3082 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3083 : QQmlSA::ScopeType::GroupedPropertyScope;
3086 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3088 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + idName,
3089 group->firstSourceLocation()));
3091 ++scopesEnteredCounter;
3092 needsResolution = needsResolution || !exists;
3097 for (
int i=0; i < scopesEnteredCounter; ++i) {
3102 if (needsResolution) {
3103 QQmlJSScope::resolveTypes(
3104 m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3107 enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
3108 uiob->qualifiedTypeNameId->identifierToken);
3110 m_qmlTypes.append(m_currentScope);
3111 m_objectBindingScopes << m_currentScope;
3115void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
3117 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3119 const QQmlJSScope::Ptr childScope = m_currentScope;
3122 auto group = uiob->qualifiedId;
3123 int scopesEnteredCounter = 0;
3126 for (; group->next; group = group->next) {
3127 const QString idName = group->name.toString();
3129 if (idName.isEmpty())
3132 if (group == uiob->qualifiedId && isImportPrefix(idName)) {
3133 prefix = idName + u'.';
3137 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3138 : QQmlSA::ScopeType::GroupedPropertyScope;
3140 [[maybe_unused]]
bool exists =
3141 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3143 scopesEnteredCounter++;
3151 const QString propertyName = group->name.toString();
3153 if (m_currentScope->isNameDeferred(propertyName)) {
3154 bool foundIds =
false;
3155 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
3157 while (!childScopes.isEmpty()) {
3158 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
3159 m_scopesById.possibleIds(
3160 scope, scope, Default,
3161 [&](
const QString &id, QQmlJSScopesById::Confidence confidence) {
3164 Q_UNUSED(confidence);
3166 return QQmlJSScopesById::CallbackResult::StopSearch;
3169 childScopes << scope->childScopes();
3174 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
3176 qmlDeferredPropertyId, uiob->firstSourceLocation());
3180 if (checkCustomParser(m_currentScope)) {
3184 m_pendingPropertyObjectBindings
3185 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
3186 uiob->firstSourceLocation(), uiob->hasOnToken };
3188 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
3189 if (uiob->hasOnToken) {
3190 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
3191 binding.setInterceptor(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3192 QQmlJSScope::ConstPtr(childScope));
3194 binding.setValueSource(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3195 QQmlJSScope::ConstPtr(childScope));
3198 binding.setObject(getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3199 QQmlJSScope::ConstPtr(childScope));
3201 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
3204 for (
int i = 0; i < scopesEnteredCounter; ++i)
3208bool QQmlJSImportVisitor::visit(ExportDeclaration *)
3210 Q_ASSERT(rootScopeIsValid());
3211 Q_ASSERT(m_exportedRootScope != m_globalScope);
3212 Q_ASSERT(m_currentScope == m_globalScope);
3213 m_currentScope = m_exportedRootScope;
3217void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
3219 Q_ASSERT(rootScopeIsValid());
3220 m_currentScope = m_exportedRootScope->parentScope();
3221 Q_ASSERT(m_currentScope == m_globalScope);
3224bool QQmlJSImportVisitor::visit(ESModule *module)
3226 Q_ASSERT(!rootScopeIsValid());
3227 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"module"),
3228 module->firstSourceLocation());
3229 m_currentScope->setIsScript(
true);
3230 importBaseModules();
3235void QQmlJSImportVisitor::endVisit(ESModule *)
3237 QQmlJSScope::resolveTypes(
3238 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3241bool QQmlJSImportVisitor::visit(Program *program)
3243 Q_ASSERT(m_globalScope == m_currentScope);
3244 Q_ASSERT(!rootScopeIsValid());
3245 enterRootScope(QQmlSA::ScopeType::JSFunctionScope, u"script"_s, program->firstSourceLocation());
3246 m_exportedRootScope->setIsScript(
true);
3247 importBaseModules();
3251void QQmlJSImportVisitor::endVisit(Program *)
3253 QQmlJSScope::resolveTypes(
3254 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3257void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
3261 const QString name = fieldMember->name.toString();
3262 if (m_importTypeLocationMap.contains(name)) {
3263 const QQmlJSImportedScope type = m_rootScopeImports.type(name);
3264 if (type.scope.isNull()) {
3265 if (m_rootScopeImports.hasType(name))
3266 m_usedTypes.insert(name);
3267 }
else if (!type.scope->ownAttachedTypeName().isEmpty()) {
3268 m_usedTypes.insert(name);
3273bool QQmlJSImportVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
3275 const QString name = idexp->name.toString();
3276 if (m_importTypeLocationMap.contains(name)) {
3277 m_usedTypes.insert(name);
3283bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
3286 if (element->isVariableDeclaration()) {
3287 QQmlJS::AST::BoundNames names;
3288 element->boundNames(&names);
3289 for (
const auto &name : std::as_const(names)) {
3290 std::optional<QString> typeName;
3291 if (TypeAnnotation *annotation = name.typeAnnotation.data())
3292 if (Type *type = annotation->type)
3293 typeName = type->toString();
3294 const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
3296 { (element->scope == QQmlJS::AST::VariableScope::Var)
3297 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
3298 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
3299 name.location, typeName,
3300 element->scope == QQmlJS::AST::VariableScope::Const });
3309bool QQmlJSImportVisitor::visit(IfStatement *statement)
3311 if (BinaryExpression *binary = cast<BinaryExpression *>(statement->expression)) {
3312 if (binary->op == QSOperator::Assign) {
3314 "Assignment in condition: did you mean to use \"===\" or \"==\" instead of \"=\"?"_L1,
3315 qmlAssignmentInCondition, binary->operatorToken);