diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index ab0bdf4d34f..da0c8a944eb 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -253,6 +253,8 @@ QuickFixOperationTest::QuickFixOperationTest(const QListm_editorWidget->document()->toPlainText(); removeTrailingWhitespace(result); + QEXPECT_FAIL("escape string literal: raw string literal", "FIXME", Continue); + QEXPECT_FAIL("escape string literal: unescape adjacent literals", "FIXME", Continue); if (!expectedFailMessage.isEmpty()) QEXPECT_FAIL("", expectedFailMessage.data(), Continue); else if (result != testDocument->m_expectedSource) { @@ -1750,6 +1752,26 @@ void CppEditorPlugin::test_quickfix_data() << CppQuickFixFactoryPtr(new ConvertToCamelCase(true)) << _("void @WhAt_TODO_hErE();\n") << _("void WhAtTODOHErE();\n"); + QTest::newRow("escape string literal: simple case") + << CppQuickFixFactoryPtr(new EscapeStringLiteral) + << _(R"(const char *str = @"àxyz";)") + << _(R"(const char *str = "\xc3\xa0xyz";)"); + QTest::newRow("escape string literal: simple case reverse") + << CppQuickFixFactoryPtr(new EscapeStringLiteral) + << _(R"(const char *str = @"\xc3\xa0xyz";)") + << _(R"(const char *str = "àxyz";)"); + QTest::newRow("escape string literal: raw string literal") + << CppQuickFixFactoryPtr(new EscapeStringLiteral) + << _(R"x(const char *str = @R"(àxyz)";)x") + << _(R"x(const char *str = R"(\xc3\xa0xyz)";)x"); + QTest::newRow("escape string literal: splitting required") + << CppQuickFixFactoryPtr(new EscapeStringLiteral) + << _(R"(const char *str = @"àf23бgб1";)") + << _(R"(const char *str = "\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)"); + QTest::newRow("escape string literal: unescape adjacent literals") + << CppQuickFixFactoryPtr(new EscapeStringLiteral) + << _(R"(const char *str = @"\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)") + << _(R"(const char *str = "àf23бgб1";)"); } void CppEditorPlugin::test_quickfix() diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 928a26f8532..60d18cb65c4 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -7079,17 +7079,25 @@ private: return false; } - static QByteArray escapeString(const QByteArray &contents) + static QByteArrayList escapeString(const QByteArray &contents) { - QByteArray newContents; + QByteArrayList newContents; + QByteArray chunk; + bool wasEscaped = false; for (const quint8 c : contents) { - if (isascii(c) && isprint(c)) { - newContents += c; - } else { - newContents += QByteArray("\\x") + - QByteArray::number(c, 16).rightJustified(2, '0'); + const bool needsEscape = !isascii(c) || !isprint(c); + if (!needsEscape && wasEscaped && std::isxdigit(c) && !chunk.isEmpty()) { + newContents << chunk; + chunk.clear(); } + if (needsEscape) + chunk += QByteArray("\\x") + QByteArray::number(c, 16).rightJustified(2, '0'); + else + chunk += c; + wasEscaped = needsEscape; } + if (!chunk.isEmpty()) + newContents << chunk; return newContents; } @@ -7154,25 +7162,35 @@ public: QTC_ASSERT(stringLiteral, return); const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token). identifier->chars()); - QByteArray newContents; + QByteArrayList newContents; if (m_escape) newContents = escapeString(oldContents); else - newContents = unescapeString(oldContents); + newContents = {unescapeString(oldContents)}; - if (oldContents != newContents) { - // Check UTF-8 byte array is correct or not. - QTextCodec *utf8codec = QTextCodec::codecForName("UTF-8"); - QScopedPointer decoder(utf8codec->makeDecoder()); - const QString str = decoder->toUnicode(newContents); - const QByteArray utf8buf = str.toUtf8(); - if (utf8codec->canEncode(str) && newContents == utf8buf) { - ChangeSet changes; - changes.replace(startPos + 1, endPos - 1, str); - currentFile->setChangeSet(changes); - currentFile->apply(); - } + if (newContents.isEmpty() + || (newContents.size() == 1 && newContents.first() == oldContents)) { + return; } + + QTextCodec *utf8codec = QTextCodec::codecForName("UTF-8"); + QScopedPointer decoder(utf8codec->makeDecoder()); + ChangeSet changes; + + bool replace = true; + for (const QByteArray &chunk : qAsConst(newContents)) { + const QString str = decoder->toUnicode(chunk); + const QByteArray utf8buf = str.toUtf8(); + if (!utf8codec->canEncode(str) || chunk != utf8buf) + return; + if (replace) + changes.replace(startPos + 1, endPos - 1, str); + else + changes.insert(endPos, "\"" + str + "\""); + replace = false; + } + currentFile->setChangeSet(changes); + currentFile->apply(); } private: