aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2025-01-16 18:32:24 +0100
committerUlf Hermann <[email protected]>2025-01-17 14:13:55 +0100
commit746f38bf56027c29a72bd8a3c3a5f9aad73b9bd3 (patch)
treec1f779a136b03784e17a7518c74b22fa511fcbdf
parentc159f4e837d99c8cb4b260e7bbc390cdb83d5e19 (diff)
QmlCompiler: Reduce duplication of warnings about missing properties
If a property cannot be found, we don't need to check its type or even try to compile a binding for it. We also shouldn't claim a property doesn't exist if we cannot find it. We may have failed to resolve the type after all. Pick-to: 6.9 6.8 Task-number: QTBUG-124913 Change-Id: I93ef12e888762ae03f8fa6b1bef2e8d04ba3d4b2 Reviewed-by: Sami Shalayel <[email protected]>
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp9
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp103
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp15
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h3
-rw-r--r--src/qmlcompiler/qqmljslintercodegen.cpp12
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp40
6 files changed, 104 insertions, 78 deletions
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index 8143f588ae..30c869c439 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -674,8 +674,13 @@ std::variant<QQmlJSAotFunction, QList<QQmlJS::DiagnosticMessage>> QQmlJSAotCompi
const QString name = m_document->stringAt(irBinding.propertyNameIndex);
QQmlJSCompilePass::Function function = initializer.run(
context, name, astNode, irBinding, &errors);
- const QQmlJSAotFunction aotFunction = doCompileAndRecordAotStats(
- context, &function, &errors, name, astNode->firstSourceLocation());
+
+ QQmlJSAotFunction aotFunction;
+ if (errors.isEmpty()) {
+ aotFunction = doCompileAndRecordAotStats(
+ context, &function, &errors, name, astNode->firstSourceLocation());
+ }
+
if (!errors.isEmpty()) {
for (auto &error : errors) {
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index ffd6bf7308..95bd243f0b 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -168,71 +168,64 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
}
function.isProperty = m_objectType->hasProperty(propertyName);
- if (!function.isProperty) {
- if (QQmlSignalNames::isHandlerName(propertyName)) {
- if (auto actualPropertyName =
- QQmlSignalNames::changedHandlerNameToPropertyName(propertyName);
- actualPropertyName && m_objectType->hasProperty(*actualPropertyName)) {
- function.isSignalHandler = true;
- } else {
- auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
- const auto methods = m_objectType->methods(*signalName);
- for (const auto &method : methods) {
- if (method.isCloned())
- continue;
- if (method.methodType() == QQmlJSMetaMethodType::Signal) {
- function.isSignalHandler = true;
- const auto arguments = method.parameters();
- for (qsizetype i = 0, end = arguments.size(); i < end; ++i) {
- const auto &type = arguments[i].type();
- if (type.isNull()) {
- diagnose(u"Cannot resolve the argument type %1."_s.arg(
- arguments[i].typeName()),
- QtDebugMsg, bindingLocation, errors);
- function.argumentTypes.append(
- m_typeResolver->namedType(m_typeResolver->varType()));
- } else {
- function.argumentTypes.append(m_typeResolver->namedType(type));
- }
- }
- break;
- }
- }
- if (!function.isSignalHandler) {
- diagnose(u"Could not compile signal handler for %1: The signal does not exist"_s.arg(
- *signalName),
- QtWarningMsg, bindingLocation, errors);
- }
- }
- }
- }
-
- if (!function.isSignalHandler) {
- if (!function.isProperty) {
- diagnose(u"Could not compile binding for %1: The property does not exist"_s.arg(
- propertyName),
- QtWarningMsg, bindingLocation, errors);
- }
-
+ if (function.isProperty) {
const auto property = m_objectType->property(propertyName);
if (const QQmlJSScope::ConstPtr propertyType = property.type()) {
function.returnType = m_typeResolver->namedType(propertyType->isListProperty()
? m_typeResolver->qObjectListType()
: QQmlJSScope::ConstPtr(property.type()));
} else {
- QString message = u"Cannot resolve property type %1 for binding on %2."_s
- .arg(property.typeName(), propertyName);
- if (m_objectType->isNameDeferred(propertyName)) {
- // If the property doesn't exist but the name is deferred, then
- // it's deferred via the presence of immediate names. Those are
- // most commonly used to enable generalized grouped properties.
- message += u" You may want use ID-based grouped properties here.";
- }
- diagnose(message, QtWarningMsg, bindingLocation, errors);
+ diagnose(u"Cannot resolve property type %1 for binding on %2."_s
+ .arg(property.typeName(), propertyName),
+ QtWarningMsg, bindingLocation, errors);
}
if (!property.bindable().isEmpty() && !property.isPrivate())
function.isQPropertyBinding = true;
+ } else if (QQmlSignalNames::isHandlerName(propertyName)) {
+ if (auto actualPropertyName =
+ QQmlSignalNames::changedHandlerNameToPropertyName(propertyName);
+ actualPropertyName && m_objectType->hasProperty(*actualPropertyName)) {
+ function.isSignalHandler = true;
+ } else {
+ auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
+ const auto methods = m_objectType->methods(*signalName);
+ for (const auto &method : methods) {
+ if (method.isCloned())
+ continue;
+ if (method.methodType() == QQmlJSMetaMethodType::Signal) {
+ function.isSignalHandler = true;
+ const auto arguments = method.parameters();
+ for (qsizetype i = 0, end = arguments.size(); i < end; ++i) {
+ const auto &type = arguments[i].type();
+ if (type.isNull()) {
+ diagnose(u"Cannot resolve the argument type %1."_s.arg(
+ arguments[i].typeName()),
+ QtDebugMsg, bindingLocation, errors);
+ function.argumentTypes.append(
+ m_typeResolver->namedType(m_typeResolver->varType()));
+ } else {
+ function.argumentTypes.append(m_typeResolver->namedType(type));
+ }
+ }
+ break;
+ }
+ }
+ if (!function.isSignalHandler) {
+ diagnose(u"Could not find signal \"%1\"."_s.arg(*signalName),
+ QtWarningMsg, bindingLocation, errors);
+ }
+ }
+ } else {
+ QString message = u"Could not find property \"%1\"."_s.arg(propertyName);
+ if (m_objectType->isNameDeferred(propertyName)) {
+ // If the property doesn't exist but the name is deferred, then
+ // it's deferred via the presence of immediate names. Those are
+ // most commonly used to enable generalized grouped properties.
+ message += u" You may want use ID-based grouped properties here.";
+ }
+
+ diagnose(message, QtWarningMsg, bindingLocation, errors);
}
QQmlJS::MemoryPool pool;
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 62c08d2bfb..9db0e9af69 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -222,6 +222,14 @@ void QQmlJSImportVisitor::warnUnresolvedType(const QQmlJSScope::ConstPtr &type)
qmlUnresolvedType, type->sourceLocation());
}
+void QQmlJSImportVisitor::warnMissingPropertyForBinding(
+ const QString &property, const QQmlJS::SourceLocation &location,
+ const std::optional<QQmlJSFixSuggestion> &fixSuggestion)
+{
+ m_logger->log(QStringLiteral("Could not find property \"%1\".").arg(property),
+ qmlMissingProperty, location, true, true, fixSuggestion);
+}
+
static bool mayBeUnresolvedGroupedProperty(const QQmlJSScope::ConstPtr &scope)
{
return scope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope && !scope->baseType();
@@ -753,8 +761,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
QQmlJSMetaProperty property = objectBinding.scope->property(propertyName);
if (!property.isValid()) {
- m_logger->log(QStringLiteral("Property \"%1\" does not exist").arg(propertyName),
- qmlMissingProperty, objectBinding.location);
+ warnMissingPropertyForBinding(propertyName, objectBinding.location);
continue;
}
const auto handleUnresolvedProperty = [&](const QQmlJSScope::ConstPtr &) {
@@ -976,9 +983,7 @@ void QQmlJSImportVisitor::processPropertyBindings()
}
}
- m_logger->log(QStringLiteral("Property \"%1\" does not exist.")
- .arg(name),
- qmlMissingProperty, location, true, true, fixSuggestion);
+ warnMissingPropertyForBinding(name, location, fixSuggestion);
continue;
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index 02c8109d75..b9abf6ce7a 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -259,6 +259,9 @@ protected:
}
void warnUnresolvedType(const QQmlJSScope::ConstPtr &type) const;
+ void warnMissingPropertyForBinding(
+ const QString &property, const QQmlJS::SourceLocation &location,
+ const std::optional<QQmlJSFixSuggestion> &fixSuggestion = {});
QVector<QQmlJSAnnotation> parseAnnotations(QQmlJS::AST::UiAnnotationList *list);
void setAllBindings();
diff --git a/src/qmlcompiler/qqmljslintercodegen.cpp b/src/qmlcompiler/qqmljslintercodegen.cpp
index e33fe7139b..0717769a91 100644
--- a/src/qmlcompiler/qqmljslintercodegen.cpp
+++ b/src/qmlcompiler/qqmljslintercodegen.cpp
@@ -41,8 +41,10 @@ QQmlJSLinterCodegen::compileBinding(const QV4::Compiler::Context *context,
const QString name = m_document->stringAt(irBinding.propertyNameIndex);
QQmlJSCompilePass::Function function =
initializer.run(context, name, astNode, irBinding, &initializationErrors);
- for (const auto &error : initializationErrors)
- diagnose(error.message, error.type, error.loc);
+ for (const auto &error : initializationErrors) {
+ diagnose(u"Could not determine signature of binding for %1: %2"_s.arg(name, error.message),
+ error.type, error.loc);
+ }
QList<QQmlJS::DiagnosticMessage> analyzeErrors;
if (!analyzeFunction(context, &function, &analyzeErrors)) {
@@ -70,8 +72,10 @@ QQmlJSLinterCodegen::compileFunction(const QV4::Compiler::Context *context,
&m_typeResolver, m_currentObject->location, m_currentScope->location);
QQmlJSCompilePass::Function function =
initializer.run(context, name, astNode, &initializationErrors);
- for (const auto &error : initializationErrors)
- diagnose(error.message, error.type, error.loc);
+ for (const auto &error : initializationErrors) {
+ diagnose(u"Could not determine signature of function %1: %2"_s.arg(name, error.message),
+ error.type, error.loc);
+ }
QList<QQmlJS::DiagnosticMessage> analyzeErrors;
if (!analyzeFunction(context, &function, &analyzeErrors)) {
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 5c64d293e8..3256d7b423 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -621,7 +621,7 @@ void TestQmllint::dirtyQmlCode_data()
"unknown attached property scope WrongAttached.") } } };
QTest::newRow("BadBinding") << QStringLiteral("badBinding.qml")
<< Result{ { Message{ QStringLiteral(
- "Property \"doesNotExist\" does not exist.") } } };
+ "Could not find property \"doesNotExist\".") } } };
QTest::newRow("bad template literal (simple)")
<< QStringLiteral("badTemplateStringSimple.qml")
<< Result { { Message {
@@ -641,11 +641,11 @@ void TestQmllint::dirtyQmlCode_data()
QStringLiteral("Cannot assign literal of type string to int") } } };
QTest::newRow("BadScriptBindingOnGroup")
<< QStringLiteral("badScriptBinding.group.qml")
- << Result{ { Message{ QStringLiteral("Property \"bogusProperty\" does not exist."), 3,
+ << Result{ { Message{ QStringLiteral("Could not find property \"bogusProperty\"."), 3,
10 } } };
QTest::newRow("BadScriptBindingOnAttachedType")
<< QStringLiteral("badScriptBinding.attached.qml")
- << Result{ { Message{ QStringLiteral("Property \"bogusProperty\" does not exist."), 5,
+ << Result{ { Message{ QStringLiteral("Could not find property \"bogusProperty\"."), 5,
12 } } };
QTest::newRow("BadScriptBindingOnAttachedSignalHandler")
<< QStringLiteral("badScriptBinding.attachedSignalHandler.qml")
@@ -844,7 +844,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
"Cannot combine value source and binding on property \"objs\"") } } };
QTest::newRow("NonExistentListProperty")
<< QStringLiteral("nonExistentListProperty.qml")
- << Result { { Message { QStringLiteral("Property \"objs\" does not exist") } } };
+ << Result { { Message { QStringLiteral("Could not find property \"objs\".") } } };
QTest::newRow("QtQuick.Window 2.0")
<< QStringLiteral("qtquickWindow20.qml")
<< Result { { Message { QStringLiteral(
@@ -853,7 +853,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
<< QStringLiteral("unresolvedAttachedType.qml")
<< Result { { Message { QStringLiteral(
"unknown attached property scope UnresolvedAttachedType.") } },
- { Message { QStringLiteral("Property \"property\" does not exist.") } } };
+ { Message { QStringLiteral("Could not find property \"property\".") } } };
QTest::newRow("nestedInlineComponents")
<< QStringLiteral("nestedInlineComponents.qml")
<< Result { { Message {
@@ -906,7 +906,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
"MenuItem is part of an inheritance cycle: MenuItem -> MenuItem") } } };
QTest::newRow("badGeneralizedGroup1")
<< QStringLiteral("badGeneralizedGroup1.qml")
- << Result{ { Message{ QStringLiteral("Property \"aaaa\" does not exist.") } } };
+ << Result{ { Message{ QStringLiteral("Could not find property \"aaaa\".") } } };
QTest::newRow("badGeneralizedGroup2")
<< QStringLiteral("badGeneralizedGroup2.qml")
<< Result { { Message { QStringLiteral("unknown grouped property scope aself") } } };
@@ -971,7 +971,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)",
"no matching signal found for handler \"onWannabeSignal\"") } } };
QTest::newRow("didYouMean(binding)")
<< QStringLiteral("didYouMeanBinding.qml")
- << Result{ { Message{ QStringLiteral("Property \"witdh\" does not exist.") } },
+ << Result{ { Message{ QStringLiteral("Could not find property \"witdh\".") } },
{},
{ Message{ QStringLiteral("width") } } };
QTest::newRow("didYouMean(unqualified)")
@@ -1465,32 +1465,38 @@ void TestQmllint::compilerWarnings_data()
<< true;
QTest::newRow("tooFewParameters")
<< QStringLiteral("tooFewParams.qml")
- << Result { { Message { QStringLiteral("No matching override found") } } } << true;
+ << Result { { Message { QStringLiteral("Could not compile binding for a: "
+ "No matching override found") } } } << true;
QTest::newRow("javascriptVariableArgs")
<< QStringLiteral("javascriptVariableArgs.qml")
<< Result { { Message {
- QStringLiteral("Function expects 0 arguments, but 2 were provided") } } }
+ QStringLiteral("Could not compile binding for onCompleted: "
+ "Function expects 0 arguments, but 2 were provided") } } }
<< true;
QTest::newRow("unknownTypeInRegister")
<< QStringLiteral("unknownTypeInRegister.qml")
<< Result { { Message {
- QStringLiteral("Functions without type annotations won't be compiled") } } }
+ QStringLiteral("Could not determine signature of function foo: "
+ "Functions without type annotations won't be compiled") } } }
<< true;
QTest::newRow("pragmaStrict")
<< QStringLiteral("pragmaStrict.qml")
<< Result { { { QStringLiteral(
+ "Could not determine signature of function add: "
"Functions without type annotations won't be compiled") } } }
<< true;
QTest::newRow("generalizedGroupHint")
<< QStringLiteral("generalizedGroupHint.qml")
<< Result { { { QStringLiteral(
- "Cannot resolve property type for binding on myColor. "
+ "Could not determine signature of binding for myColor: "
+ "Could not find property \"myColor\". "
"You may want use ID-based grouped properties here.") } } }
<< true;
QTest::newRow("invalidIdLookup")
<< QStringLiteral("invalidIdLookup.qml")
<< Result { { {
- QStringLiteral("Cannot retrieve a non-object type by ID: stateMachine")
+ QStringLiteral("Could not compile binding for objectName: "
+ "Cannot retrieve a non-object type by ID: stateMachine")
} } }
<< true;
QTest::newRow("returnTypeAnnotation-component")
@@ -1531,6 +1537,16 @@ void TestQmllint::compilerWarnings_data()
<< QStringLiteral("functionAssign1.qml") << Result::clean() << true;
QTest::newRow("functionAssign2")
<< QStringLiteral("functionAssign2.qml") << Result::clean() << true;
+
+ // We want to see the warning about the missing property only once.
+ QTest::newRow("unresolvedType2")
+ << QStringLiteral("unresolvedType2.qml")
+ << Result { { Message { QStringLiteral(
+ "Could not determine signature of binding for text: "
+ "Could not find property \"text\".") } },
+ { Message { QStringLiteral(
+ "Cannot resolve property type for binding on text.") }, },
+ } << true;
}
void TestQmllint::compilerWarnings()