151 const QString &implicitImportDirectory,
152 const QStringList &qmldirFiles)
153 : m_implicitImportDirectory(implicitImportDirectory),
154 m_qmldirFiles(qmldirFiles),
155 m_exportedRootScope(QQmlJSScope::resetForReparse(importer->importFile(logger->filePath()))),
156 m_importer(importer),
158 m_rootScopeImports(QQmlJS::ContextualTypes(
159 QQmlJS::ContextualTypes::QML, { }, { },
160 importer->builtinInternalNames().contextualTypes().arrayType()),
166 m_exportedRootScope->setScopeType(QQmlSA::ScopeType::QMLScope);
167 m_exportedRootScope->setBaseTypeName(QQmlJSImporter::s_inProcessMarker);
168 m_exportedRootScope->setFilePath(m_logger->filePath());
169 m_exportedRootScope->setIsComposite(
true);
172
173
174
175
176
177 auto globalScope = QQmlJSScope::create();
178 globalScope->setInternalName(u"global"_s);
179 globalScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
181 QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
182 QQmlJSScope::JavaScriptIdentifier::LexicalScoped, QQmlJS::SourceLocation(), std::nullopt,
186 QV4::Compiler::Codegen::forEachGlobalName([&](QLatin1StringView globalName) {
187 globalScope->insertJSIdentifier(globalName, globalJavaScript);
190 m_globalScope = globalScope;
191 m_currentScope = globalScope;
194QQmlJSImportVisitor::~QQmlJSImportVisitor() =
default;
196void QQmlJSImportVisitor::populateCurrentScope(
197 QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
199 m_currentScope->setScopeType(type);
200 m_currentScope->setIsComposite(
true);
201 m_currentScope->setFilePath(m_logger->filePath());
202 m_currentScope->setSourceLocation(location);
203 setScopeName(m_currentScope, type, name);
204 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
207void QQmlJSImportVisitor::enterRootScope(QQmlJSScope::ScopeType type,
const QString &name,
const QQmlJS::SourceLocation &location)
209 Q_ASSERT(m_currentScope == m_globalScope);
210 QQmlJSScope::reparent(m_currentScope, m_exportedRootScope);
211 m_currentScope = m_exportedRootScope;
212 populateCurrentScope(type, name, location);
215void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type,
const QString &name,
216 const QQmlJS::SourceLocation &location)
218 QQmlJSScope::Ptr newScope = QQmlJSScope::create();
219 QQmlJSScope::reparent(m_currentScope, newScope);
220 m_currentScope = std::move(newScope);
221 populateCurrentScope(type, name, location);
224bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type,
226 const QQmlJS::SourceLocation &location)
228 Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope
229 || type == QQmlSA::ScopeType::AttachedPropertyScope);
231 const auto pred = [&](
const QQmlJSScope::ConstPtr &s) {
234 return s->internalName() == name;
236 const auto scopes = m_currentScope->childScopes();
239 auto it = std::find_if(scopes.begin(), scopes.end(), pred);
240 if (it == scopes.end()) {
242 enterEnvironment(type, name, location);
246 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, *it);
247 m_currentScope = *it;
251void QQmlJSImportVisitor::leaveEnvironment()
253 m_currentScope = m_currentScope->parentScope();
256void QQmlJSImportVisitor::warnUnresolvedType(
const QQmlJSScope::ConstPtr &type)
const
258 m_logger->log(QStringLiteral(
"Type %1 is used but it is not resolved")
259 .arg(QQmlJSUtils::getScopeName(type, type->scopeType())),
260 qmlUnresolvedType, type->sourceLocation());
263void QQmlJSImportVisitor::warnMissingPropertyForBinding(
264 const QString &property,
const QQmlJS::SourceLocation &location,
265 const std::optional<QQmlJSFixSuggestion> &fixSuggestion)
267 m_logger->log(QStringLiteral(
"Could not find property \"%1\".").arg(property),
268 qmlMissingProperty, location,
true,
true, fixSuggestion);
276void QQmlJSImportVisitor::resolveAliases()
278 QQueue<QQmlJSScope::Ptr> objects;
279 objects.enqueue(m_exportedRootScope);
281 qsizetype lastRequeueLength = std::numeric_limits<qsizetype>::max();
282 QQueue<QQmlJSScope::Ptr> requeue;
284 while (!objects.isEmpty()) {
285 const QQmlJSScope::Ptr object = objects.dequeue();
286 const auto properties = object->ownProperties();
288 bool doRequeue =
false;
289 for (
const auto &property : properties) {
290 if (!property.isAlias() || !property.type().isNull())
293 QStringList components = property.aliasExpression().split(u'.');
294 QQmlJSMetaProperty targetProperty;
296 bool foundProperty =
false;
299 QQmlJSScope::ConstPtr type = m_scopesById.scope(components.takeFirst(), object);
300 QQmlJSScope::ConstPtr typeScope;
301 if (!type.isNull()) {
302 foundProperty =
true;
309 while (type && !components.isEmpty()) {
310 const QString name = components.takeFirst();
312 if (!type->hasProperty(name)) {
313 foundProperty =
false;
318 const auto target = type->property(name);
319 if (!target.type() && target.isAlias())
322 type = target.type();
323 targetProperty = target;
331 m_logger->log(QStringLiteral(
"Cannot deduce type of alias \"%1\"")
332 .arg(property.propertyName()),
333 qmlMissingType, property.sourceLocation());
335 m_logger->log(QStringLiteral(
"Cannot resolve alias \"%1\"")
336 .arg(property.propertyName()),
337 qmlUnresolvedAlias, property.sourceLocation());
340 Q_ASSERT(property.index() >= 0);
341 object->addOwnProperty(property);
344 QQmlJSMetaProperty newProperty = property;
345 newProperty.setType(type);
347 newProperty.setIsList(targetProperty.isList());
348 newProperty.setIsWritable(targetProperty.isWritable());
349 newProperty.setIsFinal(targetProperty.isFinal());
350 newProperty.setIsPointer(targetProperty.isPointer());
352 const bool onlyId = !property.aliasExpression().contains(u'.');
354 newProperty.setAliasTargetScope(type);
355 newProperty.setAliasTargetName(QStringLiteral(
"id-only-alias"));
357 const auto &ownerScope = QQmlJSScope::ownerOfProperty(
358 typeScope, targetProperty.propertyName()).scope;
359 newProperty.setAliasTargetScope(ownerScope);
360 newProperty.setAliasTargetName(targetProperty.propertyName());
363 if (
const QString internalName = type->internalName(); !internalName.isEmpty())
364 newProperty.setTypeName(internalName);
366 Q_ASSERT(newProperty.index() >= 0);
367 object->addOwnProperty(newProperty);
368 m_aliasDefinitions.append({ object, property.propertyName() });
372 const auto childScopes = object->childScopes();
373 for (
const auto &childScope : childScopes)
374 objects.enqueue(childScope);
377 requeue.enqueue(object);
379 if (objects.isEmpty() && requeue.size() < lastRequeueLength) {
380 lastRequeueLength = requeue.size();
381 objects.swap(requeue);
385 while (!requeue.isEmpty()) {
386 const QQmlJSScope::Ptr object = requeue.dequeue();
387 const auto properties = object->ownProperties();
388 for (
const auto &property : properties) {
389 if (!property.isAlias() || property.type())
391 m_logger->log(QStringLiteral(
"Alias \"%1\" is part of an alias cycle")
392 .arg(property.propertyName()),
393 qmlAliasCycle, property.sourceLocation());
398void QQmlJSImportVisitor::resolveGroupProperties()
400 QQueue<QQmlJSScope::Ptr> objects;
401 objects.enqueue(m_exportedRootScope);
403 while (!objects.isEmpty()) {
404 const QQmlJSScope::Ptr object = objects.dequeue();
405 const auto childScopes = object->childScopes();
406 for (
const auto &childScope : childScopes) {
407 if (mayBeUnresolvedGroupedProperty(childScope)) {
408 const QString name = childScope->internalName();
409 if (object->isNameDeferred(name)) {
410 const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
411 if (!deferred.isNull()) {
412 QQmlJSScope::resolveGroup(
413 childScope, deferred, m_rootScopeImports.contextualTypes(),
416 }
else if (
const QQmlJSScope::ConstPtr propType = object->property(name).type()) {
417 QQmlJSScope::resolveGroup(
418 childScope, propType, m_rootScopeImports.contextualTypes(),
422 objects.enqueue(childScope);
427QString QQmlJSImportVisitor::implicitImportDirectory(
428 const QString &localFile, QQmlJSResourceFileMapper *mapper)
431 const auto resource = mapper->entry(
432 QQmlJSResourceFileMapper::localFileFilter(localFile));
433 if (resource.isValid()) {
434 return resource.resourcePath.contains(u'/')
435 ? (u':' + resource.resourcePath.left(
436 resource.resourcePath.lastIndexOf(u'/') + 1))
437 : QStringLiteral(
":/");
441 return QFileInfo(localFile).canonicalPath() + u'/';
444void QQmlJSImportVisitor::processImportWarnings(
445 const QString &what,
const QList<QQmlJS::DiagnosticMessage> &warnings,
446 const QQmlJS::SourceLocation &srcLocation)
448 if (warnings.isEmpty())
451 QList<QQmlJS::DiagnosticMessage> importWarnings = warnings;
454 auto fileSelectorWarningsIt = std::partition(importWarnings.begin(), importWarnings.end(),
455 [](
const QQmlJS::DiagnosticMessage &message) {
456 return message.type != QtMsgType::QtInfoMsg;
458 if (fileSelectorWarningsIt != importWarnings.end()) {
459 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImportFileSelector,
461 m_logger->processMessages(QSpan(fileSelectorWarningsIt, importWarnings.end()),
462 qmlImportFileSelector, srcLocation);
465 if (fileSelectorWarningsIt == importWarnings.begin())
468 m_logger->log(QStringLiteral(
"Warnings occurred while importing %1:").arg(what), qmlImport,
470 m_logger->processMessages(QSpan(importWarnings.begin(), fileSelectorWarningsIt), qmlImport,
474void QQmlJSImportVisitor::importBaseModules()
476 Q_ASSERT(m_rootScopeImports.isEmpty());
477 m_rootScopeImports = m_importer->importHardCodedBuiltins();
479 const QQmlJS::SourceLocation invalidLoc;
480 const auto types = m_rootScopeImports.types();
481 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
482 addImportWithLocation(*it, invalidLoc,
false);
484 if (!m_qmldirFiles.isEmpty())
485 m_rootScopeImports.addWarnings(m_importer->importQmldirs(m_qmldirFiles));
489 if (!m_logger->filePath().endsWith(u".qmltypes"_s)) {
490 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
491 m_rootScopeImports.add(m_importer->importDirectory(m_implicitImportDirectory, precedence));
496 if (QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper()) {
497 const QStringList resourcePaths = mapper->resourcePaths(QQmlJSResourceFileMapper::Filter {
498 m_logger->filePath(), QStringList(), QQmlJSResourceFileMapper::Resource });
499 for (
const QString &path : resourcePaths) {
500 const qsizetype lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
503 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
504 m_rootScopeImports.add(m_importer->importDirectory(path.first(lastSlash),
510 processImportWarnings(QStringLiteral(
"base modules"), m_rootScopeImports.warnings());
513bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiProgram *)
517 if (
auto elementName = QFileInfo(m_logger->filePath()).baseName();
518 !elementName.isEmpty() && elementName[0].isUpper()) {
519 auto precedence = quint8(QQmlJS::PrecedenceValues::ImplicitImport);
520 m_rootScopeImports.setType(elementName,
521 { m_exportedRootScope, QTypeRevision{ }, precedence });
527void QQmlJSImportVisitor::endVisit(UiProgram *)
529 for (
const auto &scope : std::as_const(m_objectBindingScopes)) {
530 breakInheritanceCycles(scope);
531 checkDeprecation(scope);
532 checkForComponentTypeWithProperties(scope);
535 for (
const auto &scope : std::as_const(m_objectDefinitionScopes)) {
536 if (m_pendingDefaultProperties.contains(scope))
538 breakInheritanceCycles(scope);
539 checkDeprecation(scope);
540 checkForComponentTypeWithProperties(scope);
543 const auto &keys = m_pendingDefaultProperties.keys();
544 for (
const auto &scope : keys) {
545 breakInheritanceCycles(scope);
546 checkDeprecation(scope);
547 checkForComponentTypeWithProperties(scope);
551 resolveGroupProperties();
553 for (
const auto &scope : std::as_const(m_objectDefinitionScopes))
554 checkGroupedAndAttachedScopes(scope);
557 processDefaultProperties();
558 processPropertyTypes();
559 processMethodTypes();
560 processPropertyBindings();
561 processPropertyBindingObjects();
562 checkRequiredProperties();
564 auto unusedImports = m_importLocations;
565 for (
const QString &type : std::as_const(m_usedTypes)) {
566 const auto &importLocations = m_importTypeLocationMap.values(type);
567 for (
const auto &importLocation : importLocations)
568 unusedImports.remove(importLocation);
571 if (unusedImports.isEmpty())
575 const auto &imports = m_importStaticModuleLocationMap.values();
576 for (
const QQmlJS::SourceLocation &import : imports)
577 unusedImports.remove(import);
579 for (
const auto &import : unusedImports) {
580 m_logger->log(QString::fromLatin1(
"Unused import"), qmlUnusedImports, import);
583 populateRuntimeFunctionIndicesForDocument();
588 ExpressionStatement *expr = cast<ExpressionStatement *>(statement);
590 if (!statement || !expr->expression)
593 switch (expr->expression->kind) {
594 case Node::Kind_StringLiteral:
595 return cast<StringLiteral *>(expr->expression)->value.toString();
596 case Node::Kind_NumericLiteral:
597 return cast<NumericLiteral *>(expr->expression)->value;
603QList<QQmlJSAnnotation> QQmlJSImportVisitor::parseAnnotations(QQmlJS::AST::UiAnnotationList *list)
606 QList<QQmlJSAnnotation> annotationList;
608 for (UiAnnotationList *item = list; item !=
nullptr; item = item->next) {
609 UiAnnotation *annotation = item->annotation;
611 QQmlJSAnnotation qqmljsAnnotation;
612 qqmljsAnnotation.name = buildName(annotation->qualifiedTypeNameId);
614 for (UiObjectMemberList *memberItem = annotation->initializer->members; memberItem !=
nullptr; memberItem = memberItem->next) {
615 switch (memberItem->member->kind) {
616 case Node::Kind_UiScriptBinding: {
617 auto *scriptBinding = QQmlJS::AST::cast<UiScriptBinding*>(memberItem->member);
618 qqmljsAnnotation.bindings[buildName(scriptBinding->qualifiedId)]
619 = bindingToVariant(scriptBinding->statement);
628 annotationList.append(qqmljsAnnotation);
631 return annotationList;
634void QQmlJSImportVisitor::setAllBindings()
636 using Key = std::pair<QQmlJSScope::ConstPtr, QString>;
637 QHash<Key, QQmlJS::SourceLocation> foundBindings;
639 for (
auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
641 const QQmlJSScope::Ptr type = it->owner;
642 if (!checkTypeResolved(type))
651 if (!type->isFullyResolved())
653 auto binding = it->create();
654 if (!binding.isValid())
656 type->addOwnPropertyBinding(binding, it->specifier);
659 if (binding.hasInterceptor() || binding.hasValueSource())
661 const QString propertyName = binding.propertyName();
662 QQmlJSMetaProperty property = type->property(propertyName);
665
666
667
668
669
670 if (!property.isValid())
674 if (property.isList())
677 const Key key = std::make_pair(type, propertyName);
678 auto sourceLocationIt = foundBindings.constFind(key);
679 if (sourceLocationIt == foundBindings.constEnd()) {
680 foundBindings.insert(key, binding.sourceLocation());
684 const QQmlJS::SourceLocation location = binding.sourceLocation();
685 m_logger->log(
"Duplicate binding on property '%1'"_L1.arg(propertyName),
686 qmlDuplicatePropertyBinding, location);
687 m_logger->log(
"Note: previous binding on '%1' here"_L1.arg(propertyName),
688 qmlDuplicatePropertyBinding, *sourceLocationIt,
true,
true, {},
693void QQmlJSImportVisitor::processDefaultProperties()
695 for (
auto it = m_pendingDefaultProperties.constBegin();
696 it != m_pendingDefaultProperties.constEnd(); ++it) {
697 QQmlJSScope::ConstPtr parentScope = it.key();
700 if (checkCustomParser(parentScope))
703 if (!checkTypeResolved(parentScope))
707
708
709
710
711
712
713
714
715
717 parentScope = parentScope->baseType();
719 const QString defaultPropertyName =
720 parentScope ? parentScope->defaultPropertyName() : QString();
722 if (defaultPropertyName.isEmpty()) {
725 bool isComponent =
false;
726 for (QQmlJSScope::ConstPtr s = parentScope; s; s = s->baseType()) {
727 if (s->internalName() == QStringLiteral(
"QQmlComponent")) {
733 if (!isComponent && checkTypeResolved(parentScope)) {
734 m_logger->log(QStringLiteral(
"Cannot assign to non-existent default property"),
735 qmlMissingProperty, it.value().constFirst()->sourceLocation());
741 const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
742 auto propType = defaultProp.type();
743 const auto handleUnresolvedDefaultProperty = [&](
const QQmlJSScope::ConstPtr &) {
745 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
746 "missing an import.")
747 .arg(defaultPropertyName)
748 .arg(defaultProp.typeName()),
749 qmlUnresolvedType, it.value().constFirst()->sourceLocation());
752 const auto assignToUnknownProperty = [&]() {
755 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it))
756 scope->setAssignedToUnknownProperty(
true);
759 if (propType.isNull()) {
760 handleUnresolvedDefaultProperty(propType);
761 assignToUnknownProperty();
765 if (it.value().size() > 1
766 && !defaultProp.isList()
767 && !propType->isListProperty()) {
769 QStringLiteral(
"Cannot assign multiple objects to a default non-list property"),
770 qmlNonListProperty, it.value().constFirst()->sourceLocation());
773 if (!checkTypeResolved(propType, handleUnresolvedDefaultProperty)) {
774 assignToUnknownProperty();
778 for (
const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
779 if (!checkTypeResolved(scope))
784 if (propType->canAssign(scope)) {
785 scope->setIsWrappedInImplicitComponent(
786 causesImplicitComponentWrapping(defaultProp, scope));
790 m_logger->log(QStringLiteral(
"Cannot assign to default property of incompatible type"),
791 qmlIncompatibleType, scope->sourceLocation());
796void QQmlJSImportVisitor::processPropertyTypes()
798 for (
const PendingPropertyType &type : std::as_const(m_pendingPropertyTypes)) {
799 Q_ASSERT(type.scope->hasOwnProperty(type.name));
801 auto property = type.scope->ownProperty(type.name);
803 if (
const auto propertyType = QQmlJSScope::findType(
804 property.typeName(), m_rootScopeImports.contextualTypes()).scope) {
805 property.setType(property.isList() ? propertyType->listType() : propertyType);
806 type.scope->addOwnProperty(property);
808 QString msg = property.typeName() +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports;
809 if (property.typeName() ==
"list"_L1)
810 msg +=
" list is not a type. It requires an element type argument (eg. list<int>)"_L1;
811 m_logger->log(msg, qmlImport, type.location);
816void QQmlJSImportVisitor::processMethodTypes()
818 const auto isEnumUsedAsType = [&](QStringView typeName,
const QQmlJS::SourceLocation &loc) {
819 if (typeName ==
"enum"_L1) {
823 const auto split = typeName.tokenize(u'.').toContainer<QVarLengthArray<QStringView, 4>>();
824 if (split.size() != 2)
827 const QStringView scopeName = split[0];
828 const QStringView enumName = split[1];
830 if (
auto scope = QQmlJSScope::findType(scopeName.toString(),
831 m_rootScopeImports.contextualTypes()).scope) {
832 if (scope->enumeration(enumName.toString()).isValid()) {
834 "QML enumerations are not types. Use int, or use double if the enum's underlying type does not fit into int."_L1,
835 qmlEnumsAreNotTypes, loc);
842 for (
const auto &method : std::as_const(m_pendingMethodTypeAnnotations)) {
843 for (
auto [it, end] = method.scope->mutableOwnMethodsRange(method.methodName); it != end; ++it) {
844 const auto [parameterBegin, parameterEnd] = it->mutableParametersRange();
845 for (
auto parameter = parameterBegin; parameter != parameterEnd; ++parameter) {
846 const int parameterIndex = parameter - parameterBegin;
847 if (isEnumUsedAsType(parameter->typeName(), method.locations[parameterIndex]))
849 if (
const auto parameterType = QQmlJSScope::findType(
850 parameter->typeName(), m_rootScopeImports.contextualTypes()).scope) {
851 parameter->setType({ parameterType });
854 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
855 .arg(parameter->typeName(), parameter->name(), it->methodName()),
856 qmlUnresolvedType, method.locations[parameter - parameterBegin]);
860 if (isEnumUsedAsType(it->returnTypeName(), method.locations.last()))
862 if (
const auto returnType = QQmlJSScope::findType(
863 it->returnTypeName(), m_rootScopeImports.contextualTypes()).scope) {
864 it->setReturnType({ returnType });
866 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
867 it->returnTypeName(), it->methodName()),
868 qmlUnresolvedType, method.locations.last());
876
877
878
879
880
881
882
886 for (QStringView propertyName: possiblyGroupedProperty.tokenize(u".")) {
887 property = scope->property(propertyName.toString());
888 if (property.isValid())
889 scope = property.type();
896void QQmlJSImportVisitor::processPropertyBindingObjects()
898 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundLiterals;
906 QSet<std::pair<QQmlJSScope::Ptr, QString>> visited;
907 for (
const PendingPropertyObjectBinding &objectBinding :
908 std::as_const(m_pendingPropertyObjectBindings)) {
910 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
911 if (visited.contains(uniqueBindingId))
913 visited.insert(uniqueBindingId);
915 auto [existingBindingsBegin, existingBindingsEnd] =
916 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
917 const bool hasLiteralBindings =
918 std::any_of(existingBindingsBegin, existingBindingsEnd,
919 [](
const QQmlJSMetaPropertyBinding &x) {
return x.hasLiteral(); });
920 if (hasLiteralBindings)
921 foundLiterals.insert(uniqueBindingId);
925 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundObjects;
926 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundInterceptors;
927 QSet<std::pair<QQmlJSScope::Ptr, QString>> foundValueSources;
929 for (
const PendingPropertyObjectBinding &objectBinding :
930 std::as_const(m_pendingPropertyObjectBindings)) {
931 const QString propertyName = objectBinding.name;
932 QQmlJSScope::Ptr childScope = objectBinding.childScope;
934 const auto assignToUnknownProperty = [&]() {
937 childScope->setAssignedToUnknownProperty(
true);
941 if (!checkTypeResolved(objectBinding.scope)) {
942 assignToUnknownProperty();
946 QQmlJSMetaProperty property = resolveProperty(propertyName, objectBinding.scope);
948 if (!property.isValid()) {
949 warnMissingPropertyForBinding(propertyName, objectBinding.location);
952 const auto handleUnresolvedProperty = [&](
const QQmlJSScope::ConstPtr &) {
954 m_logger->log(QStringLiteral(
"Property \"%1\" has incomplete type \"%2\". You may be "
955 "missing an import.")
957 .arg(property.typeName()),
958 qmlUnresolvedType, objectBinding.location);
961 if (property.type().isNull()) {
962 assignToUnknownProperty();
963 handleUnresolvedProperty(property.type());
968 if (!checkTypeResolved(property.type(), handleUnresolvedProperty)) {
969 assignToUnknownProperty();
971 }
else if (!checkTypeResolved(childScope)) {
975 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
976 m_logger->log(QStringLiteral(
"Cannot assign object of type %1 to %2")
977 .arg(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope))
978 .arg(property.typeName()),
979 qmlIncompatibleType, childScope->sourceLocation());
983 childScope->setIsWrappedInImplicitComponent(
984 causesImplicitComponentWrapping(property, childScope));
987 const auto uniqueBindingId = std::make_pair(objectBinding.scope, objectBinding.name);
988 const QString typeName = QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope);
990 auto isConditionalBinding = [&]() ->
bool {
992
993
994
995
996 return childScope->hasOwnPropertyBindings(u"enabled"_s)
997 || childScope->hasOwnPropertyBindings(u"when"_s)
998 || childScope->hasOwnPropertyBindings(u"running"_s);
1001 if (objectBinding.onToken) {
1002 if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueInterceptor"))) {
1003 if (foundInterceptors.contains(uniqueBindingId)) {
1004 if (!isConditionalBinding()) {
1005 m_logger->log(QStringLiteral(
"Duplicate interceptor on property \"%1\"")
1007 qmlDuplicatePropertyBinding, objectBinding.location);
1010 foundInterceptors.insert(uniqueBindingId);
1012 }
else if (childScope->hasInterface(QStringLiteral(
"QQmlPropertyValueSource"))) {
1013 if (foundValueSources.contains(uniqueBindingId)) {
1014 if (!isConditionalBinding()) {
1015 m_logger->log(QStringLiteral(
"Duplicate value source on property \"%1\"")
1017 qmlDuplicatePropertyBinding, objectBinding.location);
1019 }
else if (foundObjects.contains(uniqueBindingId)
1020 || foundLiterals.contains(uniqueBindingId)) {
1021 if (!isConditionalBinding()) {
1022 m_logger->log(QStringLiteral(
"Cannot combine value source and binding on "
1025 qmlDuplicatePropertyBinding, objectBinding.location);
1028 foundValueSources.insert(uniqueBindingId);
1031 m_logger->log(QStringLiteral(
"On-binding for property \"%1\" has wrong type \"%2\"")
1034 qmlIncompatibleType, objectBinding.location);
1037 if (foundValueSources.contains(uniqueBindingId)) {
1038 if (!isConditionalBinding()) {
1040 QStringLiteral(
"Cannot combine value source and binding on property \"%1\"")
1042 qmlDuplicatePropertyBinding, objectBinding.location);
1045 foundObjects.insert(uniqueBindingId);
1053 QList<QQmlJSScope::ConstPtr> descendants;
1054 std::vector<QQmlJSScope::ConstPtr> toVisit;
1056 toVisit.push_back(scope);
1057 while (!toVisit.empty()) {
1058 const QQmlJSScope::ConstPtr s = toVisit.back();
1064 toVisit.insert(toVisit.end(), s->childScopesBegin(), s->childScopesEnd());
1071void QQmlJSImportVisitor::populatePropertyAliases()
1073 for (
const auto &alias : std::as_const(m_aliasDefinitions)) {
1074 const auto &[aliasScope, aliasName] = alias;
1075 if (aliasScope.isNull())
1078 auto property = aliasScope->ownProperty(aliasName);
1079 if (!property.isValid() || !property.aliasTargetScope())
1082 Property target(property.aliasTargetScope(), property.aliasTargetName());
1085 m_propertyAliases[target].append(alias);
1086 property = target.scope->property(target.name);
1087 target = Property(property.aliasTargetScope(), property.aliasTargetName());
1088 }
while (property.isAlias());
1092void QQmlJSImportVisitor::checkRequiredProperties()
1094 for (
const auto &required : std::as_const(m_requiredProperties)) {
1095 if (!required.scope->hasProperty(required.name)) {
1097 QStringLiteral(
"Property \"%1\" was marked as required but does not exist.")
1098 .arg(required.name),
1099 qmlRequired, required.location);
1103 const auto compType = m_rootScopeImports.type(u"Component"_s).scope;
1104 const auto isComponentRoot = [&](
const QQmlJSScope::ConstPtr &requiredScope) {
1105 if (requiredScope->isWrappedInImplicitComponent())
1107 if (
const auto s = requiredScope->parentScope(); s && s->baseType() == compType)
1112 const auto scopeRequiresProperty = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1113 const QString &propName,
1114 const QQmlJSScope::ConstPtr &descendant) {
1115 if (!requiredScope->isPropertyLocallyRequired(propName))
1119 return QQmlJSScope::ownerOfProperty(requiredScope, propName).scope
1120 == QQmlJSScope::ownerOfProperty(descendant, propName).scope;
1123 const auto requiredHasBinding = [](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1124 const QQmlJSScope::ConstPtr &owner,
1125 const QString &propName) {
1126 for (
const auto &scope : scopesToSearch) {
1127 if (scope->property(propName).isAlias())
1129 const auto &[begin, end] = scope->ownPropertyBindings(propName);
1130 for (
auto it = begin; it != end; ++it) {
1132 const bool isRelevantBinding = QQmlSA::isRegularBindingType(it->bindingType())
1133 || it->bindingType() == QQmlSA::BindingType::Interceptor
1134 || it->bindingType() == QQmlSA::BindingType::ValueSource;
1135 if (!isRelevantBinding)
1137 if (QQmlJSScope::ownerOfProperty(scope, propName).scope == owner)
1145 const auto requiredUsedInRootAlias = [&](
const QQmlJSScope::ConstPtr &requiredScope,
1146 const QString &propName) {
1147 const Property target(requiredScope, propName);
1150 const auto allAliasesToTargetIt = m_propertyAliases.constFind(target);
1151 if (allAliasesToTargetIt == m_propertyAliases.constEnd())
1158 allAliasesToTargetIt->constBegin(), allAliasesToTargetIt->constEnd(),
1159 [](
const Property &property) {
return property.scope->isFileRootComponent(); });
1162 const auto requiredSetThroughAlias = [&](
const QList<QQmlJSScope::ConstPtr> &scopesToSearch,
1163 const QQmlJSScope::ConstPtr &requiredScope,
1164 const QString &propName) {
1165 const auto &propertyDefScope = QQmlJSScope::ownerOfProperty(requiredScope, propName);
1166 const auto &propertyAliases = m_propertyAliases[{ propertyDefScope.scope, propName }];
1167 for (
const auto &alias : propertyAliases) {
1168 for (
const auto &s : scopesToSearch) {
1169 if (s->hasOwnPropertyBindings(alias.name))
1176 const auto warn = [
this](
const QQmlJSScope::ConstPtr &prevRequiredScope,
1177 const QString &propName,
const QQmlJSScope::ConstPtr &defScope,
1178 const QQmlJSScope::ConstPtr &requiredScope,
1179 const QQmlJSScope::ConstPtr &descendant) {
1180 const auto &propertyScope = QQmlJSScope::ownerOfProperty(requiredScope, propName).scope;
1181 const QString propertyScopeName = !propertyScope.isNull()
1182 ? QQmlJSUtils::getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope)
1185 std::optional<QQmlJSFixSuggestion> suggestion;
1187 QString message = QStringLiteral(
"Component is missing required property %1 from %2")
1189 .arg(propertyScopeName);
1190 if (requiredScope != descendant) {
1191 const QString requiredScopeName = prevRequiredScope
1192 ? QQmlJSUtils::getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
1195 if (!prevRequiredScope.isNull()) {
1196 if (
auto sourceScope = prevRequiredScope->baseType()) {
1197 suggestion = QQmlJSFixSuggestion{
1198 "%1:%2:%3: Property marked as required in %4."_L1
1199 .arg(sourceScope->filePath())
1200 .arg(sourceScope->sourceLocation().startLine)
1201 .arg(sourceScope->sourceLocation().startColumn)
1202 .arg(requiredScopeName),
1203 sourceScope->sourceLocation()
1207 if (sourceScope->isComposite())
1208 suggestion->setFilename(sourceScope->filePath());
1211 message +=
" (marked as required by %1)"_L1.arg(requiredScopeName);
1215 m_logger->log(message, qmlRequired, defScope->sourceLocation(),
true,
true, suggestion);
1218 populatePropertyAliases();
1220 for (
const auto &[_, defScope] : m_scopesByIrLocation.asKeyValueRange()) {
1221 if (defScope->isFileRootComponent() || defScope->isInlineComponent()
1222 || defScope->componentRootStatus() != QQmlJSScope::IsComponentRoot::No
1223 || defScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
1227 QList<QQmlJSScope::ConstPtr> scopesToSearch;
1228 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
1229 const auto descendants = QList<QQmlJSScope::ConstPtr>()
1230 << scope << qmlScopeDescendants(scope);
1231 for (
const QQmlJSScope::ConstPtr &descendant : std::as_const(descendants)) {
1234 if (descendant != scope && descendant->isInlineComponent())
1236 scopesToSearch << descendant;
1237 const auto ownProperties = descendant->ownProperties();
1238 for (
auto propertyIt = ownProperties.constBegin();
1239 propertyIt != ownProperties.constEnd(); ++propertyIt) {
1240 const QString propName = propertyIt.key();
1241 if (descendant->hasOwnPropertyBindings(propName))
1244 QQmlJSScope::ConstPtr prevRequiredScope;
1245 for (
const QQmlJSScope::ConstPtr &requiredScope : std::as_const(scopesToSearch)) {
1248 if (isComponentRoot(requiredScope))
1251 if (!scopeRequiresProperty(requiredScope, propName, descendant)) {
1252 prevRequiredScope = requiredScope;
1256 if (requiredHasBinding(scopesToSearch, descendant, propName))
1259 if (requiredUsedInRootAlias(requiredScope, propName))
1262 if (requiredSetThroughAlias(scopesToSearch, requiredScope, propName))
1265 warn(prevRequiredScope, propName, defScope, requiredScope, descendant);
1266 prevRequiredScope = requiredScope;
1274void QQmlJSImportVisitor::processPropertyBindings()
1276 for (
auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
1277 QQmlJSScope::Ptr scope = it.key();
1278 for (
auto &[visibilityScope, location, name] : it.value()) {
1279 if (!scope->hasProperty(name) && !m_logger->isDisabled()) {
1283 if (checkCustomParser(scope))
1287 std::optional<QQmlJSFixSuggestion> fixSuggestion;
1289 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
1290 baseScope = baseScope->baseType()) {
1291 if (
auto suggestion = QQmlJSUtils::didYouMean(
1292 name, baseScope->ownProperties().keys(), m_logger->filePath(), location);
1293 suggestion.has_value()) {
1294 fixSuggestion = suggestion;
1299 if (checkTypeResolved(scope))
1300 warnMissingPropertyForBinding(name, location, fixSuggestion);
1304 const auto property = scope->property(name);
1305 if (!property.type()) {
1306 m_logger->log(QStringLiteral(
"No type found for property \"%1\". This may be due "
1307 "to a missing import statement or incomplete "
1310 qmlMissingType, location);
1313 const auto &annotations = property.annotations();
1315 const auto deprecationAnn =
1316 std::find_if(annotations.cbegin(), annotations.cend(),
1317 [](
const QQmlJSAnnotation &ann) {
return ann.isDeprecation(); });
1319 if (deprecationAnn != annotations.cend()) {
1320 const auto deprecation = deprecationAnn->deprecation();
1322 QString message = QStringLiteral(
"Binding on deprecated property \"%1\"")
1323 .arg(property.propertyName());
1325 if (!deprecation.reason.isEmpty())
1326 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1328 m_logger->log(message, qmlDeprecated, location);
1334void QQmlJSImportVisitor::checkSignal(
1335 const QQmlJSScope::ConstPtr &signalScope,
const QQmlJS::SourceLocation &location,
1336 const QString &handlerName,
const QStringList &handlerParameters)
1338 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1340 std::optional<QQmlJSMetaMethod> signalMethod;
1341 const auto setSignalMethod = [&](
const QQmlJSScope::ConstPtr &scope,
const QString &name) {
1342 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1343 if (!methods.isEmpty())
1344 signalMethod = methods[0];
1347 if (signal.has_value()) {
1348 if (signalScope->hasMethod(*signal)) {
1349 setSignalMethod(signalScope, *signal);
1350 }
else if (
auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1355 if (
auto notify = p->notify(); !notify.isEmpty()) {
1356 setSignalMethod(signalScope, notify);
1358 Q_ASSERT(!p->bindable().isEmpty());
1359 signalMethod = QQmlJSMetaMethod {};
1364 if (!signalMethod.has_value()) {
1369 if (signalScope->baseTypeName() == QStringLiteral(
"Connections")) {
1371 u"Implicitly defining \"%1\" as signal handler in Connections is deprecated. "
1372 u"Create a function instead: \"function %2(%3) { ... }\"."_s.arg(
1373 handlerName, handlerName, handlerParameters.join(u", ")),
1374 qmlUnqualified, location,
true,
true);
1378 auto baseType = QQmlJSScope::nonCompositeBaseType(signalScope);
1379 if (baseType && baseType->hasCustomParser())
1383 QStringLiteral(
"no matching signal found for handler \"%1\"").arg(handlerName),
1384 qmlUnqualified, location,
true,
true);
1388 const auto signalParameters = signalMethod->parameters();
1389 QHash<QString, qsizetype> parameterNameIndexes;
1391 for (
int i = 0, end = signalParameters.size(); i < end; i++) {
1392 auto &p = signalParameters[i];
1393 parameterNameIndexes[p.name()] = i;
1395 auto signalName = [&]() {
1397 return u" called %1"_s.arg(*signal);
1400 auto type = p.type();
1403 "Type %1 of parameter %2 in signal%3 was not found, but is required to compile "
1405 p.typeName(), p.name(), signalName(),
1406 handlerName, didYouAddAllImports),
1407 qmlSignalParameters, location);
1411 if (type->isComposite())
1419 auto parameterName = [&]() {
1420 if (p.name().isEmpty())
1422 return u" called %1"_s.arg(p.name());
1424 switch (type->accessSemantics()) {
1425 case QQmlJSScope::AccessSemantics::Reference:
1427 m_logger->log(QStringLiteral(
"Type %1 of parameter%2 in signal%3 should be "
1428 "passed by pointer to be able to compile %4. ")
1429 .arg(p.typeName(), parameterName(), signalName(),
1431 qmlSignalParameters, location);
1433 case QQmlJSScope::AccessSemantics::Value:
1434 case QQmlJSScope::AccessSemantics::Sequence:
1438 "Type %1 of parameter%2 in signal%3 should be passed by "
1439 "value or const reference to be able to compile %4. ")
1440 .arg(p.typeName(), parameterName(), signalName(),
1442 qmlSignalParameters, location);
1444 case QQmlJSScope::AccessSemantics::None:
1446 QStringLiteral(
"Type %1 of parameter%2 in signal%3 required by the "
1447 "compilation of %4 cannot be used. ")
1448 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1449 qmlSignalParameters, location);
1454 if (handlerParameters.size() > signalParameters.size()) {
1455 m_logger->log(QStringLiteral(
"Signal handler for \"%2\" has more formal"
1456 " parameters than the signal it handles.")
1458 qmlSignalParameters, location);
1462 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1463 const QStringView handlerParameter = handlerParameters.at(i);
1464 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1465 if (it == parameterNameIndexes.constEnd())
1467 const qsizetype j = *it;
1472 m_logger->log(QStringLiteral(
"Parameter %1 to signal handler for \"%2\""
1473 " is called \"%3\". The signal has a parameter"
1474 " of the same name in position %4.")
1476 .arg(handlerName, handlerParameter)
1478 qmlSignalParameters, location);
1482void QQmlJSImportVisitor::addDefaultProperties()
1484 QQmlJSScope::ConstPtr parentScope = m_currentScope->parentScope();
1485 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1486 || m_currentScope->isInlineComponent())
1489 m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
1491 if (checkCustomParser(parentScope))
1495
1496
1497
1498
1499
1500
1501
1502
1503
1505 parentScope = parentScope->baseType();
1507 const QString defaultPropertyName =
1508 parentScope ? parentScope->defaultPropertyName() : QString();
1510 if (defaultPropertyName.isEmpty())
1515 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1516 binding.setObject(QQmlJSUtils::getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope),
1517 QQmlJSScope::ConstPtr(m_currentScope));
1518 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() {
return binding; },
1519 QQmlJSScope::UnnamedPropertyTarget });
1522void QQmlJSImportVisitor::breakInheritanceCycles(
const QQmlJSScope::Ptr &originalScope)
1524 QList<QQmlJSScope::ConstPtr> scopes;
1525 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1526 if (scopes.contains(scope)) {
1527 QString inheritenceCycle;
1528 for (
const auto &seen : std::as_const(scopes)) {
1529 inheritenceCycle.append(seen->baseTypeName());
1530 inheritenceCycle.append(QLatin1String(
" -> "));
1532 inheritenceCycle.append(scopes.first()->baseTypeName());
1534 const QString message = QStringLiteral(
"%1 is part of an inheritance cycle: %2")
1535 .arg(originalScope->baseTypeName(), inheritenceCycle);
1536 m_logger->log(message, qmlInheritanceCycle, scope->sourceLocation());
1537 originalScope->clearBaseType();
1538 originalScope->setBaseTypeError(message);
1542 scopes.append(scope);
1544 const auto newScope = scope->baseType();
1545 if (newScope.isNull()) {
1546 const QString error = scope->baseTypeError();
1547 const QString name = scope->baseTypeName();
1548 if (!error.isEmpty()) {
1549 m_logger->log(error, qmlImport, scope->sourceLocation(),
true,
true);
1550 }
else if (!name.isEmpty() && !m_unresolvedTypes.hasSeen(scope)
1551 && !m_logger->isDisabled()) {
1553 name +
' '_L1 + wasNotFound +
' '_L1 + didYouAddAllImports,
1554 qmlImport, scope->sourceLocation(),
true,
true,
1555 QQmlJSUtils::didYouMean(scope->baseTypeName(),
1556 m_rootScopeImports.types().keys(),
1557 m_logger->filePath(),
1558 scope->sourceLocation()));
1566void QQmlJSImportVisitor::checkDeprecation(
const QQmlJSScope::ConstPtr &originalScope)
1568 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1569 for (
const QQmlJSAnnotation &annotation : scope->annotations()) {
1570 if (annotation.isDeprecation()) {
1571 QQQmlJSDeprecation deprecation = annotation.deprecation();
1574 QStringLiteral(
"Type \"%1\" is deprecated").arg(scope->internalName());
1576 if (!deprecation.reason.isEmpty())
1577 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
1579 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1585void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope)
1589 if (checkCustomParser(scope))
1592 auto children = scope->childScopes();
1593 while (!children.isEmpty()) {
1594 auto childScope = children.takeFirst();
1595 const auto type = childScope->scopeType();
1597 case QQmlSA::ScopeType::GroupedPropertyScope:
1598 case QQmlSA::ScopeType::AttachedPropertyScope:
1599 if (!childScope->baseType()) {
1600 m_logger->log(QStringLiteral(
"unknown %1 property scope %2.")
1601 .arg(type == QQmlSA::ScopeType::GroupedPropertyScope
1602 ? QStringLiteral(
"grouped")
1603 : QStringLiteral(
"attached"),
1604 childScope->internalName()),
1605 qmlUnqualified, childScope->sourceLocation());
1607 children.append(childScope->childScopes());
1615void QQmlJSImportVisitor::checkForComponentTypeWithProperties(
const QQmlJSScope::ConstPtr &scope)
1617 const QQmlJSScope::ConstPtr base = scope->baseType();
1624 if (base->isComposite())
1627 if (base->internalName() !=
"QQmlComponent"_L1)
1630 const auto ownProperties = scope->ownProperties();
1631 for (
const auto &property : ownProperties) {
1632 m_logger->log(
"Component objects cannot declare new properties."_L1,
1633 qmlSyntax, property.sourceLocation());
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);
1820 const QTypeRevision revision = m_currentScope->baseTypeRevision();
1821 if (
auto base = m_currentScope->baseType(); base) {
1822 if (isRoot && base->internalName() == u"QQmlComponent") {
1823 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1824 definition->qualifiedTypeNameId->identifierToken,
true,
true);
1826 if (base->isSingleton() && m_currentScope->isComposite()) {
1827 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1828 m_currentScope->baseTypeName()),
1829 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1832 }
else if (!base->isCreatable()) {
1834 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1835 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1839 if (m_nextIsInlineComponent) {
1840 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1841 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1842 m_currentScope->setIsInlineComponent(
true);
1843 m_currentScope->setInlineComponentName(name);
1844 m_currentScope->setOwnModuleName(m_exportedRootScope->moduleName());
1845 auto precedence = quint8(QQmlJS::PrecedenceValues::InlineComponent);
1846 m_rootScopeImports.setType(name, { m_currentScope, revision, precedence });
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 const QString signalName = publicMember->name.toString();
1914 UiParameterList *param = publicMember->parameters;
1915 QQmlJSMetaMethod method;
1916 method.setMethodType(QQmlJSMetaMethodType::Signal);
1917 method.setReturnTypeName(QStringLiteral(
"void"));
1918 method.setMethodName(signalName);
1919 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1920 publicMember->lastSourceLocation()));
1921 method.setOtherMethodIndex(
1922 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
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 const QString propertyName = publicMember->name.toString();
1936 QString typeName = buildName(publicMember->memberType);
1937 if (typeName.contains(u'.') && typeName.front().isLower()) {
1938 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1942 const bool isAlias = (typeName == u"alias"_s);
1944 auto tryParseAlias = [&]() {
1946 if (!publicMember->statement) {
1947 m_logger->log(QStringLiteral(
"Invalid alias expression - an initializer is needed."),
1948 qmlSyntax, publicMember->memberType->firstSourceLocation());
1951 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1952 auto node = expression ? expression->expression :
nullptr;
1953 auto fex = cast<FieldMemberExpression *>(node);
1956 aliasExpr.prepend(u'.' + fex->name.toString());
1957 fex = cast<FieldMemberExpression *>(node);
1960 if (
const auto idExpression = cast<IdentifierExpression *>(node)) {
1961 aliasExpr.prepend(idExpression->name.toString());
1965 m_logger->log(QStringLiteral(
"Invalid alias expression. Only IDs and field "
1966 "member expressions can be aliased."),
1967 qmlSyntax, publicMember->statement->firstSourceLocation());
1972 if (m_rootScopeImports.hasType(typeName)
1973 && !m_rootScopeImports.type(typeName).scope.isNull()) {
1974 if (m_importTypeLocationMap.contains(typeName))
1975 m_usedTypes.insert(typeName);
1978 QQmlJSMetaProperty prop;
1979 prop.setPropertyName(propertyName);
1980 prop.setIsList(publicMember->typeModifier == QLatin1String(
"list"));
1981 prop.setIsWritable(!publicMember->isReadonly());
1982 prop.setIsFinal(publicMember->isFinal());
1983 prop.setIsVirtual(publicMember->isVirtual());
1984 prop.setIsOverride(publicMember->isOverride());
1985 prop.setAliasExpression(aliasExpr);
1986 prop.setSourceLocation(
1987 combine(publicMember->firstSourceLocation(), publicMember->colonToken));
1989 isAlias ? QQmlJSScope::ConstPtr() : m_rootScopeImports.type(typeName).scope;
1991 prop.setType(prop.isList() ? type->listType() : type);
1992 const QString internalName = type->internalName();
1993 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
1994 }
else if (!isAlias) {
1995 m_pendingPropertyTypes << PendingPropertyType { m_currentScope, prop.propertyName(),
1996 publicMember->firstSourceLocation() };
1997 prop.setTypeName(typeName);
1999 prop.setAnnotations(parseAnnotations(publicMember->annotations));
2000 if (publicMember->isDefaultMember())
2001 m_currentScope->setOwnDefaultPropertyName(propertyName);
2002 prop.setIndex(m_currentScope->ownProperties().size());
2003 m_currentScope->addOwnProperty(prop);
2005 QQmlJSMetaMethod method(
2006 QQmlSignalNames::propertyNameToChangedSignalName(propertyName), u"void"_s);
2007 method.setMethodType(QQmlJSMetaMethodType::Signal);
2008 method.setIsImplicitQmlPropertyChangeSignal(
true);
2009 method.setOtherMethodIndex(
2010 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2011 m_currentScope->addOwnMethod(method);
2013 if (publicMember->isRequired())
2014 m_currentScope->setPropertyLocallyRequired(prop.propertyName(),
true);
2016 BindingExpressionParseResult parseResult = BindingExpressionParseResult::Invalid;
2020 parseBindingExpression(publicMember->name.toString(), publicMember->statement,
2026 if (parseResult == BindingExpressionParseResult::Script) {
2027 Q_ASSERT(!m_savedBindingOuterScope);
2028 m_savedBindingOuterScope = m_currentScope;
2029 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope, QStringLiteral(
"binding"),
2030 publicMember->statement->firstSourceLocation());
2040void QQmlJSImportVisitor::endVisit(UiPublicMember *publicMember)
2042 if (m_savedBindingOuterScope) {
2043 m_currentScope = m_savedBindingOuterScope;
2044 m_savedBindingOuterScope = {};
2046 forgetFunctionExpression(publicMember->name.toString());
2050bool QQmlJSImportVisitor::visit(UiRequired *required)
2052 const QString name = required->name.toString();
2054 m_requiredProperties << RequiredProperty { m_currentScope, name,
2055 required->firstSourceLocation() };
2057 m_currentScope->setPropertyLocallyRequired(name,
true);
2061void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
2063 using namespace QQmlJS::AST;
2064 auto name = fexpr->name.toString();
2065 if (!name.isEmpty()) {
2066 QQmlJSMetaMethod method(name);
2067 method.setMethodType(QQmlJSMetaMethodType::Method);
2068 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
2070 if (!m_pendingMethodAnnotations.isEmpty()) {
2071 method.setAnnotations(m_pendingMethodAnnotations);
2072 m_pendingMethodAnnotations.clear();
2076 const bool parseTypes = m_scopesById.signaturesAreEnforced();
2078 bool formalsFullyTyped = parseTypes;
2079 bool anyFormalTyped =
false;
2080 PendingMethodTypeAnnotations pending{ m_currentScope, name, {} };
2083 for (
auto formals = fexpr->formals; formals; formals = formals->next) {
2084 PatternElement *e = formals->element;
2087 if (e->typeAnnotation && (e->bindingTarget || e->initializer))
2088 m_logger->log(
"Type annotations on default parameters are not supported"_L1,
2090 combine(e->firstSourceLocation(), e->lastSourceLocation()));
2093 if (
const auto *formals = parseTypes ? fexpr->formals :
nullptr) {
2094 const auto parameters = formals->formals();
2095 for (
const auto ¶meter : parameters) {
2096 const QString type = parameter.typeAnnotation
2097 ? parameter.typeAnnotation->type->toString()
2099 if (type.isEmpty()) {
2100 formalsFullyTyped =
false;
2101 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral(
"var")));
2102 pending.locations.emplace_back();
2104 anyFormalTyped =
true;
2105 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
2106 pending.locations.append(
2107 combine(parameter.typeAnnotation->firstSourceLocation(),
2108 parameter.typeAnnotation->lastSourceLocation()));
2114 method.setIsJavaScriptFunction(!formalsFullyTyped);
2120 if (parseTypes && fexpr->typeAnnotation) {
2121 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
2122 pending.locations.append(combine(fexpr->typeAnnotation->firstSourceLocation(),
2123 fexpr->typeAnnotation->lastSourceLocation()));
2124 }
else if (anyFormalTyped) {
2125 method.setReturnTypeName(QStringLiteral(
"void"));
2127 method.setReturnTypeName(QStringLiteral(
"var"));
2130 const auto &locs = pending.locations;
2131 if (std::any_of(locs.cbegin(), locs.cend(), [](
const auto &loc) {
return loc.isValid(); }))
2132 m_pendingMethodTypeAnnotations << pending;
2134 method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
2135 method.setOtherMethodIndex(
2136 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentScope->ownMethods().size()));
2138 if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) {
2140 const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
2141 ? fexpr->identifierToken
2142 : fexpr->functionToken;
2143 safeInsertJSIdentifier(m_currentScope, name,
2144 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2145 functionLocation, method.returnTypeName(),
2148 m_currentScope->addOwnMethod(method);
2150 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
2152 addFunctionOrExpression(m_currentScope, QStringLiteral(
"<anon>"));
2153 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral(
"<anon>"),
2154 fexpr->firstSourceLocation());
2158bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
2160 visitFunctionExpressionHelper(fexpr);
2164void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionExpression *fexpr)
2166 forgetFunctionExpression(fexpr->name.toString());
2170bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiSourceElement *srcElement)
2172 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
2176bool QQmlJSImportVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
2178 if (!fdecl->name.isEmpty()) {
2179 const QString name = fdecl->name.toString();
2180 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) {
2181 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax,
2182 fdecl->identifierToken);
2183 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax,
2184 previousDeclaration->location);
2187 visitFunctionExpressionHelper(fdecl);
2191void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *fdecl)
2193 forgetFunctionExpression(fdecl->name.toString());
2197bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast)
2199 QQmlJSMetaProperty prop;
2200 prop.setPropertyName(ast->name.toString());
2201 m_currentScope->addOwnProperty(prop);
2202 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2203 ast->firstSourceLocation());
2207void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
2213 QQmlJS::AST::ArgumentList *args)
2215 QStringView contextString;
2216 QStringView mainString;
2217 QStringView commentString;
2218 auto registerContextString = [&](QStringView string) {
2219 contextString = string;
2222 auto registerMainString = [&](QStringView string) {
2223 mainString = string;
2226 auto registerCommentString = [&](QStringView string) {
2227 commentString = string;
2230 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
2231 QV4::CompiledData::TranslationData data) {
2232 if (type == QV4::CompiledData::Binding::Type_Translation) {
2233 binding.setTranslation(mainString, commentString, contextString, data.number);
2234 }
else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
2235 binding.setTranslationId(mainString, data.number);
2237 binding.setStringLiteral(mainString);
2240 QmlIR::tryGeneratingTranslationBindingBase(
2242 registerMainString, registerCommentString, registerContextString, finalizeBinding);
2245QQmlJSImportVisitor::BindingExpressionParseResult
2246QQmlJSImportVisitor::parseBindingExpression(
2247 const QString &name,
const QQmlJS::AST::Statement *statement,
2248 const UiPublicMember *associatedPropertyDefinition)
2250 if (statement ==
nullptr)
2251 return BindingExpressionParseResult::Invalid;
2253 const auto *exprStatement = cast<
const ExpressionStatement *>(statement);
2255 if (exprStatement ==
nullptr) {
2256 QQmlJS::SourceLocation location = statement->firstSourceLocation();
2258 if (
const auto *block = cast<
const Block *>(statement); block && block->statements) {
2259 location = block->statements->firstSourceLocation();
2262 QQmlJSMetaPropertyBinding binding(location, name);
2263 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2264 QQmlSA::ScriptBindingKind::PropertyBinding, ScriptValue_Function);
2265 m_bindings.append(UnfinishedBinding {
2267 [binding = std::move(binding)]() {
return binding; }
2269 return BindingExpressionParseResult::Script;
2272 auto expr = exprStatement->expression;
2273 QQmlJSMetaPropertyBinding binding(
2274 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
2277 ScriptBindingValueType scriptBindingValuetype = ScriptValue_Unknown;
2279 switch (expr->kind) {
2280 case Node::Kind_TrueLiteral:
2281 binding.setBoolLiteral(
true);
2283 case Node::Kind_FalseLiteral:
2284 binding.setBoolLiteral(
false);
2286 case Node::Kind_NullExpression:
2287 binding.setNullLiteral();
2289 case Node::Kind_IdentifierExpression: {
2290 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
2292 if (idExpr->name == u"undefined")
2293 scriptBindingValuetype = ScriptValue_Undefined;
2296 case Node::Kind_FunctionDeclaration:
2297 case Node::Kind_FunctionExpression:
2298 case Node::Kind_Block: {
2299 scriptBindingValuetype = ScriptValue_Function;
2302 case Node::Kind_NumericLiteral:
2303 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
2305 case Node::Kind_StringLiteral:
2306 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
2308 case Node::Kind_RegExpLiteral:
2309 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
2311 case Node::Kind_TemplateLiteral: {
2312 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
2313 Q_ASSERT(templateLit);
2314 if (templateLit->hasNoSubstitution) {
2315 binding.setStringLiteral(templateLit->value);
2317 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2318 QQmlSA::ScriptBindingKind::PropertyBinding);
2319 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
2320 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
2321 expression->accept(
this);
2327 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
2328 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
2329 binding.setNumberLiteral(-lit->value);
2330 }
else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
2331 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
2332 handleTranslationBinding(binding, base->name, call->arguments);
2337 if (!binding.isValid()) {
2339 binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name),
2340 QQmlSA::ScriptBindingKind::PropertyBinding,
2341 scriptBindingValuetype);
2343 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
2346 if (binding.bindingType() == QQmlSA::BindingType::Translation
2347 || binding.bindingType() == QQmlSA::BindingType::TranslationById) {
2348 return BindingExpressionParseResult::Translation;
2350 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
2351 return BindingExpressionParseResult::Script;
2353 if (associatedPropertyDefinition)
2354 handleLiteralBinding(binding, associatedPropertyDefinition);
2356 return BindingExpressionParseResult::Literal;
2359bool QQmlJSImportVisitor::isImportPrefix(QString prefix)
const
2361 if (prefix.isEmpty() || !prefix.front().isUpper())
2364 return m_rootScopeImports.isNullType(prefix);
2367void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
2369 if (m_currentScope->scopeType() != QQmlJSScope::ScopeType::QMLScope) {
2370 m_logger->log(u"id declarations are only allowed in objects"_s, qmlSyntax,
2371 scriptBinding->statement->firstSourceLocation());
2374 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
2376 m_logger->log(u"id must be followed by an identifier"_s, qmlSyntax,
2377 scriptBinding->statement->firstSourceLocation());
2380 const QString name = [&]() {
2381 if (
const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2382 return idExpression->name.toString();
2383 else if (
const auto *idString = cast<StringLiteral *>(statement->expression)) {
2384 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2385 idString->firstSourceLocation());
2386 return idString->value.toString();
2388 m_logger->log(u"Failed to parse id"_s, qmlSyntax,
2389 statement->expression->firstSourceLocation());
2393 if (!name.isEmpty() && !name.front().isLower() && name.front() != u'_') {
2394 m_logger->log(u"Id must start with a lower case letter or an '_'"_s, qmlSyntax,
2395 statement->expression->firstSourceLocation());
2398 m_currentScope->setIdSourceLocation(combine(scriptBinding->statement->firstSourceLocation(),
2399 scriptBinding->statement->lastSourceLocation()));
2400 if (m_scopesById.existsAnywhereInDocument(name)) {
2403 breakInheritanceCycles(m_currentScope);
2404 m_scopesById.possibleScopes(
2405 name, m_currentScope, QQmlJSScopesByIdOption::Default,
2406 [&](
const QQmlJSScope::ConstPtr &otherScopeWithID,
2407 QQmlJSScopesById::Confidence confidence) {
2409 Q_UNUSED(confidence);
2411 auto otherLocation = otherScopeWithID->sourceLocation();
2415 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2416 name, QString::number(otherLocation.startLine),
2417 QString::number(otherLocation.startColumn)),
2418 qmlSyntaxDuplicateIds,
2419 scriptBinding->firstSourceLocation());
2420 return QQmlJSScopesById::CallbackResult::ContinueSearch;
2423 if (!name.isEmpty())
2424 m_scopesById.insert(name, m_currentScope);
2427void QQmlJSImportVisitor::handleLiteralBinding(
const QQmlJSMetaPropertyBinding &binding,
2428 const UiPublicMember *associatedPropertyDefinition)
2432 Q_UNUSED(associatedPropertyDefinition);
2436
2437
2438
2439
2440
2443 const QQmlJS::SourceLocation &srcLocation)
2445 const auto createBinding = [=]() {
2446 const QQmlJSScope::ScopeType type = scope->scopeType();
2453 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2454 const bool alreadyHasBinding =
std::any_of(propertyBindings.first, propertyBindings.second,
2455 [&](
const QQmlJSMetaPropertyBinding &binding) {
2456 return binding.bindingType() == bindingType;
2458 if (alreadyHasBinding)
2459 return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {});
2462 if (type == QQmlSA::ScopeType::GroupedPropertyScope)
2463 binding.setGroupBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2465 binding.setAttachedBinding(
static_cast<QSharedPointer<QQmlJSScope>>(scope));
2468 return { scope->parentScope(), createBinding };
2471bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
2473 Q_ASSERT(!m_savedBindingOuterScope);
2474 Q_ASSERT(!m_thisScriptBindingIsJavaScript);
2475 m_savedBindingOuterScope = m_currentScope;
2476 const auto id = scriptBinding->qualifiedId;
2477 if (!id->next && id->name == QLatin1String(
"id")) {
2478 handleIdDeclaration(scriptBinding);
2485 for (; group->next; group = group->next) {
2486 const QString name = group->name.toString();
2490 if (group == id && isImportPrefix(name)) {
2491 prefix = name + u'.';
2495 const bool isAttachedProperty = name.front().isUpper();
2496 if (isAttachedProperty) {
2498 enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name,
2499 group->firstSourceLocation());
2502 enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name,
2503 group->firstSourceLocation());
2505 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name,
2506 group->firstSourceLocation()));
2511 const auto name = group->name.toString();
2515 const auto signal = QQmlSignalNames::handlerNameToSignalName(name);
2517 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2518 m_propertyBindings[m_currentScope].append(
2519 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2521 auto result = parseBindingExpression(name, scriptBinding->statement);
2522 m_thisScriptBindingIsJavaScript = (result == BindingExpressionParseResult::Script);
2524 const auto statement = scriptBinding->statement;
2525 QStringList signalParameters;
2527 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2528 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2529 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2530 signalParameters << formal->element->bindingIdentifier.toString();
2534 QQmlJSMetaMethod scopeSignal;
2535 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2536 if (!methods.isEmpty())
2537 scopeSignal = methods[0];
2539 const auto firstSourceLocation = statement->firstSourceLocation();
2540 bool hasMultilineStatementBody =
2541 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2542 m_pendingSignalHandler = firstSourceLocation;
2543 m_signalHandlers.insert(firstSourceLocation,
2544 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2548 const auto index = addFunctionOrExpression(m_currentScope, name);
2549 const auto createBinding = [
2551 scope = m_currentScope,
2552 signalName = *signal,
2555 firstSourceLocation,
2556 groupLocation = group->firstSourceLocation(),
2557 signalParameters]() {
2559 Q_ASSERT(scope->isFullyResolved());
2560 QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Invalid;
2561 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2562 if (!methods.isEmpty()) {
2563 kind = QQmlSA::ScriptBindingKind::SignalHandler;
2564 checkSignal(scope, groupLocation, name, signalParameters);
2565 }
else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2566 kind = QQmlSA::ScriptBindingKind::ChangeHandler;
2567 checkSignal(scope, groupLocation, name, signalParameters);
2568 }
else if (scope->hasProperty(name)) {
2571 kind = QQmlSA::ScriptBindingKind::PropertyBinding;
2572 m_signalHandlers.remove(firstSourceLocation);
2575 checkSignal(scope, groupLocation, name, signalParameters);
2578 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2579 binding.setScriptBinding(index, kind, ScriptValue_Function);
2582 m_bindings.append(UnfinishedBinding { m_currentScope, createBinding });
2583 m_thisScriptBindingIsJavaScript =
true;
2589 while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope
2590 || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) {
2595 enterEnvironment(QQmlSA::ScopeType::SignalHandlerFunctionScope,
2597 scriptBinding->statement->firstSourceLocation());
2599 enterEnvironment(QQmlSA::ScopeType::BindingFunctionScope,
2601 scriptBinding->statement->firstSourceLocation());
2607void QQmlJSImportVisitor::endVisit(UiScriptBinding *)
2609 if (m_savedBindingOuterScope) {
2610 m_currentScope = m_savedBindingOuterScope;
2611 m_savedBindingOuterScope = {};
2617 if (m_thisScriptBindingIsJavaScript) {
2618 m_thisScriptBindingIsJavaScript =
false;
2619 Q_ASSERT(!m_functionStack.isEmpty());
2620 m_functionStack.pop();
2624bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding)
2626 createAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2627 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2628 arrayBinding->firstSourceLocation());
2629 m_currentScope->setIsArrayScope(
true);
2633void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
2640 const auto children = m_currentScope->childScopes();
2643 const int scopesEnteredCounter = openAttachedAndGroupedScopes(arrayBinding->qualifiedId);
2644 auto guard = qScopeGuard([
this, scopesEnteredCounter]() {
2645 for (
int i = 0; i < scopesEnteredCounter; ++i)
2649 if (checkCustomParser(m_currentScope)) {
2655 auto group = arrayBinding->qualifiedId;
2656 for (; group->next; group = group->next) { }
2657 const QString propertyName = group->name.toString();
2660 for (
auto element = arrayBinding->members; element; element = element->next, ++i) {
2661 const auto &type = children[i];
2662 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2663 m_logger->log(u"Declaring an object which is not a Qml object"
2664 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2667 m_pendingPropertyObjectBindings
2668 << PendingPropertyObjectBinding { m_currentScope, type, propertyName,
2669 element->firstSourceLocation(),
false };
2670 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2671 binding.setObject(QQmlJSUtils::getScopeName(type, QQmlSA::ScopeType::QMLScope),
2672 QQmlJSScope::ConstPtr(type));
2673 m_bindings.append(UnfinishedBinding {
2675 [binding = std::move(binding)]() {
return binding; },
2676 QQmlJSScope::ListPropertyTarget
2681bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
2683 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2684 qmlEnum.setIsQml(
true);
2685 qmlEnum.setLineNumber(uied->enumToken.startLine);
2686 for (
const auto *member = uied->members; member; member = member->next) {
2687 qmlEnum.addKey(member->member.toString());
2688 qmlEnum.addValue(
int(member->value));
2690 m_currentScope->addOwnEnumeration(qmlEnum);
2694void QQmlJSImportVisitor::addImportWithLocation(
2695 const QString &name,
const QQmlJS::SourceLocation &loc,
bool hadWarnings)
2697 if (m_importTypeLocationMap.contains(name)
2698 && m_importTypeLocationMap.values(name).contains(loc)) {
2702 m_importTypeLocationMap.insert(name, loc);
2707 if (!hadWarnings && loc.isValid())
2708 m_importLocations.insert(loc);
2711QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromHost(
2712 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2714 QFileInfo fileInfo(path);
2715 if (!fileInfo.exists()) {
2716 m_logger->log(
"File or directory you are trying to import does not exist: %1."_L1.arg(path),
2717 qmlImport, location);
2721 if (fileInfo.isFile()) {
2722 const auto scope = m_importer->importFile(path);
2723 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2724 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2725 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2726 addImportWithLocation(actualPrefix, location,
false);
2730 if (fileInfo.isDir()) {
2731 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2732 auto scopes = m_importer->importDirectory(path, precedence, prefix);
2733 const auto types = scopes.types();
2734 const auto warnings = scopes.warnings();
2735 m_rootScopeImports.add(std::move(scopes));
2736 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2737 addImportWithLocation(*it, location, !warnings.isEmpty());
2742 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2744 qmlImport, location);
2748QList<QQmlJS::DiagnosticMessage> QQmlJSImportVisitor::importFromQrc(
2749 const QString &path,
const QString &prefix,
const QQmlJS::SourceLocation &location)
2751 Q_ASSERT(path.startsWith(u':'));
2752 const QQmlJSResourceFileMapper *mapper = m_importer->resourceFileMapper();
2756 const auto pathNoColon = QStringView(path).mid(1);
2757 if (mapper->isFile(pathNoColon)) {
2758 const auto entry = m_importer->resourceFileMapper()->entry(
2759 QQmlJSResourceFileMapper::resourceFileFilter(pathNoColon.toString()));
2760 const auto scope = m_importer->importFile(entry.filePath);
2761 const QString actualPrefix =
2762 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2763 auto precedence = quint8(QQmlJS::PrecedenceValues::Default);
2764 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision(), precedence });
2765 addImportWithLocation(actualPrefix, location,
false);
2769 auto scopes = m_importer->importDirectory(path, quint8(QQmlJS::PrecedenceValues::Default), prefix);
2770 const auto types = scopes.types();
2771 const auto warnings = scopes.warnings();
2772 m_rootScopeImports.add(std::move(scopes));
2773 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2774 addImportWithLocation(*it, location, !warnings.isEmpty());
2778bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
2781 QString prefix = QLatin1String(
"");
2782 if (import->asToken.isValid()) {
2783 prefix += import->importId;
2784 if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
2785 m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
2787 qmlImport, import->importIdToken,
true,
true);
2789 m_seenModuleQualifiers.append(prefix);
2792 const QString filename = import->fileName.toString();
2793 if (!filename.isEmpty()) {
2794 const QUrl url(filename);
2795 const QString scheme = url.scheme();
2796 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2797 if (scheme ==
""_L1) {
2798 QFileInfo fileInfo(url.path());
2799 QString absolute = fileInfo.isRelative()
2800 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2802 auto warnings = absolute.startsWith(u':')
2803 ? importFromQrc(absolute, prefix, importLocation)
2804 : importFromHost(absolute, prefix, importLocation);
2805 processImportWarnings(
"path \"%1\""_L1.arg(url.path()), warnings, importLocation);
2807 }
else if (scheme ==
"file"_L1) {
2808 auto warnings = importFromHost(url.path(), prefix, importLocation);
2809 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2811 }
else if (scheme ==
"qrc"_L1) {
2812 auto warnings = importFromQrc(
":"_L1 + url.path(), prefix, importLocation);
2813 processImportWarnings(
"URL \"%1\""_L1.arg(url.path()), warnings, importLocation);
2816 m_logger->log(
"Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2817 qmlImport, import->firstSourceLocation());
2821 const QString path = buildName(import->importUri);
2823 QStringList staticModulesProvided;
2825 auto imported = m_importer->importModule(
2826 path, quint8(QQmlJS::PrecedenceValues::Default), prefix,
2827 import->version ? import->version->version : QTypeRevision(), &staticModulesProvided);
2828 const auto types = imported.types();
2829 const auto warnings = imported.warnings();
2830 m_rootScopeImports.add(std::move(imported));
2831 for (
auto it = types.keyBegin(), end = types.keyEnd(); it != end; it++)
2832 addImportWithLocation(*it, import->firstSourceLocation(), !warnings.isEmpty());
2834 if (prefix.isEmpty()) {
2835 for (
const QString &staticModule : std::as_const(staticModulesProvided)) {
2837 if (path != staticModule && m_importStaticModuleLocationMap.contains(staticModule))
2840 m_importStaticModuleLocationMap[staticModule] = import->firstSourceLocation();
2844 processImportWarnings(
2845 QStringLiteral(
"module \"%1\"").arg(path), warnings, import->firstSourceLocation());
2849#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
2851void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2853 for (
const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2858void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2860 assign(pragma->value);
2864bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiPragma *pragma)
2866 if (pragma->name == u"Strict"_s) {
2871 if (!m_logger->wasCategoryChanged(qmlCompiler))
2872 m_logger->setCategorySeverity(qmlCompiler, QQmlJS::WarningSeverity::Warning);
2873 }
else if (pragma->name == u"ComponentBehavior") {
2874 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2875 if (value == u"Bound") {
2876 m_scopesById.setComponentsAreBound(
true);
2877 }
else if (value == u"Unbound") {
2878 m_scopesById.setComponentsAreBound(
false);
2880 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2881 qmlSyntax, pragma->firstSourceLocation());
2884 }
else if (pragma->name == u"FunctionSignatureBehavior") {
2885 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2886 if (value == u"Enforced") {
2887 m_scopesById.setSignaturesAreEnforced(
true);
2888 }
else if (value == u"Ignored") {
2889 m_scopesById.setSignaturesAreEnforced(
false);
2892 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2893 qmlSyntax, pragma->firstSourceLocation());
2896 }
else if (pragma->name == u"ValueTypeBehavior") {
2897 handlePragmaValues(pragma, [
this, pragma](QStringView value) {
2898 if (value == u"Copy") {
2900 }
else if (value == u"Reference") {
2902 }
else if (value == u"Addressable") {
2903 m_scopesById.setValueTypesAreAddressable(
true);
2904 }
else if (value == u"Inaddressable") {
2905 m_scopesById.setValueTypesAreAddressable(
false);
2906 }
else if (value == u"Assertable") {
2907 m_scopesById.setValueTypesAreAssertable(
true);
2908 }
else if (value == u"Inassertable") {
2909 m_scopesById.setValueTypesAreAssertable(
false);
2911 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2912 qmlSyntax, pragma->firstSourceLocation());
2920void QQmlJSImportVisitor::throwRecursionDepthError()
2922 m_logger->log(QStringLiteral(
"Maximum statement or expression depth exceeded"),
2923 qmlRecursionDepthErrors, QQmlJS::SourceLocation());
2926bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
2928 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(),
2929 ast->firstSourceLocation());
2933void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
2938bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast)
2940 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"forloop"),
2941 ast->firstSourceLocation());
2945void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *)
2950bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast)
2952 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"foreachloop"),
2953 ast->firstSourceLocation());
2957void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
2962bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast)
2964 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"block"),
2965 ast->firstSourceLocation());
2967 if (m_pendingSignalHandler.isValid())
2968 flushPendingSignalParameters();
2973void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *)
2978bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast)
2980 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"case"),
2981 ast->firstSourceLocation());
2985void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *)
2990bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
2992 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"catch"),
2993 catchStatement->firstSourceLocation());
2997void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *)
3002bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast)
3004 enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"with"),
3005 ast->firstSourceLocation());
3007 m_logger->log(QStringLiteral(
"with statements are strongly discouraged in QML "
3008 "and might cause false positives when analysing unqualified "
3010 qmlWith, ast->firstSourceLocation());
3015void QQmlJSImportVisitor::endVisit(QQmlJS::AST::WithStatement *)
3020bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
3022 const auto &boundedNames = fpl->boundNames();
3023 for (
auto const &boundName : boundedNames) {
3025 std::optional<QString> typeName;
3026 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
3027 if (Type *type = annotation->type)
3028 typeName = type->toString();
3029 safeInsertJSIdentifier(m_currentScope, boundName.id,
3030 { QQmlJSScope::JavaScriptIdentifier::Parameter,
3031 boundName.location, typeName,
false });
3036void QQmlJSImportVisitor::createAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3038 bool needsResolution =
false;
3039 int scopesEnteredCounter = 0;
3041 for (
auto group = propertyName; group->next; group = group->next) {
3042 const QString idName = group->name.toString();
3044 if (idName.isEmpty())
3047 if (group == propertyName && isImportPrefix(idName)) {
3048 prefix = idName + u'.';
3052 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3053 : QQmlSA::ScopeType::GroupedPropertyScope;
3056 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3058 m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + idName,
3059 group->firstSourceLocation()));
3061 ++scopesEnteredCounter;
3062 needsResolution = needsResolution || !exists;
3067 for (
int i=0; i < scopesEnteredCounter; ++i) {
3072 if (needsResolution) {
3073 QQmlJSScope::resolveTypes(
3074 m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3078bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
3082 Q_ASSERT(uiob->qualifiedTypeNameId);
3084 const QString typeName = buildName(uiob->qualifiedTypeNameId);
3085 if (typeName.front().isLower() && typeName.contains(u'.')) {
3086 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
3089 createAttachedAndGroupedScopes(uiob->qualifiedId);
3091 enterEnvironment(QQmlSA::ScopeType::QMLScope, typeName,
3092 uiob->qualifiedTypeNameId->identifierToken);
3094 m_qmlTypes.append(m_currentScope);
3095 m_objectBindingScopes << m_currentScope;
3099int QQmlJSImportVisitor::openAttachedAndGroupedScopes(UiQualifiedId *propertyName)
3102 int scopesEnteredCounter = 0;
3103 auto group = propertyName;
3104 for (; group->next; group = group->next) {
3105 const QString idName = group->name.toString();
3107 if (idName.isEmpty())
3110 if (group == propertyName && isImportPrefix(idName)) {
3111 prefix = idName + u'.';
3115 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
3116 : QQmlSA::ScopeType::GroupedPropertyScope;
3118 [[maybe_unused]]
bool exists =
3119 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
3121 scopesEnteredCounter++;
3125 return scopesEnteredCounter;
3128void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
3130 QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3132 const QQmlJSScope::Ptr childScope = m_currentScope;
3135 const int scopesEnteredCounter = openAttachedAndGroupedScopes(uiob->qualifiedId);
3140 auto group = uiob->qualifiedId;
3141 for (; group->next; group = group->next) { }
3142 const QString propertyName = group->name.toString();
3144 if (m_currentScope->isNameDeferred(propertyName)) {
3145 bool foundIds =
false;
3146 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
3148 while (!childScopes.isEmpty()) {
3149 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
3150 m_scopesById.possibleIds(
3151 scope, scope, QQmlJSScopesByIdOption::Default,
3152 [&](
const QString &id, QQmlJSScopesById::Confidence confidence) {
3155 Q_UNUSED(confidence);
3157 return QQmlJSScopesById::CallbackResult::StopSearch;
3160 childScopes << scope->childScopes();
3165 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
3167 qmlDeferredPropertyId, uiob->firstSourceLocation());
3171 if (checkCustomParser(m_currentScope)) {
3175 m_pendingPropertyObjectBindings
3176 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
3177 uiob->firstSourceLocation(), uiob->hasOnToken };
3179 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
3180 if (uiob->hasOnToken) {
3181 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
3182 binding.setInterceptor(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3183 QQmlJSScope::ConstPtr(childScope));
3185 binding.setValueSource(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3186 QQmlJSScope::ConstPtr(childScope));
3189 binding.setObject(QQmlJSUtils::getScopeName(childScope, QQmlSA::ScopeType::QMLScope),
3190 QQmlJSScope::ConstPtr(childScope));
3192 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() {
return binding; } });
3195 for (
int i = 0; i < scopesEnteredCounter; ++i)
3199bool QQmlJSImportVisitor::visit(ExportDeclaration *)
3201 Q_ASSERT(rootScopeIsValid());
3202 Q_ASSERT(m_exportedRootScope != m_globalScope);
3203 Q_ASSERT(m_currentScope == m_globalScope);
3204 m_currentScope = m_exportedRootScope;
3208void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
3210 Q_ASSERT(rootScopeIsValid());
3211 m_currentScope = m_exportedRootScope->parentScope();
3212 Q_ASSERT(m_currentScope == m_globalScope);
3215bool QQmlJSImportVisitor::visit(ESModule *module)
3217 Q_ASSERT(!rootScopeIsValid());
3218 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral(
"module"),
3219 module->firstSourceLocation());
3220 m_currentScope->setIsScript(
true);
3221 importBaseModules();
3226void QQmlJSImportVisitor::endVisit(ESModule *)
3228 QQmlJSScope::resolveTypes(
3229 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3232bool QQmlJSImportVisitor::visit(Program *program)
3234 Q_ASSERT(m_globalScope == m_currentScope);
3235 Q_ASSERT(!rootScopeIsValid());
3236 enterRootScope(QQmlSA::ScopeType::JSFunctionScope, u"script"_s, program->firstSourceLocation());
3237 m_exportedRootScope->setIsScript(
true);
3238 importBaseModules();
3242void QQmlJSImportVisitor::endVisit(Program *)
3244 QQmlJSScope::resolveTypes(
3245 m_exportedRootScope, m_rootScopeImports.contextualTypes(), &m_usedTypes);
3248void QQmlJSImportVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
3252 const QString name = fieldMember->name.toString();
3253 if (m_importTypeLocationMap.contains(name)) {
3254 const QQmlJSImportedScope type = m_rootScopeImports.type(name);
3255 if (type.scope.isNull()) {
3256 if (m_rootScopeImports.hasType(name))
3257 m_usedTypes.insert(name);
3258 }
else if (!type.scope->ownAttachedTypeName().isEmpty()) {
3259 m_usedTypes.insert(name);
3264bool QQmlJSImportVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
3266 const QString name = idexp->name.toString();
3267 if (m_importTypeLocationMap.contains(name)) {
3268 m_usedTypes.insert(name);
3274bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
3277 if (element->isVariableDeclaration()) {
3278 QQmlJS::AST::BoundNames names;
3279 element->boundNames(&names);
3280 for (
const auto &name : std::as_const(names)) {
3281 std::optional<QString> typeName;
3282 if (TypeAnnotation *annotation = name.typeAnnotation.data())
3283 if (Type *type = annotation->type)
3284 typeName = type->toString();
3285 using Kind = QQmlJSScope::JavaScriptIdentifier::Kind;
3286 const Kind kind = (element->scope == QQmlJS::AST::VariableScope::Var)
3287 ? Kind::FunctionScoped
3288 : Kind::LexicalScoped;
3289 const QString variableName = name.id;
3290 if (kind == Kind::LexicalScoped) {
3291 const QQmlJS::SourceLocation location = element->firstSourceLocation();
3292 if (
auto previousDeclaration = m_currentScope->ownJSIdentifier(variableName)) {
3293 m_logger->log(
"Identifier '%1' has already been declared"_L1.arg(variableName), qmlSyntax,
3295 m_logger->log(
"Note: previous declaration of '%1' here"_L1.arg(variableName), qmlSyntax,
3296 previousDeclaration->location);
3299 const bool isConstVariable = element->scope == QQmlJS::AST::VariableScope::Const;
3300 const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
3302 { (element->scope == QQmlJS::AST::VariableScope::Var)
3303 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
3304 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
3305 name.location, typeName,
3315bool QQmlJSImportVisitor::visit(IfStatement *statement)
3317 if (BinaryExpression *binary = cast<BinaryExpression *>(statement->expression)) {
3318 if (binary->op == QSOperator::Assign) {
3320 "Assignment in condition: did you mean to use \"===\" or \"==\" instead of \"=\"?"_L1,
3321 qmlAssignmentInCondition, binary->operatorToken);