diff --git a/simplecpp.cpp b/simplecpp.cpp index 2223d431..504f89cf 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2831,12 +2831,12 @@ namespace simplecpp { strAB = ","; } else { - checkPunctuatorPaste(tok->location, name(), A, tokensB.cfront()); + checkPaste(tok->location, name(), A, tokensB.cfront()); strAB = A->str() + tokensB.cfront()->str(); tokensB.deleteToken(tokensB.front()); } } else { - checkPunctuatorPaste(tok->location, name(), A, B); + checkPaste(tok->location, name(), A, B); strAB = A->str() + B->str(); } @@ -2907,16 +2907,21 @@ namespace simplecpp { } /** - * When A is a punctuator, check that pasting A and B gives a valid token. + * Check that pasting A and B gives a valid token, when A is a punctuator + * or B is a string or character literal. * @param loc the location of the ## * @param macroName the macro that is expanded * @param A the token before ## * @param B the first token after ##, after expansion * @throws invalidHashHash if the result is not a valid token */ - static void checkPunctuatorPaste(const Location &loc, const std::string ¯oName, const Token *A, const Token *B) { + static void checkPaste(const Location &loc, const std::string ¯oName, const Token *A, const Token *B) { if (isPunctuatorOperand(A) && !isValidPunctuatorPaste(A->str() + B->str())) throw invalidHashHash::cannotCombine(loc, macroName, A, B); + // a name or number followed by a literal: only an encoding prefix such as L or u8 is valid + if ((A->name || A->number) && (isStringLiteral(B->str()) || isCharLiteral(B->str())) && + !(A->name && isStringLiteralPrefix(A->str()))) + throw invalidHashHash::cannotCombine(loc, macroName, A, B); } /** diff --git a/test.cpp b/test.cpp index 3f996167..bb2a4ecd 100644 --- a/test.cpp +++ b/test.cpp @@ -2554,9 +2554,29 @@ static void hashhash_invalid_name_string() "#define cat(a,b) a##b\n" "cat(a,\"s\")\n"; simplecpp::OutputList outputList; - // TODO: a"s" is not a valid token, gcc reports an error - TODO_ASSERT_EQUALS("", "\na\"s\"", preprocess(code, &outputList)); - TODO_ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'cat', Invalid ## usage when expanding 'cat': Combining 'a' and '\"s\"' yields an invalid token.\n", "", toString(outputList)); + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'cat', Invalid ## usage when expanding 'cat': Combining 'a' and '\"s\"' yields an invalid token.\n", toString(outputList)); +} + +static void hashhash_invalid_number_string() +{ + const char code[] = + "#define cat(a,b) a##b\n" + "cat(1,\"s\")\n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'cat', Invalid ## usage when expanding 'cat': Combining '1' and '\"s\"' yields an invalid token.\n", toString(outputList)); +} + +static void hashhash_encoding_prefix() +{ + const char code[] = + "#define cat(a,b) a##b\n" + "#define WIDE(s) L ## s\n" + "cat(L,\"s\") cat(u8,\"s\") cat(R,\"(x)\") cat(L,'c') WIDE(\"s\")\n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("\n\nL\"s\" u8\"s\" R\"(x)\" L'c' L\"s\"", preprocess(code, &outputList)); + ASSERT_EQUALS("", toString(outputList)); } static void hashhash_va_args_unexpected() @@ -5689,6 +5709,8 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(hashhash_empty_placemarker); TEST_CASE(hashhash_invalid_dotdot); TEST_CASE(hashhash_invalid_name_string); + TEST_CASE(hashhash_invalid_number_string); + TEST_CASE(hashhash_encoding_prefix); TEST_CASE(hashhash_va_args_unexpected); // C standard, 5.1.1.2, paragraph 4: