Skip to content

Fix #724 (##: valid token pastes are rejected) - #725

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

danmar merged 2 commits into
masterfrom
fix-724

Conversation

@danmar

@danmar danmar commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator

When the left operand of ## is a punctuator, check the pasted result after the right operand has been expanded, instead of checking the unexpanded parameter name.

When the left operand of ## is a punctuator, check the pasted result
after the right operand has been expanded, instead of checking the
unexpanded parameter name.

@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 #724: -##>, cat(<<,=), cat3(<,<,=), cat(+,), +##__VA_ARGS__ and similar pastes now give the same result as gcc and clang. I found one regression that matters for cppcheck.

Regression: a punctuator pasted with a macro parameter used to be accepted without an error when the result was not a valid token. It is now a hard syntax error. The base version only threw unexpectedToken for unexpectedA in the varargs case and in the non-parameter case. The non-variadic parameter path accepted it. MSVC accepts obj->##m and obj.##m, and these patterns appear in Windows code. With this PR cppcheck reports failed to expand and does not preprocess the file. gcc and clang also reject these pastes, so the new behaviour is "correct". But it is a compatibility change that the PR does not mention, and no test covers it. Other inputs that now fail where base produced output: cat(.,x), cat3(.,.,.), cat(.,.5), F('a') with . ## x, and (##x) with a parameter x.

I found no other wrong results.

Testing

Build and test suites

  • Built make -j8 testrunner simplecpp CXXOPTS=-Werror in pr/ with no warnings. Also built base/.
  • ./testrunner: no failures.
  • python3 run-tests.py: 176 tests, 0 failed. base gives the same result.
  • python3 -m pytest integration_test.py: 128 passed.
  • ./selfcheck.sh: exit code 0.

Own inputs
I wrote about 40 small inputs in /work/scratch and compared gcc -E, clang -E, base and pr.

The PR now matches gcc for:

  • F(x) -##x with F(>), F(> y) and F()
  • cat3(<,<,=)
  • cat(+,)
  • +##__VA_ARGS__
  • cat(<,:), cat(#,#), cat(:,:), cat(=,=), cat(<, <=)
  • .##1.5e+3 and .##1a
  • the GNU , ## args and , ## __VA_ARGS__ forms (empty and non-empty)
  • [a##b] with an empty a

Invalid pastes that gcc rejects are also rejected now: -##>>, +##+=, -##"x" and +##PP.

Regression (reproduced)
m1.c:

#define GET(obj, m) obj->##m
#define GET2(obj, m) obj.##m
int a = GET(p, x) + GET2(s, y);
$ /work/base/simplecpp m1.c
int a = p ->x + s .y ;
$ /work/pr/simplecpp m1.c
m1.c:1: syntax error: failed to expand 'GET', Invalid ## usage when expanding 'GET': Combining '->' and 'x' yields an invalid token.

gcc and clang report an error for this input, but MSVC accepts it. The same change from output to error happens for:

Input base output pr
cat(.,x) .x error
cat3(.,.,.) ... error
cat(.,.5) ..5 error
#define F(x) (##x with F(a) (a ) error

Minor
cat(%:,%:) with digraphs gives %:%: with gcc. base gave the wrong output % :% :. pr gives the error Combining ':' and '%', because simplecpp splits %: into two tokens.


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

Comment thread simplecpp.cpp Resolved
@danmar
danmar merged commit b833a75 into master Nov 6, 2026
47 checks passed
@danmar
danmar deleted the fix-724 branch November 6, 2026 19:25
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