Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions externals/simplecpp/simplecpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2377,15 +2377,54 @@ namespace simplecpp {
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);
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) {
// Determine where '(' for the concatenated function-like macro begins.
const Token *lpar = (sameline(B, B->next) && B->next->op == '(') ? B->next : nullptr;
if (!lpar && !expandResult) {
// Forward scan: handles patterns like PAR(PREFIX_ ## kind, (__VA_ARGS__))
// where '(' is not immediately adjacent to B in the replacement text
// but follows comma separators or parameter tokens on the same line.
// Restricted to appendTokens context (expandResult==false) to avoid
// unintended side-effects in the main expansion loop.
const Token *scan = nextTok;
while (scan && sameline(B, scan)) {
if (scan->op == '(') {
// Literal '(' found after skipping separators
lpar = scan;
forwardScan = true;
break;
}
if (scan->op == ',') {
// Skip argument separator and keep scanning
scan = scan->next;
continue;
}
if (scan->name) {
// Named token — check if it's a parameter that expands to '(...)'
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 scan at any name token (consumed or not)
}
break; // 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 test/testpreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ class TestPreprocessor : public TestFixture {
TEST_CASE(preprocessor_undef);
TEST_CASE(defdef); // Defined multiple times
TEST_CASE(preprocessor_doublesharp);
TEST_CASE(preprocessor_doublesharp_funclike_forward_scan); // simplecpp: ## result used as function-like macro via PAR-style indirection
TEST_CASE(preprocessor_include_in_str);
TEST_CASE(va_args_1);
//TEST_CASE(va_args_2); invalid code
Expand Down Expand Up @@ -1355,6 +1356,26 @@ class TestPreprocessor : public TestFixture {
ASSERT_EQUALS("\n\n\n\nx_y", expandMacros(filedata6, *this));
}

// Test for simplecpp forward-scan fix: when ## concatenation produces a function-like macro
// name and '(' is not immediately adjacent in the replacement text (e.g. PAR pattern), the
// forward scan must find '(' across comma separators or parameter tokens on the same line.
void preprocessor_doublesharp_funclike_forward_scan() {
// PAR pattern: PREFIX_ ## kind, (__VA_ARGS__) where '(' is separated by ','
// Previously caused [unknownMacro] and aborted TU analysis in cppcheck.
const char filedata1[] = "#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";
ASSERT_EQUALS("\n\n\nint x", expandMacros(filedata1, *this));

// Chained: two DISPATCH calls in a struct context
const char filedata2[] = "#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";
ASSERT_EQUALS("\n\n\n\nint x float arr [ 10 ]", expandMacros(filedata2, *this));
}


void preprocessor_include_in_str() {
Expand Down