From 2bd1f1d8dc32440c301d2c68a3d297e5bc8c195a Mon Sep 17 00:00:00 2001 From: PKEuS Date: Wed, 11 Jul 2012 10:46:35 -0700 Subject: [PATCH] Improved check: Sign checking in printf format string (#3511) Removed some redundant code (already covered by token list simplifications and symboldatabase) --- lib/checkio.cpp | 40 +++++++++++++++++++++++++++++----------- lib/checkio.h | 6 +++++- test/testio.cpp | 46 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 18 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 512c611bf..068c26a85 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -519,23 +519,31 @@ void CheckIO::checkWrongPrintfScanfArguments() invalidPrintfArgTypeError_n(tok, numFormat); break; case 'c': - case 'd': - case 'i': - case 'u': case 'x': case 'X': case 'o': - if (varTypeTok && varTypeTok->str() == "const") - varTypeTok = varTypeTok->next(); - if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| bool|short|long|int|char|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray())) + if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) invalidPrintfArgTypeError_int(tok, numFormat, *i); else if (argListTok->type() == Token::eString) invalidPrintfArgTypeError_int(tok, numFormat, *i); break; + case 'd': + case 'i': + if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { + if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") + invalidPrintfArgTypeError_sint(tok, numFormat, *i); + } else if (argListTok->type() == Token::eString) + invalidPrintfArgTypeError_sint(tok, numFormat, *i); + break; + case 'u': + if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { + if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "short|long|int|size_t")) && varTypeTok->str() != "bool") + invalidPrintfArgTypeError_uint(tok, numFormat, *i); + } else if (argListTok->type() == Token::eString) + invalidPrintfArgTypeError_uint(tok, numFormat, *i); + break; case 'p': - if (varTypeTok && varTypeTok->str() == "const") - varTypeTok = varTypeTok->next(); - if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| short|long|int|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray()) + if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) invalidPrintfArgTypeError_p(tok, numFormat); else if (argListTok->type() == Token::eString) invalidPrintfArgTypeError_p(tok, numFormat); @@ -545,8 +553,6 @@ void CheckIO::checkWrongPrintfScanfArguments() case 'f': case 'g': case 'G': - if (varTypeTok && varTypeTok->str() == "const") - varTypeTok = varTypeTok->next(); if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) invalidPrintfArgTypeError_float(tok, numFormat, *i); else if (argListTok->type() == Token::eString) @@ -628,6 +634,18 @@ void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFo errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list"; reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str()); } +void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c) +{ + std::ostringstream errmsg; + errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an unsigned integer given in the argument list"; + reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str()); +} +void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c) +{ + std::ostringstream errmsg; + errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a signed integer given in the argument list"; + reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str()); +} void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c) { std::ostringstream errmsg; diff --git a/lib/checkio.h b/lib/checkio.h index 92ec8c0dd..4c23f78fb 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -87,6 +87,8 @@ private: void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat); void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat); void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c); + void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c); + void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c); void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c); void invalidScanfFormatWidthError(const Token* tok, const std::string &functionName, unsigned int numFormat, int width, const Variable *var); @@ -105,7 +107,9 @@ private: c.invalidPrintfArgTypeError_s(0, 1); c.invalidPrintfArgTypeError_n(0, 1); c.invalidPrintfArgTypeError_p(0, 1); - c.invalidPrintfArgTypeError_int(0, 1, 'u'); + c.invalidPrintfArgTypeError_int(0, 1, 'X'); + c.invalidPrintfArgTypeError_uint(0, 1, 'u'); + c.invalidPrintfArgTypeError_sint(0, 1, 'i'); c.invalidPrintfArgTypeError_float(0, 1, 'f'); c.invalidScanfFormatWidthError(0, "scanf", 10, 5, NULL); } diff --git a/test/testio.cpp b/test/testio.cpp index 28f99c5bf..2f1fd4cc7 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -531,18 +531,52 @@ private: "[test.cpp:7]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n", errout.str()); check("class foo {};\n" - "void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n" - " printf(\"%i\", f);\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, unsigned int u) {\n" + " printf(\"%X\", f);\n" " printf(\"%c\", \"s4\");\n" " printf(\"%o\", d);\n" - " printf(\"%i\", cpi);\n" - " printf(\"%u\", b);\n" - " printf(\"%u\", bp);\n" + " printf(\"%x\", cpi);\n" + " printf(\"%o\", b);\n" + " printf(\"%X\", bp);\n" + " printf(\"%X\", u);\n" + " printf(\"%X\", i);\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires an integer given in the argument list\n" + ASSERT_EQUALS("[test.cpp:3]: (warning) %X in format string (no. 1) requires an integer given in the argument list\n" "[test.cpp:4]: (warning) %c in format string (no. 1) requires an integer given in the argument list\n" "[test.cpp:5]: (warning) %o in format string (no. 1) requires an integer given in the argument list\n", errout.str()); + check("class foo {};\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, double d, unsigned int u, unsigned char uc) {\n" + " printf(\"%i\", f);\n" + " printf(\"%d\", \"s4\");\n" + " printf(\"%d\", d);\n" + " printf(\"%d\", u);\n" + " printf(\"%d\", cpi);\n" + " printf(\"%i\", b);\n" + " printf(\"%i\", bp);\n" + " printf(\"%i\", uc);\n" // char is smaller than int, so there shouldn't be a problem + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires a signed integer given in the argument list\n" + "[test.cpp:4]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list\n" + "[test.cpp:5]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list\n" + "[test.cpp:6]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list\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" + " printf(\"%u\", f);\n" + " printf(\"%u\", \"s4\");\n" + " printf(\"%u\", d);\n" + " printf(\"%u\", i);\n" + " printf(\"%u\", cpi);\n" + " printf(\"%u\", b);\n" + " printf(\"%u\", bp);\n" + " printf(\"%u\", bo);\n" // bool shouldn't have a negative sign + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list\n" + "[test.cpp:4]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list\n" + "[test.cpp:5]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list\n" + "[test.cpp:6]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list\n", errout.str()); + check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, char c) {\n" " printf(\"%p\", f);\n"