From ccdea4dc2b04d9af78e506056735ecda0b8e7d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 20 Dec 2012 19:45:30 +0100 Subject: [PATCH] Uninitialized variables: Improved checking of functions --- lib/checkuninitvar.cpp | 60 +++++++++++++++++++++++++++++++++--------- lib/checkuninitvar.h | 6 ++--- test/testuninitvar.cpp | 40 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index ff5fb54dd..4e9251a0c 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -1080,7 +1080,7 @@ void CheckUninitVar::checkScope(const Scope* scope) while (tok && tok->str() != ";") tok = tok->next(); if (stdtype || i->isPointer()) - checkScopeForVariable(tok, *i, NULL, NULL); + checkScopeForVariable(scope, tok, *i, NULL, NULL); } for (std::list::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); @@ -1118,7 +1118,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var // Unconditional inner scope.. if (tok->str() == "{" && Token::Match(tok->previous(), "[;{}]")) { - if (checkScopeForVariable(tok->next(), var, possibleInit, NULL)) + if (checkScopeForVariable(scope, tok->next(), var, possibleInit, NULL)) return true; tok = tok->link(); continue; @@ -1131,7 +1131,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var // Inner scope.. if (Token::simpleMatch(tok, "if (")) { // 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; // 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() == "{") { bool possibleInitIf(number_of_if > 0 || suppressErrors); bool noreturnIf = false; - const bool initif = checkScopeForVariable(tok->next(), var, &possibleInitIf, &noreturnIf); + const bool initif = checkScopeForVariable(scope, tok->next(), var, &possibleInitIf, &noreturnIf); // goto the } tok = tok->link(); @@ -1163,7 +1163,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var bool possibleInitElse(number_of_if > 0 || suppressErrors); bool noreturnElse = false; - const bool initelse = checkScopeForVariable(tok->next(), var, &possibleInitElse, NULL); + const bool initelse = checkScopeForVariable(scope, tok->next(), var, &possibleInitElse, NULL); // goto the } tok = tok->link(); @@ -1201,7 +1201,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var // for/while.. if (Token::Match(tok, "for|while (")) { // 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; // goto the { @@ -1209,7 +1209,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var if (tok2 && tok2->str() == "{") { 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.. if (possibleinit || init) @@ -1217,7 +1217,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var // is variable used in for-head? if (!suppressErrors) { - checkIfForWhileHead(tok->next(), var, false, bool(number_of_if == 0)); + checkIfForWhileHead(scope, tok->next(), var, false, bool(number_of_if == 0)); } // goto "}" @@ -1246,7 +1246,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var // variable is seen.. if (tok->varId() == var.varId()) { // Use variable - if (!suppressErrors && isVariableUsage(tok, var.isPointer())) + if (!suppressErrors && isVariableUsage(scope, tok, var.isPointer())) uninitvarError(tok, tok->str()); else @@ -1258,12 +1258,12 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var 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(); for (const Token *tok = startparanthesis->next(); tok && tok != endpar; tok = tok->next()) { if (tok->varId() == var.varId()) { - if (isVariableUsage(tok, var.isPointer())) { + if (isVariableUsage(scope, tok, var.isPointer())) { if (!suppressErrors) uninitvarError(tok, tok->str()); else @@ -1279,11 +1279,45 @@ bool CheckUninitVar::checkIfForWhileHead(const Token *startparanthesis, const Va 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") 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 (vartok->previous()->str() == ">>" && _tokenizer->isCPP()) { // assume that variable is initialized diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h index e5c72d83f..236ce1932 100644 --- a/lib/checkuninitvar.h +++ b/lib/checkuninitvar.h @@ -64,9 +64,9 @@ public: /** Check for uninitialized variables */ void check(); void checkScope(const Scope* scope); - bool checkScopeForVariable(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 isVariableUsage(const Token *vartok, bool ispointer) const; + bool checkScopeForVariable(const Scope* scope, const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn); + bool checkIfForWhileHead(const Scope *scope, const Token *startparanthesis, const Variable& var, bool suppressErrors, bool isuninit); + bool isVariableUsage(const Scope* scope, const Token *vartok, bool ispointer) const; /** diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 42361ae80..6a91a12e7 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -57,6 +57,7 @@ private: TEST_CASE(uninitvar4); // #3869 (reference) TEST_CASE(uninitvar5); // #3861 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") { @@ -2373,6 +2374,45 @@ private: checkUninitVar2(code, "test.c"); 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)