Uninitialized variables: Add experimental checking of struct members
This commit is contained in:
parent
4cd8c7c608
commit
5773e69ab1
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue