diff options
author | Maximilian Goldstein <max.goldstein@qt.io> | 2022-02-25 12:15:06 +0100 |
---|---|---|
committer | Maximilian Goldstein <max.goldstein@qt.io> | 2022-03-02 14:21:08 +0100 |
commit | 31abba8cdc82c5e4c5d731f219884c1a6a9ca376 (patch) | |
tree | 19d2495137bbfd6bf0b40b7b6a99ecaa4b03fef3 | |
parent | 7e7582fc70fecc1b2306ef5f4c10b3fa54b171e9 (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.cpp | 32 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljslogger.cpp | 5 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml | 10 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/multilineStringTortureTick.qml | 10 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 36 |
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; + } } } } |