Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions simplecpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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 &macroName, const Token *A, const Token *B) {
static void checkPaste(const Location &loc, const std::string &macroName, 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())) &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

The check misses a literal that already has an encoding prefix. isStringLiteral/isCharLiteral only match a token whose first character is a quote, so L"s", u8"s" and u'c' as B are not checked.

With #define cat(a,b) a##b, simplecpp outputs cat(a,L"s") as aL"s", cat(a,u8"s") as au8"s" and cat(L,L"s") as LL"s". gcc reports pasting "a" and "L"s"" does not give a valid preprocessing token for these.

A possible fix: treat B as a literal when it has a quote after a valid prefix, like stringifyToken does. Then the paste is valid only if A is a prefix and B has no prefix.

!(A->name && isStringLiteralPrefix(A->str())))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

isStringLiteralPrefix also accepts the raw prefixes R, uR, UR, LR and u8R. Those are only valid on string literals, not on character literals. So cat(R,'c') gives R'c' and cat(u8R,'c') gives u8R'c', where gcc reports pasting "R" and "'c'" does not give a valid preprocessing token.

For a character literal B, only u, U, L and u8 should be allowed.

throw invalidHashHash::cannotCombine(loc, macroName, A, B);
}

/**
Expand Down
28 changes: 25 additions & 3 deletions test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand Down
Loading