diff --git a/run-tests.py b/run-tests.py index 20f59a49..71d59b15 100644 --- a/run-tests.py +++ b/run-tests.py @@ -80,7 +80,6 @@ def cleanup(out: str) -> str: 'macro_rescan_varargs.c', # todo, high priority - 'c99-6_10_3_4_p5.c', 'c99-6_10_3_4_p6.c', 'expr_usual_conversions.c', # condition is true: 4U - 30 >= 0 'stdint.c', diff --git a/simplecpp.cpp b/simplecpp.cpp index 4b24e311..a01ec030 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2174,24 +2174,34 @@ namespace simplecpp { 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. + /** Expand while the expansion result in @p temp ends with the name of a + * function-like macro whose arguments are supplied by the tokens after @p tok. + * Each round consumes that macro call from the token stream, so tok always + * advances. The @p gatherCall callback copies the "( ... )" tokens that follow + * @p lpar into its output list and returns the matching ')', or nullptr when no + * complete call is available. Returns the last consumed token. */ + template + const Token *expandTrailingCalls(TokenList &output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, GatherCall gatherCall) const { while (const Macro * const calledMacro = rescanMacro(temp, tok, macros, expandedmacros)) { TokenList temp2(files); - temp2.push_back(new Token(temp.cback()->str(), tok->location)); + temp2.push_back(new Token(temp.cback()->str(), tok->next->location)); - const Token * const tok2 = appendTokens(temp2, loc, tok->next, macros, expandedmacros, parametertokens); - if (!tok2) + const Token * const closingPar = gatherCall(temp2, tok->next); + if (!closingPar) break; output.takeTokens(temp); output.deleteToken(output.back()); calledMacro->expand(temp, loc, temp2.cfront(), macros, expandedmacros); - tok = tok2; + tok = closingPar; } output.takeTokens(temp); - return tok->next; + return tok; + } + + 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 { + return expandTrailingCalls(output, temp, loc, tok, macros, expandedmacros, [&](TokenList &temp2, const Token *lpar) { + return appendTokens(temp2, loc, lpar, macros, expandedmacros, parametertokens); + })->next; } const Token *expandToken(TokenList &output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { @@ -2310,12 +2320,35 @@ namespace simplecpp { return false; if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter return true; - for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) { + const Token * const argEnd = parametertokens[argnr + 1U]; + for (const Token *partok = parametertokens[argnr]->next; partok != argEnd;) { const MacroMap::const_iterator it = macros.find(partok->str()); if (it != macros.end() && !partok->isExpandedFrom(&it->second) && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end())) { std::set expandedmacros2(expandedmacros); // temporary amnesia to allow reexpansion of currently expanding macros during argument evaluation expandedmacros2.erase(name()); - partok = it->second.expand(output, loc, partok, macros, std::move(expandedmacros2)); + TokenList temp(files); + partok = it->second.expand(temp, loc, partok, macros, std::move(expandedmacros2)); + if (partok->op == '(' && temp.cback() && temp.cback()->name) { + // the expansion result may end with the name of a function-like + // macro whose arguments are supplied by the remaining argument tokens + std::set expandedmacros3(expandedmacros); + expandedmacros3.erase(name()); + partok = expandTrailingCalls(output, temp, loc, partok->previous, macros, expandedmacros3, [&](TokenList &temp2, const Token *lpar) -> const Token * { + unsigned int par = 0; + for (const Token *tok2 = lpar; tok2 != argEnd; tok2 = tok2->next) { + temp2.push_back(new Token(*tok2)); + if (tok2->op == '(') + ++par; + else if (tok2->op == ')') { + if (--par == 0U) + return tok2; + } + } + return nullptr; + })->next; + } else { + output.takeTokens(temp); + } } else { output.push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros), partok)); output.back()->macro = partok->macro; diff --git a/test.cpp b/test.cpp index 247f63e1..8223f6f6 100644 --- a/test.cpp +++ b/test.cpp @@ -989,6 +989,22 @@ static void define26() "f ( ) ( )", preprocess(code2)); } +static void define27() +{ + // an expansion result inside a macro argument that is a function-like macro + // name must be rescanned against the remaining argument tokens + const char code[] = "#define o1(a) a()\n" + "#define b(c, d) c##d\n" + "#define l(c, d) b(c, 1)\n" + "#define m(e, f)\n" + "#define g(h, i) m(h, )\n" + "#define j(h, i) g(h, )\n" + "#define k() l(j(, ), )\n" + "#define n(a) j(l(o, )(a), )\n" + "n(k)\n"; + ASSERT_EQUALS("", preprocess(code)); +} + static void define_invalid_1() { @@ -4563,6 +4579,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(define24); TEST_CASE(define25); TEST_CASE(define26); + TEST_CASE(define27); TEST_CASE(define_invalid_1); TEST_CASE(define_invalid_2); TEST_CASE(define_invalid_3);