Skip to content

Fix #718 (# operator drops the space that a comment stands for) - #719

Merged
danmar merged 2 commits into
masterfrom
fix-stringify-comment
Nov 6, 2026
Merged

danmar merged 2 commits into
masterfrom
fix-stringify-comment

Conversation

@danmar

@danmar danmar commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator

'str(a/**/b)' gave "ab" instead of "a b". When comments are removed, mark the previous token as followed by whitespace.

'str(a/**/b)' gave "ab" instead of "a b". When comments are removed,
mark the previous token as followed by whitespace.
@danmar
danmar force-pushed the fix-stringify-comment branch from da98bf2 to 2e16061 Compare November 6, 2026 12:42

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

The PR fixes the reported case, but it breaks stringification when a macro body ends with a comment. In removeComments() a trailing comment in a #define now sets whitespaceahead on the last token of the macro body. expandArg (simplecpp.cpp:2392) passes that flag on to the expansion, so # inserts a space that gcc and clang don't produce:

#define MAJOR 1/*major*/
#define MINOR 2/*minor*/
xstr(MAJOR.MINOR)

gives "1 .2" with the PR. Base, gcc and clang all give "1.2". The same bug already exists in base when there is a real space before the end of the body (#define E3(x) x /**/ gives "a b" where gcc gives "ab"). The PR spreads it to all trailing comments that have no space before them. A trailing comment in a #define is common code, so this is a real regression. Whitespace at the end of a replacement list should not count for stringification. No other problems found.

Testing

Build and test suites

  • make -j8 testrunner simplecpp CXXOPTS=-Werror in pr/ built cleanly.
  • ./testrunner: 0 failed.
  • python3 run-tests.py: 176 tests, 0 failed.
  • pytest integration_test.py: 128 passed.
  • ./selfcheck.sh: exit 0.

I also built base/ and compared /work/{pr,base}/simplecpp with gcc -E -P and clang -E -P.

Cases that now match gcc (base got them wrong):

  • str(a/**/b)
  • a block comment that spans lines inside the argument
  • str(a/**/+/**/b) gives "a + b"
  • str("x"/**/'y')
  • a comment inside the macro body: #define F(x) str(x/**/y)
  • xstr(H()) with #define H() a/**/b
  • xstr(E(a)/**/b)

These already matched gcc in base and still do: str(/**/a), str(a/**/), xstr(CAT(a,/**/b)).

Regression: macro body that ends with a comment

scratch/t4.c:

#define str(x) #x
#define xstr(x) str(x)
#define MAJOR 1/*major*/
#define MINOR 2/*minor*/
xstr(MAJOR.MINOR)
$ /work/pr/simplecpp t4.c    -> "1 .2"
$ /work/base/simplecpp t4.c  -> "1.2"
$ gcc -E -P t4.c             -> "1.2"   (clang: "1.2")

More cases (scratch/t3.c), with #define E(x) x/**/, #define E2 a/**/, #define P(x) (x)/**/:

input PR base gcc / clang
xstr(E(a)b) "a b" "ab" "ab"
xstr(E2;) "a ;" "a;" "a;"
xstr(P(a)b) "(a) b" "(a)b" "(a)b"

The same bug already exists in base with an explicit space: #define E3(x) x /**/ then xstr(E3(a)b) gives "a b" in both pr and base, and "ab" in gcc.


Automated review of 2e16061 by Claude. The pull request was built and tested locally.

Comment thread simplecpp.cpp Resolved
…fying

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".
@danmar
danmar merged commit 768e6c3 into master Nov 6, 2026
47 checks passed
@danmar
danmar deleted the fix-stringify-comment branch November 6, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants