diff --git a/lib/checkio.cpp b/lib/checkio.cpp index aaf9561e7..72f8ede6e 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -585,16 +585,36 @@ void CheckIO::checkWrongPrintfScanfArguments() case 'i': specifier += *i; if (argInfo.functionInfo || argInfo.variableInfo) { - if ((argInfo.isKnownType() && !argInfo.isArrayOrPointer()) && - (((argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|long|int")) && argInfo.typeToken->str() != "char") || - (specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) || - (specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())))) { + if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) { + if ((argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|long|int")) && !Token::Match(argInfo.typeToken, "char|short")) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if ((specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) || + (specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()))) { + // %l requires long and %ll or %I64 requires long long + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if ((specifier[0] == 't' || (specifier[0] == 'I' && (specifier[1] == 'd' || specifier[1] == 'i'))) && + argInfo.typeToken->originalName() != "ptrdiff_t") { + // use %t or %I on ptrdiff_t + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.typeToken->originalName() == "ptrdiff_t" && + (specifier[0] != 't' && !(specifier[0] == 'I' && (specifier[1] == 'd' || specifier[1] == 'i')))) { + // ptrdiff_t requires %t or %I + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } + } else if ((!argInfo.element && argInfo.isArrayOrPointer()) || + (argInfo.element && !argInfo.isArrayOrPointer())) { + // use %p on pointers and arrays invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } } else if (argInfo.typeToken->type() == Token::eString) { invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); - } else if (argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|int|long")) { - invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else { + if ((argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|int|long")) && !Token::Match(argInfo.typeToken, "char|short")) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if ((specifier[0] == 'l' && (argInfo.typeToken->str() != "long" || (specifier[1] == 'l' && !argInfo.typeToken->isLong()))) || + (specifier.find("I64") != std::string::npos && (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()))) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } } done = true; break; diff --git a/test/testio.cpp b/test/testio.cpp index f7c67b764..dc8b3c117 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -666,7 +666,9 @@ private: ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires a signed integer but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %d in format string (no. 1) requires a signed integer but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %d in format string (no. 1) requires a signed integer but the argument type is 'double'.\n" - "[test.cpp:6]: (warning) %d in format string (no. 1) requires a signed integer but the argument type is 'unsigned int'.\n", errout.str()); + "[test.cpp:6]: (warning) %d in format string (no. 1) requires a signed integer but the argument type is 'unsigned int'.\n" + "[test.cpp:7]: (warning) %d in format string (no. 1) requires a signed integer but the argument type is 'int *'.\n" + "[test.cpp:9]: (warning) %i in format string (no. 1) requires a signed integer but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) {\n" @@ -853,8 +855,7 @@ private: check("unsigned short f() { return 0; }\n" "void foo() { printf(\"%u %d %ld %I64d %I64u %f %lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); - ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires a signed integer but the argument type is 'unsigned short'.\n" - "[test.cpp:2]: (warning) %ld in format string (no. 3) requires a signed long integer but the argument type is 'unsigned short'.\n" + ASSERT_EQUALS("[test.cpp:2]: (warning) %ld in format string (no. 3) requires a signed long integer but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires a signed long long integer but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires an unsigned long long integer but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires a floating point number but the argument type is 'unsigned short'.\n" @@ -1168,6 +1169,54 @@ private: "[test.cpp:11]: (warning) %llu in format string (no. 1) requires an unsigned long long integer but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:11]: (warning) %llu in format string (no. 2) requires an unsigned long long integer but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); + check("bool b; bool bf(){ return 0; }\n" + "char c; char cf(){ return 0; }\n" + "signed char sc; signed char scf(){ return 0; }\n" + "unsigned char uc; unsigned char ucf(){ return 0; }\n" + "short s; short sf(){ return 0; }\n" + "unsigned short us; unsigned short usf(){ return 0; }\n" + "size_t st; size_t stf(){ return 0; }\n" + "ptrdiff_t pt; ptrdiff_t ptf(){ return 0; }\n" + "char * pc; char * pcf(){ return 0; }\n" + "char cl[] = \"123\";\n" + "char ca[3];\n" + "void foo() {\n" + " printf(\"%td %zd %d %d %d %d %d %d %d %d %d %d %d\", pt, pt, b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" + " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\", b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" + " printf(\"%td %zd %d %d %d %d %d %d %d %d %d\", ptf(), ptf(), bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" + " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld\", bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" + "}\n", false, false, Settings::Unix64); + ASSERT_EQUALS("[test.cpp:13]: (warning) %zd in format string (no. 2) requires a signed integer but the argument type is 'ptrdiff_t {aka long}'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 9) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 10) requires a signed integer but the argument type is 'ptrdiff_t {aka long}'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 11) requires a signed integer but the argument type is 'char *'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 12) requires a signed integer but the argument type is 'char *'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 13) requires a signed integer but the argument type is 'char *'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 1) requires a signed long integer but the argument type is 'bool'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 2) requires a signed long integer but the argument type is 'char'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 3) requires a signed long integer but the argument type is 'signed char'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 4) requires a signed long integer but the argument type is 'unsigned char'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 5) requires a signed long integer but the argument type is 'short'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 6) requires a signed long integer but the argument type is 'unsigned short'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 7) requires a signed long integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 8) requires a signed long integer but the argument type is 'ptrdiff_t {aka long}'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 9) requires a signed long integer but the argument type is 'char *'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 10) requires a signed long integer but the argument type is 'char *'.\n" + "[test.cpp:14]: (warning) %ld in format string (no. 11) requires a signed long integer but the argument type is 'char *'.\n" + "[test.cpp:15]: (warning) %zd in format string (no. 2) requires a signed integer but the argument type is 'ptrdiff_t {aka long}'.\n" + "[test.cpp:15]: (warning) %d in format string (no. 9) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:15]: (warning) %d in format string (no. 10) requires a signed integer but the argument type is 'ptrdiff_t {aka long}'.\n" + "[test.cpp:15]: (warning) %d in format string (no. 11) requires a signed integer but the argument type is 'char *'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 1) requires a signed long integer but the argument type is 'bool'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 2) requires a signed long integer but the argument type is 'char'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 3) requires a signed long integer but the argument type is 'signed char'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 4) requires a signed long integer but the argument type is 'unsigned char'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 5) requires a signed long integer but the argument type is 'short'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 6) requires a signed long integer but the argument type is 'unsigned short'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 7) requires a signed long integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 8) requires a signed long integer but the argument type is 'ptrdiff_t {aka long}'.\n" + "[test.cpp:16]: (warning) %ld in format string (no. 9) requires a signed long integer but the argument type is 'char *'.\n", errout.str()); + } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings