Uninitialized variables: Improved checking of functions
This commit is contained in:
parent
d82048f5f5
commit
ccdea4dc2b
|
@ -1080,7 +1080,7 @@ void CheckUninitVar::checkScope(const Scope* scope)
|
||||||
while (tok && tok->str() != ";")
|
while (tok && tok->str() != ";")
|
||||||
tok = tok->next();
|
tok = tok->next();
|
||||||
if (stdtype || i->isPointer())
|
if (stdtype || i->isPointer())
|
||||||
checkScopeForVariable(tok, *i, NULL, NULL);
|
checkScopeForVariable(scope, tok, *i, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) {
|
for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) {
|
||||||
|
@ -1089,7 +1089,7 @@ void CheckUninitVar::checkScope(const Scope* scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn)
|
bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn)
|
||||||
{
|
{
|
||||||
const bool suppressErrors(possibleInit && *possibleInit);
|
const bool suppressErrors(possibleInit && *possibleInit);
|
||||||
|
|
||||||
|
@ -1118,7 +1118,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
|
|
||||||
// Unconditional inner scope..
|
// Unconditional inner scope..
|
||||||
if (tok->str() == "{" && Token::Match(tok->previous(), "[;{}]")) {
|
if (tok->str() == "{" && Token::Match(tok->previous(), "[;{}]")) {
|
||||||
if (checkScopeForVariable(tok->next(), var, possibleInit, NULL))
|
if (checkScopeForVariable(scope, tok->next(), var, possibleInit, NULL))
|
||||||
return true;
|
return true;
|
||||||
tok = tok->link();
|
tok = tok->link();
|
||||||
continue;
|
continue;
|
||||||
|
@ -1131,7 +1131,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
// Inner scope..
|
// Inner scope..
|
||||||
if (Token::simpleMatch(tok, "if (")) {
|
if (Token::simpleMatch(tok, "if (")) {
|
||||||
// initialization / usage in condition..
|
// initialization / usage in condition..
|
||||||
if (checkIfForWhileHead(tok->next(), var, suppressErrors, bool(number_of_if == 0)))
|
if (checkIfForWhileHead(scope, tok->next(), var, suppressErrors, bool(number_of_if == 0)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// checking if a not-zero variable is zero => bail out
|
// checking if a not-zero variable is zero => bail out
|
||||||
|
@ -1146,7 +1146,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
if (tok->str() == "{") {
|
if (tok->str() == "{") {
|
||||||
bool possibleInitIf(number_of_if > 0 || suppressErrors);
|
bool possibleInitIf(number_of_if > 0 || suppressErrors);
|
||||||
bool noreturnIf = false;
|
bool noreturnIf = false;
|
||||||
const bool initif = checkScopeForVariable(tok->next(), var, &possibleInitIf, &noreturnIf);
|
const bool initif = checkScopeForVariable(scope, tok->next(), var, &possibleInitIf, &noreturnIf);
|
||||||
|
|
||||||
// goto the }
|
// goto the }
|
||||||
tok = tok->link();
|
tok = tok->link();
|
||||||
|
@ -1163,7 +1163,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
|
|
||||||
bool possibleInitElse(number_of_if > 0 || suppressErrors);
|
bool possibleInitElse(number_of_if > 0 || suppressErrors);
|
||||||
bool noreturnElse = false;
|
bool noreturnElse = false;
|
||||||
const bool initelse = checkScopeForVariable(tok->next(), var, &possibleInitElse, NULL);
|
const bool initelse = checkScopeForVariable(scope, tok->next(), var, &possibleInitElse, NULL);
|
||||||
|
|
||||||
// goto the }
|
// goto the }
|
||||||
tok = tok->link();
|
tok = tok->link();
|
||||||
|
@ -1201,7 +1201,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
// for/while..
|
// for/while..
|
||||||
if (Token::Match(tok, "for|while (")) {
|
if (Token::Match(tok, "for|while (")) {
|
||||||
// is variable initialized in for-head (don't report errors yet)?
|
// is variable initialized in for-head (don't report errors yet)?
|
||||||
if (checkIfForWhileHead(tok->next(), var, true, false))
|
if (checkIfForWhileHead(scope, tok->next(), var, true, false))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// goto the {
|
// goto the {
|
||||||
|
@ -1209,7 +1209,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
|
|
||||||
if (tok2 && tok2->str() == "{") {
|
if (tok2 && tok2->str() == "{") {
|
||||||
bool possibleinit = true;
|
bool possibleinit = true;
|
||||||
bool init = checkScopeForVariable(tok2->next(), var, &possibleinit, NULL);
|
bool init = checkScopeForVariable(scope, tok2->next(), var, &possibleinit, NULL);
|
||||||
|
|
||||||
// variable is initialized in the loop..
|
// variable is initialized in the loop..
|
||||||
if (possibleinit || init)
|
if (possibleinit || init)
|
||||||
|
@ -1217,7 +1217,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
|
|
||||||
// is variable used in for-head?
|
// is variable used in for-head?
|
||||||
if (!suppressErrors) {
|
if (!suppressErrors) {
|
||||||
checkIfForWhileHead(tok->next(), var, false, bool(number_of_if == 0));
|
checkIfForWhileHead(scope, tok->next(), var, false, bool(number_of_if == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// goto "}"
|
// goto "}"
|
||||||
|
@ -1246,7 +1246,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
// variable is seen..
|
// variable is seen..
|
||||||
if (tok->varId() == var.varId()) {
|
if (tok->varId() == var.varId()) {
|
||||||
// Use variable
|
// Use variable
|
||||||
if (!suppressErrors && isVariableUsage(tok, var.isPointer()))
|
if (!suppressErrors && isVariableUsage(scope, tok, var.isPointer()))
|
||||||
uninitvarError(tok, tok->str());
|
uninitvarError(tok, tok->str());
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -1258,12 +1258,12 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckUninitVar::checkIfForWhileHead(const Token *startparanthesis, const Variable& var, bool suppressErrors, bool isuninit)
|
bool CheckUninitVar::checkIfForWhileHead(const Scope *scope, const Token *startparanthesis, const Variable& var, bool suppressErrors, bool isuninit)
|
||||||
{
|
{
|
||||||
const Token * const endpar = startparanthesis->link();
|
const Token * const endpar = startparanthesis->link();
|
||||||
for (const Token *tok = startparanthesis->next(); tok && tok != endpar; tok = tok->next()) {
|
for (const Token *tok = startparanthesis->next(); tok && tok != endpar; tok = tok->next()) {
|
||||||
if (tok->varId() == var.varId()) {
|
if (tok->varId() == var.varId()) {
|
||||||
if (isVariableUsage(tok, var.isPointer())) {
|
if (isVariableUsage(scope, tok, var.isPointer())) {
|
||||||
if (!suppressErrors)
|
if (!suppressErrors)
|
||||||
uninitvarError(tok, tok->str());
|
uninitvarError(tok, tok->str());
|
||||||
else
|
else
|
||||||
|
@ -1279,11 +1279,45 @@ bool CheckUninitVar::checkIfForWhileHead(const Token *startparanthesis, const Va
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer) const
|
bool CheckUninitVar::isVariableUsage(const Scope* scope, const Token *vartok, bool pointer) const
|
||||||
{
|
{
|
||||||
if (vartok->previous()->str() == "return")
|
if (vartok->previous()->str() == "return")
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Passing variable to function..
|
||||||
|
if (Token::Match(vartok->previous(), "[(,] %var% [,)]") || Token::Match(vartok->tokAt(-2), "[(,] & %var% [,)]")) {
|
||||||
|
const bool address(vartok->previous()->str() == "&");
|
||||||
|
|
||||||
|
// locate start parenthesis in function call..
|
||||||
|
int argumentNumber = 0;
|
||||||
|
const Token *start = vartok;
|
||||||
|
while (start && !Token::Match(start, "[;{}(]")) {
|
||||||
|
if (start->str() == ")")
|
||||||
|
start = start->link();
|
||||||
|
else if (start->str() == ",")
|
||||||
|
++argumentNumber;
|
||||||
|
start = start->previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
// is this a function call?
|
||||||
|
if (start && Token::Match(start->previous(), "%var% (")) {
|
||||||
|
// check how function handle uninitialized data arguments..
|
||||||
|
const Function *func = _tokenizer->getSymbolDatabase()->findFunctionByNameAndArgs(start->previous(), scope);
|
||||||
|
if (func) {
|
||||||
|
const Variable *arg = func->getArgumentVar(argumentNumber);
|
||||||
|
if (arg) {
|
||||||
|
const Token *argStart = arg->typeStartToken();
|
||||||
|
while (argStart->previous() && argStart->previous()->isName())
|
||||||
|
argStart = argStart->previous();
|
||||||
|
if (argStart->isStandardType() && Token::Match(argStart, "%type% %var% [,)]"))
|
||||||
|
return true;
|
||||||
|
if (Token::Match(argStart, "const"))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Token::Match(vartok->previous(), "++|--|%op%")) {
|
if (Token::Match(vartok->previous(), "++|--|%op%")) {
|
||||||
if (vartok->previous()->str() == ">>" && _tokenizer->isCPP()) {
|
if (vartok->previous()->str() == ">>" && _tokenizer->isCPP()) {
|
||||||
// assume that variable is initialized
|
// assume that variable is initialized
|
||||||
|
|
|
@ -64,9 +64,9 @@ public:
|
||||||
/** Check for uninitialized variables */
|
/** Check for uninitialized variables */
|
||||||
void check();
|
void check();
|
||||||
void checkScope(const Scope* scope);
|
void checkScope(const Scope* scope);
|
||||||
bool checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn);
|
bool checkScopeForVariable(const Scope* scope, const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn);
|
||||||
bool checkIfForWhileHead(const Token *startparanthesis, const Variable& var, bool suppressErrors, bool isuninit);
|
bool checkIfForWhileHead(const Scope *scope, const Token *startparanthesis, const Variable& var, bool suppressErrors, bool isuninit);
|
||||||
bool isVariableUsage(const Token *vartok, bool ispointer) const;
|
bool isVariableUsage(const Scope* scope, const Token *vartok, bool ispointer) const;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -57,6 +57,7 @@ private:
|
||||||
TEST_CASE(uninitvar4); // #3869 (reference)
|
TEST_CASE(uninitvar4); // #3869 (reference)
|
||||||
TEST_CASE(uninitvar5); // #3861
|
TEST_CASE(uninitvar5); // #3861
|
||||||
TEST_CASE(uninitvar6); // handling unknown types in C and C++ files
|
TEST_CASE(uninitvar6); // handling unknown types in C and C++ files
|
||||||
|
TEST_CASE(uninitvar2_func); // function calls
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkUninitVar(const char code[], const char filename[] = "test.cpp") {
|
void checkUninitVar(const char code[], const char filename[] = "test.cpp") {
|
||||||
|
@ -2373,6 +2374,45 @@ private:
|
||||||
checkUninitVar2(code, "test.c");
|
checkUninitVar2(code, "test.c");
|
||||||
ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: a\n", errout.str());
|
ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: a\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handling of unknown types. Assume they are POD in C.
|
||||||
|
void uninitvar2_func() {
|
||||||
|
checkUninitVar2("void a(char c);\n"
|
||||||
|
"void b() {\n"
|
||||||
|
" char c;\n"
|
||||||
|
" a(c);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str());
|
||||||
|
|
||||||
|
checkUninitVar2("void a(const char *s);\n"
|
||||||
|
"void b() {\n"
|
||||||
|
" char c;\n"
|
||||||
|
" a(&c);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str());
|
||||||
|
|
||||||
|
checkUninitVar2("void a(char *s);\n"
|
||||||
|
"void b() {\n"
|
||||||
|
" char c;\n"
|
||||||
|
" a(&c);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkUninitVar2("void a(const ABC *abc);\n"
|
||||||
|
"void b() {\n"
|
||||||
|
" ABC abc;\n"
|
||||||
|
" a(&abc);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkUninitVar2("void a(const ABC *abc);\n"
|
||||||
|
"void b() {\n"
|
||||||
|
" ABC abc;\n"
|
||||||
|
" a(&abc);\n"
|
||||||
|
"}", "test.c");
|
||||||
|
ASSERT_EQUALS("[test.c:4]: (error) Uninitialized variable: abc\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestUninitVar)
|
REGISTER_TEST(TestUninitVar)
|
||||||
|
|
Loading…
Reference in New Issue