diff --git a/lib/checkio.cpp b/lib/checkio.cpp index f0321e4e1..bdabff12d 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -1056,6 +1056,13 @@ void CheckIO::checkFormatString(const Token * const tok, invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (argInfo.typeToken->str() != "char") + invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); + } else if (argInfo.typeToken->str() != "short") + invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); + break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) @@ -1121,6 +1128,13 @@ void CheckIO::checkFormatString(const Token * const tok, invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (!(argInfo.typeToken->str() == "char" && !argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (!(argInfo.typeToken->str() == "short" && !argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) @@ -1191,6 +1205,13 @@ void CheckIO::checkFormatString(const Token * const tok, invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) @@ -1888,6 +1909,11 @@ static void printfFormatType(std::ostream& os, const std::string& specifier, boo os << (isUnsigned ? "unsigned " : "") << "long long"; else os << (isUnsigned ? "unsigned " : "") << "long"; + } else if (specifier[0] == 'h') { + if (specifier[1] == 'h') + os << (isUnsigned ? "unsigned " : "") << "char"; + else + os << (isUnsigned ? "unsigned " : "") << "short"; } else if (specifier.find("I32") != std::string::npos) { os << (isUnsigned ? "unsigned " : "") << "__int32"; } else if (specifier.find("I64") != std::string::npos) { diff --git a/test/testio.cpp b/test/testio.cpp index 18c3126c6..b814f19f2 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -1526,7 +1526,7 @@ private: " printf(\"%hhx %hhd\", sc, uc);\n" " printf(\"%hd %hu\", si, usi);\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n", errout.str()); check("void foo(long long int lli, unsigned long long int ulli, long int li, unsigned long int uli) {\n" " printf(\"%llo %llx\", lli, ulli);\n" @@ -1570,8 +1570,8 @@ private: " printf(\"%ld\", i);\n" " printf(\"%lld\", i);\n" "}"); - ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" - "[test.cpp:3]: (warning) %hhd in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'unsigned int'.\n" + "[test.cpp:3]: (warning) %hhd in format string (no. 1) requires 'char' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'unsigned int'.\n" , errout.str()); @@ -2251,6 +2251,70 @@ private: "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %Lu in format string (no. 2) requires 'unsigned long long' but the argument type is 'unsigned int'.\n", errout.str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hhd %hhd %hhd %hhd %hhd %hhd %hhd %hhd\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 3) requires 'char' but the argument type is 'short'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 4) requires 'char' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 5) requires 'char' but the argument type is 'int'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 6) requires 'char' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 7) requires 'char' but the argument type is 'long'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 8) requires 'char' but the argument type is 'unsigned long'.\n", errout.str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhu in format string (no. 1) requires 'unsigned char' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 3) requires 'unsigned char' but the argument type is 'short'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 5) requires 'unsigned char' but the argument type is 'int'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 7) requires 'unsigned char' but the argument type is 'long'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout.str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhx in format string (no. 3) requires 'unsigned char' but the argument type is 'short'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 5) requires 'unsigned char' but the argument type is 'int'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 7) requires 'unsigned char' but the argument type is 'long'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout.str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hd %hd %hd %hd %hd %hd %hd %hd\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 2) requires 'short' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 4) requires 'short' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 5) requires 'short' but the argument type is 'int'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 6) requires 'short' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 7) requires 'short' but the argument type is 'long'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 8) requires 'short' but the argument type is 'unsigned long'.\n", errout.str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hu %hu %hu %hu %hu %hu %hu %hu\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hu in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 3) requires 'unsigned short' but the argument type is 'short'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 5) requires 'unsigned short' but the argument type is 'int'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 7) requires 'unsigned short' but the argument type is 'long'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout.str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hx %hx %hx %hx %hx %hx %hx %hx\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hx in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 5) requires 'unsigned short' but the argument type is 'int'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 7) requires 'unsigned short' but the argument type is 'long'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout.str()); } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings