Fix 10294: ValueFlow: Wrong <Uninit> value below loop (#3291)
This commit is contained in:
parent
1e78b30958
commit
f3a33ea330
|
@ -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() {}
|
||||||
|
|
|
@ -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
|
|
||||||
if (condTok && (!isDoWhile || (!bodyAnalysis.isModified() && !bodyAnalysis.isIdempotent()))) {
|
|
||||||
if (updateRecursive(condTok) == Progress::Break)
|
|
||||||
return Break();
|
|
||||||
|
|
||||||
bool checkThen = true;
|
bool checkThen = true;
|
||||||
bool checkElse = false;
|
bool checkElse = false;
|
||||||
std::tie(checkThen, checkElse) = evalCond(condTok);
|
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
|
// condition is false, we don't enter the loop
|
||||||
if (checkElse)
|
if (checkElse)
|
||||||
return Progress::Continue;
|
return Progress::Continue;
|
||||||
}
|
if (checkThen || isDoWhile) {
|
||||||
if (allAnalysis.isModified() && alwaysEnterLoop)
|
if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break)
|
||||||
|
return Break();
|
||||||
|
// If loop re-enters then it could be modified again
|
||||||
|
if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok))
|
||||||
return Break(Terminate::Bail);
|
return Break(Terminate::Bail);
|
||||||
|
if (allAnalysis.isIncremental())
|
||||||
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) {
|
||||||
|
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);
|
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();
|
||||||
|
|
|
@ -199,6 +199,10 @@ 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;
|
||||||
|
MathLib::bigint result = 0;
|
||||||
|
bool error = false;
|
||||||
|
execute(condTok, &pm, &result, &error);
|
||||||
|
if (error)
|
||||||
programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue