Fix issue 10102: False positive: knownConditionTrueFalse in for loop (#3038)
This commit is contained in:
parent
f018163551
commit
a3617fe573
|
@ -1306,93 +1306,6 @@ static void valueFlowSameExpressions(TokenList *tokenlist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
|
||||||
{
|
|
||||||
const bool cpp = symboldatabase->isCPP();
|
|
||||||
using Condition = std::pair<const Token*, const Scope*>;
|
|
||||||
for (const Scope * scope : symboldatabase->functionScopes) {
|
|
||||||
bool skipFunction = false;
|
|
||||||
std::vector<Condition> conds;
|
|
||||||
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
|
||||||
if (tok->isIncompleteVar()) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailoutIncompleteVar(tokenlist, errorLogger, tok, "Skipping function due to incomplete variable " + tok->str());
|
|
||||||
skipFunction = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!Token::simpleMatch(tok, "if ("))
|
|
||||||
continue;
|
|
||||||
// Skip known values
|
|
||||||
if (tok->next()->hasKnownValue())
|
|
||||||
continue;
|
|
||||||
const Token * condTok = tok->next();
|
|
||||||
if (!Token::simpleMatch(condTok->link(), ") {"))
|
|
||||||
continue;
|
|
||||||
const Token * blockTok = condTok->link()->tokAt(1);
|
|
||||||
// Check if the block terminates early
|
|
||||||
if (!isEscapeScope(blockTok, tokenlist))
|
|
||||||
continue;
|
|
||||||
// Check if any variables are modified in scope
|
|
||||||
if (isExpressionChanged(condTok->astOperand2(), blockTok->link(), scope->bodyEnd, settings, cpp))
|
|
||||||
continue;
|
|
||||||
// TODO: Handle multiple conditions
|
|
||||||
if (Token::Match(condTok->astOperand2(), "%oror%|%or%|&|&&"))
|
|
||||||
continue;
|
|
||||||
const Scope * condScope = nullptr;
|
|
||||||
for (const Scope * parent = condTok->scope(); parent; parent = parent->nestedIn) {
|
|
||||||
if (parent->type == Scope::eIf ||
|
|
||||||
parent->type == Scope::eWhile ||
|
|
||||||
parent->type == Scope::eSwitch) {
|
|
||||||
condScope = parent;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conds.emplace_back(condTok->astOperand2(), condScope);
|
|
||||||
}
|
|
||||||
if (skipFunction)
|
|
||||||
break;
|
|
||||||
for (Condition cond:conds) {
|
|
||||||
if (!cond.first)
|
|
||||||
continue;
|
|
||||||
Token *const startToken = cond.first->findExpressionStartEndTokens().second->next();
|
|
||||||
for (Token* tok = startToken; tok != scope->bodyEnd; tok = tok->next()) {
|
|
||||||
if (!Token::Match(tok, "%comp%"))
|
|
||||||
continue;
|
|
||||||
// Skip known values
|
|
||||||
if (tok->hasKnownValue())
|
|
||||||
continue;
|
|
||||||
if (cond.second) {
|
|
||||||
bool bail = true;
|
|
||||||
for (const Scope * parent = tok->scope()->nestedIn; parent; parent = parent->nestedIn) {
|
|
||||||
if (parent == cond.second) {
|
|
||||||
bail = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bail)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ErrorPath errorPath;
|
|
||||||
if (isOppositeCond(true, cpp, tok, cond.first, settings->library, true, true, &errorPath)) {
|
|
||||||
ValueFlow::Value val(1);
|
|
||||||
val.setKnown();
|
|
||||||
val.condition = cond.first;
|
|
||||||
val.errorPath = errorPath;
|
|
||||||
val.errorPath.emplace_back(cond.first, "Assuming condition '" + cond.first->expressionString() + "' is false");
|
|
||||||
setTokenValue(tok, val, tokenlist->getSettings());
|
|
||||||
} else if (isSameExpression(cpp, true, tok, cond.first, settings->library, true, true, &errorPath)) {
|
|
||||||
ValueFlow::Value val(0);
|
|
||||||
val.setKnown();
|
|
||||||
val.condition = cond.first;
|
|
||||||
val.errorPath = errorPath;
|
|
||||||
val.errorPath.emplace_back(cond.first, "Assuming condition '" + cond.first->expressionString() + "' is false");
|
|
||||||
setTokenValue(tok, val, tokenlist->getSettings());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue)
|
static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue)
|
||||||
{
|
{
|
||||||
if (expr->hasKnownIntValue()) {
|
if (expr->hasKnownIntValue()) {
|
||||||
|
@ -2392,6 +2305,20 @@ struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OppositeExpressionAnalyzer : ExpressionAnalyzer {
|
||||||
|
bool isNot;
|
||||||
|
|
||||||
|
OppositeExpressionAnalyzer() : ExpressionAnalyzer(), isNot(false) {}
|
||||||
|
|
||||||
|
OppositeExpressionAnalyzer(bool pIsNot, const Token* e, const ValueFlow::Value& val, const TokenList* t)
|
||||||
|
: ExpressionAnalyzer(e, val, t), isNot(pIsNot)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual bool match(const Token* tok) const OVERRIDE {
|
||||||
|
return isOppositeCond(isNot, isCPP(), expr, tok, getSettings()->library, true, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static Analyzer::Action valueFlowForwardExpression(Token* startToken,
|
static Analyzer::Action valueFlowForwardExpression(Token* startToken,
|
||||||
const Token* endToken,
|
const Token* endToken,
|
||||||
const Token* exprTok,
|
const Token* exprTok,
|
||||||
|
@ -3807,6 +3734,58 @@ static void valueFlowAfterMove(TokenList *tokenlist, SymbolDatabase* symboldatab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const Token* findIncompleteVar(const Token* start, const Token* end)
|
||||||
|
{
|
||||||
|
for (const Token* tok = start; tok != end; tok = tok->next()) {
|
||||||
|
if (tok->isIncompleteVar())
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
|
{
|
||||||
|
for (const Scope * scope : symboldatabase->functionScopes) {
|
||||||
|
if (const Token* incompleteTok = findIncompleteVar(scope->bodyStart, scope->bodyEnd)) {
|
||||||
|
if (incompleteTok->isIncompleteVar()) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailoutIncompleteVar(tokenlist, errorLogger, incompleteTok, "Skipping function due to incomplete variable " + incompleteTok->str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
|
if (!Token::simpleMatch(tok, "if ("))
|
||||||
|
continue;
|
||||||
|
// Skip known values
|
||||||
|
if (tok->next()->hasKnownValue())
|
||||||
|
continue;
|
||||||
|
const Token * parenTok = tok->next();
|
||||||
|
if (!Token::simpleMatch(parenTok->link(), ") {"))
|
||||||
|
continue;
|
||||||
|
const Token * blockTok = parenTok->link()->tokAt(1);
|
||||||
|
// Check if the block terminates early
|
||||||
|
if (!isEscapeScope(blockTok, tokenlist))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Token* condTok = parenTok->astOperand2();
|
||||||
|
ValueFlow::Value v1(0);
|
||||||
|
v1.setKnown();
|
||||||
|
v1.condition = condTok;
|
||||||
|
v1.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is true");
|
||||||
|
ExpressionAnalyzer a1(condTok, v1, tokenlist);
|
||||||
|
valueFlowGenericForward(blockTok->link()->next(), scope->bodyEnd, a1, settings);
|
||||||
|
|
||||||
|
ValueFlow::Value v2(1);
|
||||||
|
v2.setKnown();
|
||||||
|
v2.condition = condTok;
|
||||||
|
v2.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is false");
|
||||||
|
OppositeExpressionAnalyzer a2(true, condTok, v2, tokenlist);
|
||||||
|
valueFlowGenericForward(blockTok->link()->next(), scope->bodyEnd, a2, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowForwardAssign(Token * const tok,
|
static void valueFlowForwardAssign(Token * const tok,
|
||||||
const Variable * const var,
|
const Variable * const var,
|
||||||
std::list<ValueFlow::Value> values,
|
std::list<ValueFlow::Value> values,
|
||||||
|
|
|
@ -4188,7 +4188,8 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
" if (i != j) {}\n"
|
" if (i != j) {}\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1);
|
ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1);
|
||||||
|
ASSERT_EQUALS(false, valueOfTok(code, "!=").isKnown());
|
||||||
|
|
||||||
code = "void f(int i, int j, bool a) {\n"
|
code = "void f(int i, int j, bool a) {\n"
|
||||||
" if (i != j) {}\n"
|
" if (i != j) {}\n"
|
||||||
|
@ -4243,6 +4244,20 @@ private:
|
||||||
" if ( this->FileIndex < 0 ) {}\n"
|
" if ( this->FileIndex < 0 ) {}\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(false, valueOfTok(code, "<").intvalue == 1);
|
ASSERT_EQUALS(false, valueOfTok(code, "<").intvalue == 1);
|
||||||
|
|
||||||
|
code = "int f(int p) {\n"
|
||||||
|
" int v = 0;\n"
|
||||||
|
" for (int i = 0; i < 1; ++i) {\n"
|
||||||
|
" if (p == 0)\n"
|
||||||
|
" v = 1;\n"
|
||||||
|
" if (v == 1)\n"
|
||||||
|
" break;\n"
|
||||||
|
" }\n"
|
||||||
|
" int x = v;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(false, testValueOfXKnown(code, 10U, 0));
|
||||||
|
ASSERT_EQUALS(false, testValueOfXKnown(code, 10U, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string isPossibleContainerSizeValue(const std::list<ValueFlow::Value> &values, MathLib::bigint i) {
|
static std::string isPossibleContainerSizeValue(const std::list<ValueFlow::Value> &values, MathLib::bigint i) {
|
||||||
|
|
Loading…
Reference in New Issue