Files
qt6-declarative/0001-qmlcachegen-fix-crash-on-unresolved-type-with-requir.patch

178 lines
6.1 KiB
Diff
Raw Permalink Normal View History

From 79079a0c6df5e02c4c47003fb88656c37aaf3d0a Mon Sep 17 00:00:00 2001
From: Sami Shalayel <sami.shalayel@qt.io>
Date: Thu, 5 Jun 2025 10:51:46 +0200
Subject: [PATCH] qmlcachegen: fix crash on unresolved type with required
property
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
qmlcachegen can't resolve all types when importing QtQuick.Controls, so
scopes from QtQuick.Controls might be unresolved.
Check the scope before creating a fix suggesion when checking the
required properties, and add a test that tests a file with required
properties on an unresolved base type "Tumbler".
This also fixes the crashes from QTBUG-137196 and QTBUG-136998 it seems.
Pick-to: 6.9 6.8 6.5
Fixes: QTBUG-137411
Fixes: QTBUG-137196
Fixes: QTBUG-136998
Change-Id: Ibf461b54abf84ba13bff8c4833940c7359cf2d8e
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit c9713681e8d0ee7b7a69d38c5972ee73e8ee4b78)
---
src/qmlcompiler/qqmljsimportvisitor.cpp | 21 ++++----
.../data/crashes/buggyFixSuggestion.qml | 25 +++++++++
.../auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 54 +++++++++++++++++++
3 files changed, 90 insertions(+), 10 deletions(-)
create mode 100644 tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 2a1f31e..e2371a5 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -1041,16 +1041,17 @@ void QQmlJSImportVisitor::checkRequiredProperties()
: u"here"_s;
if (!prevRequiredScope.isNull()) {
- auto sourceScope = prevRequiredScope->baseType();
- suggestion = QQmlJSFixSuggestion{
- "%1:%2:%3: Property marked as required in %4."_L1
- .arg(sourceScope->filePath())
- .arg(sourceScope->sourceLocation().startLine)
- .arg(sourceScope->sourceLocation().startColumn)
- .arg(requiredScopeName),
- sourceScope->sourceLocation()
- };
- suggestion->setFilename(sourceScope->filePath());
+ if (auto sourceScope = prevRequiredScope->baseType()) {
+ suggestion = QQmlJSFixSuggestion{
+ "%1:%2:%3: Property marked as required in %4."_L1
+ .arg(sourceScope->filePath())
+ .arg(sourceScope->sourceLocation().startLine)
+ .arg(sourceScope->sourceLocation().startColumn)
+ .arg(requiredScopeName),
+ sourceScope->sourceLocation()
+ };
+ suggestion->setFilename(sourceScope->filePath());
+ }
} else {
message += " (marked as required by %1)"_L1.arg(requiredScopeName);
}
diff --git a/tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml b/tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml
new file mode 100644
index 0000000..f435d2e
--- /dev/null
+++ b/tests/auto/qml/qmlcachegen/data/crashes/buggyFixSuggestion.qml
@@ -0,0 +1,25 @@
+import QtQuick
+
+Item {
+ id: root
+
+ Item {
+ id: inner
+
+ Tumbler {
+ id: year
+
+ delegate: Rectangle {
+ required property var modelData
+ }
+ }
+
+ Tumbler {
+ id: month
+
+ delegate: Rectangle {
+ required property var modelData
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 3c602f5..bb8fc5c 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -73,6 +73,9 @@ private slots:
void aotstatsSerialization();
void aotstatsGeneration_data();
void aotstatsGeneration();
+
+ void crash_data();
+ void crash();
};
// A wrapper around QQmlComponent to ensure the temporary reference counts
@@ -121,6 +124,36 @@ static bool generateCache(const QString &qmlFileName, QByteArray *capturedStderr
return proc.exitCode() == 0;
}
+static bool generateCpp(const QString &qmlFileName, QByteArray *capturedStderr = nullptr)
+{
+#if defined(QTEST_CROSS_COMPILED)
+ QTest::qFail("You cannot call qmlcachegen on the target.", __FILE__, __LINE__);
+ return false;
+#endif
+ QProcess proc;
+ if (capturedStderr == nullptr)
+ proc.setProcessChannelMode(QProcess::ForwardedChannels);
+ proc.setProgram(QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath)
+ + QLatin1String("/qmlcachegen"));
+ QTemporaryDir outputDir;
+ const QString outputFile = outputDir.filePath("output.cpp"_L1);
+ proc.setArguments(QStringList{ "--resource-path"_L1, "qrc:/qt/qml/Crashes/testFile.qml"_L1,
+ "-o"_L1, outputFile, qmlFileName });
+ proc.start();
+ if (!proc.waitForFinished())
+ return false;
+
+ if (capturedStderr)
+ *capturedStderr = proc.readAllStandardError();
+
+ if (!QFile::exists(outputFile))
+ return false;
+
+ if (proc.exitStatus() != QProcess::NormalExit)
+ return false;
+ return proc.exitCode() == 0;
+}
+
tst_qmlcachegen::tst_qmlcachegen()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
@@ -1023,6 +1056,27 @@ void tst_qmlcachegen::aotstatsGeneration()
}
}
+void tst_qmlcachegen::crash_data()
+{
+ QTest::addColumn<QString>("fileName");
+
+ QTest::addRow("buggyFixSuggestion") << u"buggyFixSuggestion.qml"_s;
+}
+
+void tst_qmlcachegen::crash()
+{
+#if defined(QTEST_CROSS_COMPILED)
+ QSKIP("Cannot call qmlcachegen on cross-compiled target.");
+#endif
+
+ QFETCH(QString, fileName);
+ const QString filePath = testFile("crashes/" + fileName);
+
+ QFile file(filePath);
+ QVERIFY(file.exists());
+ QVERIFY(generateCpp(filePath));
+}
+
const QQmlScriptString &ScriptStringProps::undef() const
{
return m_undef;
--
2.49.0