Skip to content

Commit 2a51846

Browse files
dmcppcheckclaude
andcommitted
Fix #756 (##: pasting a name or number with a string or character literal is an error)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent fb8808b commit 2a51846

2 files changed

Lines changed: 34 additions & 7 deletions

File tree

‎simplecpp.cpp‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2831,12 +2831,12 @@ namespace simplecpp {
28312831
strAB = ",";
28322832
}
28332833
else {
2834-
checkPunctuatorPaste(tok->location, name(), A, tokensB.cfront());
2834+
checkPaste(tok->location, name(), A, tokensB.cfront());
28352835
strAB = A->str() + tokensB.cfront()->str();
28362836
tokensB.deleteToken(tokensB.front());
28372837
}
28382838
} else {
2839-
checkPunctuatorPaste(tok->location, name(), A, B);
2839+
checkPaste(tok->location, name(), A, B);
28402840
strAB = A->str() + B->str();
28412841
}
28422842

@@ -2907,16 +2907,21 @@ namespace simplecpp {
29072907
}
29082908

29092909
/**
2910-
* When A is a punctuator, check that pasting A and B gives a valid token.
2910+
* Check that pasting A and B gives a valid token, when A is a punctuator
2911+
* or B is a string or character literal.
29112912
* @param loc the location of the ##
29122913
* @param macroName the macro that is expanded
29132914
* @param A the token before ##
29142915
* @param B the first token after ##, after expansion
29152916
* @throws invalidHashHash if the result is not a valid token
29162917
*/
2917-
static void checkPunctuatorPaste(const Location &loc, const std::string &macroName, const Token *A, const Token *B) {
2918+
static void checkPaste(const Location &loc, const std::string &macroName, const Token *A, const Token *B) {
29182919
if (isPunctuatorOperand(A) && !isValidPunctuatorPaste(A->str() + B->str()))
29192920
throw invalidHashHash::cannotCombine(loc, macroName, A, B);
2921+
// a name or number followed by a literal: only an encoding prefix such as L or u8 is valid
2922+
if ((A->name || A->number) && (isStringLiteral(B->str()) || isCharLiteral(B->str())) &&
2923+
!(A->name && isStringLiteralPrefix(A->str())))
2924+
throw invalidHashHash::cannotCombine(loc, macroName, A, B);
29202925
}
29212926

29222927
/**

‎test.cpp‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,9 +2554,29 @@ static void hashhash_invalid_name_string()
25542554
"#define cat(a,b) a##b\n"
25552555
"cat(a,\"s\")\n";
25562556
simplecpp::OutputList outputList;
2557-
// TODO: a"s" is not a valid token, gcc reports an error
2558-
TODO_ASSERT_EQUALS("", "\na\"s\"", preprocess(code, &outputList));
2559-
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));
2557+
ASSERT_EQUALS("", preprocess(code, &outputList));
2558+
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));
2559+
}
2560+
2561+
static void hashhash_invalid_number_string()
2562+
{
2563+
const char code[] =
2564+
"#define cat(a,b) a##b\n"
2565+
"cat(1,\"s\")\n";
2566+
simplecpp::OutputList outputList;
2567+
ASSERT_EQUALS("", preprocess(code, &outputList));
2568+
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));
2569+
}
2570+
2571+
static void hashhash_encoding_prefix()
2572+
{
2573+
const char code[] =
2574+
"#define cat(a,b) a##b\n"
2575+
"#define WIDE(s) L ## s\n"
2576+
"cat(L,\"s\") cat(u8,\"s\") cat(R,\"(x)\") cat(L,'c') WIDE(\"s\")\n";
2577+
simplecpp::OutputList outputList;
2578+
ASSERT_EQUALS("\n\nL\"s\" u8\"s\" R\"(x)\" L'c' L\"s\"", preprocess(code, &outputList));
2579+
ASSERT_EQUALS("", toString(outputList));
25602580
}
25612581

25622582
static void hashhash_va_args_unexpected()
@@ -5689,6 +5709,8 @@ static void runTests(int argc, char **argv, Input input)
56895709
TEST_CASE(hashhash_empty_placemarker);
56905710
TEST_CASE(hashhash_invalid_dotdot);
56915711
TEST_CASE(hashhash_invalid_name_string);
5712+
TEST_CASE(hashhash_invalid_number_string);
5713+
TEST_CASE(hashhash_encoding_prefix);
56925714
TEST_CASE(hashhash_va_args_unexpected);
56935715

56945716
// C standard, 5.1.1.2, paragraph 4:

0 commit comments

Comments
 (0)