diff --git a/lib/checkio.cpp b/lib/checkio.cpp index e05f7b10c..aaf9561e7 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -574,10 +574,10 @@ void CheckIO::checkWrongPrintfScanfArguments() (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|size_t") || (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_int(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } } else if (argInfo.typeToken->type() == Token::eString) { - invalidPrintfArgTypeError_int(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } done = true; break; @@ -589,42 +589,62 @@ void CheckIO::checkWrongPrintfScanfArguments() (((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())))) { - invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } } else if (argInfo.typeToken->type() == Token::eString) { - invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken); + 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.typeToken); + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } done = true; break; case 'u': specifier += *i; if (argInfo.functionInfo || argInfo.variableInfo) { - if ((argInfo.isKnownType() && !argInfo.isArrayOrPointer()) && - (((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool") || - (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_uint(tok, numFormat, specifier, argInfo.typeToken); + if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) { + if ((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool") { + invalidPrintfArgTypeError_uint(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_uint(tok, numFormat, specifier, &argInfo); + } else if ((specifier[0] == 'z' || (specifier[0] == 'I' && specifier[1] == 'u')) && argInfo.typeToken->originalName() != "size_t") { + // use %z or %I on size_t + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.typeToken->originalName() == "size_t" && (specifier[0] != 'z' && !(specifier[0] == 'I' && specifier[1] == 'u'))) { + // size_t requires %z or %I + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } + } else if ((!argInfo.element && argInfo.isArrayOrPointer()) || + (argInfo.element && !argInfo.isArrayOrPointer())) { + // use %p on pointers and arrays + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } } else if (argInfo.typeToken->type() == Token::eString) { - invalidPrintfArgTypeError_uint(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else { - if (((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool") || - (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_uint(tok, numFormat, specifier, argInfo.typeToken); + if ((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool") { + invalidPrintfArgTypeError_uint(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_uint(tok, numFormat, specifier, &argInfo); + } else if ((specifier[0] == 'z' || (specifier[0] == 'I' && specifier[1] == 'u')) && argInfo.typeToken->originalName() != "size_t") { + // use %z or %I on size_t + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.typeToken->originalName() == "size_t" && (specifier[0] != 'z' && !(specifier[0] == 'I' && specifier[1] == 'u'))) { + // size_t requires %z or %I + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } } done = true; break; case 'p': if (argInfo.functionInfo && argInfo.typeToken->type() == Token::eType && !argInfo.isArrayOrPointer()) - invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken); + invalidPrintfArgTypeError_p(tok, numFormat, &argInfo); else if (argInfo.variableInfo && argInfo.typeToken && argInfo.isKnownType() && !argInfo.isArrayOrPointer()) - invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken); + invalidPrintfArgTypeError_p(tok, numFormat, &argInfo); else if (argInfo.typeToken->type() == Token::eString) - invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken); + invalidPrintfArgTypeError_p(tok, numFormat, &argInfo); done = true; break; case 'e': @@ -639,18 +659,18 @@ void CheckIO::checkWrongPrintfScanfArguments() (argInfo.element && !argInfo.isArrayOrPointer()) || (specifier[0] == 'l' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) || (specifier[0] != 'l' && argInfo.typeToken->isLong())) { - invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } } else if (argInfo.variableInfo) { if ((argInfo.isKnownType() && !Token::Match(argInfo.typeToken, "float|double")) || (!argInfo.element && argInfo.isArrayOrPointer()) || (argInfo.element && !argInfo.isArrayOrPointer())) { - invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } } else if (argInfo.typeToken->type() == Token::eString) - invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); else if (!Token::Match(argInfo.typeToken, "float|double")) - invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken); + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); done = true; break; case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) @@ -927,27 +947,15 @@ void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numForm errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list."; reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str()); } -void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const Token* type) +void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo) { std::ostringstream errmsg; - errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is \'"; - if (type) { - if (type->type() == Token::eString) { - if (type->isLong()) - errmsg << "const wchar_t *"; - else - errmsg << "const char *"; - } else { - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; - } - } else - errmsg << "Unknown"; - errmsg << "\'."; + errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str()); } -void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type) +void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a"; @@ -956,38 +964,12 @@ void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFo else errmsg << (specifier[0] == 'l' ? " long " : "n ") << (specifier[0] == 'l' && specifier[1] == 'l' ? "long " : ""); - errmsg << "integer but the argument type is \'"; - if (type) { - if (type->type() == Token::eString) { - if (type->isLong()) - errmsg << "const wchar_t *"; - else - errmsg << "const char *"; - } else { - if (type->originalName().empty()) { - if (type->str() == "const") { - errmsg << "const "; - type = type->next(); - } - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; - } else { - if ((type->originalName() == "__int64" || type->originalName() == "__int32") && type->isUnsigned()) - errmsg << "unsigned "; - errmsg << type->originalName() << " {aka "; - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; - errmsg << "}"; - } - } - } else - errmsg << "Unknown"; - errmsg << "\'."; + errmsg << "integer but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str()); } -void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type) +void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires an unsigned "; @@ -996,38 +978,12 @@ void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numF else errmsg << (specifier[0] == 'l' ? "long " : "") << (specifier[0] == 'l' && specifier[1] == 'l' ? "long " : ""); - errmsg << "integer but the argument type is \'"; - if (type) { - if (type->type() == Token::eString) { - if (type->isLong()) - errmsg << "const wchar_t *"; - else - errmsg << "const char *"; - } else { - if (type->originalName().empty()) { - if (type->str() == "const") { - errmsg << "const "; - type = type->next(); - } - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; - } else { - if ((type->originalName() == "__int64" || type->originalName() == "__int32") && type->isUnsigned()) - errmsg << "unsigned "; - errmsg << type->originalName() << " {aka "; - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; - errmsg << "}"; - } - } - } else - errmsg << "Unknown"; - errmsg << "\'."; + errmsg << "integer but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str()); } -void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type) +void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a signed "; @@ -1036,71 +992,56 @@ void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numF else errmsg << (specifier[0] == 'l' ? "long " : "") << (specifier[0] == 'l' && specifier[1] == 'l' ? "long " : ""); - errmsg << "integer but the argument type is \'"; - if (type) { - if (type->type() == Token::eString) { - if (type->isLong()) - errmsg << "const wchar_t *"; - else - errmsg << "const char *"; - } else { - if (type->originalName().empty()) { - if (type->str() == "const") { - errmsg << "const "; - type = type->next(); - } - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; - } else { - if ((type->originalName() == "__int64" || type->originalName() == "__int32") && type->isUnsigned()) - errmsg << "unsigned "; - errmsg << type->originalName() << " {aka "; - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; - errmsg << "}"; - } - } - } else - errmsg << "Unknown"; - errmsg << "\'."; + errmsg << "integer but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str()); } -void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type) +void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { std::ostringstream errmsg; - errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a floating point number but the argument type is \'"; - if (type) { + errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a floating point number but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str()); +} + +void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) +{ + if (argInfo) { + os << "\'"; + const Token *type = argInfo->typeToken; if (type->type() == Token::eString) { if (type->isLong()) - errmsg << "const wchar_t *"; + os << "const wchar_t *"; else - errmsg << "const char *"; + os << "const char *"; } else { if (type->originalName().empty()) { if (type->str() == "const") { - errmsg << "const "; + os << "const "; type = type->next(); } - type->stringify(errmsg, false, true); - if (type->strAt(1) == "*") - errmsg << " *"; + type->stringify(os, false, true); + if (type->strAt(1) == "*" || + (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())) { + os << " *"; + } } else { if ((type->originalName() == "__int64" || type->originalName() == "__int32") && type->isUnsigned()) - errmsg << "unsigned "; - errmsg << type->originalName() << " {aka "; - type->stringify(errmsg, false, true); + os << "unsigned "; + os << type->originalName() << " {aka "; + type->stringify(os, false, true); if (type->strAt(1) == "*") - errmsg << " *"; - errmsg << "}"; + os << " *"; + os << "}"; } } + os << "\'"; } else - errmsg << "Unknown"; - errmsg << "\'."; - reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str()); + os << "Unknown"; } + void CheckIO::invalidLengthModifierError(const Token* tok, unsigned int numFormat, const std::string& modifier) { std::ostringstream errmsg; diff --git a/lib/checkio.h b/lib/checkio.h index 9d8e889c6..890f56a80 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -90,6 +90,7 @@ private: const Function *functionInfo; bool element; Token *tempToken; + private: ArgumentInfo(const ArgumentInfo &); // not implemented ArgumentInfo operator = (const ArgumentInfo &); // not implemented @@ -112,13 +113,14 @@ private: void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat); void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat); void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat); - void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const Token* type); - void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type); - void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type); - void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type); - void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const Token* type); + void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidLengthModifierError(const Token* tok, unsigned int numFormat, const std::string& modifier); void invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var); + void argumentType(std::ostream & s, const ArgumentInfo * argInfo); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckIO c(0, settings, errorLogger); diff --git a/test/testio.cpp b/test/testio.cpp index 67f4a0c3f..f7c67b764 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -620,7 +620,8 @@ private: "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires a char* given in the argument list.\n" "[test.cpp:4]: (warning) %s in format string (no. 2) requires a char* given in the argument list.\n" - "[test.cpp:5]: (warning) %s in format string (no. 1) requires a char* given in the argument list.\n", errout.str()); + "[test.cpp:5]: (warning) %s in format string (no. 1) requires a char* given in the argument list.\n" + "[test.cpp:7]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'char *'.\n", errout.str()); check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n" " printf(\"%n\", cpi);\n" @@ -681,7 +682,9 @@ private: ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'double'.\n" - "[test.cpp:6]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'int'.\n", errout.str()); + "[test.cpp:6]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'int'.\n" + "[test.cpp:7]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'int *'.\n" + "[test.cpp:9]: (warning) %u in format string (no. 1) requires an unsigned 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, char c) {\n" @@ -1143,6 +1146,28 @@ private: "[test.cpp:3]: (warning) %d in format string (no. 4) requires a signed integer but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %f in format string (no. 5) requires a floating point number but the argument type is 'const char *'.\n", errout.str()); + check("std::vector array;\n" + "char * p = 0;\n" + "char q[] = \"abc\";\n" + "char r[10] = { 0 };\n" + "size_t s;\n" + "void foo() {\n" + " printf(\"%zu %zu\", array.size(), s);\n" + " printf(\"%u %u %u\", p, q, r);\n" + " printf(\"%u %u\", array.size(), s);\n" + " printf(\"%lu %lu\", array.size(), s);\n" + " printf(\"%llu %llu\", array.size(), s);\n" + "}\n", false, false, Settings::Unix64); + ASSERT_EQUALS("[test.cpp:8]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'char *'.\n" + "[test.cpp:8]: (warning) %u in format string (no. 2) requires an unsigned integer but the argument type is 'char *'.\n" + "[test.cpp:8]: (warning) %u in format string (no. 3) requires an unsigned integer but the argument type is 'char *'.\n" + "[test.cpp:9]: (warning) %u in format string (no. 1) requires an unsigned integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:9]: (warning) %u in format string (no. 2) requires an unsigned integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:10]: (warning) %lu in format string (no. 1) requires an unsigned long integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:10]: (warning) %lu in format string (no. 2) requires an unsigned long integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[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()); + } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings