aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2022-02-25 12:15:06 +0100
committerMaximilian Goldstein <max.goldstein@qt.io>2022-03-02 14:21:08 +0100
commit31abba8cdc82c5e4c5d731f219884c1a6a9ca376 (patch)
tree19d2495137bbfd6bf0b40b7b6a99ecaa4b03fef3
parent7e7582fc70fecc1b2306ef5f4c10b3fa54b171e9 (diff)
qmlcompiler: Suggest fix for multiline strings
We can now suggest a template string to use instead that is properly escaped. Once auto-fixing becomes available we can automatically replace deprecated multiline strings with the new suggestion. Task-number: QTBUG-92448 Change-Id: I4e77820b66ae960cde558a62689c5da328b8df5b Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp32
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp5
-rw-r--r--tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml10
-rw-r--r--tests/auto/qml/qmllint/data/multilineStringTortureTick.qml10
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp36
5 files changed, 84 insertions, 9 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index cef9b45c45..8ebdb0bed5 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -1112,10 +1112,36 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::StringLiteral *sl)
if (s.contains(QLatin1Char('\r')) || s.contains(QLatin1Char('\n')) || s.contains(QChar(0x2028u))
|| s.contains(QChar(0x2029u))) {
+ QString templateString;
+
+ bool escaped = false;
+ const QChar stringQuote = s[0];
+ for (qsizetype i = 1; i < s.length() - 1; i++) {
+ const QChar c = s[i];
+
+ if (c == u'\\') {
+ escaped = !escaped;
+ } else if (escaped) {
+ // If we encounter an escaped quote, unescape it since we use backticks here
+ if (c == stringQuote)
+ templateString.chop(1);
+
+ escaped = false;
+ } else {
+ if (c == u'`')
+ templateString += u'\\';
+ if (c == u'$' && i + 1 < s.length() - 1 && s[i + 1] == u'{')
+ templateString += u'\\';
+ }
+
+ templateString += c;
+ }
+
+ const FixSuggestion suggestion = { { { u"Use a template literal instead"_qs,
+ sl->literalToken, u"`" + templateString + u"`" } } };
m_logger->log(QStringLiteral("String contains unescaped line terminator which is "
- "deprecated. Use a template "
- "literal instead."),
- Log_MultilineString, sl->literalToken);
+ "deprecated."),
+ Log_MultilineString, sl->literalToken, true, true, suggestion);
}
return true;
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index 54cbd6e45d..cba6a24d60 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -241,6 +241,11 @@ void QQmlJSLogger::printFix(const FixSuggestion &fix)
m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
int tabCount = issueLocationWithContext.beforeText().count(u'\t');
+
+ // Do not draw location indicator for multiline replacement strings
+ if (fixItem.replacementString.contains(u'\n'))
+ continue;
+
m_output.write(u" "_qs.repeated(
issueLocationWithContext.beforeText().length() - tabCount)
+ u"\t"_qs.repeated(tabCount)
diff --git a/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml b/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml
new file mode 100644
index 0000000000..ca8924f3f3
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property string quote: "
+ quote: \" \\\" \\\\\"
+ ticks: ` \` \\\` \\\`
+ singleTicks: ' \' \\' \\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ "
+}
diff --git a/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml b/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml
new file mode 100644
index 0000000000..40484603d0
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property string quote: '
+ quote: " \" \\" \\\"
+ ticks: \` \` \\\` \\\`
+ singleTicks: \' \\\' \\\\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ '
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 9ca5db7db3..fc5214204d 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -659,9 +659,26 @@ void TestQmllint::dirtyQmlCode_data()
<< QString() << false;
QTest::newRow("multilineString")
<< QStringLiteral("multilineString.qml")
- << QStringLiteral("String contains unescaped line terminator which is deprecated. Use "
- "a template literal instead.")
+ << QStringLiteral("String contains unescaped line terminator which is deprecated.")
<< QString() << QString() << true;
+ QTest::newRow("multilineStringTortureQuote")
+ << QStringLiteral("multilineStringTortureQuote.qml")
+ << QStringLiteral("String contains unescaped line terminator which is deprecated.")
+ << QString() << QStringLiteral(R"(`
+ quote: " \\" \\\\"
+ ticks: \` \` \\\` \\\`
+ singleTicks: ' \' \\' \\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ `)") << true;
+ QTest::newRow("multilineStringTortureTick")
+ << QStringLiteral("multilineStringTortureTick.qml")
+ << QStringLiteral("String contains unescaped line terminator which is deprecated.")
+ << QString() << QStringLiteral(R"(`
+ quote: " \" \\" \\\"
+ ticks: \` \` \\\` \\\`
+ singleTicks: ' \\' \\\\'
+ expression: \${expr} \${expr} \\\${expr} \\\${expr}
+ `)") << true;
QTest::newRow("unresolvedType")
<< QStringLiteral("unresolvedType.qml")
<< QStringLiteral("UnresolvedType was not found. Did you add all import paths?")
@@ -1278,10 +1295,17 @@ void TestQmllint::searchWarnings(const QJsonArray &warnings, const QString &subs
contains = true;
break;
}
- if (searchReplacements == DoReplacementSearch
- && fix[u"replacement"].toString().contains(substring)) {
- contains = true;
- break;
+ if (searchReplacements == DoReplacementSearch) {
+ QString replacement = fix[u"replacement"].toString();
+#ifdef Q_OS_WIN
+ // Replacements can contain native line endings
+ // but we need them to be uniform in order for them to conform to our test data
+ replacement = replacement.replace(u"\r\n"_qs, u"\n"_qs);
+#endif
+ if (replacement.contains(substring)) {
+ contains = true;
+ break;
+ }
}
}
}