Fixed #10327 (ValueFlow; Wrong Uninit value in called function)
This commit is contained in:
parent
869eac5670
commit
1a5449cbeb
|
@ -1093,7 +1093,7 @@ static bool isVoidCast(const Token *tok)
|
||||||
return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() && tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0;
|
return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() && tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
|
const Token* CheckUninitVar::isVariableUsage(bool cpp, const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect)
|
||||||
{
|
{
|
||||||
const Token *valueExpr = vartok; // non-dereferenced , no address of value as variable
|
const Token *valueExpr = vartok; // non-dereferenced , no address of value as variable
|
||||||
while (Token::Match(valueExpr->astParent(), ".|::") && astIsRhs(valueExpr))
|
while (Token::Match(valueExpr->astParent(), ".|::") && astIsRhs(valueExpr))
|
||||||
|
@ -1197,17 +1197,17 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer,
|
||||||
parent = parent->astParent();
|
parent = parent->astParent();
|
||||||
if (Token::simpleMatch(parent, "{"))
|
if (Token::simpleMatch(parent, "{"))
|
||||||
return valueExpr;
|
return valueExpr;
|
||||||
const int use = isFunctionParUsage(valueExpr, pointer, alloc, indirect);
|
const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect);
|
||||||
return (use>0) ? valueExpr : nullptr;
|
return (use>0) ? valueExpr : nullptr;
|
||||||
}
|
}
|
||||||
if (derefValue && Token::Match(derefValue->astParent(), "[(,]") && (derefValue->astParent()->str() == "," || astIsRhs(derefValue))) {
|
if (derefValue && Token::Match(derefValue->astParent(), "[(,]") && (derefValue->astParent()->str() == "," || astIsRhs(derefValue))) {
|
||||||
const int use = isFunctionParUsage(derefValue, false, NO_ALLOC, indirect);
|
const int use = isFunctionParUsage(derefValue, library, false, NO_ALLOC, indirect);
|
||||||
return (use>0) ? derefValue : nullptr;
|
return (use>0) ? derefValue : nullptr;
|
||||||
}
|
}
|
||||||
if (valueExpr->astParent()->isUnaryOp("&")) {
|
if (valueExpr->astParent()->isUnaryOp("&")) {
|
||||||
const Token *parent = valueExpr->astParent();
|
const Token *parent = valueExpr->astParent();
|
||||||
if (Token::Match(parent->astParent(), "[(,]") && (parent->astParent()->str() == "," || astIsRhs(parent))) {
|
if (Token::Match(parent->astParent(), "[(,]") && (parent->astParent()->str() == "," || astIsRhs(parent))) {
|
||||||
const int use = isFunctionParUsage(valueExpr, pointer, alloc, indirect);
|
const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect);
|
||||||
return (use>0) ? valueExpr : nullptr;
|
return (use>0) ? valueExpr : nullptr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1253,18 +1253,18 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer,
|
||||||
|
|
||||||
// Stream read/write
|
// Stream read/write
|
||||||
// FIXME this code is a hack!!
|
// FIXME this code is a hack!!
|
||||||
if (mTokenizer->isCPP() && Token::Match(valueExpr->astParent(), "<<|>>")) {
|
if (cpp && Token::Match(valueExpr->astParent(), "<<|>>")) {
|
||||||
if (isLikelyStreamRead(mTokenizer->isCPP(), vartok->previous()))
|
if (isLikelyStreamRead(cpp, vartok->previous()))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (valueExpr->valueType() && valueExpr->valueType()->type == ValueType::Type::VOID)
|
if (valueExpr->valueType() && valueExpr->valueType()->type == ValueType::Type::VOID)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (astIsRhs(derefValue) && isLikelyStreamRead(mTokenizer->isCPP(), derefValue->astParent()))
|
if (astIsRhs(derefValue) && isLikelyStreamRead(cpp, derefValue->astParent()))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Assignment with overloaded &
|
// Assignment with overloaded &
|
||||||
if (mTokenizer->isCPP() && Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr)) {
|
if (cpp && Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr)) {
|
||||||
const Token *parent = valueExpr->astParent();
|
const Token *parent = valueExpr->astParent();
|
||||||
while (Token::simpleMatch(parent, "&") && parent->isBinaryOp())
|
while (Token::simpleMatch(parent, "&") && parent->isBinaryOp())
|
||||||
parent = parent->astParent();
|
parent = parent->astParent();
|
||||||
|
@ -1280,13 +1280,18 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer,
|
||||||
return derefValue ? derefValue : valueExpr;
|
return derefValue ? derefValue : valueExpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
|
||||||
|
{
|
||||||
|
return CheckUninitVar::isVariableUsage(mTokenizer->isCPP(), vartok, mSettings->library, pointer, alloc, indirect);
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Is function parameter "used" so a "usage of uninitialized variable" can
|
* Is function parameter "used" so a "usage of uninitialized variable" can
|
||||||
* be written? If parameter is passed "by value" then it is "used". If it
|
* be written? If parameter is passed "by value" then it is "used". If it
|
||||||
* is passed "by reference" then it is not necessarily "used".
|
* is passed "by reference" then it is not necessarily "used".
|
||||||
* @return -1 => unknown 0 => not used 1 => used
|
* @return -1 => unknown 0 => not used 1 => used
|
||||||
*/
|
*/
|
||||||
int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
|
int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect)
|
||||||
{
|
{
|
||||||
bool unknown = false;
|
bool unknown = false;
|
||||||
const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown);
|
const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown);
|
||||||
|
@ -1339,11 +1344,11 @@ int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc
|
||||||
// control-flow statement reading the variable "by value"
|
// control-flow statement reading the variable "by value"
|
||||||
return alloc == NO_ALLOC;
|
return alloc == NO_ALLOC;
|
||||||
} else {
|
} else {
|
||||||
const bool isnullbad = mSettings->library.isnullargbad(start->previous(), argumentNumber + 1);
|
const bool isnullbad = library.isnullargbad(start->previous(), argumentNumber + 1);
|
||||||
if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC)
|
if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC)
|
||||||
return 1;
|
return 1;
|
||||||
bool hasIndirect = false;
|
bool hasIndirect = false;
|
||||||
const bool isuninitbad = mSettings->library.isuninitargbad(start->previous(), argumentNumber + 1, indirect, &hasIndirect);
|
const bool isuninitbad = library.isuninitargbad(start->previous(), argumentNumber + 1, indirect, &hasIndirect);
|
||||||
if (alloc != NO_ALLOC)
|
if (alloc != NO_ALLOC)
|
||||||
return (isnullbad || hasIndirect) && isuninitbad;
|
return (isnullbad || hasIndirect) && isuninitbad;
|
||||||
return isuninitbad && (!address || isnullbad);
|
return isuninitbad && (!address || isnullbad);
|
||||||
|
@ -1354,6 +1359,11 @@ int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
|
||||||
|
{
|
||||||
|
return CheckUninitVar::isFunctionParUsage(vartok, mSettings->library, pointer, alloc, indirect);
|
||||||
|
}
|
||||||
|
|
||||||
bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const
|
bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const
|
||||||
{
|
{
|
||||||
if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) {
|
if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) {
|
||||||
|
|
|
@ -84,7 +84,9 @@ public:
|
||||||
bool checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors);
|
bool checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors);
|
||||||
const Token* checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const;
|
const Token* checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const;
|
||||||
void checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar);
|
void checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar);
|
||||||
|
static const Token *isVariableUsage(bool cpp, const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0);
|
||||||
const Token *isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const;
|
const Token *isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const;
|
||||||
|
static int isFunctionParUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0);
|
||||||
int isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const;
|
int isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const;
|
||||||
bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const;
|
bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const;
|
||||||
bool isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const;
|
bool isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const;
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
|
|
||||||
#include "analyzer.h"
|
#include "analyzer.h"
|
||||||
#include "astutils.h"
|
#include "astutils.h"
|
||||||
|
#include "checkuninitvar.h"
|
||||||
#include "errorlogger.h"
|
#include "errorlogger.h"
|
||||||
#include "errortypes.h"
|
#include "errortypes.h"
|
||||||
#include "forwardanalyzer.h"
|
#include "forwardanalyzer.h"
|
||||||
|
@ -4255,6 +4256,12 @@ static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldat
|
||||||
values.remove_if([&](const ValueFlow::Value& value) {
|
values.remove_if([&](const ValueFlow::Value& value) {
|
||||||
return value.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
return value.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
||||||
});
|
});
|
||||||
|
// If assignment copy by value, remove Uninit values..
|
||||||
|
if ((tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->pointer == 0) ||
|
||||||
|
(tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference() && tok->astOperand1()->variable()->nameToken() == tok->astOperand1()))
|
||||||
|
values.remove_if([&](const ValueFlow::Value& value) {
|
||||||
|
return value.isUninitValue();
|
||||||
|
});
|
||||||
if (values.empty())
|
if (values.empty())
|
||||||
continue;
|
continue;
|
||||||
const bool init = vars.size() == 1 && vars.front()->nameToken() == tok->astOperand1();
|
const bool init = vars.size() == 1 && vars.front()->nameToken() == tok->astOperand1();
|
||||||
|
@ -5777,6 +5784,11 @@ static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldat
|
||||||
});
|
});
|
||||||
// Don't forward container sizes for now since programmemory can't evaluate conditions
|
// Don't forward container sizes for now since programmemory can't evaluate conditions
|
||||||
argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isContainerSizeValue));
|
argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isContainerSizeValue));
|
||||||
|
// Remove uninit values if argument is passed by value
|
||||||
|
if (argtok->variable() && !argtok->variable()->isPointer() && argvalues.size() == 1 && argvalues.front().isUninitValue()) {
|
||||||
|
if (CheckUninitVar::isVariableUsage(tokenlist->isCPP(), argtok, settings->library, false, CheckUninitVar::Alloc::NO_ALLOC, 0))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (argvalues.empty())
|
if (argvalues.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -4844,6 +4844,24 @@ private:
|
||||||
" return f.i;\n"
|
" return f.i;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// #10327 - avoid extra warnings for uninitialized variable
|
||||||
|
valueFlowUninit("void dowork( int me ) {\n"
|
||||||
|
" if ( me == 0 ) {}\n" // <- not uninitialized
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"int main() {\n"
|
||||||
|
" int me;\n"
|
||||||
|
" dowork(me);\n" // <- me is uninitialized
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: me\n", errout.str());
|
||||||
|
|
||||||
|
valueFlowUninit("int foo() {\n"
|
||||||
|
" int x;\n"
|
||||||
|
" int a = x;\n" // <- x is uninitialized
|
||||||
|
" return a;\n" // <- a has been initialized
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninitvar_ipa() {
|
void uninitvar_ipa() {
|
||||||
|
|
Loading…
Reference in New Issue