forked from qt-creator/qt-creator
CppEditor: Do not produce invalid code when escaping string literals
Fixes: QTCREATORBUG-26003 Change-Id: Ie4d0ae85cc0ae2d1d45ae0bedbf0212d217aa69b Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -253,6 +253,8 @@ QuickFixOperationTest::QuickFixOperationTest(const QList<QuickFixTestDocument::P
|
|||||||
// Check
|
// Check
|
||||||
QString result = testDocument->m_editorWidget->document()->toPlainText();
|
QString result = testDocument->m_editorWidget->document()->toPlainText();
|
||||||
removeTrailingWhitespace(result);
|
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())
|
if (!expectedFailMessage.isEmpty())
|
||||||
QEXPECT_FAIL("", expectedFailMessage.data(), Continue);
|
QEXPECT_FAIL("", expectedFailMessage.data(), Continue);
|
||||||
else if (result != testDocument->m_expectedSource) {
|
else if (result != testDocument->m_expectedSource) {
|
||||||
@@ -1750,6 +1752,26 @@ void CppEditorPlugin::test_quickfix_data()
|
|||||||
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
|
<< CppQuickFixFactoryPtr(new ConvertToCamelCase(true))
|
||||||
<< _("void @WhAt_TODO_hErE();\n")
|
<< _("void @WhAt_TODO_hErE();\n")
|
||||||
<< _("void WhAtTODOHErE();\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()
|
void CppEditorPlugin::test_quickfix()
|
||||||
|
|||||||
@@ -7079,17 +7079,25 @@ private:
|
|||||||
return false;
|
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) {
|
for (const quint8 c : contents) {
|
||||||
if (isascii(c) && isprint(c)) {
|
const bool needsEscape = !isascii(c) || !isprint(c);
|
||||||
newContents += c;
|
if (!needsEscape && wasEscaped && std::isxdigit(c) && !chunk.isEmpty()) {
|
||||||
} else {
|
newContents << chunk;
|
||||||
newContents += QByteArray("\\x") +
|
chunk.clear();
|
||||||
QByteArray::number(c, 16).rightJustified(2, '0');
|
|
||||||
}
|
}
|
||||||
|
if (needsEscape)
|
||||||
|
chunk += QByteArray("\\x") + QByteArray::number(c, 16).rightJustified(2, '0');
|
||||||
|
else
|
||||||
|
chunk += c;
|
||||||
|
wasEscaped = needsEscape;
|
||||||
}
|
}
|
||||||
|
if (!chunk.isEmpty())
|
||||||
|
newContents << chunk;
|
||||||
return newContents;
|
return newContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7154,25 +7162,35 @@ public:
|
|||||||
QTC_ASSERT(stringLiteral, return);
|
QTC_ASSERT(stringLiteral, return);
|
||||||
const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).
|
const QByteArray oldContents(currentFile->tokenAt(stringLiteral->literal_token).
|
||||||
identifier->chars());
|
identifier->chars());
|
||||||
QByteArray newContents;
|
QByteArrayList newContents;
|
||||||
if (m_escape)
|
if (m_escape)
|
||||||
newContents = escapeString(oldContents);
|
newContents = escapeString(oldContents);
|
||||||
else
|
else
|
||||||
newContents = unescapeString(oldContents);
|
newContents = {unescapeString(oldContents)};
|
||||||
|
|
||||||
if (oldContents != newContents) {
|
if (newContents.isEmpty()
|
||||||
// Check UTF-8 byte array is correct or not.
|
|| (newContents.size() == 1 && newContents.first() == oldContents)) {
|
||||||
QTextCodec *utf8codec = QTextCodec::codecForName("UTF-8");
|
return;
|
||||||
QScopedPointer<QTextDecoder> 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTextCodec *utf8codec = QTextCodec::codecForName("UTF-8");
|
||||||
|
QScopedPointer<QTextDecoder> 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:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user