From 0fa51dc7dc6a5e29128be8b27e20ff9fa7124a36 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 13 Jul 2026 10:42:46 -0500 Subject: [PATCH 1/4] Fix test case with recursive scans --- simplecpp.cpp | 47 +++++++++++++++++++++-------------------------- test.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 748ea6a9..9a9ce360 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2090,38 +2090,33 @@ namespace simplecpp { } const Token *recursiveExpandToken(TokenList &output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { - if (!temp.cback() || !temp.cback()->name || !tok->next || tok->next->op != '(') { - output.takeTokens(temp); - return tok->next; - } + // Loop: the expansion result may itself end with the name of a function-like + // macro whose arguments are supplied by the tokens that follow it. + while (true) { + if (!temp.cback() || !temp.cback()->name || !tok->next || tok->next->op != '(' || !sameline(tok, tok->next)) + break; - if (!sameline(tok, tok->next)) { - output.takeTokens(temp); - return tok->next; - } + const MacroMap::const_iterator it = macros.find(temp.cback()->str()); + if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) + break; - const MacroMap::const_iterator it = macros.find(temp.cback()->str()); - if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) { - output.takeTokens(temp); - return tok->next; - } + const Macro &calledMacro = it->second; + if (!calledMacro.functionLike() || temp.cback()->isExpandedFrom(&calledMacro)) + break; - const Macro &calledMacro = it->second; - if (!calledMacro.functionLike()) { + TokenList temp2(files); + temp2.push_back(new Token(temp.cback()->str(), tok->location)); + + const Token * const tok2 = appendTokens(temp2, loc, tok->next, macros, expandedmacros, parametertokens); + if (!tok2) + break; output.takeTokens(temp); - return tok->next; + output.deleteToken(output.back()); + calledMacro.expand(temp, loc, temp2.cfront(), macros, expandedmacros); + tok = tok2; } - - TokenList temp2(files); - temp2.push_back(new Token(temp.cback()->str(), tok->location)); - - const Token * const tok2 = appendTokens(temp2, loc, tok->next, macros, expandedmacros, parametertokens); - if (!tok2) - return tok->next; output.takeTokens(temp); - output.deleteToken(output.back()); - calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros); - return tok2->next; + return tok->next; } const Token *expandToken(TokenList &output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { diff --git a/test.cpp b/test.cpp index 990a6daf..3d963c24 100644 --- a/test.cpp +++ b/test.cpp @@ -909,6 +909,31 @@ static void define23() // #40 "unsigned A , B ;", preprocess(code)); } +static void define24() +{ + // an expansion result that is a function-like macro name must be rescanned + // repeatedly against the tokens that follow it + const char code[] = "#define a(b, c) c\n" + "#define d() a\n" + "#define g(e) h(e, ) h(e, )\n" + "#define h(e, b) d()(, e)()\n" + "#define i()\n" + "g(i)\n"; + ASSERT_EQUALS("", preprocess(code)); +} + +static void define25() +{ + // a macro name that came from expanding that same macro must not be + // re-expanded when rescanned with the tokens that follow it + const char code[] = "#define f() f\n" + "#define wrap(x) x()\n" + "wrap(f())\n"; + ASSERT_EQUALS("\n" + "\n" + "f ( )", preprocess(code)); +} + static void define_invalid_1() { @@ -4096,6 +4121,8 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(define21); // #66 TEST_CASE(define22); // #40 TEST_CASE(define23); // #40 + TEST_CASE(define24); + TEST_CASE(define25); TEST_CASE(define_invalid_1); TEST_CASE(define_invalid_2); TEST_CASE(define_invalid_3); From 96fc5645ce7b8c694e9bf12684ce152e6d396e1d Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 13 Jul 2026 11:01:10 -0500 Subject: [PATCH 2/4] Avoid a recursive expansion --- simplecpp.cpp | 2 +- test.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 9a9ce360..3d62fff6 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1635,7 +1635,7 @@ namespace simplecpp { if (output2.cfront() != output2.cback() && macro2tok->str() == this->name()) break; const MacroMap::const_iterator macro = macros.find(macro2tok->str()); - if (macro == macros.end() || !macro->second.functionLike()) + if (macro == macros.end() || !macro->second.functionLike() || macro2tok->isExpandedFrom(¯o->second)) break; TokenList rawtokens2(inputFiles); const Location loc(macro2tok->location); diff --git a/test.cpp b/test.cpp index 3d963c24..a88900ef 100644 --- a/test.cpp +++ b/test.cpp @@ -934,6 +934,21 @@ static void define25() "f ( )", preprocess(code)); } +static void define26() +{ + // a macro name that came from expanding that same macro must not be + // re-expanded with arguments taken from the raw token stream + const char code[] = "#define f() f\n" + "f()()\n"; + ASSERT_EQUALS("\n" + "f ( )", preprocess(code)); + + const char code2[] = "#define f() f\n" + "f()()()\n"; + ASSERT_EQUALS("\n" + "f ( ) ( )", preprocess(code2)); +} + static void define_invalid_1() { @@ -4123,6 +4138,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(define23); // #40 TEST_CASE(define24); TEST_CASE(define25); + TEST_CASE(define26); TEST_CASE(define_invalid_1); TEST_CASE(define_invalid_2); TEST_CASE(define_invalid_3); From fd4f09262c3f2a186527ce6c66eec38e5bed3dd1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 13 Jul 2026 11:11:20 -0500 Subject: [PATCH 3/4] Remove the while(true) loop --- simplecpp.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 3d62fff6..3fb9e2a6 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2089,21 +2089,24 @@ namespace simplecpp { return functionLike() ? parametertokens2.back()->next : nameTokInst->next; } - const Token *recursiveExpandToken(TokenList &output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { - // Loop: the expansion result may itself end with the name of a function-like - // macro whose arguments are supplied by the tokens that follow it. - while (true) { - if (!temp.cback() || !temp.cback()->name || !tok->next || tok->next->op != '(' || !sameline(tok, tok->next)) - break; - - const MacroMap::const_iterator it = macros.find(temp.cback()->str()); - if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) - break; - - const Macro &calledMacro = it->second; - if (!calledMacro.functionLike() || temp.cback()->isExpandedFrom(&calledMacro)) - break; + /** Returns the macro to expand when the last token of @p temp is the name of a + * function-like macro and the tokens after @p tok supply its arguments; nullptr otherwise */ + static const Macro *rescanMacro(const TokenList &temp, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros) { + if (!temp.cback() || !temp.cback()->name || !sameline(tok, tok->next) || tok->next->op != '(') + return nullptr; + const MacroMap::const_iterator it = macros.find(temp.cback()->str()); + if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) + return nullptr; + if (!it->second.functionLike() || temp.cback()->isExpandedFrom(&it->second)) + return nullptr; + return &it->second; + } + const Token *recursiveExpandToken(TokenList &output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + // Expand while the expansion result ends with the name of a function-like + // macro whose arguments are supplied by the tokens that follow it. Each round + // consumes that macro call from the token stream, so tok always advances. + while (const Macro *calledMacro = rescanMacro(temp, tok, macros, expandedmacros)) { TokenList temp2(files); temp2.push_back(new Token(temp.cback()->str(), tok->location)); @@ -2112,7 +2115,7 @@ namespace simplecpp { break; output.takeTokens(temp); output.deleteToken(output.back()); - calledMacro.expand(temp, loc, temp2.cfront(), macros, expandedmacros); + calledMacro->expand(temp, loc, temp2.cfront(), macros, expandedmacros); tok = tok2; } output.takeTokens(temp); From 388b0ad42b982273a0a78442182f2080af9eeb96 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 13 Jul 2026 11:45:40 -0500 Subject: [PATCH 4/4] Declare variable const --- simplecpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 3fb9e2a6..2fd7d549 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2106,7 +2106,7 @@ namespace simplecpp { // Expand while the expansion result ends with the name of a function-like // macro whose arguments are supplied by the tokens that follow it. Each round // consumes that macro call from the token stream, so tok always advances. - while (const Macro *calledMacro = rescanMacro(temp, tok, macros, expandedmacros)) { + while (const Macro * const calledMacro = rescanMacro(temp, tok, macros, expandedmacros)) { TokenList temp2(files); temp2.push_back(new Token(temp.cback()->str(), tok->location));