Handle for loop conditions in afterCondition (#3561)
This commit is contained in:
parent
112363c9d1
commit
a0d3c2c719
|
@ -595,6 +595,38 @@ static T* getCondTokFromEndImpl(T* endBlock)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
|
||||||
|
static T* getInitTokImpl(T* tok)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return nullptr;
|
||||||
|
if (Token::Match(tok, "%name% ("))
|
||||||
|
return getInitTokImpl(tok->next());
|
||||||
|
if (tok->str() != "(")
|
||||||
|
return nullptr;
|
||||||
|
if (!Token::simpleMatch(tok->astOperand2(), ";"))
|
||||||
|
return nullptr;
|
||||||
|
if (Token::simpleMatch(tok->astOperand2()->astOperand1(), ";"))
|
||||||
|
return nullptr;
|
||||||
|
return tok->astOperand2()->astOperand1();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
|
||||||
|
static T* getStepTokImpl(T* tok)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return nullptr;
|
||||||
|
if (Token::Match(tok, "%name% ("))
|
||||||
|
return getStepTokImpl(tok->next());
|
||||||
|
if (tok->str() != "(")
|
||||||
|
return nullptr;
|
||||||
|
if (!Token::simpleMatch(tok->astOperand2(), ";"))
|
||||||
|
return nullptr;
|
||||||
|
if (!Token::simpleMatch(tok->astOperand2()->astOperand2(), ";"))
|
||||||
|
return nullptr;
|
||||||
|
return tok->astOperand2()->astOperand2()->astOperand2();
|
||||||
|
}
|
||||||
|
|
||||||
Token* getCondTok(Token* tok)
|
Token* getCondTok(Token* tok)
|
||||||
{
|
{
|
||||||
return getCondTokImpl(tok);
|
return getCondTokImpl(tok);
|
||||||
|
@ -613,6 +645,20 @@ const Token* getCondTokFromEnd(const Token* endBlock)
|
||||||
return getCondTokFromEndImpl(endBlock);
|
return getCondTokFromEndImpl(endBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Token* getInitTok(Token* tok) {
|
||||||
|
return getInitTokImpl(tok);
|
||||||
|
}
|
||||||
|
const Token* getInitTok(const Token* tok) {
|
||||||
|
return getInitTokImpl(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token* getStepTok(Token* tok) {
|
||||||
|
return getStepTokImpl(tok);
|
||||||
|
}
|
||||||
|
const Token* getStepTok(const Token* tok) {
|
||||||
|
return getStepTokImpl(tok);
|
||||||
|
}
|
||||||
|
|
||||||
const Token *findNextTokenFromBreak(const Token *breakToken)
|
const Token *findNextTokenFromBreak(const Token *breakToken)
|
||||||
{
|
{
|
||||||
const Scope *scope = breakToken->scope();
|
const Scope *scope = breakToken->scope();
|
||||||
|
|
|
@ -126,6 +126,12 @@ bool astIsRHS(const Token* tok);
|
||||||
Token* getCondTok(Token* tok);
|
Token* getCondTok(Token* tok);
|
||||||
const Token* getCondTok(const Token* tok);
|
const Token* getCondTok(const Token* tok);
|
||||||
|
|
||||||
|
Token* getInitTok(Token* tok);
|
||||||
|
const Token* getInitTok(const Token* tok);
|
||||||
|
|
||||||
|
Token* getStepTok(Token* tok);
|
||||||
|
const Token* getStepTok(const Token* tok);
|
||||||
|
|
||||||
Token* getCondTokFromEnd(Token* endBlock);
|
Token* getCondTokFromEnd(Token* endBlock);
|
||||||
const Token* getCondTokFromEnd(const Token* endBlock);
|
const Token* getCondTokFromEnd(const Token* endBlock);
|
||||||
|
|
||||||
|
|
|
@ -784,34 +784,6 @@ struct ForwardTraversal {
|
||||||
return parent && (parent->str() == ":" || parent->astOperand2() == tok);
|
return parent && (parent->str() == ":" || parent->astOperand2() == tok);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Token* getInitTok(Token* tok) {
|
|
||||||
if (!tok)
|
|
||||||
return nullptr;
|
|
||||||
if (Token::Match(tok, "%name% ("))
|
|
||||||
return getInitTok(tok->next());
|
|
||||||
if (tok->str() != "(")
|
|
||||||
return nullptr;
|
|
||||||
if (!Token::simpleMatch(tok->astOperand2(), ";"))
|
|
||||||
return nullptr;
|
|
||||||
if (Token::simpleMatch(tok->astOperand2()->astOperand1(), ";"))
|
|
||||||
return nullptr;
|
|
||||||
return tok->astOperand2()->astOperand1();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token* getStepTok(Token* tok) {
|
|
||||||
if (!tok)
|
|
||||||
return nullptr;
|
|
||||||
if (Token::Match(tok, "%name% ("))
|
|
||||||
return getStepTok(tok->next());
|
|
||||||
if (tok->str() != "(")
|
|
||||||
return nullptr;
|
|
||||||
if (!Token::simpleMatch(tok->astOperand2(), ";"))
|
|
||||||
return nullptr;
|
|
||||||
if (!Token::simpleMatch(tok->astOperand2()->astOperand2(), ";"))
|
|
||||||
return nullptr;
|
|
||||||
return tok->astOperand2()->astOperand2()->astOperand2();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token* getStepTokFromEnd(Token* tok) {
|
static Token* getStepTokFromEnd(Token* tok) {
|
||||||
if (!Token::simpleMatch(tok, "}"))
|
if (!Token::simpleMatch(tok, "}"))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -692,8 +692,9 @@ static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm)
|
||||||
void execute(const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result, bool* error)
|
void execute(const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result, bool* error)
|
||||||
{
|
{
|
||||||
ValueFlow::Value v = execute(expr, *programMemory);
|
ValueFlow::Value v = execute(expr, *programMemory);
|
||||||
if (!v.isIntValue() || v.isImpossible())
|
if (!v.isIntValue() || v.isImpossible()) {
|
||||||
*error = true;
|
if (error)
|
||||||
else
|
*error = true;
|
||||||
|
} else if (result)
|
||||||
*result = v.intvalue;
|
*result = v.intvalue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1695,7 +1695,7 @@ static bool isConditionKnown(const Token* tok, bool then)
|
||||||
const Token* parent = tok->astParent();
|
const Token* parent = tok->astParent();
|
||||||
while (parent && (parent->str() == op || parent->str() == "!"))
|
while (parent && (parent->str() == op || parent->str() == "!"))
|
||||||
parent = parent->astParent();
|
parent = parent->astParent();
|
||||||
return (parent && parent->str() == "(");
|
return Token::Match(parent, "(|;");
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::string& invertAssign(const std::string& assign)
|
static const std::string& invertAssign(const std::string& assign)
|
||||||
|
@ -5221,144 +5221,205 @@ struct ConditionHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) {
|
if (!top)
|
||||||
// if astParent is "!" we need to invert codeblock
|
return;
|
||||||
{
|
|
||||||
const Token* tok2 = tok;
|
if (top->previous()->isExpandedMacro())
|
||||||
while (tok2->astParent()) {
|
return;
|
||||||
const Token* parent = tok2->astParent();
|
|
||||||
while (parent && parent->str() == "&&")
|
if (!Token::Match(top->previous(), "if|while|for ("))
|
||||||
parent = parent->astParent();
|
return;
|
||||||
if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false")))
|
|
||||||
std::swap(thenValues, elseValues);
|
if (top->previous()->str() == "for") {
|
||||||
tok2 = parent;
|
if (!Token::Match(tok, "%comp%"))
|
||||||
|
return;
|
||||||
|
if (!Token::simpleMatch(tok->astParent(), ";"))
|
||||||
|
return;
|
||||||
|
const Token* stepTok = getStepTok(top);
|
||||||
|
if (cond.vartok->varId() == 0)
|
||||||
|
return;
|
||||||
|
if (!cond.vartok->variable())
|
||||||
|
return;
|
||||||
|
if (!Token::Match(stepTok, "++|--"))
|
||||||
|
return;
|
||||||
|
std::set<ValueFlow::Value::Bound> bounds;
|
||||||
|
for (const ValueFlow::Value& v : thenValues) {
|
||||||
|
if (v.bound != ValueFlow::Value::Bound::Point && v.isImpossible())
|
||||||
|
continue;
|
||||||
|
bounds.insert(v.bound);
|
||||||
|
}
|
||||||
|
if (Token::simpleMatch(stepTok, "++") && bounds.count(ValueFlow::Value::Bound::Lower) > 0)
|
||||||
|
return;
|
||||||
|
if (Token::simpleMatch(stepTok, "--") && bounds.count(ValueFlow::Value::Bound::Upper) > 0)
|
||||||
|
return;
|
||||||
|
const Token* childTok = tok->astOperand1();
|
||||||
|
if (!childTok)
|
||||||
|
childTok = tok->astOperand2();
|
||||||
|
if (!childTok)
|
||||||
|
return;
|
||||||
|
if (childTok->varId() != cond.vartok->varId())
|
||||||
|
return;
|
||||||
|
const Token* startBlock = top->link()->next();
|
||||||
|
if (isVariableChanged(startBlock,
|
||||||
|
startBlock->link(),
|
||||||
|
cond.vartok->varId(),
|
||||||
|
cond.vartok->variable()->isGlobal(),
|
||||||
|
settings,
|
||||||
|
tokenlist->isCPP()))
|
||||||
|
return;
|
||||||
|
// Check if condition in for loop is always false
|
||||||
|
const Token* initTok = getInitTok(top);
|
||||||
|
ProgramMemory pm;
|
||||||
|
execute(initTok, &pm, nullptr, nullptr);
|
||||||
|
MathLib::bigint result = 1;
|
||||||
|
execute(tok, &pm, &result, nullptr);
|
||||||
|
if (result == 0)
|
||||||
|
return;
|
||||||
|
// Remove condition since for condition is not redundant
|
||||||
|
for (std::list<ValueFlow::Value>* values : {&thenValues, &elseValues}) {
|
||||||
|
for (ValueFlow::Value& v : *values) {
|
||||||
|
v.condition = nullptr;
|
||||||
|
v.conditional = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool deadBranch[] = {false, false};
|
// if astParent is "!" we need to invert codeblock
|
||||||
// start token of conditional code
|
{
|
||||||
Token* startTokens[] = {nullptr, nullptr};
|
const Token* tok2 = tok;
|
||||||
// determine startToken(s)
|
while (tok2->astParent()) {
|
||||||
if (Token::simpleMatch(top->link(), ") {"))
|
const Token* parent = tok2->astParent();
|
||||||
startTokens[0] = top->link()->next();
|
while (parent && parent->str() == "&&")
|
||||||
if (Token::simpleMatch(top->link()->linkAt(1), "} else {"))
|
parent = parent->astParent();
|
||||||
startTokens[1] = top->link()->linkAt(1)->tokAt(2);
|
if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false")))
|
||||||
|
std::swap(thenValues, elseValues);
|
||||||
int changeBlock = -1;
|
tok2 = parent;
|
||||||
int bailBlock = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
const Token* const startToken = startTokens[i];
|
|
||||||
if (!startToken)
|
|
||||||
continue;
|
|
||||||
std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues);
|
|
||||||
valueFlowSetConditionToKnown(tok, values, i == 0);
|
|
||||||
|
|
||||||
Analyzer::Result r =
|
|
||||||
forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings);
|
|
||||||
deadBranch[i] = r.terminate == Analyzer::Terminate::Escape;
|
|
||||||
if (r.action.isModified() && !deadBranch[i])
|
|
||||||
changeBlock = i;
|
|
||||||
if (r.terminate != Analyzer::Terminate::None && r.terminate != Analyzer::Terminate::Escape &&
|
|
||||||
r.terminate != Analyzer::Terminate::Modified)
|
|
||||||
bailBlock = i;
|
|
||||||
changeKnownToPossible(values);
|
|
||||||
}
|
}
|
||||||
if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) {
|
}
|
||||||
|
|
||||||
|
bool deadBranch[] = {false, false};
|
||||||
|
// start token of conditional code
|
||||||
|
Token* startTokens[] = {nullptr, nullptr};
|
||||||
|
// determine startToken(s)
|
||||||
|
if (Token::simpleMatch(top->link(), ") {"))
|
||||||
|
startTokens[0] = top->link()->next();
|
||||||
|
if (Token::simpleMatch(top->link()->linkAt(1), "} else {"))
|
||||||
|
startTokens[1] = top->link()->linkAt(1)->tokAt(2);
|
||||||
|
|
||||||
|
int changeBlock = -1;
|
||||||
|
int bailBlock = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
const Token* const startToken = startTokens[i];
|
||||||
|
if (!startToken)
|
||||||
|
continue;
|
||||||
|
std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues);
|
||||||
|
valueFlowSetConditionToKnown(tok, values, i == 0);
|
||||||
|
|
||||||
|
Analyzer::Result r =
|
||||||
|
forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings);
|
||||||
|
deadBranch[i] = r.terminate == Analyzer::Terminate::Escape;
|
||||||
|
if (r.action.isModified() && !deadBranch[i])
|
||||||
|
changeBlock = i;
|
||||||
|
if (r.terminate != Analyzer::Terminate::None && r.terminate != Analyzer::Terminate::Escape &&
|
||||||
|
r.terminate != Analyzer::Terminate::Modified)
|
||||||
|
bailBlock = i;
|
||||||
|
changeKnownToPossible(values);
|
||||||
|
}
|
||||||
|
if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist,
|
||||||
|
errorLogger,
|
||||||
|
startTokens[changeBlock]->link(),
|
||||||
|
"valueFlowAfterCondition: " + cond.vartok->expressionString() +
|
||||||
|
" is changed in conditional block");
|
||||||
|
return;
|
||||||
|
} else if (bailBlock >= 0) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist,
|
||||||
|
errorLogger,
|
||||||
|
startTokens[bailBlock]->link(),
|
||||||
|
"valueFlowAfterCondition: bailing in conditional block");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After conditional code..
|
||||||
|
if (Token::simpleMatch(top->link(), ") {")) {
|
||||||
|
Token* after = top->link()->linkAt(1);
|
||||||
|
bool dead_if = deadBranch[0];
|
||||||
|
bool dead_else = deadBranch[1];
|
||||||
|
const Token* unknownFunction = nullptr;
|
||||||
|
if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while ("))
|
||||||
|
dead_if = !isBreakScope(after);
|
||||||
|
else if (!dead_if)
|
||||||
|
dead_if = isReturnScope(after, &settings->library, &unknownFunction);
|
||||||
|
|
||||||
|
if (!dead_if && unknownFunction) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist,
|
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
||||||
errorLogger,
|
|
||||||
startTokens[changeBlock]->link(),
|
|
||||||
"valueFlowAfterCondition: " + cond.vartok->expressionString() +
|
|
||||||
" is changed in conditional block");
|
|
||||||
return;
|
|
||||||
} else if (bailBlock >= 0) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist,
|
|
||||||
errorLogger,
|
|
||||||
startTokens[bailBlock]->link(),
|
|
||||||
"valueFlowAfterCondition: bailing in conditional block");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// After conditional code..
|
if (Token::simpleMatch(after, "} else {")) {
|
||||||
if (Token::simpleMatch(top->link(), ") {")) {
|
after = after->linkAt(2);
|
||||||
Token* after = top->link()->linkAt(1);
|
unknownFunction = nullptr;
|
||||||
bool dead_if = deadBranch[0];
|
if (!dead_else)
|
||||||
bool dead_else = deadBranch[1];
|
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
|
||||||
const Token* unknownFunction = nullptr;
|
if (!dead_else && unknownFunction) {
|
||||||
if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while ("))
|
|
||||||
dead_if = !isBreakScope(after);
|
|
||||||
else if (!dead_if)
|
|
||||||
dead_if = isReturnScope(after, &settings->library, &unknownFunction);
|
|
||||||
|
|
||||||
if (!dead_if && unknownFunction) {
|
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(after, "} else {")) {
|
|
||||||
after = after->linkAt(2);
|
|
||||||
unknownFunction = nullptr;
|
|
||||||
if (!dead_else)
|
|
||||||
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
|
|
||||||
if (!dead_else && unknownFunction) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dead_if && dead_else)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::list<ValueFlow::Value> values;
|
|
||||||
if (dead_if) {
|
|
||||||
values = elseValues;
|
|
||||||
} else if (dead_else) {
|
|
||||||
values = thenValues;
|
|
||||||
} else {
|
|
||||||
std::copy_if(thenValues.begin(),
|
|
||||||
thenValues.end(),
|
|
||||||
std::back_inserter(values),
|
|
||||||
std::mem_fn(&ValueFlow::Value::isPossible));
|
|
||||||
std::copy_if(elseValues.begin(),
|
|
||||||
elseValues.end(),
|
|
||||||
std::back_inserter(values),
|
|
||||||
std::mem_fn(&ValueFlow::Value::isPossible));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (dead_if || dead_else) {
|
|
||||||
const Token* parent = tok->astParent();
|
|
||||||
// Skip the not operator
|
|
||||||
while (Token::simpleMatch(parent, "!"))
|
|
||||||
parent = parent->astParent();
|
|
||||||
bool possible = false;
|
|
||||||
if (Token::Match(parent, "&&|%oror%")) {
|
|
||||||
std::string op = parent->str();
|
|
||||||
while (parent && parent->str() == op)
|
|
||||||
parent = parent->astParent();
|
|
||||||
if (Token::simpleMatch(parent, "!") || Token::simpleMatch(parent, "== false"))
|
|
||||||
possible = op == "||";
|
|
||||||
else
|
|
||||||
possible = op == "&&";
|
|
||||||
}
|
|
||||||
if (possible) {
|
|
||||||
values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible));
|
|
||||||
changeKnownToPossible(values);
|
|
||||||
} else {
|
|
||||||
valueFlowSetConditionToKnown(tok, values, true);
|
|
||||||
valueFlowSetConditionToKnown(tok, values, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (values.empty())
|
|
||||||
return;
|
|
||||||
forward(after, scope->bodyEnd, cond.vartok, values, tokenlist, settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dead_if && dead_else)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::list<ValueFlow::Value> values;
|
||||||
|
if (dead_if) {
|
||||||
|
values = elseValues;
|
||||||
|
} else if (dead_else) {
|
||||||
|
values = thenValues;
|
||||||
|
} else {
|
||||||
|
std::copy_if(thenValues.begin(),
|
||||||
|
thenValues.end(),
|
||||||
|
std::back_inserter(values),
|
||||||
|
std::mem_fn(&ValueFlow::Value::isPossible));
|
||||||
|
std::copy_if(elseValues.begin(),
|
||||||
|
elseValues.end(),
|
||||||
|
std::back_inserter(values),
|
||||||
|
std::mem_fn(&ValueFlow::Value::isPossible));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dead_if || dead_else) {
|
||||||
|
const Token* parent = tok->astParent();
|
||||||
|
// Skip the not operator
|
||||||
|
while (Token::simpleMatch(parent, "!"))
|
||||||
|
parent = parent->astParent();
|
||||||
|
bool possible = false;
|
||||||
|
if (Token::Match(parent, "&&|%oror%")) {
|
||||||
|
std::string op = parent->str();
|
||||||
|
while (parent && parent->str() == op)
|
||||||
|
parent = parent->astParent();
|
||||||
|
if (Token::simpleMatch(parent, "!") || Token::simpleMatch(parent, "== false"))
|
||||||
|
possible = op == "||";
|
||||||
|
else
|
||||||
|
possible = op == "&&";
|
||||||
|
}
|
||||||
|
if (possible) {
|
||||||
|
values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible));
|
||||||
|
changeKnownToPossible(values);
|
||||||
|
} else {
|
||||||
|
valueFlowSetConditionToKnown(tok, values, true);
|
||||||
|
valueFlowSetConditionToKnown(tok, values, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (values.empty())
|
||||||
|
return;
|
||||||
|
forward(after, scope->bodyEnd, cond.vartok, values, tokenlist, settings);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1317,7 +1317,8 @@ private:
|
||||||
" for (x = a; x < 50; x++) {}\n"
|
" for (x = a; x < 50; x++) {}\n"
|
||||||
" b = x;\n"
|
" b = x;\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQUALS("3,After for loop, x has value 50\n",
|
ASSERT_EQUALS("3,Assuming that condition 'x<50' is not redundant\n"
|
||||||
|
"3,Assuming that condition 'x<50' is not redundant\n",
|
||||||
getErrorPathForX(code, 4U));
|
getErrorPathForX(code, 4U));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6646,6 +6647,14 @@ private:
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 11U, "d", 0));
|
ASSERT_EQUALS(true, testValueOfX(code, 11U, "d", 0));
|
||||||
ASSERT_EQUALS(false, testValueOfXImpossible(code, 11U, 0));
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 11U, 0));
|
||||||
|
|
||||||
|
code = "void f(int * p, int len) {\n"
|
||||||
|
" for(int x = 0; x < len; ++x) {\n"
|
||||||
|
" p[x] = 1;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 3U, "len", -1));
|
||||||
|
ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "len", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowSymbolicIdentity()
|
void valueFlowSymbolicIdentity()
|
||||||
|
|
Loading…
Reference in New Issue