diff --git a/simplecpp.cpp b/simplecpp.cpp index 748ea6a9..2fd7d549 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); @@ -2089,39 +2089,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 990a6daf..a88900ef 100644 --- a/test.cpp +++ b/test.cpp @@ -909,6 +909,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() { @@ -4096,6 +4136,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);