From 74a5a63fe1f3a7263bf9e2c6a70d59b00787726d Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Tue, 4 Aug 2026 08:02:06 -0500 Subject: [PATCH 1/5] Fix rescanning when a macro expansion result forms a new function-like macro call (#682) --- simplecpp.cpp | 56 +++++++++++++++++++++++++-------------------------- test.cpp | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 29 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 93853617..4b24e311 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1707,7 +1707,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); @@ -2161,39 +2161,37 @@ 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 { - if (!temp.cback() || !temp.cback()->name || !tok->next || tok->next->op != '(') { - output.takeTokens(temp); - return tok->next; - } - - if (!sameline(tok, tok->next)) { - output.takeTokens(temp); - return tok->next; - } - + /** 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()) { - output.takeTokens(temp); - return tok->next; - } + 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 Macro &calledMacro = it->second; - if (!calledMacro.functionLike()) { + 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 * const calledMacro = rescanMacro(temp, tok, macros, expandedmacros)) { + 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 d059b237..247f63e1 100644 --- a/test.cpp +++ b/test.cpp @@ -949,6 +949,46 @@ 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 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() { @@ -4520,6 +4560,9 @@ 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(define26); TEST_CASE(define_invalid_1); TEST_CASE(define_invalid_2); TEST_CASE(define_invalid_3); From 93561ef9c4ea59b55c78b9acfbbccb79071efca3 Mon Sep 17 00:00:00 2001 From: glankk Date: Wed, 19 Aug 2026 18:04:28 +0200 Subject: [PATCH 2/5] Fix cppcheck selfcheck issue (#696) --- simplecpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 4b24e311..8939ff2e 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1771,7 +1771,7 @@ namespace simplecpp { }; struct invalidDirectiveAsMacroParameter : public Error { - invalidDirectiveAsMacroParameter(const Location &loc) + explicit invalidDirectiveAsMacroParameter(const Location &loc) : Error(loc, "it is invalid to use a preprocessor directive as macro parameter") {} }; From 439ac67df072d3108a10a51f94bad7aa5704991b Mon Sep 17 00:00:00 2001 From: glankk Date: Wed, 19 Aug 2026 18:32:21 +0200 Subject: [PATCH 3/5] Add newline to macro def string before tokenizing (#697) --- simplecpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 8939ff2e..77f635e5 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1595,7 +1595,7 @@ namespace simplecpp { * @throws std::runtime_error thrown on bad macro syntax */ Macro(const std::string &name, const std::string &value, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(false) { - const std::string def(name + ' ' + value); + const std::string def(name + ' ' + value + '\n'); StdCharBufStream stream(reinterpret_cast(def.data()), def.size()); tokenListDefine.readfile(stream); if (!parseDefine(tokenListDefine.cfront())) From 1392ebd37ccf15283f96fa97be214656f56d06cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 20 Aug 2026 10:57:08 +0200 Subject: [PATCH 4/5] Fix #700: `macro` not set for tokens expanded from `__LINE__` inside function-like macros (#701) --- simplecpp.cpp | 3 +++ test.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/simplecpp.cpp b/simplecpp.cpp index 77f635e5..81d09867 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1994,14 +1994,17 @@ namespace simplecpp { if (nameTokInst->str() == "__FILE__") { output.push_back(new Token('\"'+output.file(loc)+'\"', loc)); + output.back()->macro = "__FILE__"; return nameTokInst->next; } if (nameTokInst->str() == "__LINE__") { output.push_back(new Token(toString(loc.line), loc)); + output.back()->macro = "__LINE__"; return nameTokInst->next; } if (nameTokInst->str() == "__COUNTER__") { output.push_back(new Token(toString(usageList.size()-1U), loc)); + output.back()->macro = "__COUNTER__"; return nameTokInst->next; } diff --git a/test.cpp b/test.cpp index 247f63e1..2b0e8ee0 100644 --- a/test.cpp +++ b/test.cpp @@ -3782,6 +3782,31 @@ static void tokenMacro5() ASSERT_EQUALS("SET_BPF_JUMP", tok->macro); } +static void tokenMacro6() +{ + const char code[] = "#define FILE() __FILE__\n" + "#define LINE() __LINE__\n" + "#define COUNTER() __COUNTER__\n" + "FILE()\n" + "LINE()\n" + "COUNTER()\n"; + std::vector files; + simplecpp::FileDataCache cache; + simplecpp::TokenList tokenList(files); + const simplecpp::TokenList rawtokens = makeTokenList(code,files); + simplecpp::preprocess(tokenList, rawtokens, files, cache, simplecpp::DUI()); + const simplecpp::Token *tok; + + tok = tokenList.cfront(); + ASSERT_EQUALS("__FILE__", tok->macro); + + tok = tok->next; + ASSERT_EQUALS("__LINE__", tok->macro); + + tok = tok->next; + ASSERT_EQUALS("__COUNTER__", tok->macro); +} + static void undef() { const char code[] = "#define A\n" @@ -4781,6 +4806,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(tokenMacro3); TEST_CASE(tokenMacro4); TEST_CASE(tokenMacro5); + TEST_CASE(tokenMacro6); TEST_CASE(undef); From 2499b51390e6ea74b8fbad91154f5529134de31a Mon Sep 17 00:00:00 2001 From: glankk Date: Tue, 25 Aug 2026 18:27:55 +0200 Subject: [PATCH 5/5] Call load_callback for all includes, even cached (#703) --- simplecpp.cpp | 15 +++++++++++---- simplecpp.h | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 81d09867..8237ac75 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -3232,13 +3232,10 @@ std::pair simplecpp::FileDataCache::tryload(FileDat mImpl->mIdMap.emplace(fileId, data); mData.emplace_back(data); - if (mLoadCallback) - mLoadCallback(*data); - return {data, true}; } -std::pair simplecpp::FileDataCache::get(const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader, std::vector &filenames, simplecpp::OutputList *outputList) +std::pair simplecpp::FileDataCache::get_private(const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader, std::vector &filenames, simplecpp::OutputList *outputList) { if (isAbsolutePath(header)) { auto ins = mNameMap.emplace(simplecpp::simplifyPath(header), nullptr); @@ -3284,6 +3281,16 @@ std::pair simplecpp::FileDataCache::get(const std:: return {nullptr, false}; } +std::pair simplecpp::FileDataCache::get(const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader, std::vector &filenames, simplecpp::OutputList *outputList) +{ + auto ret = get_private(sourcefile, header, dui, systemheader, filenames, outputList); + + if (mLoadCallback && ret.first) + mLoadCallback(*ret.first, ret.second); + + return ret; +} + void simplecpp::FileDataCache::clear() { mImpl->clear(); diff --git a/simplecpp.h b/simplecpp.h index e64a8f7b..f4dca183 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -511,7 +511,7 @@ namespace simplecpp { return mData.cend(); } - using load_callback_type = std::function; + using load_callback_type = std::function; void set_load_callback(load_callback_type cb) { mLoadCallback = std::move(cb); @@ -524,6 +524,7 @@ namespace simplecpp { using name_map_type = std::unordered_map; std::pair tryload(name_map_type::iterator &name_it, const DUI &dui, std::vector &filenames, OutputList *outputList); + std::pair get_private(const std::string &sourcefile, const std::string &header, const DUI &dui, bool systemheader, std::vector &filenames, OutputList *outputList); container_type mData; name_map_type mNameMap;