valueFlowUninit: Handle arrays and pod types (#3917)
* valueFlowUninit: Handle arrays and pod types * Format * Catch another array case
This commit is contained in:
parent
3bcbba598d
commit
4b4037540a
|
@ -230,7 +230,7 @@
|
|||
<noreturn>false</noreturn>
|
||||
<returnValue type="void"/>
|
||||
<leak-ignore/>
|
||||
<arg nr="1">
|
||||
<arg nr="1" direction="out">
|
||||
<not-null/>
|
||||
<minsize type="argvalue" arg="2"/>
|
||||
</arg>
|
||||
|
|
|
@ -1539,34 +1539,41 @@ void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string
|
|||
"$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal);
|
||||
}
|
||||
|
||||
enum class FunctionUsage { None, PassedByReference, Used };
|
||||
enum class ExprUsage { None, PassedByReference, Used };
|
||||
|
||||
static FunctionUsage getFunctionUsage(const Token* tok, int indirect, const Settings* settings)
|
||||
static ExprUsage getFunctionUsage(const Token* tok, int indirect, const Settings* settings)
|
||||
{
|
||||
const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&");
|
||||
|
||||
int argnr;
|
||||
const Token* ftok = getTokenArgumentFunction(tok, argnr);
|
||||
if (!ftok)
|
||||
return FunctionUsage::None;
|
||||
return ExprUsage::None;
|
||||
if (ftok->function()) {
|
||||
std::vector<const Variable*> args = getArgumentVars(ftok, argnr);
|
||||
for (const Variable* arg : args) {
|
||||
if (!arg)
|
||||
continue;
|
||||
if (arg->isReference())
|
||||
return FunctionUsage::PassedByReference;
|
||||
return ExprUsage::PassedByReference;
|
||||
}
|
||||
} else {
|
||||
const bool isnullbad = settings->library.isnullargbad(ftok, argnr + 1);
|
||||
if (indirect == 0 && astIsPointer(tok) && !addressOf && isnullbad)
|
||||
return FunctionUsage::Used;
|
||||
return ExprUsage::Used;
|
||||
bool hasIndirect = false;
|
||||
const bool isuninitbad = settings->library.isuninitargbad(ftok, argnr + 1, indirect, &hasIndirect);
|
||||
if (isuninitbad && (!addressOf || isnullbad))
|
||||
return FunctionUsage::Used;
|
||||
return ExprUsage::Used;
|
||||
}
|
||||
return FunctionUsage::None;
|
||||
return ExprUsage::None;
|
||||
}
|
||||
|
||||
static ExprUsage getExprUsage(const Token* tok, int indirect, const Settings* settings)
|
||||
{
|
||||
if (indirect == 0 && Token::Match(tok->astParent(), "%cop%|%assign%") && tok->astParent()->str() != "=")
|
||||
return ExprUsage::Used;
|
||||
return getFunctionUsage(tok, indirect, settings);
|
||||
}
|
||||
|
||||
static bool isLeafDot(const Token* tok)
|
||||
|
@ -1634,10 +1641,10 @@ void CheckUninitVar::valueFlowUninit()
|
|||
if (Token::Match(tok->astParent(), ". %var%") && !isleaf)
|
||||
continue;
|
||||
}
|
||||
FunctionUsage fusage = getFunctionUsage(tok, v->indirect, mSettings);
|
||||
if (!v->subexpressions.empty() && fusage == FunctionUsage::PassedByReference)
|
||||
ExprUsage usage = getExprUsage(tok, v->indirect, mSettings);
|
||||
if (!v->subexpressions.empty() && usage == ExprUsage::PassedByReference)
|
||||
continue;
|
||||
if (fusage != FunctionUsage::Used) {
|
||||
if (usage != ExprUsage::Used) {
|
||||
if (!(Token::Match(tok->astParent(), ". %name% (") && uninitderef) &&
|
||||
isVariableChanged(tok, v->indirect, mSettings, mTokenizer->isCPP()))
|
||||
continue;
|
||||
|
|
|
@ -6920,6 +6920,10 @@ bool ValueType::fromLibraryType(const std::string &typestr, const Settings *sett
|
|||
type = ValueType::Type::UNKNOWN_INT;
|
||||
sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED;
|
||||
return true;
|
||||
} else if (podtype && podtype->stdtype == Library::PodType::Type::NO) {
|
||||
type = ValueType::Type::POD;
|
||||
sign = ValueType::UNKNOWN_SIGN;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Library::PlatformType *platformType = settings->library.platform_type(typestr, settings->platformString());
|
||||
|
|
|
@ -1216,6 +1216,7 @@ public:
|
|||
enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign;
|
||||
enum Type {
|
||||
UNKNOWN_TYPE,
|
||||
POD,
|
||||
NONSTD,
|
||||
RECORD,
|
||||
SMART_POINTER,
|
||||
|
|
|
@ -2338,7 +2338,7 @@ struct ValueFlowAnalyzer : Analyzer {
|
|||
} else {
|
||||
return analyzeMatch(tok, d) | Action::Match;
|
||||
}
|
||||
} else if (ref->isUnaryOp("*")) {
|
||||
} else if (ref->isUnaryOp("*") && !match(ref->astOperand1())) {
|
||||
const Token* lifeTok = nullptr;
|
||||
for (const ValueFlow::Value& v:ref->astOperand1()->values()) {
|
||||
if (!v.isLocalLifetimeValue())
|
||||
|
@ -7030,7 +7030,7 @@ static bool needsInitialization(const Variable* var, bool cpp)
|
|||
return true;
|
||||
if (var->type() && var->type()->needInitialization == Type::NeedInitialization::True)
|
||||
return true;
|
||||
if (var->valueType() && var->valueType()->isPrimitive())
|
||||
if (var->valueType() && (var->valueType()->isPrimitive() || var->valueType()->type == ValueType::Type::POD))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -7057,36 +7057,26 @@ static void addToErrorPath(ValueFlow::Value& value, const ValueFlow::Value& from
|
|||
static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDatabase*/, const Settings* settings)
|
||||
{
|
||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||
if (!Token::Match(tok,"[;{}] %type%"))
|
||||
continue;
|
||||
if (!tok->scope()->isExecutable())
|
||||
continue;
|
||||
const Token *vardecl = tok->next();
|
||||
bool stdtype = false;
|
||||
bool pointer = false;
|
||||
while (Token::Match(vardecl, "%name%|::|*") && vardecl->varId() == 0) {
|
||||
stdtype |= vardecl->isStandardType();
|
||||
pointer |= vardecl->str() == "*";
|
||||
vardecl = vardecl->next();
|
||||
}
|
||||
// if (!stdtype && !pointer)
|
||||
// continue;
|
||||
if (!Token::Match(vardecl, "%var% ;"))
|
||||
if (!Token::Match(tok, "%var% ;|["))
|
||||
continue;
|
||||
const Variable* var = tok->variable();
|
||||
if (!var)
|
||||
continue;
|
||||
if (var->nameToken() != tok || var->isInit())
|
||||
continue;
|
||||
const Variable *var = vardecl->variable();
|
||||
if (!needsInitialization(var, tokenlist->isCPP()))
|
||||
continue;
|
||||
if (var->nameToken() != vardecl || var->isInit())
|
||||
continue;
|
||||
if (!var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow())
|
||||
continue;
|
||||
if (!var->type() && !stdtype && !pointer)
|
||||
continue;
|
||||
|
||||
ValueFlow::Value uninitValue;
|
||||
uninitValue.setKnown();
|
||||
uninitValue.valueType = ValueFlow::Value::ValueType::UNINIT;
|
||||
uninitValue.tokvalue = vardecl;
|
||||
uninitValue.tokvalue = tok;
|
||||
if (var->isArray())
|
||||
uninitValue.indirect = 1;
|
||||
|
||||
bool partial = false;
|
||||
|
||||
|
@ -7104,8 +7094,8 @@ static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDataba
|
|||
partial = true;
|
||||
continue;
|
||||
}
|
||||
MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), vardecl, uninitValue, tokenlist);
|
||||
valueFlowGenericForward(vardecl->next(), vardecl->scope()->bodyEnd, analyzer, settings);
|
||||
MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), tok, uninitValue, tokenlist);
|
||||
valueFlowGenericForward(tok->next(), tok->scope()->bodyEnd, analyzer, settings);
|
||||
|
||||
for (auto&& p : *analyzer.partialReads) {
|
||||
Token* tok2 = p.first;
|
||||
|
@ -7135,7 +7125,7 @@ static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDataba
|
|||
if (partial)
|
||||
continue;
|
||||
|
||||
valueFlowForward(vardecl->next(), vardecl->scope()->bodyEnd, var->nameToken(), {uninitValue}, tokenlist, settings);
|
||||
valueFlowForward(tok->next(), tok->scope()->bodyEnd, var->nameToken(), {uninitValue}, tokenlist, settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ private:
|
|||
TEST_CASE(uninitvar_nonmember); // crash in ycmd test
|
||||
|
||||
TEST_CASE(isVariableUsageDeref); // *p
|
||||
TEST_CASE(isVariableUsageDerefValueflow); // *p
|
||||
|
||||
TEST_CASE(uninitvar_memberaccess); // (&(a))->b <=> a.b
|
||||
|
||||
|
@ -6099,6 +6100,41 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void isVariableUsageDerefValueflow()
|
||||
{
|
||||
// *p
|
||||
valueFlowUninit("void f() {\n"
|
||||
" char a[10];\n"
|
||||
" char c = *a;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: *a\n", errout.str());
|
||||
|
||||
// extracttests.start: extern const int SIZE;
|
||||
valueFlowUninit("void f() {\n"
|
||||
" char a[SIZE+10];\n"
|
||||
" char c = *a;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: *a\n", errout.str());
|
||||
|
||||
valueFlowUninit("void f() {\n"
|
||||
" char a[10];\n"
|
||||
" *a += 10;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: *a\n", errout.str());
|
||||
|
||||
valueFlowUninit("void f() {\n"
|
||||
" int a[10][10];\n"
|
||||
" dostuff(*a);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
valueFlowUninit("void f() {\n"
|
||||
" void (*fp[1]) (void) = {function1};\n"
|
||||
" (*fp[0])();\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void uninitvar_memberaccess() {
|
||||
valueFlowUninit("struct foo{char *bar;};\n"
|
||||
"void f(unsigned long long *p) {\n"
|
||||
|
|
Loading…
Reference in New Issue