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())
79 if (accumulatorOut.variant() == QQmlJSRegisterContent::Attachment
80 || accumulatorOut.variant() == QQmlJSRegisterContent::MetaType) {
81 if (m_renamedComponents) {
82 m_renamedComponents->handleRenamedType(accumulatorOut.scopeType(), name,
83 currentNonEmptySourceLocation(), m_logger);
87 const QQmlJSScope::ConstPtr scope = accumulatorOut.scopeType();
88 const QQmlJSScope::ConstPtr idScope = m_scopesById.scope(name, scope);
89 if (!idScope.isNull()) {
90 const auto log = [&](
const auto &memberType,
const auto &memberOwnerScope) {
94 if (m_idMemberShadows->contains(idMemberShadow))
97 m_idMemberShadows->insert(std::move(idMemberShadow));
98 const auto useLoc = currentSourceLocation();
99 m_logger->log(
"Id for object %1 shadows %2 \"%3\". Rename one or the other."_L1
100 .arg(idScope->baseTypeName(), memberType, name),
101 qmlIdShadowsMember, useLoc);
102 m_logger->log(
"Note: Id defined here"_L1, qmlIdShadowsMember,
103 idScope->idSourceLocation(),
true,
true, {}, useLoc.startLine);
106 if (scope->hasProperty(name)) {
107 log(
"property"_L1, scope->ownerOfProperty(scope, name).scope);
108 }
else if (scope->hasMethod(name)) {
109 const auto methods = scope->methods(name);
110 const auto &method = methods[0];
111 if (method.methodType() == QQmlSA::MethodType::Method)
112 log(
"method"_L1, scope->ownerOfMethod(scope, name).scope);
113 else if (method.methodType() == QQmlSA::MethodType::Signal)
114 log(
"signal"_L1, scope->ownerOfMethod(scope, name).scope);
139 QQmlJSTypePropagator::generate_StoreProperty(nameIndex, base);
141 auto callBase = m_state.registers[base].content;
142 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
143 const bool isAttached = callBase.variant() == QQmlJSRegisterContent::Attachment;
145 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
146 QQmlJSScope::createQQmlSAElement(callBase.containedType()),
148 QQmlJSScope::createQQmlSAElement(
149 m_state.accumulatorIn().containedType()),
150 QQmlJSScope::createQQmlSAElement(isAttached
151 ? callBase.attachee().containedType()
152 : m_function->qmlScope.containedType()),
153 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
154 currentNonEmptySourceLocation()));
159 QQmlJSTypePropagator::generate_CallProperty(nameIndex, base, argc, argv);
161 const auto saCheck = [&](
const QString &propertyName,
const QQmlJSScope::ConstPtr &baseType) {
162 const QQmlSA::Element saBaseType{ QQmlJSScope::createQQmlSAElement(baseType) };
163 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
164 m_function->qmlScope.containedType()) };
165 const QQmlSA::SourceLocation saLocation{
166 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
169 QQmlSA::PassManagerPrivate::get(m_passManager)
170 ->analyzeRead(saBaseType, propertyName, saContainedType, saLocation);
171 QQmlSA::PassManagerPrivate::get(m_passManager)
172 ->analyzeCall(saBaseType, propertyName, saContainedType, saLocation);
175 const auto callBase = m_state.registers[base].content;
176 const QString propertyName = m_jsUnitGenerator->stringForIndex(nameIndex);
177 const auto member = m_typeResolver->memberType(callBase, propertyName);
179 const bool isLoggingMethod = QQmlJSTypePropagator::isLoggingMethod(propertyName);
180 if (callBase.contains(m_typeResolver->mathObject()))
181 saCheck(propertyName, callBase.containedType());
182 else if (callBase.contains(m_typeResolver->consoleObject()) && isLoggingMethod)
183 saCheck(propertyName, callBase.containedType());
184 else if (!member.isMethod()) {
185 if (callBase.contains(m_typeResolver->jsValueType())
186 || callBase.contains(m_typeResolver->varType())) {
187 saCheck(propertyName, callBase.containedType());
210 QQmlJSTypePropagator::handleUnqualifiedAccess(name, isMethod);
212 auto location = currentSourceLocation();
214 const auto qmlScopeContained = m_function->qmlScope.containedType();
215 if (qmlScopeContained->isInCustomParserParent()) {
217 if (qmlScopeContained->baseType().isNull()
218 || qmlScopeContained->baseType()->internalName() != u"QQmlConnections"_s)
223 if (isCallingProperty(qmlScopeContained, name))
225 }
else if (propertyResolution(qmlScopeContained, name) != PropertyMissing) {
229 std::optional<QQmlJSFixSuggestion> suggestion;
231 const auto childScopes = m_function->qmlScope.containedType()->childScopes();
232 for (qsizetype i = 0, end = childScopes.size(); i < end; i++) {
233 auto &scope = childScopes[i];
234 if (location.offset > scope->sourceLocation().offset) {
236 && childScopes.at(i + 1)->sourceLocation().offset < location.offset)
238 if (scope->childScopes().size() == 0)
241 const auto jsId = scope->childScopes().first()->jsIdentifier(name);
243 if (jsId.has_value() && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
244 const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
246 QQmlJS::SourceLocation fixLocation = id.location;
247 Q_UNUSED(fixLocation)
248 fixLocation.length = 0;
250 const auto handler = m_typeResolver->signalHandlers()[id.location];
252 QString fixString = handler.isMultiline ? u"function("_s : u"("_s;
253 const auto parameters = handler.signalParameters;
254 for (
int numParams = parameters.size(); numParams > 0; --numParams) {
255 fixString += parameters.at(parameters.size() - numParams);
257 fixString += u", "_s;
260 fixString += handler.isMultiline ? u") "_s : u") => "_s;
261 const auto msg = u"\"%1\" is ambiguous. Use a function instead: %2%3"_s.arg(
262 name, fixString, handler.isMultiline ?
"{ ... }"_L1 :
"..."_L1);
263 QQmlJSDocumentEdit documentEdit{ m_logger->filePath(), fixLocation, fixString };
264 suggestion = {{ msg, fixLocation, documentEdit }};
265 suggestion->setAutoApplicable();
275 const auto qmlScope = m_function->qmlScope.containedType();
276 if (name == u"model" || name == u"index") {
277 if (
const QQmlJSScope::ConstPtr parent = qmlScope->parentScope(); !parent.isNull()) {
278 const auto bindings = parent->ownPropertyBindings(u"delegate"_s);
280 for (
auto it = bindings.first; it != bindings.second; it++) {
281 if (!it->hasObject())
283 if (it->objectType() == qmlScope) {
284 suggestion = QQmlJSFixSuggestion {
285 "'%1' is implicitly injected into this delegate. "
286 "Add a required property '%1' to the delegate instead."_L1
288 qmlScope->sourceLocation()
297 if (!suggestion.has_value()) {
298 for (QQmlJSScope::ConstPtr scope = qmlScope; !scope.isNull(); scope = scope->parentScope()) {
299 if (scope->hasProperty(name)) {
300 QQmlJSScopesById::MostLikelyCallback<QString> id;
301 m_function->addressableScopes.possibleIds(scope, qmlScope, Default, id);
303 QQmlJS::SourceLocation fixLocation = location;
304 fixLocation.length = 0;
305 QString m =
"%1 is a member of a parent element.\n You can qualify the "
306 "access with its id to avoid this warning%2.\n"_L1.arg(name);
307 m = m.arg(id.result.isEmpty() ?
" (You first have to give the element an id)"_L1 :
""_L1);
309 suggestion = QQmlJSFixSuggestion{
310 m, fixLocation, { m_logger->filePath(), fixLocation,
311 (id.result.isEmpty() ? u"<id>."_s : (id.result + u'.')) }
314 if (!id.result.isEmpty())
315 suggestion->setAutoApplicable();
320 if (!suggestion.has_value() && !m_function->addressableScopes.componentsAreBound()
321 && m_function->addressableScopes.existsAnywhereInDocument(name)) {
322 const QLatin1String replacement =
"pragma ComponentBehavior: Bound"_L1;
323 QQmlJSFixSuggestion bindComponents {
324 "Set \"%1\" in order to use IDs from outer components in nested components."_L1
326 QQmlJS::s_documentOrigin,
327 QQmlJSDocumentEdit{ m_logger->filePath(), QQmlJS::s_documentOrigin, replacement + u'\n' }
329 bindComponents.setAutoApplicable();
330 suggestion = std::move(bindComponents);
333 if (!suggestion.has_value()) {
334 if (
auto didYouMean = QQmlJSUtils::didYouMean(
335 name, qmlScope->properties().keys() + qmlScope->methods().keys(),
336 m_logger->filePath(), location);
337 didYouMean.has_value()) {
338 suggestion = std::move(didYouMean);
342 m_logger->log(QLatin1String(
"Unqualified access"), qmlUnqualified, location,
true,
true,
361 const QString &name,
bool isMethod)
const
363 QQmlJSTypePropagator::handleUnqualifiedAccessAndContextProperties(name, isMethod);
365 if (m_contextPropertyInfo.userContextProperties.isUnqualifiedAccessDisabled(name))
368 const auto warningMessage = [&name,
this]() {
370 "Potential context property access detected."
371 " Context properties are discouraged in QML: use normal, required, or singleton properties instead."_L1;
373 if (shouldMentionRequiredProperties(m_function->qmlScope.containedType())) {
375 "\nNote: '%1' assumed to be a potential context property because it is not declared as required property."_L1
381 if (m_contextPropertyInfo.userContextProperties.isOnUsageWarned(name)) {
382 m_logger->log(warningMessage(), qmlContextProperties, currentSourceLocation());
387 handleUnqualifiedAccess(name, isMethod);
389 const QList<QQmlJS::HeuristicContextProperty> definitions =
390 m_contextPropertyInfo.heuristicContextProperties.definitionsForName(name);
391 if (definitions.isEmpty())
393 QString warning = warningMessage();
394 for (
const auto &candidate : definitions) {
395 warning.append(
"\nNote: candidate context property declaration '%1' at %2:%3:%4"_L1.arg(
396 name, QDir::cleanPath(candidate.filename),
397 QString::number(candidate.location.startLine),
398 QString::number(candidate.location.startColumn)));
400 m_logger->log(warning, qmlContextProperties, currentSourceLocation());
406 QQmlJSTypePropagator::checkDeprecated(scope, name, isMethod);
408 Q_ASSERT(!scope.isNull());
409 auto qmlScope = QQmlJSScope::findCurrentQMLScope(scope);
410 if (qmlScope.isNull())
413 QList<QQmlJSAnnotation> annotations;
415 QQmlJSMetaMethod method;
418 const QList<QQmlJSMetaMethod> methods = qmlScope->methods(name);
419 if (methods.isEmpty())
421 method = methods.constFirst();
422 annotations = method.annotations();
424 QQmlJSMetaProperty property = qmlScope->property(name);
425 if (!property.isValid())
427 annotations = property.annotations();
430 auto deprecationAnn = std::find_if(
431 annotations.constBegin(), annotations.constEnd(),
432 [](
const QQmlJSAnnotation &annotation) {
return annotation.isDeprecation(); });
434 if (deprecationAnn == annotations.constEnd())
437 QQQmlJSDeprecation deprecation = deprecationAnn->deprecation();
439 QString descriptor = name;
441 descriptor += u'(' + method.parameterNames().join(u", "_s) + u')';
443 QString message =
"%1 \"%2\" is deprecated"_L1
444 .arg(isMethod ? u"Method"_s : u"Property"_s, descriptor);
446 if (!deprecation.reason.isEmpty())
447 message.append(QStringLiteral(
" (Reason: %1)").arg(deprecation.reason));
449 m_logger->log(message, qmlDeprecated, currentSourceLocation());
453 const QString &name)
const
455 const bool res = QQmlJSTypePropagator::isCallingProperty(scope, name);
457 if (
const auto property = scope->property(name); property.isValid()) {
459 if (property.type() == m_typeResolver->varType()) {
460 errorType = u"a var property. It may or may not be a method. "_s
461 u"Use a regular function instead."_s;
462 }
else if (property.type() == m_typeResolver->jsValueType()) {
463 errorType = u"a QJSValue property. It may or may not be a method. "_s
464 u"Use a regular Q_INVOKABLE instead."_s;
466 errorType = u"not a method"_s;
469 m_logger->log(u"Property \"%1\" is %2"_s.arg(name, errorType),
470 qmlUseProperFunction, currentSourceLocation(),
true,
true, {});
513 QQmlJSTypePropagator::handleLookupError(propertyName);
515 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
516 const QString typeName = accumulatorIn.containedTypeName();
518 if (typeName == u"QVariant")
520 if (accumulatorIn.isList() && propertyName == u"length")
523 auto baseType = accumulatorIn.containedType();
526 if (propertyResolution(baseType, propertyName) != PropertyMissing)
529 if (baseType->isScript())
532 std::optional<QQmlJSFixSuggestion> fixSuggestion;
534 if (
auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->properties().keys(),
535 m_logger->filePath(), currentSourceLocation());
536 suggestion.has_value()) {
537 fixSuggestion = std::move(suggestion);
540 if (!fixSuggestion.has_value()
541 && accumulatorIn.variant() == QQmlJSRegisterContent::MetaType) {
543 const QQmlJSScope::ConstPtr scopeType = accumulatorIn.scopeType();
544 const auto metaEnums = scopeType->enumerations();
545 const bool enforcesScoped = scopeType->enforcesScopedEnums();
547 QStringList enumKeys;
548 for (
const QQmlJSMetaEnum &metaEnum : metaEnums) {
549 if (!enforcesScoped || !metaEnum.isScoped())
550 enumKeys << metaEnum.keys();
553 if (
auto suggestion = QQmlJSUtils::didYouMean(
554 propertyName, enumKeys, m_logger->filePath(), currentSourceLocation());
555 suggestion.has_value()) {
556 fixSuggestion = std::move(suggestion);
560 if (checkTypeResolved(baseType)) {
561 m_logger->log(u"Member \"%1\" not found on type \"%2\""_s.arg(propertyName, typeName),
562 qmlMissingProperty, currentSourceLocation(),
true,
true, fixSuggestion);
588 QQmlJSTypePropagator::generate_StoreNameCommon(nameIndex);
590 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
591 const QQmlJSRegisterContent in = m_state.accumulatorIn();
592 const bool isAttached = in.variant() == QQmlJSRegisterContent::Attachment;
594 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeRead(
595 QQmlJSScope::createQQmlSAElement(
596 m_state.accumulatorIn().containedType()),
598 QQmlJSScope::createQQmlSAElement(isAttached
599 ? in.attachee().containedType()
600 : m_function->qmlScope.containedType()),
601 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
602 currentNonEmptySourceLocation()));
604 const auto qmlScope = m_function->qmlScope.containedType();
605 QQmlSA::PassManagerPrivate::get(m_passManager)->analyzeWrite(
606 QQmlJSScope::createQQmlSAElement(qmlScope), name,
607 QQmlJSScope::createQQmlSAElement(in.containedType()),
608 QQmlJSScope::createQQmlSAElement(qmlScope),
609 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(
610 currentNonEmptySourceLocation()));
632 QQmlJSRegisterContent scope)
634 QQmlJSTypePropagator::propagateCall(methods, argc, argv, scope);
637 const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors);
638 if (!match.isValid())
641 const QQmlSA::Element saBaseType = QQmlJSScope::createQQmlSAElement(scope.containedType());
642 const QQmlSA::SourceLocation saLocation{
643 QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(currentSourceLocation())
645 const QQmlSA::Element saContainedType{ QQmlJSScope::createQQmlSAElement(
646 m_function->qmlScope.containedType()) };
648 QQmlSA::PassManagerPrivate::get(m_passManager)
649 ->analyzeCall(saBaseType, match.methodName(), saContainedType, saLocation);