Fix issue 9637: false positive: Condition 'i<2U' is always true (#2536)

This commit is contained in:
Paul Fultz II 2020-02-15 00:57:43 -06:00 committed by GitHub
parent e04b9fe4a4
commit 61d847cac2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 155 additions and 82 deletions

View File

@ -3,6 +3,8 @@
#include "settings.h" #include "settings.h"
#include "symboldatabase.h" #include "symboldatabase.h"
#include <functional>
struct ForwardTraversal { struct ForwardTraversal {
enum class Progress { Continue, Break, Skip }; enum class Progress { Continue, Break, Skip };
ValuePtr<ForwardAnalyzer> analyzer; ValuePtr<ForwardAnalyzer> analyzer;
@ -19,6 +21,84 @@ struct ForwardTraversal {
return std::make_pair(checkThen, checkElse); return std::make_pair(checkThen, checkElse);
} }
template<class T, class F, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
Progress traverseTok(T* tok, F f, bool traverseUnknown, T** out = nullptr) {
if (Token::Match(tok, "asm|goto|continue|setjmp|longjmp"))
return Progress::Break;
else if (Token::Match(tok, "return|throw") || isEscapeFunction(tok, &settings->library)) {
traverseRecursive(tok->astOperand1(), f, traverseUnknown);
traverseRecursive(tok->astOperand2(), f, traverseUnknown);
return Progress::Break;
} else if (isUnevaluated(tok)) {
if (out)
*out = tok->link();
return Progress::Skip;
} else if (Token::Match(tok, "?|&&|%oror%")) {
if (traverseConditional(tok, f, traverseUnknown) == Progress::Break)
return Progress::Break;
if (out)
*out = nextAfterAstRightmostLeaf(tok);
return Progress::Skip;
// Skip lambdas
} else if (T* lambdaEndToken = findLambdaEndToken(tok)) {
if (checkScope(lambdaEndToken).isModified())
return Progress::Break;
if (out)
*out = lambdaEndToken;
} else {
if (f(tok) == Progress::Break)
return Progress::Break;
}
return Progress::Continue;
}
template<class T, class F, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
Progress traverseRecursive(T* tok, F f, bool traverseUnknown) {
if (!tok)
return Progress::Continue;
if (tok->astOperand1() && traverseRecursive(tok->astOperand1(), f, traverseUnknown) == Progress::Break)
return Progress::Break;
Progress p = traverseTok(tok, f, traverseUnknown);
if (p == Progress::Break)
return Progress::Break;
if (p == Progress::Continue && traverseRecursive(tok->astOperand2(), f, traverseUnknown) == Progress::Break)
return Progress::Break;
return Progress::Continue;
}
template<class T, class F, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
Progress traverseConditional(T* tok, F f, bool traverseUnknown) {
if (Token::Match(tok, "?|&&|%oror%")) {
T* condTok = tok->astOperand1();
if (traverseRecursive(condTok, f, traverseUnknown) == Progress::Break)
return Progress::Break;
T* childTok = tok->astOperand2();
bool checkThen, checkElse;
std::tie(checkThen, checkElse) = evalCond(condTok);
if (!checkThen && !checkElse) {
// Stop if the value is conditional
if (!traverseUnknown && analyzer->isConditional())
return Progress::Break;
checkThen = true;
checkElse = true;
}
if (Token::simpleMatch(childTok, ":")) {
if (checkThen && traverseRecursive(childTok->astOperand1(), f, traverseUnknown) == Progress::Break)
return Progress::Break;
if (checkElse && traverseRecursive(childTok->astOperand2(), f, traverseUnknown) == Progress::Break)
return Progress::Break;
} else {
if (!checkThen && Token::simpleMatch(tok, "&&"))
return Progress::Continue;
if (!checkElse && Token::simpleMatch(tok, "||"))
return Progress::Continue;
if (traverseRecursive(childTok, f, traverseUnknown) == Progress::Break)
return Progress::Break;
}
}
return Progress::Continue;
}
Progress update(Token* tok) { Progress update(Token* tok) {
ForwardAnalyzer::Action action = analyzer->analyze(tok); ForwardAnalyzer::Action action = analyzer->analyze(tok);
if (!action.isNone()) if (!action.isNone())
@ -29,78 +109,11 @@ struct ForwardTraversal {
} }
Progress updateTok(Token* tok, Token** out = nullptr) { Progress updateTok(Token* tok, Token** out = nullptr) {
if (Token::Match(tok, "asm|goto|continue|setjmp|longjmp")) return traverseTok(tok, [&](Token* tok2) { return update(tok2); }, false, out);
return Progress::Break;
else if (Token::Match(tok, "return|throw") || isEscapeFunction(tok, &settings->library)) {
updateRecursive(tok->astOperand1());
updateRecursive(tok->astOperand2());
return Progress::Break;
} else if (isUnevaluated(tok)) {
if (out)
*out = tok->link();
return Progress::Skip;
} else if (Token::Match(tok, "?|&&|%oror%")) {
if (updateConditional(tok) == Progress::Break)
return Progress::Break;
if (out)
*out = nextAfterAstRightmostLeaf(tok);
return Progress::Skip;
// Skip lambdas
} else if (Token* lambdaEndToken = findLambdaEndToken(tok)) {
if (checkScope(lambdaEndToken).isModified())
return Progress::Break;
if (out)
*out = lambdaEndToken;
} else {
if (update(tok) == Progress::Break)
return Progress::Break;
}
return Progress::Continue;
} }
Progress updateRecursive(Token* tok) { Progress updateRecursive(Token* tok) {
if (!tok) return traverseRecursive(tok, [&](Token* tok2) { return update(tok2); }, false);
return Progress::Continue;
if (tok->astOperand1() && updateRecursive(tok->astOperand1()) == Progress::Break)
return Progress::Break;
Progress p = updateTok(tok);
if (p == Progress::Break)
return Progress::Break;
if (p == Progress::Continue && updateRecursive(tok->astOperand2()) == Progress::Break)
return Progress::Break;
return Progress::Continue;
}
Progress updateConditional(Token* tok) {
if (Token::Match(tok, "?|&&|%oror%")) {
Token* condTok = tok->astOperand1();
if (updateRecursive(condTok) == Progress::Break)
return Progress::Break;
Token* childTok = tok->astOperand2();
bool checkThen, checkElse;
std::tie(checkThen, checkElse) = evalCond(condTok);
if (!checkThen && !checkElse) {
// Stop if the value is conditional
if (analyzer->isConditional())
return Progress::Break;
checkThen = true;
checkElse = true;
}
if (Token::simpleMatch(childTok, ":")) {
if (checkThen && updateRecursive(childTok->astOperand1()) == Progress::Break)
return Progress::Break;
if (checkElse && updateRecursive(childTok->astOperand2()) == Progress::Break)
return Progress::Break;
} else {
if (!checkThen && Token::simpleMatch(tok, "&&"))
return Progress::Continue;
if (!checkElse && Token::simpleMatch(tok, "||"))
return Progress::Continue;
if (updateRecursive(childTok) == Progress::Break)
return Progress::Break;
}
}
return Progress::Continue;
} }
template <class T, class Predicate> template <class T, class Predicate>
@ -113,11 +126,16 @@ struct ForwardTraversal {
return nullptr; return nullptr;
} }
template <class T> ForwardAnalyzer::Action analyzeRecursive(const Token* start) {
T* findActionRange(T* start, const Token* end, ForwardAnalyzer::Action action) { ForwardAnalyzer::Action result = ForwardAnalyzer::Action::None;
return findRange(start, end, [&](ForwardAnalyzer::Action a) { auto f = [&](const Token* tok) {
return a == action; result = analyzer->analyze(tok);
}); if (result.isModified() || result.isInconclusive())
return Progress::Break;
return Progress::Continue;
};
traverseRecursive(start, f, true);
return result;
} }
ForwardAnalyzer::Action analyzeRange(const Token* start, const Token* end) { ForwardAnalyzer::Action analyzeRange(const Token* start, const Token* end) {
@ -131,7 +149,7 @@ struct ForwardTraversal {
return result; return result;
} }
void forkScope(const Token* endBlock, bool isModified = false) { void forkScope(Token* endBlock, bool isModified = false) {
if (analyzer->updateScope(endBlock, isModified)) { if (analyzer->updateScope(endBlock, isModified)) {
ForwardTraversal ft = *this; ForwardTraversal ft = *this;
ft.updateRange(endBlock->link(), endBlock); ft.updateRange(endBlock->link(), endBlock);
@ -161,30 +179,43 @@ struct ForwardTraversal {
return analyzeRange(endBlock->link(), endBlock); return analyzeRange(endBlock->link(), endBlock);
} }
ForwardAnalyzer::Action checkScope(const Token* endBlock) { ForwardAnalyzer::Action checkScope(Token* endBlock) {
ForwardAnalyzer::Action a = analyzeScope(endBlock); ForwardAnalyzer::Action a = analyzeScope(endBlock);
forkScope(endBlock, a.isModified()); forkScope(endBlock, a.isModified());
return a; return a;
} }
Progress updateLoop(Token* endBlock, Token* condTok) { ForwardAnalyzer::Action checkScope(const Token* endBlock) {
ForwardAnalyzer::Action a = analyzeScope(endBlock); ForwardAnalyzer::Action a = analyzeScope(endBlock);
if (a.isInconclusive()) { return a;
}
Progress updateLoop(Token* endBlock, Token* condTok, Token* initTok = nullptr, Token* stepTok = nullptr) {
ForwardAnalyzer::Action bodyAnalysis = analyzeScope(endBlock);
ForwardAnalyzer::Action allAnalysis = bodyAnalysis;
if (initTok)
allAnalysis |= analyzeRecursive(initTok);
if (stepTok)
allAnalysis |= analyzeRecursive(stepTok);
if (allAnalysis.isInconclusive()) {
if (!analyzer->lowerToInconclusive()) if (!analyzer->lowerToInconclusive())
return Progress::Break; return Progress::Break;
} else if (a.isModified()) { } else if (allAnalysis.isModified()) {
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Progress::Break; return Progress::Break;
} }
// Traverse condition after lowering // Traverse condition after lowering
if (condTok && updateRecursive(condTok) == Progress::Break) if (condTok && updateRecursive(condTok) == Progress::Break)
return Progress::Break; return Progress::Break;
forkScope(endBlock, a.isModified()); forkScope(endBlock, allAnalysis.isModified());
if (a.isModified()) { if (bodyAnalysis.isModified()) {
Token* writeTok = findRange(endBlock->link(), endBlock, std::mem_fn(&ForwardAnalyzer::Action::isModified)); Token* writeTok = findRange(endBlock->link(), endBlock, std::mem_fn(&ForwardAnalyzer::Action::isModified));
const Token* nextStatement = Token::findmatch(writeTok, ";|}", endBlock); const Token* nextStatement = Token::findmatch(writeTok, ";|}", endBlock);
if (!Token::Match(nextStatement, ";|} break ;")) if (!Token::Match(nextStatement, ";|} break ;"))
return Progress::Break; return Progress::Break;
} else {
if (stepTok && updateRecursive(stepTok) == Progress::Break)
return Progress::Break;
} }
// TODO: Shoule we traverse the body? // TODO: Shoule we traverse the body?
// updateRange(endBlock->link(), endBlock); // updateRange(endBlock->link(), endBlock);
@ -243,7 +274,8 @@ struct ForwardTraversal {
if (initTok && updateRecursive(initTok) == Progress::Break) if (initTok && updateRecursive(initTok) == Progress::Break)
return Progress::Break; return Progress::Break;
if (Token::Match(tok, "for|while (")) { if (Token::Match(tok, "for|while (")) {
if (updateLoop(endBlock, condTok) == Progress::Break) Token* stepTok = getStepTok(tok);
if (updateLoop(endBlock, condTok, initTok, stepTok) == Progress::Break)
return Progress::Break; return Progress::Break;
tok = endBlock; tok = endBlock;
} else { } else {
@ -399,6 +431,21 @@ struct ForwardTraversal {
return tok->astOperand2()->astOperand1(); return tok->astOperand2()->astOperand1();
} }
template <class T>
static T* getStepTok(T* tok) {
if (!tok)
return nullptr;
if (Token::Match(tok, "%name% ("))
return getStepTok(tok->next());
if (!Token::simpleMatch(tok, "("))
return nullptr;
if (!Token::simpleMatch(tok->astOperand2(), ";"))
return nullptr;
if (!Token::simpleMatch(tok->astOperand2()->astOperand2(), ";"))
return nullptr;
return tok->astOperand2()->astOperand2()->astOperand2();
}
}; };
void valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<ForwardAnalyzer>& fa, const Settings* settings) void valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<ForwardAnalyzer>& fa, const Settings* settings)

View File

@ -2801,6 +2801,7 @@ private:
void valueFlowForLoop() { void valueFlowForLoop() {
const char *code; const char *code;
ValueFlow::Value value;
code = "void f() {\n" code = "void f() {\n"
" for (int x = 0; x < 10; x++)\n" " for (int x = 0; x < 10; x++)\n"
@ -3059,6 +3060,31 @@ private:
"}"; "}";
std::list<ValueFlow::Value> values = tokenValues(code, "x <"); std::list<ValueFlow::Value> values = tokenValues(code, "x <");
ASSERT(std::none_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isUninitValue))); ASSERT(std::none_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isUninitValue)));
// #9637
code = "void f() {\n"
" unsigned int x = 0;\n"
" for (x = 0; x < 2; x++) {}\n"
"}\n";
value = valueOfTok(code, "x <");
ASSERT(value.isPossible());
ASSERT_EQUALS(value.intvalue, 0);
code = "void f() {\n"
" unsigned int x = 0;\n"
" for (;x < 2; x++) {}\n"
"}\n";
value = valueOfTok(code, "x <");
ASSERT(value.isPossible());
ASSERT_EQUALS(0, value.intvalue);
code = "void f() {\n"
" unsigned int x = 1;\n"
" for (x = 0; x < 2; x++) {}\n"
"}\n";
value = valueOfTok(code, "x <");
ASSERT(value.isPossible());
ASSERT_EQUALS(0, value.intvalue);
} }
void valueFlowSubFunction() { void valueFlowSubFunction() {