From 2e16061a1511ec47ba7186fb694af8aa38a1ab1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 6 Nov 2026 13:35:23 +0100 Subject: [PATCH 1/2] Fix #718 (# operator drops the space that a comment stands for) 'str(a/**/b)' gave "ab" instead of "a b". When comments are removed, mark the previous token as followed by whitespace. --- simplecpp.cpp | 6 +++++- test.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 6fb28762..ebf27a59 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1490,8 +1490,12 @@ void simplecpp::TokenList::removeComments() while (tok) { Token * const tok1 = tok; tok = tok->next; - if (tok1->comment) + if (tok1->comment) { + // a comment is replaced by one space character + if (tok1->previous) + tok1->previous->whitespaceahead = true; deleteToken(tok1); + } } } diff --git a/test.cpp b/test.cpp index 00ebbf1e..3726ee9b 100644 --- a/test.cpp +++ b/test.cpp @@ -1731,6 +1731,31 @@ static void hashEscape() "\"L\\\"a\\\\\\\"b\\\"\"", preprocess(code)); } +static void hashComment() +{ + // a comment is replaced by one space character + simplecpp::DUI dui; + dui.removeComments = true; + const char code[] = "#define str(x) #x\n" + "#define xstr(x) str(x)\n" + "str(a/**/b)\n" + "str(a/**/ /**/b)\n" + "str(a/**/)\n" + "xstr(a/**/b)"; + ASSERT_EQUALS("\n\n" + "\"a b\"\n" + "\"a b\"\n" + "\"a\"\n" + "\"a b\"", preprocess(code, dui)); + + // comments are kept in the string when they are not removed, like gcc -C + ASSERT_EQUALS("\n\n" + "\"a/**/b\"\n" + "\"a/**/ /**/b\"\n" + "\"a/**/\"\n" + "\"a/**/b\"", preprocess(code)); +} + static void hashEscapeTrailingBackslash() { // an odd number of trailing backslashes would escape the closing quote, @@ -4836,6 +4861,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(hash); TEST_CASE(hashEscape); + TEST_CASE(hashComment); TEST_CASE(hashEscapeTrailingBackslash); TEST_CASE(hashhash1); TEST_CASE(hashhash2); From 9db6bfd983958533516bc11266cf2e77dcb54aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 6 Nov 2026 13:48:09 +0100 Subject: [PATCH 2/2] Ignore whitespace at the end of a macro replacement list when stringifying The last token of an expansion got whitespaceahead from the newline or comment after the replacement list. Use the whitespace after the macro call instead. 'xstr(MAJOR.MINOR)' with '#define MAJOR 1/*major*/' now gives "1.2", and 'xstr(F(a)b)' gives "ab". --- simplecpp.cpp | 9 ++++++++- test.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index ebf27a59..d034e969 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2191,7 +2191,14 @@ namespace simplecpp { if (!parametertokens1.empty()) parametertokens1.swap(parametertokens2); - return functionLike() ? parametertokens2.back()->next : nameTokInst->next; + const Token * const callEnd = functionLike() ? parametertokens2.back() : nameTokInst; + + // Whitespace at the end of the replacement list does not count. + // The expansion is followed by the whitespace after the macro call. + if (output.back() && output.back() != output_end_1) + output.back()->whitespaceahead = callEnd->whitespaceahead; + + return callEnd->next; } /** Returns the macro to expand when the last token of @p temp is the name of a diff --git a/test.cpp b/test.cpp index 3726ee9b..f87009f8 100644 --- a/test.cpp +++ b/test.cpp @@ -1756,6 +1756,30 @@ static void hashComment() "\"a/**/b\"", preprocess(code)); } +static void hashMacroTrailingWhitespace() +{ + // whitespace at the end of a replacement list does not count + simplecpp::DUI dui; + dui.removeComments = true; + const char code[] = "#define str(x) #x\n" + "#define xstr(x) str(x)\n" + "#define MAJOR 1/*major*/\n" + "#define MINOR 2 \n" + "#define F(x) x\n" + "#define G F(a)\n" + "xstr(MAJOR.MINOR)\n" + "xstr(F(a)b)\n" + "xstr(F(a) b)\n" + "xstr(G.b)\n" + "xstr(G b)"; + ASSERT_EQUALS("\n\n\n\n\n\n" + "\"1.2\"\n" + "\"ab\"\n" + "\"a b\"\n" + "\"a.b\"\n" + "\"a b\"", preprocess(code, dui)); +} + static void hashEscapeTrailingBackslash() { // an odd number of trailing backslashes would escape the closing quote, @@ -4862,6 +4886,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(hash); TEST_CASE(hashEscape); TEST_CASE(hashComment); + TEST_CASE(hashMacroTrailingWhitespace); TEST_CASE(hashEscapeTrailingBackslash); TEST_CASE(hashhash1); TEST_CASE(hashhash2);