Skip to content

Commit 8dead66

Browse files
committed
Fix #714 (# operator escapes backslashes outside string literals)
1 parent d5e6da6 commit 8dead66

3 files changed

Lines changed: 39 additions & 3 deletions

File tree

‎run-tests.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ def cleanup(out: str) -> str:
8080
'macro_rescan_varargs.c',
8181

8282
# todo, high priority
83-
'c99-6_10_3_4_p6.c',
8483
'expr_usual_conversions.c', # condition is true: 4U - 30 >= 0
8584
'stdint.c',
8685

‎simplecpp.cpp‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,23 @@ static bool isStringLiteralPrefix(const std::string &str)
642642
str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R";
643643
}
644644

645+
/** Spelling of a token in the result of the # operator: a \ is inserted
646+
* before each " and \ of a string literal or character constant (C 6.10.3.2p2) */
647+
static std::string stringifyToken(const std::string &str)
648+
{
649+
const std::string::size_type quote = str.find_first_of("\"\'");
650+
if (quote == std::string::npos || str.back() != str[quote] ||
651+
(quote > 0 && !isStringLiteralPrefix(str.substr(0, quote))))
652+
return str;
653+
std::string ret;
654+
for (const char c : str) {
655+
if (c == '\\' || c == '\"')
656+
ret += '\\';
657+
ret += c;
658+
}
659+
return ret;
660+
}
661+
645662
void simplecpp::TokenList::lineDirective(unsigned int fileIndex_, unsigned int line, Location &location)
646663
{
647664
if (fileIndex_ != location.fileIndex || line >= location.line) {
@@ -2381,12 +2398,12 @@ namespace simplecpp {
23812398
ostr << '\"';
23822399
for (const Token *hashtok = tokenListHash.cfront(), *next; hashtok; hashtok = next) {
23832400
next = hashtok->next;
2384-
ostr << hashtok->str();
2401+
ostr << stringifyToken(hashtok->str());
23852402
if (next && hashtok->whitespaceahead)
23862403
ostr << ' ';
23872404
}
23882405
ostr << '\"';
2389-
output.push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros)));
2406+
output.push_back(newMacroToken(ostr.str(), loc, isReplaced(expandedmacros)));
23902407
return tok;
23912408
}
23922409

‎test.cpp‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,25 @@ static void hash()
17091709
"STR(X(Y,Y))"));
17101710
}
17111711

1712+
static void hashEscape()
1713+
{
1714+
// a \ is only inserted before " and \ inside string literals and character constants
1715+
const char code[] = "#define str(x) #x\n"
1716+
"str(\\n)\n"
1717+
"str(: @\\n)\n"
1718+
"str(\"\\n\")\n"
1719+
"str('\\4')\n"
1720+
"str('\"')\n"
1721+
"str(L\"a\\\"b\")";
1722+
ASSERT_EQUALS("\n"
1723+
"\"\\n\"\n"
1724+
"\": @\\n\"\n"
1725+
"\"\\\"\\\\n\\\"\"\n"
1726+
"\"'\\\\4'\"\n"
1727+
"\"'\\\"'\"\n"
1728+
"\"L\\\"a\\\\\\\"b\\\"\"", preprocess(code));
1729+
}
1730+
17121731
static void hashhash1() // #4703
17131732
{
17141733
const char code[] = "#define MACRO( A, B, C ) class A##B##C##Creator {};\n"
@@ -4734,6 +4753,7 @@ static void runTests(int argc, char **argv, Input input)
47344753
TEST_CASE(garbage_endif);
47354754

47364755
TEST_CASE(hash);
4756+
TEST_CASE(hashEscape);
47374757
TEST_CASE(hashhash1);
47384758
TEST_CASE(hashhash2);
47394759
TEST_CASE(hashhash3);

0 commit comments

Comments
 (0)