GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix: simplecpp ## fails to expand function-like macro when '(' is not adjacent by paulafy6 · Pull Request #655 · cppcheck-opensource/simplecpp

Fix: simplecpp ## fails to expand function-like macro when '(' is not adjacent #655

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Sign up for GitHub

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
paulafy6 wants to merge 2 commits into cppcheck-opensource:master
base: master
Loading
Loading
from paulafy6:fix-preprocessor-concat
+72 −6
Open

Fix: simplecpp ## fails to expand function-like macro when '(' is not adjacent #655

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
2 commits
Select commit Hold shift + click to select a range
a328c4d
Fix: simplecpp ## fails to expand function-like macro when '(' is not…
paulafy6 Aug 18, 2026
a2f6f52
test: add regression test for ## PAR-style indirection (hashhash_func…
paulafy6 Aug 18, 2026
File filter
Filter by extension

Clear filters
Conversations
Failed to load comments.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Jump to
Jump to file
Failed to load files.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Diff view
Diff view

There are no files selected for viewing

57 changes: 51 additions & 6 deletions 57 simplecpp.cpp
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -2453,16 +2453,61 @@ namespace simplecpp {
output.deleteToken(A);
TokenList tokens(files);
tokens.push_back(new Token(strAB, tok->location));
// for function like macros, push the (...)
if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') {
const MacroMap::const_iterator it = macros.find(strAB);
if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) {
const Token * const tok2 = appendTokens(tokens, loc, B->next, macros, expandedmacros, parametertokens);
// If strAB names a function-like macro, locate and append its argument list '(...)'
// from the remaining replacement tokens, then expand the whole call.
// 'forwardScan' is true when the argument list was consumed via the forward scan
// path below (not adjacent), so that expandToken() is called instead of
// the normal takeTokens() path even when expandResult is false.
bool forwardScan = false;
const MacroMap::const_iterator it = macros.find(strAB);
const bool isFunctionLikeMacro = (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike());
if (tokensB.empty() && isFunctionLikeMacro) {
// Fast path: '(' is the very next token after B on the same line.
const Token *lpar = (sameline(B, B->next) && B->next->op == '(') ? B->next : nullptr;
if (!lpar && !expandResult) {
// Forward-scan path: '(' is not immediately adjacent to B.
// This handles PAR-style indirection patterns such as:
// #define PAR(a, ...) a __VA_ARGS__
// #define DISPATCH(kind, ...) PAR(PREFIX_ ## kind, (__VA_ARGS__))
// where the '(' for PREFIX_kind belongs to the __VA_ARGS__ parameter
// and is separated from B by a comma in the replacement text.
// Only active in the appendTokens context (expandResult==false) to
// avoid unintended side-effects inside the main expansion loop.
for (const Token *scan = nextTok; sameline(B, scan); scan = scan->next) {
if (scan->op == '(') {
// Found a literal '(' after skipping separators.
lpar = scan;
forwardScan = true;
break;
}
if (scan->op == ',') {
// Argument separator — skip and keep scanning.
continue;
}
if (scan->name) {
// Named token: expand it and check whether it starts with '(...)'
// (covers the case where __VA_ARGS__ expands to a parenthesised list).
TokenList expanded(files);
if (expandArg(expanded, scan, loc, macros, expandedmacros, parametertokens) &&
expanded.cfront() && expanded.cfront()->op == '(') {
for (Token *t = expanded.front(); t; t = t->next)
t->location = loc;
tokens.takeTokens(expanded);
nextTok = scan->next;
forwardScan = true;
}
break; // stop at any name token, whether consumed or not
}
break; // any other operator — stop scan
}
}
if (lpar) {
const Token * const tok2 = appendTokens(tokens, loc, lpar, macros, expandedmacros, parametertokens);
if (tok2)
nextTok = tok2->next;
}
}
if (expandResult)
if (expandResult || forwardScan)
expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens);
else
output.takeTokens(tokens);
Expand Down
21 changes: 21 additions & 0 deletions 21 test.cpp
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,26 @@ static void hashhash_universal_character_2()
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'A', Invalid ## usage when expanding 'A': Combining '\\U0104' and '0104' yields universal character '\\U01040104'. This is undefined behavior according to C standard chapter 5.1.1.2, paragraph 4.\n", toString(outputList));
}

// Regression test: ## result is a function-like macro whose '(' is not adjacent.
// PAR-style indirection hides '(' behind a comma/parameter in the replacement text.
// Previously caused [unknownMacro] and aborted TU expansion.
static void hashhash_funclike_par_indirection()
{
// Single dispatch: PREFIX_ ## kind → PREFIX_SCALAR, '(' separated by ','
ASSERT_EQUALS("\n\n\nint x",
preprocess("#define PAR(a, ...) a __VA_ARGS__\n"
"#define PREFIX_SCALAR(T, N) T N\n"
"#define DISPATCH(kind, ...) PAR(PREFIX_ ## kind, (__VA_ARGS__))\n"
"DISPATCH(SCALAR, int, x)\n"));

// Chained: two DISPATCH calls with different PREFIX_ specialisations
ASSERT_EQUALS("\n\n\n\nint x float arr [ 10 ]",
preprocess("#define PAR(a, ...) a __VA_ARGS__\n"
"#define PREFIX_SCALAR(T, N) T N\n"
"#define PREFIX_ARRAY(T, N, S) T N[S]\n"
"#define DISPATCH(kind, ...) PAR(PREFIX_ ## kind, (__VA_ARGS__))\n"
"DISPATCH(SCALAR, int, x) DISPATCH(ARRAY, float, arr, 10)\n"));
}

static void has_include_1()
{
Expand Down Expand Up @@ -4669,6 +4689,7 @@ static void runTests(int argc, char **argv, Input input)
// the behavior is undefined."
TEST_CASE(hashhash_universal_character);
TEST_CASE(hashhash_universal_character_2);
TEST_CASE(hashhash_funclike_par_indirection); // PAR-style ## indirection: '(' not adjacent to ## result

// c++17 __has_include
TEST_CASE(has_include_1);
Expand Down
Loading
Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.