Skip to content

Commit 8a1f103

Browse files
committed
fix
1 parent 8dead66 commit 8a1f103

2 files changed

Lines changed: 49 additions & 6 deletions

File tree

‎simplecpp.cpp‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,16 @@ namespace simplecpp {
17971797
wrongNumberOfParameters(const Location &loc, const std::string &macroName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {}
17981798
};
17991799

1800+
/** Struct that is thrown when there is invalid # usage */
1801+
struct invalidHash : public Error {
1802+
invalidHash(const Location &loc, const std::string &macroName, const std::string &message)
1803+
: Error(loc, "Invalid # usage when expanding \'" + macroName + "\': " + message) {}
1804+
1805+
static inline invalidHash unterminatedString(const Location &loc, const std::string &macroName) {
1806+
return {loc, macroName, "Stringified argument ends with an odd number of backslashes, which is undefined behavior according to C standard chapter 6.10.3.2, paragraph 2."};
1807+
}
1808+
};
1809+
18001810
/** Struct that is thrown when there is invalid ## usage */
18011811
struct invalidHashHash : public Error {
18021812
static inline std::string format(const std::string &macroName, const std::string &message) {
@@ -2394,16 +2404,21 @@ namespace simplecpp {
23942404
TokenList tokenListHash(files);
23952405
const MacroMap macros2; // temporarily bypass macro expansion
23962406
tok = expandToken(tokenListHash, loc, tok->next, macros2, expandedmacros, parametertokens);
2397-
std::ostringstream ostr;
2398-
ostr << '\"';
2407+
std::string str;
23992408
for (const Token *hashtok = tokenListHash.cfront(), *next; hashtok; hashtok = next) {
24002409
next = hashtok->next;
2401-
ostr << stringifyToken(hashtok->str());
2410+
str += stringifyToken(hashtok->str());
24022411
if (next && hashtok->whitespaceahead)
2403-
ostr << ' ';
2412+
str += ' ';
24042413
}
2405-
ostr << '\"';
2406-
output.push_back(newMacroToken(ostr.str(), loc, isReplaced(expandedmacros)));
2414+
// An odd number of trailing backslashes would escape the closing quote and
2415+
// yield an unterminated string literal (undefined behavior, C 6.10.3.2p2).
2416+
std::size_t trailing = 0;
2417+
while (trailing < str.size() && str[str.size() - 1U - trailing] == '\\')
2418+
++trailing;
2419+
if (trailing % 2U == 1U)
2420+
throw invalidHash::unterminatedString(loc, name());
2421+
output.push_back(newMacroToken('\"' + str + '\"', loc, isReplaced(expandedmacros)));
24072422
return tok;
24082423
}
24092424

‎test.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,33 @@ static void hashEscape()
17281728
"\"L\\\"a\\\\\\\"b\\\"\"", preprocess(code));
17291729
}
17301730

1731+
static void hashEscapeTrailingBackslash()
1732+
{
1733+
// an odd number of trailing backslashes would escape the closing quote
1734+
const char msg[] = "Invalid # usage when expanding 'str': Stringified argument ends with an odd number of backslashes, "
1735+
"which is undefined behavior according to C standard chapter 6.10.3.2, paragraph 2.\n";
1736+
{
1737+
const char code[] = "#define str(x) #x\n"
1738+
"str(\\)\n";
1739+
simplecpp::OutputList outputList;
1740+
ASSERT_EQUALS("", preprocess(code, &outputList));
1741+
ASSERT_EQUALS(std::string("file0,2,syntax_error,failed to expand 'str', ") + msg, toString(outputList));
1742+
}
1743+
{
1744+
const char code[] = "#define str(x) #x\n"
1745+
"str(a\\)\n";
1746+
simplecpp::OutputList outputList;
1747+
ASSERT_EQUALS("", preprocess(code, &outputList));
1748+
ASSERT_EQUALS(std::string("file0,2,syntax_error,failed to expand 'str', ") + msg, toString(outputList));
1749+
}
1750+
{
1751+
// even number of backslashes is fine
1752+
const char code[] = "#define str(x) #x\n"
1753+
"str(\\\\)";
1754+
ASSERT_EQUALS("\n\"\\\\\"", preprocess(code));
1755+
}
1756+
}
1757+
17311758
static void hashhash1() // #4703
17321759
{
17331760
const char code[] = "#define MACRO( A, B, C ) class A##B##C##Creator {};\n"
@@ -4754,6 +4781,7 @@ static void runTests(int argc, char **argv, Input input)
47544781

47554782
TEST_CASE(hash);
47564783
TEST_CASE(hashEscape);
4784+
TEST_CASE(hashEscapeTrailingBackslash);
47574785
TEST_CASE(hashhash1);
47584786
TEST_CASE(hashhash2);
47594787
TEST_CASE(hashhash3);

0 commit comments

Comments
 (0)