diff --git a/lib/checkio.cpp b/lib/checkio.cpp index fb7ab30a1..e05f7b10c 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -403,28 +403,6 @@ void CheckIO::invalidScanfError(const Token *tok, bool portability) // printf("%u%s", 1); // Too few arguments // printf("", 1); // Too much arguments //--------------------------------------------------------------------------- -static bool isComplexType(const Variable* var, const Token* varTypeTok) -{ - if (var->type()) - return (true); - - static std::set knownTypes; - if (knownTypes.empty()) { - knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type - knownTypes.insert("string"); - knownTypes.insert("wstring"); - } - - if (varTypeTok->str() == "std") - varTypeTok = varTypeTok->tokAt(2); - return ((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !var->isPointer() && !var->isArray()); -} - -static bool isKnownType(const Variable* var, const Token* varTypeTok) -{ - return (varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok)); -} - void CheckIO::checkWrongPrintfScanfArguments() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); @@ -555,24 +533,18 @@ void CheckIO::checkWrongPrintfScanfArguments() } // Perform type checks - const Variable *variableInfo; - const Token *varTypeTok; - const Function *functionInfo; - bool element; + ArgumentInfo argInfo(argListTok, _settings); - if (getArgumentInfo(argListTok, &variableInfo, &varTypeTok, &functionInfo, element)) { - if (varTypeTok->str() == "static") - varTypeTok = varTypeTok->next(); - - if (scan && varTypeTok) { - if (warning && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) + if (argInfo.typeToken) { + if (scan) { + if (warning && ((!argInfo.variableInfo->isPointer() && !argInfo.variableInfo->isArray()) || argInfo.typeToken->strAt(-1) == "const")) invalidScanfArgTypeError(tok, tok->str(), numFormat); - if (*i == 's' && variableInfo && isKnownType(variableInfo, varTypeTok) && variableInfo->isArray() && (variableInfo->dimensions().size() == 1) && variableInfo->dimensions()[0].known) { + if (*i == 's' && argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) { if (!width.empty()) { int numWidth = std::atoi(width.c_str()); - if (numWidth != (variableInfo->dimension(0) - 1)) - invalidScanfFormatWidthError(tok, numFormat, numWidth, variableInfo); + if (numWidth != (argInfo.variableInfo->dimension(0) - 1)) + invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo); } } } else if (!scan && warning) { @@ -581,12 +553,14 @@ void CheckIO::checkWrongPrintfScanfArguments() while (!done) { switch (*i) { case 's': - if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray())) + if (argInfo.variableInfo && argListTok->type() != Token::eString && + argInfo.isKnownType() && + !argInfo.isArrayOrPointer()) invalidPrintfArgTypeError_s(tok, numFormat); done = true; break; case 'n': - if ((variableInfo && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString) + if ((argInfo.variableInfo && argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->type() == Token::eString) invalidPrintfArgTypeError_n(tok, numFormat); done = true; break; @@ -595,69 +569,62 @@ void CheckIO::checkWrongPrintfScanfArguments() case 'X': case 'o': specifier += *i; - if (functionInfo && ((varTypeTok->isStandardType() || functionInfo->retType) && varTypeTok->next()->str() != "*")) { - if (!Token::Match(varTypeTok, "bool|short|long|int|char|size_t") || - (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) || - (specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) { - invalidPrintfArgTypeError_int(tok, numFormat, specifier, varTypeTok); + if (argInfo.functionInfo || argInfo.variableInfo) { + if ((argInfo.isKnownType() && !argInfo.isArrayOrPointer()) && + (!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); } - } else if (variableInfo && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { - if (!Token::Match(varTypeTok, "bool|short|long|int|char|size_t") || - (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) || - (specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) { - invalidPrintfArgTypeError_int(tok, numFormat, specifier, varTypeTok); - } - } else if (varTypeTok->type() == Token::eString) { - invalidPrintfArgTypeError_int(tok, numFormat, specifier, varTypeTok); + } else if (argInfo.typeToken->type() == Token::eString) { + invalidPrintfArgTypeError_int(tok, numFormat, specifier, argInfo.typeToken); } done = true; break; case 'd': case 'i': specifier += *i; - if (functionInfo && (varTypeTok->isStandardType() || functionInfo->retType) && varTypeTok->next()->str() != "*") { - if (((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") || - (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) || - (specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) { - invalidPrintfArgTypeError_sint(tok, numFormat, specifier, varTypeTok); + 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())))) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken); } - } else if (variableInfo && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { - if (((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") || - (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) || - (specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) { - invalidPrintfArgTypeError_sint(tok, numFormat, specifier, varTypeTok); - } - } else if (varTypeTok->type() == Token::eString) { - invalidPrintfArgTypeError_sint(tok, numFormat, specifier, varTypeTok); + } else if (argInfo.typeToken->type() == Token::eString) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken); + } else if (argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|int|long")) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, argInfo.typeToken); } done = true; break; case 'u': specifier += *i; - if (functionInfo && ((varTypeTok->isStandardType() || functionInfo->retType) || varTypeTok->next()->str() != "*")) { - if (((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") || - (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) || - (specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) { - invalidPrintfArgTypeError_uint(tok, numFormat, specifier, varTypeTok); + 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); } - } else if (variableInfo && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { - if (((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") || - (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong()))) || - (specifier.find("I64") != std::string::npos && (varTypeTok->str() != "long" || !varTypeTok->isLong()))) { - invalidPrintfArgTypeError_uint(tok, numFormat, specifier, varTypeTok); + } else if (argInfo.typeToken->type() == Token::eString) { + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, argInfo.typeToken); + } 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); } - } else if (varTypeTok->type() == Token::eString) { - invalidPrintfArgTypeError_uint(tok, numFormat, specifier, varTypeTok); } done = true; break; case 'p': - if (functionInfo && varTypeTok->type() == Token::eType && varTypeTok->next()->str() != "*") - invalidPrintfArgTypeError_p(tok, numFormat, varTypeTok); - else if (variableInfo && varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) - invalidPrintfArgTypeError_p(tok, numFormat, varTypeTok); - else if (varTypeTok->type() == Token::eString) - invalidPrintfArgTypeError_p(tok, numFormat, varTypeTok); + if (argInfo.functionInfo && argInfo.typeToken->type() == Token::eType && !argInfo.isArrayOrPointer()) + invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken); + else if (argInfo.variableInfo && argInfo.typeToken && argInfo.isKnownType() && !argInfo.isArrayOrPointer()) + invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken); + else if (argInfo.typeToken->type() == Token::eString) + invalidPrintfArgTypeError_p(tok, numFormat, argInfo.typeToken); done = true; break; case 'e': @@ -666,18 +633,24 @@ void CheckIO::checkWrongPrintfScanfArguments() case 'g': case 'G': specifier += *i; - if (functionInfo && (((varTypeTok->isStandardType() || functionInfo->retType) && !Token::Match(varTypeTok, "float|double")) || - (!element && Token::simpleMatch(varTypeTok->next(), "*")) || - (element && !Token::simpleMatch(varTypeTok->next(), "*")) || - (specifier[0] == 'l' && (!varTypeTok->isLong() || varTypeTok->str() != "double")) || - (specifier[0] != 'l' && varTypeTok->isLong()))) - invalidPrintfArgTypeError_float(tok, numFormat, specifier, varTypeTok); - else if (variableInfo && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || - (!element && variableInfo->isArrayOrPointer()) || - (element && !variableInfo->isArrayOrPointer()))) - invalidPrintfArgTypeError_float(tok, numFormat, specifier, varTypeTok); - else if (varTypeTok->type() == Token::eString) - invalidPrintfArgTypeError_float(tok, numFormat, specifier, varTypeTok); + if (argInfo.functionInfo) { + if (((argInfo.typeToken->isStandardType() || argInfo.functionInfo->retType) && !Token::Match(argInfo.typeToken, "float|double")) || + (!argInfo.element && argInfo.isArrayOrPointer()) || + (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); + } + } 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); + } + } else if (argInfo.typeToken->type() == Token::eString) + invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken); + else if (!Token::Match(argInfo.typeToken, "float|double")) + invalidPrintfArgTypeError_float(tok, numFormat, specifier, argInfo.typeToken); done = true; break; case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) @@ -770,20 +743,22 @@ void CheckIO::checkWrongPrintfScanfArguments() // We currently only support string literals, variables, and functions. /// @todo add non-string literals, and generic expressions -bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Token **typeTok, const Function **func, bool &element) const +CheckIO::ArgumentInfo::ArgumentInfo(const Token * tok, const Settings *settings) + : variableInfo(0) + , typeToken(0) + , functionInfo(0) + , element(false) + , tempToken(0) { if (tok) { if (tok->type() == Token::eString) { - *var = 0; - *typeTok = tok; - *func = 0; - element = false; - return true; + typeToken = tok; + return; } else if (tok->type() == Token::eVariable || tok->type() == Token::eFunction || Token::Match(tok, "%type% ::")) { while (Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); if (!tok || !(tok->type() == Token::eVariable || tok->type() == Token::eFunction)) - return false; + return; const Token *varTok = 0; const Token *tok1 = tok->next(); for (; tok1; tok1 = tok1->next()) { @@ -793,24 +768,22 @@ bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Tok if (varTok->str() == ")" && varTok->link()->previous()->type() == Token::eFunction) { const Function * function = varTok->link()->previous()->function(); if (function && function->retDef) { - *var = 0; - *typeTok = function->retDef; - *func = function; + typeToken = function->retDef; + functionInfo = function; element = true; - return true; + return; } else - return false; + return; } } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->type() == Token::eFunction) { const Function * function = tok1->linkAt(-1)->previous()->function(); if (function && function->retDef) { - *var = 0; - *typeTok = function->retDef; - *func = function; + typeToken = function->retDef; + functionInfo = function; element = false; - return true; + return; } else - return false; + return; } else varTok = tok1->previous(); break; @@ -818,30 +791,86 @@ bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Tok tok1 = tok1->link(); else if (tok1->str() == "<" && tok1->link()) tok1 = tok1->link(); - else if (!(tok1->str() == "." || tok1->type() == Token::eVariable || tok1->type() == Token::eFunction)) - return false; + + // check for some common well known functions + else if (Token::Match(tok1->previous(), "%var% . size|empty|c_str ( )") && tok1->previous()->variable() && + (Token::Match(tok1->previous()->variable()->typeStartToken(), "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <") || + Token::Match(tok1->previous()->variable()->typeStartToken(), "std :: string|wstring"))) { + tempToken = new Token(0); + tempToken->fileIndex(tok1->fileIndex()); + tempToken->linenr(tok1->linenr()); + if (tok1->next()->str() == "size") { + // size_t is platform dependent + if (settings->sizeof_size_t == 8) { + tempToken->str("long"); + if (settings->sizeof_long != 8) + tempToken->isLong(true); + } else if (settings->sizeof_size_t == 4 && settings->sizeof_long == 4) + tempToken->str("long"); + else if (settings->sizeof_size_t == 4) + tempToken->str("int"); + + tempToken->originalName("size_t"); + tempToken->isUnsigned(true); + } else if (tok1->next()->str() == "empty") { + tempToken->str("bool"); + } else if (tok1->next()->str() == "c_str") { + tempToken->str("const"); + tempToken->insertToken("*"); + if (tok1->previous()->variable()->typeStartToken()->strAt(2) == "string") + tempToken->insertToken("char"); + else + tempToken->insertToken("wchar_t"); + } + typeToken = tempToken; + return; + } else if (!(tok1->str() == "." || tok1->type() == Token::eVariable || tok1->type() == Token::eFunction)) + return; } if (varTok) { - const Variable *variableInfo = varTok->variable(); - *var = variableInfo; + variableInfo = varTok->variable(); element = tok1->previous()->str() == "]"; - *func = 0; // look for std::vector operator [] and use template type as return type if (variableInfo) { if (element && Token::Match(variableInfo->typeStartToken(), "std :: vector|array <")) { - *typeTok = variableInfo->typeStartToken()->tokAt(4); + typeToken = variableInfo->typeStartToken()->tokAt(4); element = false; // not really an array element } else - *typeTok = variableInfo->typeStartToken(); - } else - return false; + typeToken = variableInfo->typeStartToken(); + } - return true; + return; } } } +} + +bool CheckIO::ArgumentInfo::isComplexType() const +{ + if (variableInfo->type()) + return (true); + + static std::set knownTypes; + if (knownTypes.empty()) { + knownTypes.insert("string"); + knownTypes.insert("wstring"); + } + + const Token* varTypeTok = typeToken; + if (varTypeTok->str() == "std") + varTypeTok = varTypeTok->tokAt(2); + + return ((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer()); +} + +bool CheckIO::ArgumentInfo::isKnownType() const +{ + if (variableInfo) + return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType()); + else if (functionInfo) + return (typeToken->isStandardType() || functionInfo->retType); return false; } @@ -936,6 +965,10 @@ void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFo 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 << " *"; @@ -972,6 +1005,10 @@ void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numF 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 << " *"; @@ -1008,6 +1045,10 @@ void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numF 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 << " *"; @@ -1038,6 +1079,10 @@ void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int num 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 << " *"; diff --git a/lib/checkio.h b/lib/checkio.h index 6c4ac6b80..e4f579954 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -23,8 +23,7 @@ #include "check.h" #include "config.h" - -class Variable; +#include "symboldatabase.h" /// @addtogroup Checks /// @{ @@ -70,7 +69,31 @@ public: void checkWrongPrintfScanfArguments(); private: - bool getArgumentInfo(const Token *tok, const Variable **var, const Token **typeTok, const Function **func, bool &element) const; + class ArgumentInfo { + public: + ArgumentInfo(const Token *arg, const Settings *settings); + ~ArgumentInfo() { + delete tempToken; + } + bool isArrayOrPointer() const { + if (variableInfo) + return variableInfo->isArrayOrPointer(); + else if (functionInfo) + return typeToken->next()->str() == "*"; + return false; + } + bool isComplexType() const; + bool isKnownType() const; + + const Variable *variableInfo; + const Token *typeToken; + const Function *functionInfo; + bool element; + Token *tempToken; + private: + ArgumentInfo(const ArgumentInfo &); // not implemented + ArgumentInfo operator = (const ArgumentInfo &); // not implemented + }; // Reporting errors.. void coutCerrMisusageError(const Token* tok, const std::string& streamName); diff --git a/test/testio.cpp b/test/testio.cpp index acffcccbd..67f4a0c3f 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -1081,6 +1081,68 @@ private: "}\n"); ASSERT_EQUALS("", errout.str()); + check("std::vector v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", false, false, Settings::Win32A); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); + + check("std::vector v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", false, false, Settings::Win64); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long long}'.\n" + "[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long long}'.\n" + "[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long long}'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long long}'.\n", errout.str()); + + check("std::vector v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", false, false, Settings::Unix32); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); + + check("std::vector v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", false, false, Settings::Unix64); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); + + check("class Fred : public std::vector {} v;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", false, false, Settings::Unix64); + TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %d in format string (no. 3) requires a signed integer but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 4) requires a floating point number but the argument type is 'size_t {aka unsigned long}'.\n", "", errout.str()); + + check("std::string s;\n" + "void foo() {\n" + " printf(\"%s %p %u %d %f\", s.c_str(), s.c_str(), s.c_str(), s.c_str(), s.c_str());\n" + "}\n", false, false, Settings::Unix64); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 3) requires an unsigned integer but the argument type is 'const char *'.\n" + "[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()); + } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings