Fix issue 9890: ValueFlow: known value not found (variable is changed in path that returns) (#3010)

This commit is contained in:
Paul Fultz II 2021-01-05 09:49:08 -06:00 committed by GitHub
parent a95c931da0
commit f0b5668436
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 28 deletions

View File

@ -1,4 +1,5 @@
#include "forwardanalyzer.h" #include "forwardanalyzer.h"
#include "analyzer.h"
#include "astutils.h" #include "astutils.h"
#include "settings.h" #include "settings.h"
#include "symboldatabase.h" #include "symboldatabase.h"
@ -18,6 +19,29 @@ struct ForwardTraversal {
Analyzer::Action actions; Analyzer::Action actions;
bool analyzeOnly; bool analyzeOnly;
struct Branch {
Analyzer::Action action = Analyzer::Action::None;
bool check = false;
bool escape = false;
bool escapeUnknown = false;
const Token* endBlock = nullptr;
bool isEscape() const {
return escape || escapeUnknown;
}
bool isConclusiveEscape() const {
return escape && !escapeUnknown;
}
bool isModified() const {
return action.isModified() && !isConclusiveEscape();
}
bool isInconclusive() const {
return action.isInconclusive() && !isConclusiveEscape();
}
bool isDead() const {
return action.isModified() || action.isInconclusive() || isEscape();
}
};
bool stopUpdates() { bool stopUpdates() {
analyzeOnly = true; analyzeOnly = true;
return actions.isModified(); return actions.isModified();
@ -186,6 +210,11 @@ struct ForwardTraversal {
return result; return result;
} }
void forkRange(Token* start, const Token* end) {
ForwardTraversal ft = *this;
ft.updateRange(start, end);
}
void forkScope(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;
@ -197,11 +226,11 @@ struct ForwardTraversal {
return Token::findsimplematch(endBlock->link(), "goto", endBlock); return Token::findsimplematch(endBlock->link(), "goto", endBlock);
} }
bool isEscapeScope(const Token* endBlock, bool unknown = false) { bool isEscapeScope(const Token* endBlock, bool& unknown) {
const Token* ftok = nullptr; const Token* ftok = nullptr;
bool r = isReturnScope(endBlock, &settings->library, &ftok); bool r = isReturnScope(endBlock, &settings->library, &ftok);
if (!r && ftok) if (!r && ftok)
return unknown; unknown = true;
return r; return r;
} }
@ -379,34 +408,32 @@ struct ForwardTraversal {
// Traverse condition // Traverse condition
if (updateRecursive(condTok) == Progress::Break) if (updateRecursive(condTok) == Progress::Break)
return Progress::Break; return Progress::Break;
Branch thenBranch{};
Branch elseBranch{};
// Check if condition is true or false // Check if condition is true or false
bool checkThen, checkElse; std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok);
std::tie(checkThen, checkElse) = evalCond(condTok);
Analyzer::Action thenAction = Analyzer::Action::None;
Analyzer::Action elseAction = Analyzer::Action::None;
bool hasElse = Token::simpleMatch(endBlock, "} else {"); bool hasElse = Token::simpleMatch(endBlock, "} else {");
bool bail = false; bool bail = false;
// Traverse then block // Traverse then block
bool returnThen = isEscapeScope(endBlock, true); thenBranch.escape = isEscapeScope(endBlock, thenBranch.escapeUnknown);
bool returnElse = false; if (thenBranch.check) {
if (checkThen) {
if (updateRange(endCond->next(), endBlock) == Progress::Break) if (updateRange(endCond->next(), endBlock) == Progress::Break)
return Progress::Break; return Progress::Break;
} else if (!checkElse) { } else if (!elseBranch.check) {
thenAction = checkScope(endBlock); thenBranch.action = checkScope(endBlock);
if (hasGoto(endBlock)) if (hasGoto(endBlock))
bail = true; bail = true;
} }
// Traverse else block // Traverse else block
if (hasElse) { if (hasElse) {
returnElse = isEscapeScope(endBlock->linkAt(2), true); elseBranch.escape = isEscapeScope(endBlock->linkAt(2), elseBranch.escapeUnknown);
if (checkElse) { if (elseBranch.check) {
Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2)); Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2));
if (result == Progress::Break) if (result == Progress::Break)
return Progress::Break; return Progress::Break;
} else if (!checkThen) { } else if (!thenBranch.check) {
elseAction = checkScope(endBlock->linkAt(2)); elseBranch.action = checkScope(endBlock->linkAt(2));
if (hasGoto(endBlock)) if (hasGoto(endBlock))
bail = true; bail = true;
} }
@ -414,18 +441,18 @@ struct ForwardTraversal {
} else { } else {
tok = endBlock; tok = endBlock;
} }
actions |= (thenAction | elseAction); actions |= (thenBranch.action | elseBranch.action);
if (bail) if (bail)
return Progress::Break; return Progress::Break;
if (returnThen && returnElse) if (thenBranch.isDead() && elseBranch.isDead())
return Progress::Break;
else if (thenAction.isModified() && elseAction.isModified())
return Progress::Break;
else if ((returnThen || returnElse) && (thenAction.isModified() || elseAction.isModified()))
return Progress::Break; return Progress::Break;
// Conditional return // Conditional return
if (returnThen && !hasElse) { if (thenBranch.isEscape() && !hasElse) {
if (checkThen) { if (!thenBranch.isConclusiveEscape()) {
if (!analyzer->lowerToInconclusive())
return Progress::Break;
}
else if (thenBranch.check) {
return Progress::Break; return Progress::Break;
} else { } else {
if (analyzer->isConditional() && stopUpdates()) if (analyzer->isConditional() && stopUpdates())
@ -433,15 +460,15 @@ struct ForwardTraversal {
analyzer->assume(condTok, false); analyzer->assume(condTok, false);
} }
} }
if (thenAction.isInconclusive() || elseAction.isInconclusive()) { if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) {
if (!analyzer->lowerToInconclusive()) if (!analyzer->lowerToInconclusive())
return Progress::Break; return Progress::Break;
} else if (thenAction.isModified() || elseAction.isModified()) { } else if (thenBranch.isModified() || elseBranch.isModified()) {
if (!hasElse && analyzer->isConditional() && stopUpdates()) if (!hasElse && analyzer->isConditional() && stopUpdates())
return Progress::Break; return Progress::Break;
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Progress::Break; return Progress::Break;
analyzer->assume(condTok, elseAction.isModified()); analyzer->assume(condTok, elseBranch.isModified());
} }
} }
} else if (Token::simpleMatch(tok, "try {")) { } else if (Token::simpleMatch(tok, "try {")) {

View File

@ -445,7 +445,7 @@ unsigned int TemplateSimplifier::templateParameters(const Token *tok)
} }
// skip std:: // skip std::
if (tok && tok->str() == "::") if (tok->str() == "::")
tok = tok->next(); tok = tok->next();
while (Token::Match(tok, "%name% ::")) { while (Token::Match(tok, "%name% ::")) {
tok = tok->tokAt(2); tok = tok->tokAt(2);

View File

@ -2314,7 +2314,7 @@ struct SingleValueFlowAnalyzer : ValueFlowAnalyzer {
if (value.conditional) if (value.conditional)
return true; return true;
if (value.condition) if (value.condition)
return !value.isImpossible(); return !value.isKnown() && !value.isImpossible();
return false; return false;
} }

View File

@ -3494,6 +3494,23 @@ private:
" if (p != (uint32_t) -1) {}\n" " if (p != (uint32_t) -1) {}\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// #9890
check("int g(int);\n"
"bool h(int*);\n"
"int f(int *x) {\n"
" int y = g(0);\n"
" if (!y) {\n"
" if (h(x)) {\n"
" y = g(1);\n"
" if (y) {}\n"
" return 0;\n"
" }\n"
" if (!y) {}\n"
" }\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:11]: (style) Condition '!y' is always true\n", errout.str());
} }
void alwaysTrueInfer() { void alwaysTrueInfer() {