Uninitialized variables: Add experimental checking of struct members

This commit is contained in:
Daniel Marjamäki 2013-01-16 20:28:29 +01:00
parent 4cd8c7c608
commit 5773e69ab1
3 changed files with 56 additions and 15 deletions

View File

@ -1081,7 +1081,18 @@ 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(scope, tok, *i, NULL, NULL); checkScopeForVariable(scope, tok, *i, NULL, NULL, NULL);
if (_settings->experimental && Token::Match(i->typeStartToken(), "struct %type% %var% ;")) {
const std::string structname(i->typeStartToken()->next()->str());
const SymbolDatabase * symbolDatabase = _tokenizer->getSymbolDatabase();
for (std::size_t j = 0U; j < symbolDatabase->classAndStructScopes.size(); ++j) {
const Scope *scope2 = symbolDatabase->classAndStructScopes[j];
if (scope2->className == structname && scope2->numConstructors == 0U) {
for (std::list<Variable>::const_iterator it = scope2->varlist.begin(); it != scope2->varlist.end(); ++it)
checkScopeForVariable(scope, tok, *i, NULL, NULL, &(*it));
}
}
}
} }
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) {
@ -1090,7 +1101,7 @@ void CheckUninitVar::checkScope(const Scope* scope)
} }
} }
bool CheckUninitVar::checkScopeForVariable(const Scope* scope, 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 Variable * const membervar)
{ {
const bool suppressErrors(possibleInit && *possibleInit); const bool suppressErrors(possibleInit && *possibleInit);
@ -1118,7 +1129,7 @@ bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok,
// Unconditional inner scope.. // Unconditional inner scope..
if (tok->str() == "{" && Token::Match(tok->previous(), "[;{}]")) { if (tok->str() == "{" && Token::Match(tok->previous(), "[;{}]")) {
if (checkScopeForVariable(scope, tok->next(), var, possibleInit, NULL)) if (checkScopeForVariable(scope, tok->next(), var, possibleInit, NULL, membervar))
return true; return true;
tok = tok->link(); tok = tok->link();
continue; continue;
@ -1168,7 +1179,7 @@ bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok,
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 = !alwaysTrue && checkScopeForVariable(scope, tok->next(), var, &possibleInitIf, &noreturnIf); const bool initif = !alwaysTrue && checkScopeForVariable(scope, tok->next(), var, &possibleInitIf, &noreturnIf, membervar);
// bail out for such code: // bail out for such code:
// if (a) x=0; // conditional initialization // if (a) x=0; // conditional initialization
@ -1215,7 +1226,7 @@ bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok,
bool possibleInitElse(number_of_if > 0 || suppressErrors); bool possibleInitElse(number_of_if > 0 || suppressErrors);
bool noreturnElse = false; bool noreturnElse = false;
const bool initelse = checkScopeForVariable(scope, tok->next(), var, &possibleInitElse, NULL); const bool initelse = checkScopeForVariable(scope, tok->next(), var, &possibleInitElse, NULL, membervar);
std::map<unsigned int, int> varValueElse; std::map<unsigned int, int> varValueElse;
if (!initelse) { if (!initelse) {
@ -1278,7 +1289,7 @@ bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok,
if (tok2 && tok2->str() == "{") { if (tok2 && tok2->str() == "{") {
bool possibleinit = true; bool possibleinit = true;
bool init = checkScopeForVariable(scope, tok2->next(), var, &possibleinit, NULL); bool init = checkScopeForVariable(scope, tok2->next(), var, &possibleinit, NULL, membervar);
// variable is initialized in the loop.. // variable is initialized in the loop..
if (possibleinit || init) if (possibleinit || init)
@ -1331,6 +1342,12 @@ bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok,
// variable is seen.. // variable is seen..
if (tok->varId() == var.varId()) { if (tok->varId() == var.varId()) {
if (membervar) {
if (Token::Match(tok, "%var% . %var% =") && tok->strAt(2) == membervar->name())
return true;
else if (Token::Match(tok->previous(), "[(,] %var% [,)]") && isVariableUsage(scope, tok, var.isPointer()))
uninitvarError(tok, tok->str() + "." + membervar->name());
} else {
// Use variable // Use variable
if (!suppressErrors && isVariableUsage(scope, tok, var.isPointer())) if (!suppressErrors && isVariableUsage(scope, tok, var.isPointer()))
uninitvarError(tok, tok->str()); uninitvarError(tok, tok->str());
@ -1340,6 +1357,7 @@ bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok,
return true; return true;
} }
} }
}
return false; return false;
} }
@ -1395,7 +1413,7 @@ bool CheckUninitVar::isVariableUsage(const Scope* scope, const Token *vartok, bo
const Token *argStart = arg->typeStartToken(); const Token *argStart = arg->typeStartToken();
while (argStart->previous() && argStart->previous()->isName()) while (argStart->previous() && argStart->previous()->isName())
argStart = argStart->previous(); argStart = argStart->previous();
if (!address && Token::Match(argStart, "const| %type% %var% [,)]")) if (!address && Token::Match(argStart, "const| struct| %type% %var% [,)]"))
return true; return true;
if (Token::Match(argStart, "const %type% & %var% [,)]")) if (Token::Match(argStart, "const %type% & %var% [,)]"))
return true; return true;

View File

@ -64,7 +64,7 @@ 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 Scope* scope, 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, const Variable * const membervar);
bool checkIfForWhileHead(const Scope *scope, const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit); bool checkIfForWhileHead(const Scope *scope, const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit);
bool isVariableUsage(const Scope* scope, const Token *vartok, bool ispointer) const; bool isVariableUsage(const Scope* scope, const Token *vartok, bool ispointer) const;

View File

@ -59,6 +59,7 @@ private:
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 TEST_CASE(uninitvar2_func); // function calls
TEST_CASE(uninitvar2_value); // value flow TEST_CASE(uninitvar2_value); // value flow
TEST_CASE(uninitvar2_structmembers); // struct members
} }
void checkUninitVar(const char code[], const char filename[] = "test.cpp") { void checkUninitVar(const char code[], const char filename[] = "test.cpp") {
@ -1937,11 +1938,12 @@ private:
/** New checking that doesn't rely on ExecutionPath */ /** New checking that doesn't rely on ExecutionPath */
void checkUninitVar2(const char code[], const char fname[] = "test.cpp") { void checkUninitVar2(const char code[], const char fname[] = "test.cpp", bool experimental = false) {
// Clear the error buffer.. // Clear the error buffer..
errout.str(""); errout.str("");
Settings settings; Settings settings;
settings.experimental = experimental;
// Tokenize.. // Tokenize..
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
@ -2550,6 +2552,27 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void uninitvar2_structmembers() { // struct members
checkUninitVar2("struct AB { int a; int b; };\n"
"void do_something(const struct AB ab);\n"
"void f(void) {\n"
" struct AB ab;\n"
" ab.a = 0;\n"
" do_something(ab);\n"
"}\n", "test.c", true);
ASSERT_EQUALS("[test.c:6]: (error) Uninitialized variable: ab.b\n", errout.str());
checkUninitVar2("struct AB { int a; int b; };\n"
"void do_something(const struct AB ab);\n"
"void f(void) {\n"
" struct AB ab;\n"
" ab.a = 0;\n"
" ab.b = 0;\n"
" do_something(ab);\n"
"}\n", "test.c", true);
ASSERT_EQUALS("", errout.str());
}
}; };
REGISTER_TEST(TestUninitVar) REGISTER_TEST(TestUninitVar)