Fix 12031: False positive: uninitialized variable (#5637)

This commit is contained in:
Paul Fultz II 2023-12-10 12:42:35 -06:00 committed by GitHub
parent 233e27b579
commit 243fa66bd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 370 additions and 107 deletions

View File

@ -107,31 +107,17 @@ static int getArgumentPos(const Token* ftok, const Token* tokToFind){
return findArgumentPos(startTok, tokToFind); return findArgumentPos(startTok, tokToFind);
} }
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
static void astFlattenRecursive(T* tok, std::vector<T*>& result, const char* op, nonneg int depth = 0)
{
++depth;
if (!tok || depth >= 100)
return;
if (tok->str() == op) {
astFlattenRecursive(tok->astOperand1(), result, op, depth);
astFlattenRecursive(tok->astOperand2(), result, op, depth);
} else {
result.push_back(tok);
}
}
std::vector<const Token*> astFlatten(const Token* tok, const char* op) std::vector<const Token*> astFlatten(const Token* tok, const char* op)
{ {
std::vector<const Token*> result; std::vector<const Token*> result;
astFlattenRecursive(tok, result, op); astFlattenCopy(tok, op, std::back_inserter(result));
return result; return result;
} }
std::vector<Token*> astFlatten(Token* tok, const char* op) std::vector<Token*> astFlatten(Token* tok, const char* op)
{ {
std::vector<Token*> result; std::vector<Token*> result;
astFlattenRecursive(tok, result, op); astFlattenCopy(tok, op, std::back_inserter(result));
return result; return result;
} }
@ -163,6 +149,15 @@ bool astHasVar(const Token * tok, nonneg int varid)
return astHasVar(tok->astOperand1(), varid) || astHasVar(tok->astOperand2(), varid); return astHasVar(tok->astOperand1(), varid) || astHasVar(tok->astOperand2(), varid);
} }
bool astHasExpr(const Token* tok, nonneg int exprid)
{
if (!tok)
return false;
if (tok->exprId() == exprid)
return true;
return astHasExpr(tok->astOperand1(), exprid) || astHasExpr(tok->astOperand2(), exprid);
}
static bool astIsCharWithSign(const Token *tok, ValueType::Sign sign) static bool astIsCharWithSign(const Token *tok, ValueType::Sign sign)
{ {
if (!tok) if (!tok)

View File

@ -116,6 +116,21 @@ const Token* findExpression(const nonneg int exprid,
const std::function<bool(const Token*)>& pred); const std::function<bool(const Token*)>& pred);
const Token* findExpression(const Token* start, const nonneg int exprid); const Token* findExpression(const Token* start, const nonneg int exprid);
template<class T, class OuputIterator, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
void astFlattenCopy(T* tok, const char* op, OuputIterator out, nonneg int depth = 100)
{
--depth;
if (!tok || depth < 0)
return;
if (tok->str() == op) {
astFlattenCopy(tok->astOperand1(), op, out, depth);
astFlattenCopy(tok->astOperand2(), op, out, depth);
} else {
*out = tok;
++out;
}
}
std::vector<const Token*> astFlatten(const Token* tok, const char* op); std::vector<const Token*> astFlatten(const Token* tok, const char* op);
std::vector<Token*> astFlatten(Token* tok, const char* op); std::vector<Token*> astFlatten(Token* tok, const char* op);
@ -123,6 +138,7 @@ nonneg int astCount(const Token* tok, const char* op, int depth = 100);
bool astHasToken(const Token* root, const Token * tok); bool astHasToken(const Token* root, const Token * tok);
bool astHasExpr(const Token* tok, nonneg int exprid);
bool astHasVar(const Token * tok, nonneg int varid); bool astHasVar(const Token * tok, nonneg int varid);
bool astIsPrimitive(const Token* tok); bool astIsPrimitive(const Token* tok);

View File

@ -40,14 +40,6 @@
#include <vector> #include <vector>
namespace { namespace {
struct OnExit {
std::function<void()> f;
~OnExit() {
f();
}
};
struct ForwardTraversal { struct ForwardTraversal {
enum class Progress { Continue, Break, Skip }; enum class Progress { Continue, Break, Skip };
enum class Terminate { None, Bail, Inconclusive }; enum class Terminate { None, Bail, Inconclusive };

View File

@ -40,6 +40,10 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <iostream>
ExprIdToken::ExprIdToken(const Token* tok) : tok(tok), exprid(tok ? tok->exprId() : 0) {}
nonneg int ExprIdToken::getExpressionId() const { nonneg int ExprIdToken::getExpressionId() const {
return tok ? tok->exprId() : exprid; return tok ? tok->exprId() : exprid;
} }
@ -193,24 +197,12 @@ void ProgramMemory::insert(const ProgramMemory &pm)
mValues.insert(p); mValues.insert(p);
} }
static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings = nullptr); static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings);
static bool evaluateCondition(const std::string& op, static bool evaluateCondition(MathLib::bigint r, const Token* condition, ProgramMemory& pm, const Settings* settings)
MathLib::bigint r,
const Token* condition,
ProgramMemory& pm,
const Settings* settings)
{ {
if (!condition) if (!condition)
return false; return false;
if (condition->str() == op) {
if (evaluateCondition(op, r, condition->astOperand1(), pm, settings) ||
evaluateCondition(op, r, condition->astOperand2(), pm, settings)) {
return true;
}
if (!pm.hasValue(condition->exprId()))
return false;
}
MathLib::bigint result = 0; MathLib::bigint result = 0;
bool error = false; bool error = false;
execute(condition, pm, &result, &error, settings); execute(condition, pm, &result, &error, settings);
@ -219,12 +211,12 @@ static bool evaluateCondition(const std::string& op,
bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings) bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings)
{ {
return evaluateCondition("&&", 0, condition, pm, settings); return evaluateCondition(0, condition, pm, settings);
} }
bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings) bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings)
{ {
return evaluateCondition("||", 1, condition, pm, settings); return evaluateCondition(1, condition, pm, settings);
} }
static bool frontIs(const std::vector<MathLib::bigint>& v, bool i) static bool frontIs(const std::vector<MathLib::bigint>& v, bool i)
@ -238,6 +230,8 @@ static bool frontIs(const std::vector<MathLib::bigint>& v, bool i)
static bool isTrue(const ValueFlow::Value& v) static bool isTrue(const ValueFlow::Value& v)
{ {
if (v.isUninitValue())
return false;
if (v.isImpossible()) if (v.isImpossible())
return v.intvalue == 0; return v.intvalue == 0;
return v.intvalue != 0; return v.intvalue != 0;
@ -245,11 +239,20 @@ static bool isTrue(const ValueFlow::Value& v)
static bool isFalse(const ValueFlow::Value& v) static bool isFalse(const ValueFlow::Value& v)
{ {
if (v.isUninitValue())
return false;
if (v.isImpossible()) if (v.isImpossible())
return false; return false;
return v.intvalue == 0; return v.intvalue == 0;
} }
static bool isTrueOrFalse(const ValueFlow::Value& v, bool b)
{
if (b)
return isTrue(v);
return isFalse(v);
}
// If the scope is a non-range for loop // If the scope is a non-range for loop
static bool isBasicForLoop(const Token* tok) static bool isBasicForLoop(const Token* tok)
{ {
@ -276,7 +279,7 @@ void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Toke
return {t->values().front().intvalue}; return {t->values().front().intvalue};
MathLib::bigint result = 0; MathLib::bigint result = 0;
bool error = false; bool error = false;
execute(t, pm, &result, &error); execute(t, pm, &result, &error, settings);
if (!error) if (!error)
return {result}; return {result};
return std::vector<MathLib::bigint>{}; return std::vector<MathLib::bigint>{};
@ -322,6 +325,7 @@ void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Toke
if (endTok && findExpressionChanged(tok, tok->next(), endTok, settings, true)) if (endTok && findExpressionChanged(tok, tok->next(), endTok, settings, true))
return; return;
pm.setIntValue(tok, 0, then); pm.setIntValue(tok, 0, then);
assert(settings);
const Token* containerTok = settings->library.getContainerFromYield(tok, Library::Container::Yield::EMPTY); const Token* containerTok = settings->library.getContainerFromYield(tok, Library::Container::Yield::EMPTY);
if (containerTok) if (containerTok)
pm.setContainerSizeValue(containerTok, 0, then); pm.setContainerSizeValue(containerTok, 0, then);
@ -342,7 +346,7 @@ static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scop
return; return;
MathLib::bigint result = 0; MathLib::bigint result = 0;
bool error = false; bool error = false;
execute(condTok, pm, &result, &error); execute(condTok, pm, &result, &error, settings);
if (error) if (error)
programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse); programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse);
} }
@ -372,7 +376,7 @@ static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok
if (!setvar) { if (!setvar) {
if (!pm.hasValue(vartok->exprId())) { if (!pm.hasValue(vartok->exprId())) {
const Token* valuetok = tok2->astOperand2(); const Token* valuetok = tok2->astOperand2();
pm.setValue(vartok, execute(valuetok, pm)); pm.setValue(vartok, execute(valuetok, pm, settings));
} }
} }
} else if (tok2->exprId() > 0 && Token::Match(tok2, ".|(|[|*|%var%") && !pm.hasValue(tok2->exprId()) && } else if (tok2->exprId() > 0 && Token::Match(tok2, ".|(|[|*|%var%") && !pm.hasValue(tok2->exprId()) &&
@ -384,7 +388,7 @@ static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok
if (indentlevel <= 0) { if (indentlevel <= 0) {
const Token* cond = getCondTokFromEnd(tok2->link()); const Token* cond = getCondTokFromEnd(tok2->link());
// Keep progressing with anonymous/do scopes and always true branches // Keep progressing with anonymous/do scopes and always true branches
if (!Token::Match(tok2->previous(), "do|; {") && !conditionIsTrue(cond, state) && if (!Token::Match(tok2->previous(), "do|; {") && !conditionIsTrue(cond, state, settings) &&
(cond || !isBasicForLoop(tok2))) (cond || !isBasicForLoop(tok2)))
break; break;
} else } else
@ -396,12 +400,12 @@ static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok
const Token *cond = getCondTokFromEnd(tok2); const Token *cond = getCondTokFromEnd(tok2);
const bool inElse = Token::simpleMatch(tok2->link()->previous(), "else {"); const bool inElse = Token::simpleMatch(tok2->link()->previous(), "else {");
if (cond) { if (cond) {
if (conditionIsFalse(cond, state)) { if (conditionIsFalse(cond, state, settings)) {
if (inElse) { if (inElse) {
++indentlevel; ++indentlevel;
continue; continue;
} }
} else if (conditionIsTrue(cond, state)) { } else if (conditionIsTrue(cond, state, settings)) {
if (inElse) if (inElse)
tok2 = tok2->link()->tokAt(-2); tok2 = tok2->link()->tokAt(-2);
++indentlevel; ++indentlevel;
@ -494,9 +498,9 @@ void ProgramMemoryState::removeModifiedVars(const Token* tok)
{ {
ProgramMemory pm = state; ProgramMemory pm = state;
auto eval = [&](const Token* cond) -> std::vector<MathLib::bigint> { auto eval = [&](const Token* cond) -> std::vector<MathLib::bigint> {
if (conditionIsTrue(cond, pm)) if (conditionIsTrue(cond, pm, settings))
return {1}; return {1};
if (conditionIsFalse(cond, pm)) if (conditionIsFalse(cond, pm, settings))
return {0}; return {0};
return {}; return {};
}; };
@ -1200,20 +1204,183 @@ static BuiltinLibraryFunction getBuiltinLibraryFunction(const std::string& name)
return nullptr; return nullptr;
return it->second; return it->second;
} }
static bool TokenExprIdCompare(const Token* tok1, const Token* tok2) {
return tok1->exprId() < tok2->exprId();
}
static bool TokenExprIdEqual(const Token* tok1, const Token* tok2) {
return tok1->exprId() == tok2->exprId();
}
static std::vector<const Token*> setDifference(const std::vector<const Token*>& v1, const std::vector<const Token*>& v2)
{
std::vector<const Token*> result;
std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result), &TokenExprIdCompare);
return result;
}
static bool evalSameCondition(const ProgramMemory& state,
const Token* storedValue,
const Token* cond,
const Settings* settings)
{
assert(!conditionIsTrue(cond, state, settings));
ProgramMemory pm = state;
programMemoryParseCondition(pm, storedValue, nullptr, settings, true);
if (pm == state)
return false;
return conditionIsTrue(cond, pm, settings);
}
static void pruneConditions(std::vector<const Token*>& conds,
bool b,
const std::unordered_map<nonneg int, ValueFlow::Value>& state)
{
conds.erase(std::remove_if(conds.begin(),
conds.end(),
[&](const Token* cond) {
if (cond->exprId() == 0)
return false;
auto it = state.find(cond->exprId());
if (it == state.end())
return false;
const ValueFlow::Value& v = it->second;
return isTrueOrFalse(v, !b);
}),
conds.end());
}
namespace { namespace {
struct Executor { struct Executor {
ProgramMemory* pm = nullptr; ProgramMemory* pm = nullptr;
const Settings* settings = nullptr; const Settings* settings = nullptr;
int fdepth = 4; int fdepth = 4;
int depth = 10;
explicit Executor(ProgramMemory* pm = nullptr, const Settings* settings = nullptr) : pm(pm), settings(settings) {} explicit Executor(ProgramMemory* pm = nullptr, const Settings* settings = nullptr) : pm(pm), settings(settings) {}
static ValueFlow::Value unknown() {
return ValueFlow::Value::unknown();
}
std::unordered_map<nonneg int, ValueFlow::Value> executeAll(const std::vector<const Token*>& toks,
const bool* b = nullptr) const
{
std::unordered_map<nonneg int, ValueFlow::Value> result;
auto state = *this;
for (const Token* tok : toks) {
ValueFlow::Value r = state.execute(tok);
if (r.isUninitValue())
continue;
result.insert(std::make_pair(tok->exprId(), r));
// Short-circuit evaluation
if (b && isTrueOrFalse(r, *b))
break;
}
return result;
}
static std::vector<const Token*> flattenConditions(const Token* tok)
{
return astFlatten(tok, tok->str().c_str());
}
static bool sortConditions(std::vector<const Token*>& conditions)
{
if (std::any_of(conditions.begin(), conditions.end(), [](const Token* child) {
return Token::Match(child, "&&|%oror%");
}))
return false;
std::sort(conditions.begin(), conditions.end(), &TokenExprIdCompare);
return !conditions.empty() && conditions.front()->exprId() != 0;
}
ValueFlow::Value executeMultiCondition(bool b, const Token* expr)
{
if (pm->hasValue(expr->exprId())) {
const ValueFlow::Value& v = pm->at(expr->exprId());
if (v.isIntValue())
return v;
}
// Evaluate recursively if there are no exprids
if ((expr->astOperand1() && expr->astOperand1()->exprId() == 0) ||
(expr->astOperand2() && expr->astOperand2()->exprId() == 0)) {
ValueFlow::Value lhs = execute(expr->astOperand1());
if (isTrueOrFalse(lhs, b))
return lhs;
ValueFlow::Value rhs = execute(expr->astOperand2());
if (isTrueOrFalse(rhs, b))
return rhs;
if (isTrueOrFalse(lhs, !b) && isTrueOrFalse(rhs, !b))
return lhs;
return unknown();
}
nonneg int n = astCount(expr, expr->str().c_str());
if (n > 50)
return unknown();
std::vector<const Token*> conditions1 = flattenConditions(expr);
if (conditions1.empty())
return unknown();
std::unordered_map<nonneg int, ValueFlow::Value> condValues = executeAll(conditions1, &b);
bool allNegated = true;
ValueFlow::Value negatedValue = unknown();
for (const auto& p : condValues) {
const ValueFlow::Value& v = p.second;
if (isTrueOrFalse(v, b))
return v;
allNegated &= isTrueOrFalse(v, !b);
if (allNegated && negatedValue.isUninitValue())
negatedValue = v;
}
if (condValues.size() == conditions1.size() && allNegated)
return negatedValue;
if (n > 4)
return unknown();
if (!sortConditions(conditions1))
return unknown();
for (const auto& p : *pm) {
const Token* tok = p.first.tok;
const ValueFlow::Value& value = p.second;
if (tok->str() == expr->str() && !astHasExpr(tok, expr->exprId())) {
// TODO: Handle when it is greater
if (n != astCount(tok, expr->str().c_str()))
continue;
std::vector<const Token*> conditions2 = flattenConditions(tok);
if (!sortConditions(conditions2))
return unknown();
if (conditions1.size() == conditions2.size() &&
std::equal(conditions1.begin(), conditions1.end(), conditions2.begin(), &TokenExprIdEqual))
return value;
std::vector<const Token*> diffConditions1 = setDifference(conditions1, conditions2);
std::vector<const Token*> diffConditions2 = setDifference(conditions2, conditions1);
pruneConditions(diffConditions1, b, condValues);
pruneConditions(diffConditions2, b, executeAll(diffConditions2));
if (diffConditions1.size() != diffConditions2.size())
continue;
if (diffConditions1.size() == conditions1.size())
continue;
for (const Token* cond1 : diffConditions1) {
auto it = std::find_if(diffConditions2.begin(), diffConditions2.end(), [&](const Token* cond2) {
return evalSameCondition(*pm, cond2, cond1, settings);
});
if (it == diffConditions2.end())
break;
diffConditions2.erase(it);
}
if (diffConditions2.empty())
return value;
}
}
return unknown();
}
ValueFlow::Value executeImpl(const Token* expr) ValueFlow::Value executeImpl(const Token* expr)
{ {
const ValueFlow::Value* value = nullptr; const ValueFlow::Value* value = nullptr;
if (!expr) if (!expr)
return ValueFlow::Value::unknown(); return unknown();
if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",") if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",")
return expr->values().front(); return expr->values().front();
if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) || if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) ||
@ -1225,10 +1392,10 @@ namespace {
} }
if (expr->isNumber()) { if (expr->isNumber()) {
if (MathLib::isFloat(expr->str())) if (MathLib::isFloat(expr->str()))
return ValueFlow::Value::unknown(); return unknown();
MathLib::bigint i = MathLib::toBigNumber(expr->str()); MathLib::bigint i = MathLib::toBigNumber(expr->str());
if (i < 0 && astIsUnsigned(expr)) if (i < 0 && astIsUnsigned(expr))
return ValueFlow::Value::unknown(); return unknown();
return ValueFlow::Value{i}; return ValueFlow::Value{i};
} }
if (expr->isBoolean()) if (expr->isBoolean())
@ -1239,14 +1406,14 @@ namespace {
if (yield == Library::Container::Yield::SIZE) { if (yield == Library::Container::Yield::SIZE) {
ValueFlow::Value v = execute(containerTok); ValueFlow::Value v = execute(containerTok);
if (!v.isContainerSizeValue()) if (!v.isContainerSizeValue())
return ValueFlow::Value::unknown(); return unknown();
v.valueType = ValueFlow::Value::ValueType::INT; v.valueType = ValueFlow::Value::ValueType::INT;
return v; return v;
} }
if (yield == Library::Container::Yield::EMPTY) { if (yield == Library::Container::Yield::EMPTY) {
ValueFlow::Value v = execute(containerTok); ValueFlow::Value v = execute(containerTok);
if (!v.isContainerSizeValue()) if (!v.isContainerSizeValue())
return ValueFlow::Value::unknown(); return unknown();
if (v.isImpossible() && v.intvalue == 0) if (v.isImpossible() && v.intvalue == 0)
return ValueFlow::Value{0}; return ValueFlow::Value{0};
if (!v.isImpossible()) if (!v.isImpossible())
@ -1256,10 +1423,10 @@ namespace {
expr->astOperand1()->exprId() > 0) { expr->astOperand1()->exprId() > 0) {
ValueFlow::Value rhs = execute(expr->astOperand2()); ValueFlow::Value rhs = execute(expr->astOperand2());
if (rhs.isUninitValue()) if (rhs.isUninitValue())
return ValueFlow::Value::unknown(); return unknown();
if (expr->str() != "=") { if (expr->str() != "=") {
if (!pm->hasValue(expr->astOperand1()->exprId())) if (!pm->hasValue(expr->astOperand1()->exprId()))
return ValueFlow::Value::unknown(); return unknown();
ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId());
rhs = evaluate(removeAssign(expr->str()), lhs, rhs); rhs = evaluate(removeAssign(expr->str()), lhs, rhs);
if (lhs.isIntValue()) if (lhs.isIntValue())
@ -1268,29 +1435,15 @@ namespace {
ValueFlow::Value::visitValue(rhs, ValueFlow::Value::visitValue(rhs,
std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1)); std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1));
else else
return ValueFlow::Value::unknown(); return unknown();
return lhs; return lhs;
} }
pm->setValue(expr->astOperand1(), rhs); pm->setValue(expr->astOperand1(), rhs);
return rhs; return rhs;
} else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) {
ValueFlow::Value lhs = execute(expr->astOperand1()); return executeMultiCondition(false, expr);
if (!lhs.isIntValue())
return ValueFlow::Value::unknown();
if (isFalse(lhs))
return lhs;
if (isTrue(lhs))
return execute(expr->astOperand2());
return ValueFlow::Value::unknown();
} else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) { } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) {
ValueFlow::Value lhs = execute(expr->astOperand1()); return executeMultiCondition(true, expr);
if (!lhs.isIntValue() || lhs.isImpossible())
return ValueFlow::Value::unknown();
if (isTrue(lhs))
return lhs;
if (isFalse(lhs))
return execute(expr->astOperand2());
return ValueFlow::Value::unknown();
} else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
execute(expr->astOperand1()); execute(expr->astOperand1());
return execute(expr->astOperand2()); return execute(expr->astOperand2());
@ -1299,10 +1452,10 @@ namespace {
return ValueFlow::Value::unknown(); return ValueFlow::Value::unknown();
ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId());
if (!lhs.isIntValue()) if (!lhs.isIntValue())
return ValueFlow::Value::unknown(); return unknown();
// overflow // overflow
if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1())) if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1()))
return ValueFlow::Value::unknown(); return unknown();
if (expr->str() == "++") if (expr->str() == "++")
lhs.intvalue++; lhs.intvalue++;
@ -1316,17 +1469,17 @@ namespace {
expr->astOperand1()->values().cend(), expr->astOperand1()->values().cend(),
std::mem_fn(&ValueFlow::Value::isTokValue)); std::mem_fn(&ValueFlow::Value::isTokValue));
if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) { if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) {
return ValueFlow::Value::unknown(); return unknown();
} }
tokvalue = tokvalue_it->tokvalue; tokvalue = tokvalue_it->tokvalue;
} }
if (!tokvalue || !tokvalue->isLiteral()) { if (!tokvalue || !tokvalue->isLiteral()) {
return ValueFlow::Value::unknown(); return unknown();
} }
const std::string strValue = tokvalue->strValue(); const std::string strValue = tokvalue->strValue();
ValueFlow::Value rhs = execute(expr->astOperand2()); ValueFlow::Value rhs = execute(expr->astOperand2());
if (!rhs.isIntValue()) if (!rhs.isIntValue())
return ValueFlow::Value::unknown(); return unknown();
const MathLib::bigint index = rhs.intvalue; const MathLib::bigint index = rhs.intvalue;
if (index >= 0 && index < strValue.size()) if (index >= 0 && index < strValue.size())
return ValueFlow::Value{strValue[index]}; return ValueFlow::Value{strValue[index]};
@ -1335,7 +1488,7 @@ namespace {
} else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) {
ValueFlow::Value lhs = execute(expr->astOperand1()); ValueFlow::Value lhs = execute(expr->astOperand1());
ValueFlow::Value rhs = execute(expr->astOperand2()); ValueFlow::Value rhs = execute(expr->astOperand2());
ValueFlow::Value r = ValueFlow::Value::unknown(); ValueFlow::Value r = unknown();
if (!lhs.isUninitValue() && !rhs.isUninitValue()) if (!lhs.isUninitValue() && !rhs.isUninitValue())
r = evaluate(expr->str(), lhs, rhs); r = evaluate(expr->str(), lhs, rhs);
if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) {
@ -1351,7 +1504,7 @@ namespace {
if (!result.empty() && result.front().isKnown()) if (!result.empty() && result.front().isKnown())
return result.front(); return result.front();
} }
return ValueFlow::Value::unknown(); return unknown();
} }
return r; return r;
} }
@ -1359,14 +1512,14 @@ namespace {
else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) { else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) {
ValueFlow::Value lhs = execute(expr->astOperand1()); ValueFlow::Value lhs = execute(expr->astOperand1());
if (!lhs.isIntValue()) if (!lhs.isIntValue())
return ValueFlow::Value::unknown(); return unknown();
if (expr->str() == "!") { if (expr->str() == "!") {
if (isTrue(lhs)) { if (isTrue(lhs)) {
lhs.intvalue = 0; lhs.intvalue = 0;
} else if (isFalse(lhs)) { } else if (isFalse(lhs)) {
lhs.intvalue = 1; lhs.intvalue = 1;
} else { } else {
return ValueFlow::Value::unknown(); return unknown();
} }
lhs.setPossible(); lhs.setPossible();
lhs.bound = ValueFlow::Value::Bound::Point; lhs.bound = ValueFlow::Value::Bound::Point;
@ -1377,14 +1530,14 @@ namespace {
} else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) {
ValueFlow::Value cond = execute(expr->astOperand1()); ValueFlow::Value cond = execute(expr->astOperand1());
if (!cond.isIntValue()) if (!cond.isIntValue())
return ValueFlow::Value::unknown(); return unknown();
const Token* child = expr->astOperand2(); const Token* child = expr->astOperand2();
if (isFalse(cond)) if (isFalse(cond))
return execute(child->astOperand2()); return execute(child->astOperand2());
if (isTrue(cond)) if (isTrue(cond))
return execute(child->astOperand1()); return execute(child->astOperand1());
return ValueFlow::Value::unknown(); return unknown();
} else if (expr->str() == "(" && expr->isCast()) { } else if (expr->str() == "(" && expr->isCast()) {
if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link())
return execute(expr->astOperand2()); return execute(expr->astOperand2());
@ -1402,11 +1555,12 @@ namespace {
if (Token::Match(expr->previous(), ">|%name% {|(")) { if (Token::Match(expr->previous(), ">|%name% {|(")) {
const Token* ftok = expr->previous(); const Token* ftok = expr->previous();
const Function* f = ftok->function(); const Function* f = ftok->function();
ValueFlow::Value result = ValueFlow::Value::unknown(); ValueFlow::Value result = unknown();
if (settings && expr->str() == "(") { if (settings && expr->str() == "(") {
std::vector<const Token*> tokArgs = getArguments(expr); std::vector<const Token*> tokArgs = getArguments(expr);
std::vector<ValueFlow::Value> args(tokArgs.size()); std::vector<ValueFlow::Value> args(tokArgs.size());
std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) { std::transform(
tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) {
return execute(tok); return execute(tok);
}); });
if (f) { if (f) {
@ -1415,7 +1569,7 @@ namespace {
for (std::size_t i = 0; i < args.size(); ++i) { for (std::size_t i = 0; i < args.size(); ++i) {
const Variable* const arg = f->getArgumentVar(i); const Variable* const arg = f->getArgumentVar(i);
if (!arg) if (!arg)
return ValueFlow::Value::unknown(); return unknown();
functionState.setValue(arg->nameToken(), args[i]); functionState.setValue(arg->nameToken(), args[i]);
} }
Executor ex = *this; Executor ex = *this;
@ -1449,10 +1603,10 @@ namespace {
ValueFlow::Value& v = pm->at(child->exprId()); ValueFlow::Value& v = pm->at(child->exprId());
if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) {
if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings)) if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings))
v = ValueFlow::Value::unknown(); v = unknown();
} else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) {
if (isVariableChanged(child, v.indirect, settings, true)) if (isVariableChanged(child, v.indirect, settings, true))
v = ValueFlow::Value::unknown(); v = unknown();
} }
} }
return ChildrenToVisit::op1_and_op2; return ChildrenToVisit::op1_and_op2;
@ -1460,7 +1614,7 @@ namespace {
return result; return result;
} }
return ValueFlow::Value::unknown(); return unknown();
} }
static const ValueFlow::Value* getImpossibleValue(const Token* tok) static const ValueFlow::Value* getImpossibleValue(const Token* tok)
{ {
@ -1485,6 +1639,12 @@ namespace {
ValueFlow::Value execute(const Token* expr) ValueFlow::Value execute(const Token* expr)
{ {
depth--;
OnExit onExit{[&] {
depth++;
}};
if (depth < 0)
return unknown();
ValueFlow::Value v = executeImpl(expr); ValueFlow::Value v = executeImpl(expr);
if (!v.isUninitValue()) if (!v.isUninitValue())
return v; return v;
@ -1499,31 +1659,30 @@ namespace {
std::vector<ValueFlow::Value> execute(const Scope* scope) std::vector<ValueFlow::Value> execute(const Scope* scope)
{ {
static const std::vector<ValueFlow::Value> unknown = {ValueFlow::Value::unknown()};
if (!scope) if (!scope)
return unknown; return {unknown()};
if (!scope->bodyStart) if (!scope->bodyStart)
return unknown; return {unknown()};
for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) { for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) {
const Token* top = tok->astTop(); const Token* top = tok->astTop();
if (!top) if (!top)
return unknown; return {unknown()};
if (Token::simpleMatch(top, "return") && top->astOperand1()) if (Token::simpleMatch(top, "return") && top->astOperand1())
return {execute(top->astOperand1())}; return {execute(top->astOperand1())};
if (Token::Match(top, "%op%")) { if (Token::Match(top, "%op%")) {
if (execute(top).isUninitValue()) if (execute(top).isUninitValue())
return unknown; return {unknown()};
const Token* next = nextAfterAstRightmostLeaf(top); const Token* next = nextAfterAstRightmostLeaf(top);
if (!next) if (!next)
return unknown; return {unknown()};
tok = next; tok = next;
} else if (Token::simpleMatch(top->previous(), "if (")) { } else if (Token::simpleMatch(top->previous(), "if (")) {
const Token* condTok = top->astOperand2(); const Token* condTok = top->astOperand2();
ValueFlow::Value v = execute(condTok); ValueFlow::Value v = execute(condTok);
if (!v.isIntValue()) if (!v.isIntValue())
return unknown; return {unknown()};
const Token* thenStart = top->link()->next(); const Token* thenStart = top->link()->next();
const Token* next = thenStart->link(); const Token* next = thenStart->link();
const Token* elseStart = nullptr; const Token* elseStart = nullptr;
@ -1538,19 +1697,19 @@ namespace {
if (elseStart) if (elseStart)
result = execute(elseStart->scope()); result = execute(elseStart->scope());
} else { } else {
return unknown; return {unknown()};
} }
if (!result.empty()) if (!result.empty())
return result; return result;
tok = next; tok = next;
} else { } else {
return unknown; return {unknown()};
} }
} }
return {}; return {};
} }
}; };
} } // namespace
static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings) static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings)
{ {

View File

@ -43,7 +43,7 @@ struct ExprIdToken {
ExprIdToken() = default; ExprIdToken() = default;
// cppcheck-suppress noExplicitConstructor // cppcheck-suppress noExplicitConstructor
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
ExprIdToken(const Token* tok) : tok(tok) {} ExprIdToken(const Token* tok);
// TODO: Make this constructor only available from ProgramMemory // TODO: Make this constructor only available from ProgramMemory
// cppcheck-suppress noExplicitConstructor // cppcheck-suppress noExplicitConstructor
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
@ -55,12 +55,42 @@ struct ExprIdToken {
return getExpressionId() == rhs.getExpressionId(); return getExpressionId() == rhs.getExpressionId();
} }
bool operator<(const ExprIdToken& rhs) const {
return getExpressionId() < rhs.getExpressionId();
}
template<class T, class U> template<class T, class U>
friend bool operator!=(const T& lhs, const U& rhs) friend bool operator!=(const T& lhs, const U& rhs)
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
template<class T, class U>
friend bool operator<=(const T& lhs, const U& rhs)
{
return !(lhs > rhs);
}
template<class T, class U>
friend bool operator>(const T& lhs, const U& rhs)
{
return rhs < lhs;
}
template<class T, class U>
friend bool operator>=(const T& lhs, const U& rhs)
{
return !(lhs < rhs);
}
const Token& operator*() const NOEXCEPT {
return *tok;
}
const Token* operator->() const NOEXCEPT {
return tok;
}
struct Hash { struct Hash {
std::size_t operator()(ExprIdToken etok) const; std::size_t operator()(ExprIdToken etok) const;
}; };
@ -119,6 +149,14 @@ struct ProgramMemory {
return mValues.end(); return mValues.end();
} }
friend bool operator==(const ProgramMemory& x, const ProgramMemory& y) {
return x.mValues == y.mValues;
}
friend bool operator!=(const ProgramMemory& x, const ProgramMemory& y) {
return x.mValues != y.mValues;
}
private: private:
Map mValues; Map mValues;
}; };
@ -157,14 +195,14 @@ void execute(const Token* expr,
* \param condition top ast token in condition * \param condition top ast token in condition
* \param pm program memory * \param pm program memory
*/ */
bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings = nullptr); bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings);
/** /**
* Is condition always true when variable has given value? * Is condition always true when variable has given value?
* \param condition top ast token in condition * \param condition top ast token in condition
* \param pm program memory * \param pm program memory
*/ */
bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings = nullptr); bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings);
/** /**
* Get program memory by looking backwards from given token. * Get program memory by looking backwards from given token.

View File

@ -27,6 +27,7 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <functional>
#include <initializer_list> #include <initializer_list>
#include <limits> #include <limits>
#include <stdexcept> #include <stdexcept>
@ -49,6 +50,14 @@ struct SelectMapValues {
} }
}; };
struct OnExit {
std::function<void()> f;
~OnExit() {
f();
}
};
template<class Range, class T> template<class Range, class T>
bool contains(const Range& r, const T& x) bool contains(const Range& r, const T& x)
{ {

View File

@ -7077,8 +7077,8 @@ static void valueFlowForLoopSimplify(Token* const bodyStart,
if (Token::Match(tok2, "%oror%|&&")) { if (Token::Match(tok2, "%oror%|&&")) {
const ProgramMemory programMemory(getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings)); const ProgramMemory programMemory(getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings));
if ((tok2->str() == "&&" && !conditionIsTrue(tok2->astOperand1(), programMemory)) || if ((tok2->str() == "&&" && !conditionIsTrue(tok2->astOperand1(), programMemory, settings)) ||
(tok2->str() == "||" && !conditionIsFalse(tok2->astOperand1(), programMemory))) { (tok2->str() == "||" && !conditionIsFalse(tok2->astOperand1(), programMemory, settings))) {
// Skip second expression.. // Skip second expression..
Token *parent = tok2; Token *parent = tok2;
while (parent && parent->str() == tok2->str()) while (parent && parent->str() == tok2->str())
@ -7091,7 +7091,6 @@ static void valueFlowForLoopSimplify(Token* const bodyStart,
tok2 = tok2->linkAt(1); tok2 = tok2->linkAt(1);
} }
} }
} }
const Token* vartok = expr; const Token* vartok = expr;
const Token* rml = nextAfterAstRightmostLeaf(vartok); const Token* rml = nextAfterAstRightmostLeaf(vartok);
@ -7102,10 +7101,12 @@ static void valueFlowForLoopSimplify(Token* const bodyStart,
if ((tok2->str() == "&&" && if ((tok2->str() == "&&" &&
conditionIsFalse(tok2->astOperand1(), conditionIsFalse(tok2->astOperand1(),
getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings))) || getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings),
settings)) ||
(tok2->str() == "||" && (tok2->str() == "||" &&
conditionIsTrue(tok2->astOperand1(), conditionIsTrue(tok2->astOperand1(),
getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings)))) getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings),
settings)))
break; break;
if (Token::simpleMatch(tok2, ") {")) { if (Token::simpleMatch(tok2, ") {")) {

View File

@ -4532,6 +4532,21 @@ private:
" static_assert(static_cast<int>(E::E1) == 1);\n" " static_assert(static_cast<int>(E::E1) == 1);\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check("struct a {\n"
" bool g();\n"
" int h();\n"
"};\n"
"void f(a c, int d, int e) {\n"
" if (c.g() && c.h()) {}\n"
" else {\n"
" bool u = false;\n"
" if (d && e)\n"
" u = true;\n"
" if (u) {}\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
} }
void alwaysTrueSymbolic() void alwaysTrueSymbolic()

View File

@ -5625,6 +5625,26 @@ private:
values = tokenValues(code, "x <", ValueFlow::Value::ValueType::UNINIT); values = tokenValues(code, "x <", ValueFlow::Value::ValueType::UNINIT);
ASSERT_EQUALS(0, values.size()); ASSERT_EQUALS(0, values.size());
code = "bool do_something(int *p);\n"
"int getY();\n"
"bool bar();\n"
"void foo() {\n"
" bool flag{true};\n"
" int x;\n"
" int y = getY();\n"
" if (flag == true) {\n"
" flag = bar();\n"
" }\n"
" if ((flag == true) && y > 0) {\n"
" flag = do_something(&x);\n"
" }\n"
" for (int i = 0; (flag == true) && i < y; i++) {\n"
" if (x < 0) {}\n"
" }\n"
"}\n";
values = tokenValues(code, "x <", ValueFlow::Value::ValueType::UNINIT);
ASSERT_EQUALS(0, values.size());
code = "void g(bool *result, size_t *buflen) {\n" // #12091 code = "void g(bool *result, size_t *buflen) {\n" // #12091
" if (*result && *buflen >= 5) {}\n" // <- *buflen might not be initialized " if (*result && *buflen >= 5) {}\n" // <- *buflen might not be initialized
"}\n" "}\n"
@ -7237,6 +7257,24 @@ private:
" int& q = (&r)[0];\n" " int& q = (&r)[0];\n"
"}\n"; "}\n";
valueOfTok(code, "&"); valueOfTok(code, "&");
code = "bool a(int *);\n"
"void fn2(int b) {\n"
" if (b) {\n"
" bool c, d, e;\n"
" if (c && d)\n"
" return;\n"
" if (e && a(&b)) {\n"
" }\n"
" }\n"
"}\n";
valueOfTok(code, "e");
code = "void f(int a, int b, int c) {\n"
" if (c && (a || a && b))\n"
" if (a && b) {}\n"
"}\n";
valueOfTok(code, "a");
} }
void valueFlowHang() { void valueFlowHang() {