Fix 10294: ValueFlow: Wrong <Uninit> value below loop (#3291)

This commit is contained in:
Paul Fultz II 2021-06-09 02:20:43 -05:00 committed by GitHub
parent 1e78b30958
commit f3a33ea330
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 248 additions and 82 deletions

View File

@ -113,12 +113,20 @@ struct Analyzer {
enum class Direction { Forward, Reverse }; enum class Direction { Forward, Reverse };
struct Assume {
enum Flags {
None = 0,
Quiet = (1 << 0),
Absolute = (1 << 1),
};
};
/// Analyze a token /// Analyze a token
virtual Action analyze(const Token* tok, Direction d) const = 0; virtual Action analyze(const Token* tok, Direction d) const = 0;
/// Update the state of the value /// Update the state of the value
virtual void update(Token* tok, Action a, Direction d) = 0; virtual void update(Token* tok, Action a, Direction d) = 0;
/// Try to evaluate the value of a token(most likely a condition) /// Try to evaluate the value of a token(most likely a condition)
virtual std::vector<int> evaluate(const Token* tok) const = 0; virtual std::vector<int> evaluate(const Token* tok, const Token* ctx = nullptr) const = 0;
/// Lower any values to possible /// Lower any values to possible
virtual bool lowerToPossible() = 0; virtual bool lowerToPossible() = 0;
/// Lower any values to inconclusive /// Lower any values to inconclusive
@ -130,7 +138,7 @@ struct Analyzer {
/// If the value is conditional /// If the value is conditional
virtual bool isConditional() const = 0; virtual bool isConditional() const = 0;
/// The condition that will be assumed during analysis /// The condition that will be assumed during analysis
virtual void assume(const Token* tok, bool state, const Token* at = nullptr) = 0; virtual void assume(const Token* tok, bool state, unsigned int flags = 0) = 0;
/// Return analyzer for expression at token /// Return analyzer for expression at token
virtual ValuePtr<Analyzer> reanalyze(Token* tok, const std::string& msg = "") const = 0; virtual ValuePtr<Analyzer> reanalyze(Token* tok, const std::string& msg = "") const = 0;
virtual ~Analyzer() {} virtual ~Analyzer() {}

View File

@ -8,6 +8,8 @@
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <tuple>
#include <utility>
struct ForwardTraversal { struct ForwardTraversal {
enum class Progress { Continue, Break, Skip }; enum class Progress { Continue, Break, Skip };
@ -21,6 +23,7 @@ struct ForwardTraversal {
bool analyzeOnly; bool analyzeOnly;
bool analyzeTerminate; bool analyzeTerminate;
Terminate terminate = Terminate::None; Terminate terminate = Terminate::None;
bool forked = false;
Progress Break(Terminate t = Terminate::None) { Progress Break(Terminate t = Terminate::None) {
if ((!analyzeOnly || analyzeTerminate) && t != Terminate::None) if ((!analyzeOnly || analyzeTerminate) && t != Terminate::None)
@ -29,11 +32,12 @@ struct ForwardTraversal {
} }
struct Branch { struct Branch {
Branch(Token* tok = nullptr) : endBlock(tok) {}
Token* endBlock = nullptr;
Analyzer::Action action = Analyzer::Action::None; Analyzer::Action action = Analyzer::Action::None;
bool check = false; bool check = false;
bool escape = false; bool escape = false;
bool escapeUnknown = false; bool escapeUnknown = false;
const Token* endBlock = nullptr;
bool isEscape() const { bool isEscape() const {
return escape || escapeUnknown; return escape || escapeUnknown;
} }
@ -56,8 +60,10 @@ struct ForwardTraversal {
return actions.isModified(); return actions.isModified();
} }
std::pair<bool, bool> evalCond(const Token* tok) { std::pair<bool, bool> evalCond(const Token* tok, const Token* ctx = nullptr) const {
std::vector<int> result = analyzer->evaluate(tok); if (!tok)
return std::make_pair(false, false);
std::vector<int> result = analyzer->evaluate(tok, ctx);
// TODO: We should convert to bool // TODO: We should convert to bool
bool checkThen = std::any_of(result.begin(), result.end(), [](int x) { bool checkThen = std::any_of(result.begin(), result.end(), [](int x) {
return x == 1; return x == 1;
@ -68,6 +74,10 @@ struct ForwardTraversal {
return std::make_pair(checkThen, checkElse); return std::make_pair(checkThen, checkElse);
} }
bool isConditionTrue(const Token* tok, const Token* ctx = nullptr) const { return evalCond(tok, ctx).first; }
bool isConditionFalse(const Token* tok, const Token* ctx = nullptr) const { return evalCond(tok, ctx).second; }
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)> template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
Progress traverseTok(T* tok, std::function<Progress(T*)> f, bool traverseUnknown, T** out = nullptr) { Progress traverseTok(T* tok, std::function<Progress(T*)> f, bool traverseUnknown, T** out = nullptr) {
if (Token::Match(tok, "asm|goto|continue|setjmp|longjmp")) if (Token::Match(tok, "asm|goto|continue|setjmp|longjmp"))
@ -183,6 +193,7 @@ struct ForwardTraversal {
} }
Progress updateRecursive(Token* tok) { Progress updateRecursive(Token* tok) {
forked = false;
std::function<Progress(Token*)> f = [this](Token* tok2) { std::function<Progress(Token*)> f = [this](Token* tok2) {
return update(tok2); return update(tok2);
}; };
@ -222,30 +233,33 @@ struct ForwardTraversal {
return result; return result;
} }
void forkRange(Token* start, const Token* end) { ForwardTraversal fork(bool analyze = false) const {
ForwardTraversal ft = *this; ForwardTraversal ft = *this;
ft.updateRange(start, end);
}
ForwardTraversal forkScope(Token* endBlock, bool analyze = false) const {
ForwardTraversal ft = *this;
ft.analyzer->forkScope(endBlock);
if (analyze) { if (analyze) {
ft.analyzeOnly = true; ft.analyzeOnly = true;
ft.analyzeTerminate = true; ft.analyzeTerminate = true;
} }
ft.updateRange(endBlock->link(), endBlock); ft.actions = Analyzer::Action::None;
ft.forked = true;
return ft; return ft;
} }
std::vector<ForwardTraversal> tryForkScope(Token* endBlock, bool isModified = false) { std::vector<ForwardTraversal> tryForkScope(Token* endBlock, bool isModified = false) {
if (analyzer->updateScope(endBlock, isModified)) { if (analyzer->updateScope(endBlock, isModified)) {
ForwardTraversal ft = forkScope(endBlock); ForwardTraversal ft = fork();
return {ft}; return {ft};
} }
return std::vector<ForwardTraversal> {}; return std::vector<ForwardTraversal> {};
} }
std::vector<ForwardTraversal> tryForkUpdateScope(Token* endBlock, bool isModified = false)
{
std::vector<ForwardTraversal> result = tryForkScope(endBlock, isModified);
for (ForwardTraversal& ft : result)
ft.updateScope(endBlock);
return result;
}
static bool hasGoto(const Token* endBlock) { static bool hasGoto(const Token* endBlock) {
return Token::findsimplematch(endBlock->link(), "goto", endBlock); return Token::findsimplematch(endBlock->link(), "goto", endBlock);
} }
@ -283,7 +297,7 @@ struct ForwardTraversal {
Analyzer::Action checkScope(Token* endBlock) { Analyzer::Action checkScope(Token* endBlock) {
Analyzer::Action a = analyzeScope(endBlock); Analyzer::Action a = analyzeScope(endBlock);
tryForkScope(endBlock, a.isModified()); tryForkUpdateScope(endBlock, a.isModified());
return a; return a;
} }
@ -292,16 +306,17 @@ struct ForwardTraversal {
return a; return a;
} }
bool checkBranch(Branch& branch, Token* endBlock) { bool checkBranch(Branch& branch) {
Analyzer::Action a = analyzeScope(endBlock); Analyzer::Action a = analyzeScope(branch.endBlock);
branch.action = a; branch.action = a;
std::vector<ForwardTraversal> ft1 = tryForkScope(endBlock, a.isModified()); std::vector<ForwardTraversal> ft1 = tryForkUpdateScope(branch.endBlock, a.isModified());
bool bail = hasGoto(endBlock); bool bail = hasGoto(branch.endBlock);
if (!a.isModified() && !bail) { if (!a.isModified() && !bail) {
if (ft1.empty()) { if (ft1.empty()) {
// Traverse into the branch to see if there is a conditional escape // Traverse into the branch to see if there is a conditional escape
if (!branch.escape && hasInnerReturnScope(endBlock->previous(), endBlock->link())) { if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) {
ForwardTraversal ft2 = forkScope(endBlock, true); ForwardTraversal ft2 = fork(true);
ft2.updateScope(branch.endBlock);
if (ft2.terminate == Terminate::Escape) { if (ft2.terminate == Terminate::Escape) {
branch.escape = true; branch.escape = true;
branch.escapeUnknown = false; branch.escapeUnknown = false;
@ -317,12 +332,37 @@ struct ForwardTraversal {
return bail; return bail;
} }
void continueUpdateRangeAfterLoop(std::vector<ForwardTraversal>& ftv, Token* start, const Token* endToken) { bool reentersLoop(Token* endBlock, const Token* condTok, const Token* stepTok) {
for (ForwardTraversal& ft : ftv) { if (!condTok)
// If analysis has terminated normally, then continue analysis return true;
if (ft.terminate == Terminate::None) if (Token::simpleMatch(condTok, ":"))
ft.updateRange(start, endToken); return true;
bool changed = false;
if (stepTok) {
std::pair<const Token*, const Token*> exprToks = stepTok->findExpressionStartEndTokens();
if (exprToks.first != nullptr && exprToks.second != nullptr)
changed |= isExpressionChanged(condTok, exprToks.first, exprToks.second->next(), settings, true);
} }
changed |= isExpressionChanged(condTok, endBlock->link(), endBlock, settings, true);
// Check for mutation in the condition
changed |= nullptr !=
findAstNode(condTok, [&](const Token* tok) { return isVariableChanged(tok, 0, settings, true); });
if (!changed)
return true;
ForwardTraversal ft = fork(true);
ft.analyzer->assume(condTok, false, Analyzer::Assume::Absolute);
ft.updateScope(endBlock);
return ft.isConditionTrue(condTok);
}
Progress updateInnerLoop(Token* endBlock, Token* stepTok, Token* condTok) {
if (endBlock && updateScope(endBlock) == Progress::Break)
return Break();
if (stepTok && updateRecursive(stepTok) == Progress::Break)
return Break();
if (condTok && updateRecursive(condTok) == Progress::Break)
return Break();
return Progress::Continue;
} }
Progress updateLoop(const Token* endToken, Progress updateLoop(const Token* endToken,
@ -330,14 +370,13 @@ struct ForwardTraversal {
Token* condTok, Token* condTok,
Token* initTok = nullptr, Token* initTok = nullptr,
Token* stepTok = nullptr) { Token* stepTok = nullptr) {
if (initTok && updateRecursive(initTok) == Progress::Break)
return Break();
const bool isDoWhile = precedes(endBlock, condTok); const bool isDoWhile = precedes(endBlock, condTok);
const bool alwaysEnterLoop = !condTok || (condTok->hasKnownIntValue() && condTok->values().front().intvalue != 0);
Analyzer::Action bodyAnalysis = analyzeScope(endBlock); Analyzer::Action bodyAnalysis = analyzeScope(endBlock);
Analyzer::Action allAnalysis = bodyAnalysis; Analyzer::Action allAnalysis = bodyAnalysis;
if (condTok) if (condTok)
allAnalysis |= analyzeRecursive(condTok); allAnalysis |= analyzeRecursive(condTok);
if (initTok)
allAnalysis |= analyzeRecursive(initTok);
if (stepTok) if (stepTok)
allAnalysis |= analyzeRecursive(stepTok); allAnalysis |= analyzeRecursive(stepTok);
actions |= allAnalysis; actions |= allAnalysis;
@ -348,43 +387,64 @@ struct ForwardTraversal {
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Break(Terminate::Bail); return Break(Terminate::Bail);
} }
// Traverse condition after lowering bool checkThen = true;
if (condTok && (!isDoWhile || (!bodyAnalysis.isModified() && !bodyAnalysis.isIdempotent()))) { bool checkElse = false;
if (updateRecursive(condTok) == Progress::Break) if (condTok && !Token::simpleMatch(condTok, ":")) {
if (!isDoWhile || (!bodyAnalysis.isModified() && !bodyAnalysis.isIdempotent()))
if (updateRecursive(condTok) == Progress::Break)
return Break();
// TODO: Check cond first before lowering
std::tie(checkThen, checkElse) = evalCond(condTok, isDoWhile ? endBlock->link()->previous() : nullptr);
}
// condition is false, we don't enter the loop
if (checkElse)
return Progress::Continue;
if (checkThen || isDoWhile) {
if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break)
return Break(); return Break();
// If loop re-enters then it could be modified again
bool checkThen = true; if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok))
bool checkElse = false; return Break(Terminate::Bail);
std::tie(checkThen, checkElse) = evalCond(condTok); if (allAnalysis.isIncremental())
// condition is false, we don't enter the loop
if (checkElse)
return Progress::Continue;
}
if (allAnalysis.isModified() && alwaysEnterLoop)
return Break(Terminate::Bail);
std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, allAnalysis.isModified());
if (bodyAnalysis.isModified()) {
Token* writeTok = findRange(endBlock->link(), endBlock, std::mem_fn(&Analyzer::Action::isModified));
const Token* nextStatement = Token::findmatch(writeTok, ";|}", endBlock);
if (!Token::Match(nextStatement, ";|} break ;")) {
if (!allAnalysis.isIncremental())
continueUpdateRangeAfterLoop(ftv, endBlock, endToken);
return Break(Terminate::Bail); return Break(Terminate::Bail);
}
} else { } else {
if (stepTok && updateRecursive(stepTok) == Progress::Break) { std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, allAnalysis.isModified());
if (!allAnalysis.isIncremental()) bool forkContinue = true;
continueUpdateRangeAfterLoop(ftv, endBlock, endToken); for (ForwardTraversal& ft : ftv) {
return Break(Terminate::Bail); if (condTok)
ft.analyzer->assume(condTok, false, Analyzer::Assume::Quiet);
if (ft.updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break)
forkContinue = false;
} }
if (allAnalysis.isModified() || !forkContinue) {
// TODO: Dont bail on missing condition
if (!condTok)
return Break(Terminate::Bail);
if (analyzer->isConditional() && stopUpdates())
return Break(Terminate::Conditional);
analyzer->assume(condTok, false);
}
if (forkContinue) {
for (ForwardTraversal& ft : ftv) {
if (!ft.actions.isIncremental())
ft.updateRange(endBlock, endToken);
}
}
if (allAnalysis.isIncremental())
return Break(Terminate::Bail);
} }
// TODO: Should we traverse the body?
// updateRange(endBlock->link(), endBlock);
return Progress::Continue; return Progress::Continue;
} }
Progress updateScope(Token* endBlock) {
if (forked)
analyzer->forkScope(endBlock);
return updateRange(endBlock->link(), endBlock);
}
Progress updateRange(Token* start, const Token* end, int depth = 20) { Progress updateRange(Token* start, const Token* end, int depth = 20) {
forked = false;
if (depth < 0) if (depth < 0)
return Break(Terminate::Bail); return Break(Terminate::Bail);
std::size_t i = 0; std::size_t i = 0;
@ -454,7 +514,7 @@ struct ForwardTraversal {
if (updateRecursive(condTok) == Progress::Break) if (updateRecursive(condTok) == Progress::Break)
return Break(); return Break();
} }
analyzer->assume(condTok, !inElse, tok); analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet);
if (Token::simpleMatch(tok, "} else {")) if (Token::simpleMatch(tok, "} else {"))
tok = tok->linkAt(2); tok = tok->linkAt(2);
} else if (scope->type == Scope::eTry) { } else if (scope->type == Scope::eTry) {
@ -494,8 +554,8 @@ struct ForwardTraversal {
// Traverse condition // Traverse condition
if (updateRecursive(condTok) == Progress::Break) if (updateRecursive(condTok) == Progress::Break)
return Break(); return Break();
Branch thenBranch{}; Branch thenBranch{endBlock};
Branch elseBranch{}; Branch elseBranch{endBlock->tokAt(2) ? endBlock->linkAt(2) : nullptr};
// Check if condition is true or false // Check if condition is true or false
std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok); std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok);
bool hasElse = Token::simpleMatch(endBlock, "} else {"); bool hasElse = Token::simpleMatch(endBlock, "} else {");
@ -507,7 +567,7 @@ struct ForwardTraversal {
if (updateRange(endCond->next(), endBlock, depth - 1) == Progress::Break) if (updateRange(endCond->next(), endBlock, depth - 1) == Progress::Break)
return Break(); return Break();
} else if (!elseBranch.check) { } else if (!elseBranch.check) {
if (checkBranch(thenBranch, endBlock)) if (checkBranch(thenBranch))
bail = true; bail = true;
} }
// Traverse else block // Traverse else block
@ -518,7 +578,7 @@ struct ForwardTraversal {
if (result == Progress::Break) if (result == Progress::Break)
return Break(); return Break();
} else if (!thenBranch.check) { } else if (!thenBranch.check) {
if (checkBranch(elseBranch, endBlock->linkAt(2))) if (checkBranch(elseBranch))
bail = true; bail = true;
} }
tok = endBlock->linkAt(2); tok = endBlock->linkAt(2);
@ -583,7 +643,7 @@ struct ForwardTraversal {
if (checkElse) if (checkElse)
return Break(); return Break();
if (!checkThen) if (!checkThen)
analyzer->assume(condTok, true, tok); analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute);
} else if (Token::simpleMatch(tok, "switch (")) { } else if (Token::simpleMatch(tok, "switch (")) {
if (updateRecursive(tok->next()->astOperand2()) == Progress::Break) if (updateRecursive(tok->next()->astOperand2()) == Progress::Break)
return Break(); return Break();

View File

@ -199,7 +199,11 @@ static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scop
const Token* condTok = getCondTokFromEnd(scope->bodyEnd); const Token* condTok = getCondTokFromEnd(scope->bodyEnd);
if (!condTok) if (!condTok)
return; return;
programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse); MathLib::bigint result = 0;
bool error = false;
execute(condTok, &pm, &result, &error);
if (error)
programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse);
} }
} }
@ -321,7 +325,11 @@ void ProgramMemoryState::assume(const Token* tok, bool b)
{ {
ProgramMemory pm = state; ProgramMemory pm = state;
programMemoryParseCondition(pm, tok, nullptr, nullptr, b); programMemoryParseCondition(pm, tok, nullptr, nullptr, b);
insert(pm, tok); const Token* origin = tok;
const Token* top = tok->astTop();
if (top && Token::Match(top->previous(), "for|while ("))
origin = top->link();
replace(pm, origin);
} }
void ProgramMemoryState::removeModifiedVars(const Token* tok) void ProgramMemoryState::removeModifiedVars(const Token* tok)
@ -336,11 +344,21 @@ void ProgramMemoryState::removeModifiedVars(const Token* tok)
} }
} }
ProgramMemory ProgramMemoryState::get(const Token *tok, const ProgramMemory::Map& vars) const ProgramMemory ProgramMemoryState::get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const
{ {
ProgramMemoryState local = *this; ProgramMemoryState local = *this;
local.addState(tok, vars); if (ctx)
local.removeModifiedVars(tok); local.addState(ctx, vars);
const Token* start = previousBeforeAstLeftmostLeaf(tok);
if (!start)
start = tok;
if (!ctx || precedes(start, ctx)) {
local.addState(start, vars);
local.removeModifiedVars(start);
} else {
local.removeModifiedVars(ctx);
}
return local.state; return local.state;
} }

View File

@ -53,8 +53,7 @@ struct ProgramMemoryState {
void removeModifiedVars(const Token* tok); void removeModifiedVars(const Token* tok);
ProgramMemory get(const Token *tok, const ProgramMemory::Map& vars) const; ProgramMemory get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const;
}; };
void execute(const Token *expr, void execute(const Token *expr,

View File

@ -218,9 +218,9 @@ struct ReverseTraversal {
if (thenAction.isModified() && inLoop) if (thenAction.isModified() && inLoop)
break; break;
else if (thenAction.isModified() && !elseAction.isModified()) else if (thenAction.isModified() && !elseAction.isModified())
analyzer->assume(condTok, hasElse, condTok); analyzer->assume(condTok, hasElse);
else if (elseAction.isModified() && !thenAction.isModified()) else if (elseAction.isModified() && !thenAction.isModified())
analyzer->assume(condTok, !hasElse, condTok); analyzer->assume(condTok, !hasElse);
// Bail if one of the branches are read to avoid FPs due to over constraints // Bail if one of the branches are read to avoid FPs due to over constraints
else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() || else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() ||
elseAction.isRead()) elseAction.isRead())

View File

@ -236,6 +236,12 @@ const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, Val
if (tok->isComparisonOp()) { if (tok->isComparisonOp()) {
std::vector<MathLib::bigint> value1 = evaluate(tok->astOperand1()); std::vector<MathLib::bigint> value1 = evaluate(tok->astOperand1());
std::vector<MathLib::bigint> value2 = evaluate(tok->astOperand2()); std::vector<MathLib::bigint> value2 = evaluate(tok->astOperand2());
if (!value1.empty() && !value2.empty()) {
if (tok->astOperand1()->hasKnownIntValue())
value2.clear();
if (tok->astOperand2()->hasKnownIntValue())
value1.clear();
}
if (!value1.empty()) { if (!value1.empty()) {
if (isSaturated(value1.front())) if (isSaturated(value1.front()))
return nullptr; return nullptr;
@ -2158,11 +2164,11 @@ struct ValueFlowAnalyzer : Analyzer {
return Action::None; return Action::None;
} }
virtual std::vector<int> evaluate(const Token* tok) const OVERRIDE { virtual std::vector<int> evaluate(const Token* tok, const Token* ctx = nullptr) const OVERRIDE {
if (tok->hasKnownIntValue()) if (tok->hasKnownIntValue())
return {static_cast<int>(tok->values().front().intvalue)}; return {static_cast<int>(tok->values().front().intvalue)};
std::vector<int> result; std::vector<int> result;
ProgramMemory pm = pms.get(tok, getProgramState()); ProgramMemory pm = pms.get(tok, ctx, getProgramState());
if (Token::Match(tok, "&&|%oror%")) { if (Token::Match(tok, "&&|%oror%")) {
if (conditionIsTrue(tok, pm)) if (conditionIsTrue(tok, pm))
result.push_back(1); result.push_back(1);
@ -2179,19 +2185,33 @@ struct ValueFlowAnalyzer : Analyzer {
return result; return result;
} }
virtual void assume(const Token* tok, bool state, const Token* at) OVERRIDE { virtual void assume(const Token* tok, bool state, unsigned int flags) OVERRIDE {
// Update program state // Update program state
pms.removeModifiedVars(tok); pms.removeModifiedVars(tok);
pms.addState(tok, getProgramState()); pms.addState(tok, getProgramState());
pms.assume(tok, state); pms.assume(tok, state);
const bool isAssert = Token::Match(at, "assert|ASSERT"); bool isCondBlock = false;
const Token* parent = tok->astParent();
if (parent) {
isCondBlock = Token::Match(parent->previous(), "if|while (");
}
if (!isAssert && !Token::simpleMatch(at, "}")) { if (isCondBlock) {
const Token* startBlock = parent->link()->next();
const Token* endBlock = startBlock->link();
pms.removeModifiedVars(endBlock);
if (state)
pms.addState(endBlock->previous(), getProgramState());
else if (Token::simpleMatch(endBlock, "} else {"))
pms.addState(endBlock->linkAt(2)->previous(), getProgramState());
}
if (!(flags & Assume::Quiet)) {
std::string s = state ? "true" : "false"; std::string s = state ? "true" : "false";
addErrorPath(tok, "Assuming condition is " + s); addErrorPath(tok, "Assuming condition is " + s);
} }
if (!isAssert) if (!(flags & Assume::Absolute))
makeConditional(); makeConditional();
} }

View File

@ -1218,8 +1218,7 @@ private:
" argv32[i] = 0;\n" " argv32[i] = 0;\n"
" }\n" " }\n"
"}"); "}");
TODO_ASSERT_EQUALS("error", ASSERT_EQUALS("[test.cpp:10]: (warning) Possible null pointer dereference: argv32\n", errout.str());
"", errout.str());
// #2231 - error if assignment in loop is not used // #2231 - error if assignment in loop is not used
// extracttests.start: int y[20]; // extracttests.start: int y[20];

View File

@ -2151,6 +2151,7 @@ private:
ASSERT_EQUALS(false, testValueOfX(code, 11U, 0)); // x can't be 0 at line 11 ASSERT_EQUALS(false, testValueOfX(code, 11U, 0)); // x can't be 0 at line 11
code = "void f(const int *buf) {\n" code = "void f(const int *buf) {\n"
" int i = 0;\n"
" int x = 0;\n" " int x = 0;\n"
" while (++i < 10) {\n" " while (++i < 10) {\n"
" if (buf[i] == 123) {\n" " if (buf[i] == 123) {\n"
@ -2160,7 +2161,66 @@ private:
" }\n" " }\n"
" a = x;\n" // <- x can be 0 " a = x;\n" // <- x can be 0
"}\n"; "}\n";
ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9 ASSERT_EQUALS(true, testValueOfX(code, 10U, 0)); // x can be 0 at line 9
code = "bool maybe();\n"
"void f() {\n"
" int x = 0;\n"
" bool found = false;\n"
" while(!found) {\n"
" if (maybe()) {\n"
" x = i;\n"
" found = true;\n"
" }\n"
" }\n"
" a = x;\n" // <- x can't be 0
"}\n";
ASSERT_EQUALS(false, testValueOfX(code, 11U, 0));
code = "bool maybe();\n"
"void f() {\n"
" int x = 0;\n"
" bool found = false;\n"
" while(!found) {\n"
" if (maybe()) {\n"
" x = i;\n"
" found = true;\n"
" } else {\n"
" found = false;\n"
" }\n"
" }\n"
" a = x;\n" // <- x can't be 0
"}\n";
ASSERT_EQUALS(false, testValueOfX(code, 13U, 0));
code = "bool maybe();\n"
"void f() {\n"
" int x = 0;\n"
" bool found = false;\n"
" while(!found) {\n"
" if (maybe()) {\n"
" x = i;\n"
" break;\n"
" }\n"
" }\n"
" a = x;\n" // <- x can't be 0
"}\n";
ASSERT_EQUALS(false, testValueOfX(code, 11U, 0));
code = "bool maybe();\n"
"void f() {\n"
" int x = 0;\n"
" bool found = false;\n"
" while(!found) {\n"
" if (maybe()) {\n"
" x = i;\n"
" found = true;\n"
" break;\n"
" }\n"
" }\n"
" a = x;\n" // <- x can't be 0
"}\n";
ASSERT_EQUALS(false, testValueOfX(code, 12U, 0));
code = "void f(const int a[]) {\n" // #6616 code = "void f(const int a[]) {\n" // #6616
" const int *x = 0;\n" " const int *x = 0;\n"
@ -3176,7 +3236,9 @@ private:
" } while (condition)\n" " } while (condition)\n"
"}"; "}";
values = tokenValues(code, "=="); values = tokenValues(code, "==");
ASSERT_EQUALS(true, values.empty()); ASSERT_EQUALS(1, values.size());
ASSERT(values.front().isPossible());
ASSERT_EQUALS(1, values.front().intvalue);
// for loops // for loops
code = "struct S { int x; };\n" // #9036 code = "struct S { int x; };\n" // #9036