diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 69d52ff0a..06e249e9a 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -557,8 +557,9 @@ void CheckIO::checkWrongPrintfScanfArguments() // Perform type checks const Variable *variableInfo; const Token *varTypeTok; + const Function *functionInfo; - if (getArgumentInfo(argListTok, &variableInfo, &varTypeTok)) { + if (getArgumentInfo(argListTok, &variableInfo, &varTypeTok, &functionInfo)) { if (varTypeTok && varTypeTok->str() == "static") varTypeTok = varTypeTok->next(); @@ -593,7 +594,12 @@ void CheckIO::checkWrongPrintfScanfArguments() case 'X': case 'o': specifier += *i; - if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { + if (functionInfo && varTypeTok && (varTypeTok->isStandardType() || !Token::Match(varTypeTok->next(), "*|&"))) { + if (!Token::Match(varTypeTok, "bool|short|long|int|char|size_t") || + (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong())))) { + invalidPrintfArgTypeError_int(tok, numFormat, specifier); + } + } else if (variableInfo && varTypeTok && 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())))) { invalidPrintfArgTypeError_int(tok, numFormat, specifier); @@ -606,7 +612,12 @@ void CheckIO::checkWrongPrintfScanfArguments() case 'd': case 'i': specifier += *i; - if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { + if (functionInfo && varTypeTok && (varTypeTok->isStandardType() || Token::Match(varTypeTok->next(), "*|&"))) { + if (((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") || + (specifier[0] == 'l' && (varTypeTok->str() != "long" || (specifier[1] == 'l' && !varTypeTok->isLong())))) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier); + } + } else if (variableInfo && varTypeTok && 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())))) { invalidPrintfArgTypeError_sint(tok, numFormat, specifier); @@ -618,7 +629,12 @@ void CheckIO::checkWrongPrintfScanfArguments() break; case 'u': specifier += *i; - if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { + if (functionInfo && varTypeTok && (varTypeTok->isStandardType() || !Token::Match(varTypeTok->next(), "*|&"))) { + 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())))) { + invalidPrintfArgTypeError_uint(tok, numFormat, specifier); + } + } else if (variableInfo && varTypeTok && 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())))) { invalidPrintfArgTypeError_uint(tok, numFormat, specifier); @@ -629,7 +645,9 @@ void CheckIO::checkWrongPrintfScanfArguments() done = true; break; case 'p': - if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) + if (functionInfo && varTypeTok && varTypeTok->type() == Token::eType && varTypeTok->next()->str() != "*") + invalidPrintfArgTypeError_p(tok, numFormat); + else if (variableInfo && 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); @@ -640,13 +658,16 @@ void CheckIO::checkWrongPrintfScanfArguments() case 'f': case 'g': case 'G': - if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) { - specifier += *i; + specifier += *i; + if (functionInfo && varTypeTok && ((varTypeTok->isStandardType() && !Token::Match(varTypeTok, "float|double")) || + Token::Match(varTypeTok->next(), "*|&") || + (specifier[0] == 'l' && (!varTypeTok->isLong() || varTypeTok->str() != "double")) || + (specifier[0] != 'l' && varTypeTok->isLong()))) invalidPrintfArgTypeError_float(tok, numFormat, specifier); - } else if (argListTok->type() == Token::eString) { - specifier += *i; + else if (variableInfo && varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) + invalidPrintfArgTypeError_float(tok, numFormat, specifier); + else if (argListTok->type() == Token::eString) invalidPrintfArgTypeError_float(tok, numFormat, specifier); - } done = true; break; case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) @@ -736,23 +757,32 @@ void CheckIO::checkWrongPrintfScanfArguments() } } -// We currently only support string literals and variables. -/// @todo add non-string literals, functions, and generic expressions +// We currently only support string literals, variables, and functions. +/// @todo add non-string literals, qualification, and generic expressions -bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Token **typeTok) const +bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Token **typeTok, const Function **func) const { if (tok) { if (tok->type() == Token::eString) { *var = 0; *typeTok = 0; + *func = 0; return true; - } else if (tok->type() == Token::eVariable) { + } else if (tok->type() == Token::eVariable || tok->type() == Token::eFunction) { const Token *varTok = 0; for (const Token *tok1 = tok->next(); tok1; tok1 = tok1->next()) { if (tok1->str() == "," || tok1->str() == ")") { if (tok1->previous()->str() == "]") varTok = tok1->linkAt(-1)->previous(); - else + else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->type() == Token::eFunction) { + const Function * function = tok1->linkAt(-1)->previous()->function(); + if (function) { + *var = 0; + *typeTok = function->retDef; + *func = function; + return true; + } + } else varTok = tok1->previous(); break; } else if (tok1->str() == "(" || tok1->str() == "{" || tok1->str() == "[") @@ -766,7 +796,8 @@ bool CheckIO::getArgumentInfo(const Token * tok, const Variable **var, const Tok if (varTok) { const Variable *variableInfo = varTok->variable(); *var = variableInfo; - *typeTok = variableInfo ? variableInfo->typeStartToken() : NULL;; + *typeTok = variableInfo ? variableInfo->typeStartToken() : NULL; + *func = 0; return true; } } diff --git a/lib/checkio.h b/lib/checkio.h index 5271765b6..328a5464f 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -70,7 +70,7 @@ public: void checkWrongPrintfScanfArguments(); private: - bool getArgumentInfo(const Token *tok, const Variable **var, const Token **typeTok) const; + bool getArgumentInfo(const Token *tok, const Variable **var, const Token **typeTok, const Function **func) const; // Reporting errors.. void coutCerrMisusageError(const Token* tok, const std::string& streamName); diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 5e81040f9..dc0ad513b 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -421,6 +421,15 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti tok1 = tok1->previous(); } + // find the return type + if (function.type != Function::eConstructor) { + while (tok1 && Token::Match(tok1->next(), "virtual|static|friend|const")) + tok1 = tok1->next(); + + if (tok1) + function.retDef = tok1->next(); + } + const Token *end; if (!function.retFuncPtr) @@ -1224,6 +1233,19 @@ Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, co function->token = funcStart; function->hasBody = true; + const Token *tok1 = tok; + + // look for end of previous statement + while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{")) + tok1 = tok1->previous(); + + // find the return type + while (tok1 && Token::Match(tok1->next(), "static|const")) + tok1 = tok1->next(); + + if (tok1) + function->retDef = tok1; + addNewFunction(&scope, &tok); if (scope) { @@ -1705,6 +1727,7 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " retFuncPtr: " << (func->retFuncPtr ? "true" : "false") << std::endl; std::cout << " tokenDef: " << _tokenizer->list.fileLine(func->tokenDef) << std::endl; std::cout << " argDef: " << _tokenizer->list.fileLine(func->argDef) << std::endl; + std::cout << " retDef: " << func->retDef->str() << " " <<_tokenizer->list.fileLine(func->retDef) << std::endl; if (func->hasBody) { std::cout << " token: " << _tokenizer->list.fileLine(func->token) << std::endl; std::cout << " arg: " << _tokenizer->list.fileLine(func->arg) << std::endl; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 64b8ff420..4b637195b 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -450,6 +450,7 @@ public: argDef(NULL), token(NULL), arg(NULL), + retDef(NULL), functionScope(NULL), nestedIn(NULL), initArgCount(0), @@ -501,6 +502,7 @@ public: const Token *argDef; // function argument start '(' in class definition const Token *token; // function name token in implementation const Token *arg; // function argument start '(' + const Token *retDef; const Scope *functionScope; // scope of function body const Scope* nestedIn; // Scope the function is declared in std::list argumentList; // argument list diff --git a/test/testio.cpp b/test/testio.cpp index ba43c5959..a3c57dffd 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -816,6 +816,115 @@ private: "[test.cpp:13]: (warning) %d in format string (no. 4) requires a signed integer given in the argument list.\n" "[test.cpp:13]: (warning) %f in format string (no. 5) requires a floating point number given in the argument list.\n" "[test.cpp:13]: (warning) %f in format string (no. 6) requires a floating point number given in the argument list.\n", errout.str()); + + check("short s() { return 0; }\n" + "short * ps() { return 0; }\n" + "unsigned short us() { return 0; }\n" + "unsigned short * pus() { return 0; }\n" + "int i() { return 0; }\n" + "int * pi() { return 0; }\n" + "unsigned int ui() { return 0; }\n" + "unsigned int * pui() { return 0; }\n" + "long l() { return 0; }\n" + "long * pl() { return 0; }\n" + "unsigned long ul() { return 0; }\n" + "unsigned long * pul() { return 0; }\n" + "float f() { return 0; }\n" + "float * pf() { return 0; }\n" + "double d() { return 0; }\n" + "double * pd() { return 0; }\n" + "long double ld() { return 0; }\n" + "long double * pld() { return 0; }\n" + "void foo() {\n" + " printf(\"%d %p %u %p %d %p %u %p %ld %p %lu %p %f %p %f %p %lf %p\",\n" + " s(), ps(), us(), pus(),\n" + " i(), pi(), ui(), pui(),\n" + " l(), pl(), ul(), upl(),\n" + " f(), pf(), d(), pd(), ld(), pld());\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("short f() { return 0; }\n" + "void foo() { printf(\"%u %lu %f %lf %p\", f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 2) requires an unsigned long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 3) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 4) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 5) requires an address given in the argument list.\n", errout.str()); + + check("unsigned short f() { return 0; }\n" + "void foo() { printf(\"%d %ld %f %lf %p\", f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 2) requires a signed long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 3) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 4) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 5) requires an address given in the argument list.\n", errout.str()); + + check("int f() { return 0; }\n" + "void foo() { printf(\"%u %lu %f %lf %p\", f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 2) requires an unsigned long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 3) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 4) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 5) requires an address given in the argument list.\n", errout.str()); + + check("unsigned int f() { return 0; }\n" + "void foo() { printf(\"%d %ld %f %lf %p\", f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 2) requires a signed long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 3) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 4) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 5) requires an address given in the argument list.\n", errout.str()); + + check("long f() { return 0; }\n" + "void foo() { printf(\"%u %f %lf %p\", f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 2) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 3) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 4) requires an address given in the argument list.\n", errout.str()); + + check("unsigned long f() { return 0; }\n" + "void foo() { printf(\"%d %f %lf %p\", f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 2) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 3) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 4) requires an address given in the argument list.\n", errout.str()); + + check("float f() { return 0; }\n" + "void foo() { printf(\"%d %ld %u %lu %lf %p\", f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 2) requires a signed long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %u in format string (no. 3) requires an unsigned integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 4) requires an unsigned long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 5) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address given in the argument list.\n", errout.str()); + + check("double f() { return 0; }\n" + "void foo() { printf(\"%d %ld %u %lu %lf %p\", f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 2) requires a signed long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %u in format string (no. 3) requires an unsigned integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 4) requires an unsigned long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 5) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address given in the argument list.\n", errout.str()); + + check("long double f() { return 0; }\n" + "void foo() { printf(\"%d %ld %u %lu %f %p\", f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 2) requires a signed long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %u in format string (no. 3) requires an unsigned integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 4) requires an unsigned long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 5) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address given in the argument list.\n", errout.str()); + + check("namespace bar { int f() { return 0; } }\n" + "void foo() { printf(\"%u %lu %f %lf %p\", bar::f(), bar::f(), bar::f(), bar::f(), bar::f()); }"); + TODO_ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 2) requires an unsigned long integer given in the argument list.\n" + "[test.cpp:2]: (warning) %f in format string (no. 3) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %lf in format string (no. 4) requires a floating point number given in the argument list.\n" + "[test.cpp:2]: (warning) %p in format string (no. 5) requires an address given in the argument list.\n", "", errout.str()); + } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings