28 QQmlJSTypePropagator::generate_Ret();
30 if (m_function->isSignalHandler) {
32 }
else if (m_state.accumulatorIn().contains(m_typeResolver->voidType())) {
34 }
else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()) {
35 if (m_function->isFullyTyped) {
37 m_logger->log(u"Function without return type annotation returns %1"_s.arg(
38 m_state.accumulatorIn().containedTypeName()),
39 qmlIncompatibleType, currentFunctionSourceLocation());
41 }
else if (!canConvertFromTo(m_state.accumulatorIn(), m_returnType)) {
42 m_logger->log(u"Cannot assign binding of type %1 to %2"_s.arg(
43 m_state.accumulatorIn().containedTypeName(),
44 m_returnType.containedTypeName()),
45 qmlIncompatibleType, currentFunctionSourceLocation());
48 const QQmlJS::SourceLocation location = m_function->isProperty
49 ? currentFunctionSourceLocation()
50 : currentNonEmptySourceLocation();
51 QQmlSA::PassManagerPrivate::get(m_passManager)
53 QQmlJSScope::createQQmlSAElement(m_function->qmlScope.containedType()),
54 QQmlJSScope::createQQmlSAElement(m_state.accumulatorIn().containedType()),
55 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(location));
60 QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(index);
62 Q_ASSERT(m_idMemberShadows);
64 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
65 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
67 const auto qmlScope = m_function->qmlScope.containedType();
68 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
69 QQmlJSScope::createQQmlSAElement(qmlScope), name,
70 QQmlJSScope::createQQmlSAElement(qmlScope),
71 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
72 currentNonEmptySourceLocation()));
74 const auto &accumulatorOut = m_state.accumulatorOut();
75 if (!accumulatorOut.isValid())
78 const QQmlJSScope::ConstPtr scope = accumulatorOut.scopeType();
79 const QQmlJSScope::ConstPtr idScope = m_scopesById.scope(name, scope);
80 if (!idScope.isNull()) {
81 const auto log = [&](
const auto &memberType,
const auto &memberOwnerScope) {
85 if (m_idMemberShadows->contains(idMemberShadow))
88 m_idMemberShadows->insert(std::move(idMemberShadow));
89 const auto useLoc = currentSourceLocation();
90 m_logger->log(
"Id for object %1 shadows %2 \"%3\". Rename one or the other."_L1
91 .arg(idScope->baseTypeName(), memberType, name),
92 qmlIdShadowsMember, useLoc);
93 m_logger->log(
"Note: Id defined here"_L1, qmlIdShadowsMember,
94 idScope->idSourceLocation(),
true,
true, {}, useLoc.startLine);
97 if (scope->hasProperty(name)) {
98 log(
"property"_L1, scope->ownerOfProperty(scope, name).scope);
99 }
else if (scope->hasMethod(name)) {
100 const auto methods = scope->methods(name);
101 const auto &method = methods[0];
102 if (method.methodType() == QQmlSA::MethodType::Method)
103 log(
"method"_L1, scope->ownerOfMethod(scope, name).scope);
104 else if (method.methodType() == QQmlSA::MethodType::Signal)
105 log(
"signal"_L1, scope->ownerOfMethod(scope, name).scope);
130 QQmlJSTypePropagator::generate_StoreProperty(nameIndex, base);
132 auto callBase = m_state.registers[base].content;
133 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
134 const bool isAttached = callBase.variant() == QQmlJSRegisterContent::Attachment;
136 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
137 QQmlJSScope::createQQmlSAElement(callBase.containedType()),
139 QQmlJSScope::createQQmlSAElement(
140 m_state.accumulatorIn().containedType()),
141 QQmlJSScope::createQQmlSAElement(isAttached
142 ? callBase.attachee().containedType()
143 : m_function->qmlScope.containedType()),
144 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
145 currentNonEmptySourceLocation()));
150 QQmlJSTypePropagator::generate_CallProperty(nameIndex, base, argc, argv);
152 const auto saCheck = [&](
const QString &propertyName,
const QQmlJSScope::ConstPtr &baseType) {
153 const QQmlSA::Element saBaseType{ QQmlJSScope::createQQmlSAElement(baseType) };
154 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
155 m_function->qmlScope.containedType()) };
156 const QQmlSA::SourceLocation saLocation{
157 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
160 QQmlSA::PassManagerPrivate::get(m_passManager)
161 ->analyzeRead(saBaseType, propertyName, saContainedType, saLocation);
162 QQmlSA::PassManagerPrivate::get(m_passManager)
163 ->analyzeCall(saBaseType, propertyName, saContainedType, saLocation);
166 const auto callBase = m_state.registers[base].content;
167 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
168 const auto member = m_typeResolver->memberType(callBase, propertyName);
170 const bool isLoggingMethod = QQmlJSTypePropagator::isLoggingMethod(propertyName);
171 if (callBase.contains(m_typeResolver->mathObject()))
172 saCheck(propertyName, callBase.containedType());
173 else if (callBase.contains(m_typeResolver->consoleObject()) && isLoggingMethod)
174 saCheck(propertyName, callBase.containedType());
175 else if (!member.isMethod()) {
176 if (callBase.contains(m_typeResolver->jsValueType())
177 || callBase.contains(m_typeResolver->varType())) {
178 saCheck(propertyName, callBase.containedType());
185 QQmlJSTypePropagator::generate_CallPossiblyDirectEval(argc, argv);
187 const QQmlSA::SourceLocation saLocation{
188 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
190 const QQmlSA::Element saBaseType{ QQmlJSScope::createQQmlSAElement(
191 m_typeResolver->jsGlobalObject()) };
192 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
193 m_function->qmlScope.containedType()) };
195 QQmlSA::PassManagerPrivate::get(m_passManager)
196 ->analyzeCall(saBaseType,
"eval"_L1, saContainedType, saLocation);
201 QQmlJSTypePropagator::handleUnqualifiedAccess(name, isMethod);
203 auto location = currentSourceLocation();
205 const auto qmlScopeContained = m_function->qmlScope.containedType();
206 if (qmlScopeContained->isInCustomParserParent()) {
208 if (qmlScopeContained->baseType().isNull()
209 || qmlScopeContained->baseType()->internalName() != u"QQmlConnections"_s)
214 if (isCallingProperty(qmlScopeContained, name))
216 }
else if (propertyResolution(qmlScopeContained, name) != PropertyMissing) {
220 std::optional<QQmlJSFixSuggestion> suggestion;
222 const auto childScopes = m_function->qmlScope.containedType()->childScopes();
223 for (qsizetype i = 0, end = childScopes.size(); i < end; i++) {
224 auto &scope = childScopes[i];
225 if (location.offset > scope->sourceLocation().offset) {
227 && childScopes.at(i + 1)->sourceLocation().offset < location.offset)
229 if (scope->childScopes().size() == 0)
232 const auto jsId = scope->childScopes().first()->jsIdentifier(name);
234 if (jsId.has_value() && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
235 const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
237 QQmlJS::SourceLocation fixLocation = id.location;
238 Q_UNUSED(fixLocation)
239 fixLocation.length = 0;
241 const auto handler = m_typeResolver->signalHandlers()[id.location];
243 QString fixString = handler.isMultiline ? u"function("_s : u"("_s;
244 const auto parameters = handler.signalParameters;
245 for (
int numParams = parameters.size(); numParams > 0; --numParams) {
246 fixString += parameters.at(parameters.size() - numParams);
248 fixString += u", "_s;
251 fixString += handler.isMultiline ? u") "_s : u") => "_s;
253 suggestion = QQmlJSFixSuggestion {
254 name + u" is accessible in this scope because you are handling a signal"
255 " at %1:%2. Use a function instead.\n"_s
256 .arg(id.location.startLine)
257 .arg(id.location.startColumn),
261 suggestion->setAutoApplicable();
271 const auto qmlScope = m_function->qmlScope.containedType();
272 if (name == u"model" || name == u"index") {
273 if (
const QQmlJSScope::ConstPtr parent = qmlScope->parentScope(); !parent.isNull()) {
274 const auto bindings = parent->ownPropertyBindings(u"delegate"_s);
276 for (
auto it = bindings.first; it != bindings.second; it++) {
277 if (!it->hasObject())
279 if (it->objectType() == qmlScope) {
280 suggestion = QQmlJSFixSuggestion {
281 "'%1' is implicitly injected into this delegate. "
282 "Add a required property '%1' to the delegate instead."_L1
284 qmlScope->sourceLocation()
293 if (!suggestion.has_value()) {
294 for (QQmlJSScope::ConstPtr scope = qmlScope; !scope.isNull(); scope = scope->parentScope()) {
295 if (scope->hasProperty(name)) {
296 QQmlJSScopesById::MostLikelyCallback<QString> id;
297 m_function->addressableScopes.possibleIds(scope, qmlScope, Default, id);
299 QQmlJS::SourceLocation fixLocation = location;
300 fixLocation.length = 0;
301 QString m =
"%1 is a member of a parent element.\n You can qualify the "
302 "access with its id to avoid this warning%2.\n"_L1.arg(name);
303 m = m.arg(id.result.isEmpty() ?
" (You first have to give the element an id)"_L1 :
""_L1);
305 suggestion = QQmlJSFixSuggestion{
306 m, fixLocation, (id.result.isEmpty() ? u"<id>."_s : (id.result + u'.'))
309 if (!id.result.isEmpty())
310 suggestion->setAutoApplicable();
315 if (!suggestion.has_value() && !m_function->addressableScopes.componentsAreBound()
316 && m_function->addressableScopes.existsAnywhereInDocument(name)) {
317 const QLatin1String replacement =
"pragma ComponentBehavior: Bound"_L1;
318 QQmlJSFixSuggestion bindComponents {
319 "Set \"%1\" in order to use IDs from outer components in nested components."_L1
321 QQmlJS::s_documentOrigin,
322 replacement +
'\n'_L1
324 bindComponents.setAutoApplicable();
325 suggestion = std::move(bindComponents);
328 if (!suggestion.has_value()) {
329 if (
auto didYouMean = QQmlJSUtils::didYouMean(
330 name, qmlScope->properties().keys() + qmlScope->methods().keys(), location);
331 didYouMean.has_value()) {
332 suggestion = std::move(didYouMean);
336 m_logger->log(QLatin1String(
"Unqualified access"), qmlUnqualified, location,
true,
true,
355 const QString &name,
bool isMethod)
const
357 QQmlJSTypePropagator::handleUnqualifiedAccessAndContextProperties(name, isMethod);
359 if (m_contextPropertyInfo.userContextProperties.isUnqualifiedAccessDisabled(name))
362 const auto warningMessage = [&name,
this]() {
364 "Potential context property access detected."
365 " Context properties are discouraged in QML: use normal, required, or singleton properties instead."_L1;
367 if (shouldMentionRequiredProperties(m_function->qmlScope.containedType())) {
369 "\nNote: '%1' assumed to be a potential context property because it is not declared as required property."_L1
375 if (m_contextPropertyInfo.userContextProperties.isOnUsageWarned(name)) {
376 m_logger->log(warningMessage(), qmlContextProperties, currentSourceLocation());
381 handleUnqualifiedAccess(name, isMethod);
383 const QList<QQmlJS::HeuristicContextProperty> definitions =
384 m_contextPropertyInfo.heuristicContextProperties.definitionsForName(name);
385 if (definitions.isEmpty())
387 QString warning = warningMessage();
388 for (
const auto &candidate : definitions) {
389 warning.append(
"\nNote: candidate context property declaration '%1' at %2:%3:%4"_L1.arg(
390 name, QDir::cleanPath(candidate.filename),
391 QString::number(candidate.location.startLine),
392 QString::number(candidate.location.startColumn)));
394 m_logger->log(warning, qmlContextProperties, currentSourceLocation());
400 QQmlJSTypePropagator::checkDeprecated(scope, name, isMethod);
402 Q_ASSERT(!scope.isNull());
403 auto qmlScope = QQmlJSScope::findCurrentQMLScope(scope);
404 if (qmlScope.isNull())
407 QList<QQmlJSAnnotation> annotations;
409 QQmlJSMetaMethod method;
412 const QList<QQmlJSMetaMethod> methods = qmlScope->methods(name);
413 if (methods.isEmpty())
415 method = methods.constFirst();
416 annotations = method.annotations();
418 QQmlJSMetaProperty property = qmlScope->property(name);
419 if (!property.isValid())
421 annotations = property.annotations();
424 auto deprecationAnn = std::find_if(
425 annotations.constBegin(), annotations.constEnd(),
426 [](
const QQmlJSAnnotation &annotation) {
return annotation.isDeprecation(); });
428 if (deprecationAnn == annotations.constEnd())
431 QQQmlJSDeprecation deprecation = deprecationAnn->deprecation();
433 QString descriptor = name;
435 descriptor += u'(' + method.parameterNames().join(u", "_s) + u')';
437 QString message =
"%1 \"%2\" is deprecated"_L1
438 .arg(isMethod ? u"Method"_s : u"Property"_s, descriptor);
440 if (!deprecation.reason.isEmpty())
441 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
443 m_logger->log(message, qmlDeprecated, currentSourceLocation());
447 const QString &name)
const
449 const bool res = QQmlJSTypePropagator::isCallingProperty(scope, name);
451 if (
const auto property = scope->property(name); property.isValid()) {
453 if (property.type() == m_typeResolver->varType()) {
454 errorType = u"a var property. It may or may not be a method. "_s
455 u"Use a regular function instead."_s;
456 }
else if (property.type() == m_typeResolver->jsValueType()) {
457 errorType = u"a QJSValue property. It may or may not be a method. "_s
458 u"Use a regular Q_INVOKABLE instead."_s;
460 errorType = u"not a method"_s;
463 m_logger->log(u"Property \"%1\" is %2"_s.arg(name, errorType),
464 qmlUseProperFunction, currentSourceLocation(),
true,
true, {});
472 const auto res = QQmlJSTypePropagator::handleImportNamespaceLookup(propertyName);
474 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
475 if (m_typeResolver->isPrefix(propertyName)) {
476 if (!accumulatorIn.containedType()->isReferenceType()) {
477 m_logger->log(u"Cannot use non-QObject type %1 to access prefixed import"_s.arg(
478 accumulatorIn.containedType()->internalName()),
479 qmlPrefixedImportType,
480 currentSourceLocation());
482 }
else if (accumulatorIn.isImportNamespace()) {
483 m_logger->log(u"Type not found in namespace"_s, qmlUnresolvedType,
484 currentSourceLocation());
492 QQmlJSTypePropagator::handleLookupError(propertyName);
494 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
495 const QString typeName = accumulatorIn.containedTypeName();
497 if (typeName == u"QVariant")
499 if (accumulatorIn.isList() && propertyName == u"length")
502 auto baseType = accumulatorIn.containedType();
505 if (propertyResolution(baseType, propertyName) != PropertyMissing)
508 if (baseType->isScript())
511 std::optional<QQmlJSFixSuggestion> fixSuggestion;
513 if (
auto suggestion = QQmlJSUtils::didYouMean(
514 propertyName, baseType->properties().keys(), currentSourceLocation());
515 suggestion.has_value()) {
516 fixSuggestion = std::move(suggestion);
519 if (!fixSuggestion.has_value()
520 && accumulatorIn.variant() == QQmlJSRegisterContent::MetaType) {
522 const QQmlJSScope::ConstPtr scopeType = accumulatorIn.scopeType();
523 const auto metaEnums = scopeType->enumerations();
524 const bool enforcesScoped = scopeType->enforcesScopedEnums();
526 QStringList enumKeys;
527 for (
const QQmlJSMetaEnum &metaEnum : metaEnums) {
528 if (!enforcesScoped || !metaEnum.isScoped())
529 enumKeys << metaEnum.keys();
532 if (
auto suggestion = QQmlJSUtils::didYouMean(
533 propertyName, enumKeys, currentSourceLocation());
534 suggestion.has_value()) {
535 fixSuggestion = std::move(suggestion);
539 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(propertyName, typeName),
540 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
544 const QString &propertyName)
546 const bool res = QQmlJSTypePropagator::checkForEnumProblems(base, propertyName);
548 if (base.isEnumeration()) {
549 const auto metaEnum = base.enumeration();
550 if (!metaEnum.hasKey(propertyName)) {
551 const auto fixSuggestion = QQmlJSUtils::didYouMean(propertyName, metaEnum.keys(),
552 currentSourceLocation());
553 const QString error = u"\"%1\" is not an entry of enum \"%2\"."_s
554 .arg(propertyName, metaEnum.name());
555 m_logger->log(error, qmlMissingEnumEntry, currentSourceLocation(),
true,
true,
565 QQmlJSTypePropagator::generate_StoreNameCommon(nameIndex);
567 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
568 const QQmlJSRegisterContent in = m_state.accumulatorIn();
569 const bool isAttached = in.variant() == QQmlJSRegisterContent::Attachment;
571 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
572 QQmlJSScope::createQQmlSAElement(
573 m_state.accumulatorIn().containedType()),
575 QQmlJSScope::createQQmlSAElement(isAttached
576 ? in.attachee().containedType()
577 : m_function->qmlScope.containedType()),
578 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
579 currentNonEmptySourceLocation()));
581 const auto qmlScope = m_function->qmlScope.containedType();
582 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
583 QQmlJSScope::createQQmlSAElement(qmlScope), name,
584 QQmlJSScope::createQQmlSAElement(in.containedType()),
585 QQmlJSScope::createQQmlSAElement(qmlScope),
586 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
587 currentNonEmptySourceLocation()));
592 QQmlJSTypePropagator::propagatePropertyLookup(name, lookupIndex);
594 const QQmlJSRegisterContent in = m_state.accumulatorIn();
595 const bool isAttached = in.variant() == QQmlJSRegisterContent::Attachment;
597 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
598 QQmlJSScope::createQQmlSAElement(
599 m_state.accumulatorIn().containedType()),
601 QQmlJSScope::createQQmlSAElement(isAttached
602 ? in.attachee().containedType()
603 : m_function->qmlScope.containedType()),
604 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
605 currentNonEmptySourceLocation()));
609 QQmlJSRegisterContent scope)
611 QQmlJSTypePropagator::propagateCall(methods, argc, argv, scope);
614 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
615 if (!match.isValid())
618 const QQmlSA::Element saBaseType = QQmlJSScope::createQQmlSAElement(scope.containedType());
619 const QQmlSA::SourceLocation saLocation{
620 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
622 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
623 m_function->qmlScope.containedType()) };
625 QQmlSA::PassManagerPrivate::get(m_passManager)
626 ->analyzeCall(saBaseType, match.methodName(), saContainedType, saLocation);
662 const QQmlJSScope::ConstPtr &rhs,
663 const QQmlJSTypeResolver *resolver)
668 if (resolver->isNumeric(lhs) && resolver->isNumeric(rhs))
671 if (isVoidOrUndefined(lhs, resolver) || isVoidOrUndefined(rhs, resolver))
674 if (isStringOrNumberOrBoolean(lhs, resolver)
675 && !mightContainStringOrNumberOrBoolean(rhs, resolver)) {
679 if (isStringOrNumberOrBoolean(rhs, resolver)
680 && !mightContainStringOrNumberOrBoolean(lhs, resolver)) {