diff --git a/simplecpp.cpp b/simplecpp.cpp index 0b791945..476f42ff 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -110,6 +110,15 @@ template static std::string toString(T t) return ostr.str(); } +#ifdef SIMPLECPP_DEBUG_MACRO_EXPANSION +static std::string locstring(const simplecpp::Location &loc) +{ + std::ostringstream ostr; + ostr << '[' << loc.file() << ':' << loc.line << ':' << loc.col << ']'; + return ostr.str(); +} +#endif + static long long stringToLL(const std::string &s) { long long ret; @@ -1528,6 +1537,10 @@ namespace simplecpp { std::vector &inputFiles) const { std::set expandedmacros; +#ifdef SIMPLECPP_DEBUG_MACRO_EXPANSION + std::cout << "expand " << name() << " " << locstring(rawtok->location) << std::endl; +#endif + TokenList output2(inputFiles); if (functionLike() && rawtok->next && rawtok->next->op == '(') { @@ -1797,6 +1810,10 @@ namespace simplecpp { const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const MacroMap ¯os, std::set expandedmacros) const { expandedmacros.insert(nameTokInst->str()); +#ifdef SIMPLECPP_DEBUG_MACRO_EXPANSION + std::cout << " expand " << name() << " " << locstring(defineLocation()) << std::endl; +#endif + usageList.push_back(loc); if (nameTokInst->str() == "__FILE__") { @@ -2168,8 +2185,7 @@ namespace simplecpp { const bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>"; const bool canBeConcatenatedStringOrChar = isStringLiteral_(A->str()) || isCharLiteral_(A->str()); - if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual && !canBeConcatenatedStringOrChar) - throw invalidHashHash::unexpectedToken(tok->location, name(), A); + const bool unexpectedA = (!A->name && !A->number && !A->str().empty() && !canBeConcatenatedWithEqual && !canBeConcatenatedStringOrChar); Token * const B = tok->next->next; if (!B->name && !B->number && B->op && !B->isOneOf("#=")) @@ -2187,6 +2203,9 @@ namespace simplecpp { const Token *nextTok = B->next; if (canBeConcatenatedStringOrChar) { + if (unexpectedA) + throw invalidHashHash::unexpectedToken(tok->location, name(), A); + // It seems clearer to handle this case separately even though the code is similar-ish, but we don't want to merge here. // TODO The question is whether the ## or varargs may still apply, and how to provoke? if (expandArg(&tokensB, B, parametertokens)) { @@ -2205,13 +2224,17 @@ namespace simplecpp { if (expandArg(&tokensB, B, parametertokens)) { if (tokensB.empty()) strAB = A->str(); - else if (varargs && A->op == ',') { + else if (varargs && A->op == ',') strAB = ","; - } else { + else if (varargs && unexpectedA) + throw invalidHashHash::unexpectedToken(tok->location, name(), A); + else { strAB = A->str() + tokensB.cfront()->str(); tokensB.deleteToken(tokensB.front()); } } else { + if (unexpectedA) + throw invalidHashHash::unexpectedToken(tok->location, name(), A); strAB = A->str() + B->str(); } diff --git a/test.cpp b/test.cpp index 783e0175..0f7ebe77 100644 --- a/test.cpp +++ b/test.cpp @@ -1421,6 +1421,17 @@ static void hashhash_null_stmt() ASSERT_EQUALS("\n\n\n\n1 ;", preprocess(code, &outputList)); } +static void hashhash_empty_va_args() +{ + // #395 hash hash with an empty __VA_ARGS__ in a macro + const char code[] = + "#define CAT(a, ...) a##__VA_ARGS__\n" + "#define X(a, ...) CAT(a)\n" + "#define LEVEL_2 (2)\n" + "X(LEVEL_2)\n"; + ASSERT_EQUALS("\n\n\n( 2 )", preprocess(code)); +} + static void hashhash_universal_character() { const char code[] = @@ -3044,6 +3055,7 @@ int main(int argc, char **argv) TEST_CASE(hashhash_invalid_string_number); TEST_CASE(hashhash_invalid_missing_args); TEST_CASE(hashhash_null_stmt); + TEST_CASE(hashhash_empty_va_args); // C standard, 5.1.1.2, paragraph 4: // If a character sequence that matches the syntax of a universal // character name is produced by token concatenation (6.10.3.3),