CheckIO: Start to add checking of function return types. Ticket: #4964
This commit is contained in:
parent
9b4f6a6b3d
commit
01fd156852
|
@ -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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Variable> argumentList; // argument list
|
||||
|
|
109
test/testio.cpp
109
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
|
||||
|
|
Loading…
Reference in New Issue