Fix 10314: Possible nullPointerRedundantCheck false positive (#3298)

This commit is contained in:
Paul Fultz II 2021-06-19 06:59:48 -05:00 committed by GitHub
parent 5922d5178b
commit dd178c3ad9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 167 additions and 122 deletions

View File

@ -111,6 +111,16 @@ struct Analyzer {
unsigned int mFlag; unsigned int mFlag;
}; };
enum class Terminate { None, Bail, Escape, Modified, Inconclusive, Conditional };
struct Result {
Result(Action action = Action::None, Terminate terminate = Terminate::None)
: action(action), terminate(terminate)
{}
Action action;
Terminate terminate;
};
enum class Direction { Forward, Reverse }; enum class Direction { Forward, Reverse };
struct Assume { struct Assume {

View File

@ -1563,7 +1563,8 @@ bool isReturnScope(const Token* const endToken, const Library* library, const To
if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {")) if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {"))
return isReturnScope(prev, library, unknownFunc, functionScope) && return isReturnScope(prev, library, unknownFunc, functionScope) &&
isReturnScope(prev->link()->tokAt(-2), library, unknownFunc, functionScope); isReturnScope(prev->link()->tokAt(-2), library, unknownFunc, functionScope);
if (Token::simpleMatch(prev->link()->previous(), ") {") && // TODO: Check all cases
if (!functionScope && Token::simpleMatch(prev->link()->previous(), ") {") &&
Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") && Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") &&
!Token::findsimplematch(prev->link(), "break", prev)) { !Token::findsimplematch(prev->link(), "break", prev)) {
return true; return true;

View File

@ -22,11 +22,12 @@ struct ForwardTraversal {
Analyzer::Action actions; Analyzer::Action actions;
bool analyzeOnly; bool analyzeOnly;
bool analyzeTerminate; bool analyzeTerminate;
Terminate terminate = Terminate::None; Analyzer::Terminate terminate = Analyzer::Terminate::None;
bool forked = false; bool forked = false;
Progress Break(Terminate t = Terminate::None) { Progress Break(Analyzer::Terminate t = Analyzer::Terminate::None)
if ((!analyzeOnly || analyzeTerminate) && t != Terminate::None) {
if ((!analyzeOnly || analyzeTerminate) && t != Analyzer::Terminate::None)
terminate = t; terminate = t;
return Progress::Break; return Progress::Break;
} }
@ -89,7 +90,7 @@ struct ForwardTraversal {
else if (Token::Match(tok, "return|throw") || isEscapeFunction(tok, &settings->library)) { else if (Token::Match(tok, "return|throw") || isEscapeFunction(tok, &settings->library)) {
traverseRecursive(tok->astOperand1(), f, traverseUnknown); traverseRecursive(tok->astOperand1(), f, traverseUnknown);
traverseRecursive(tok->astOperand2(), f, traverseUnknown); traverseRecursive(tok->astOperand2(), f, traverseUnknown);
return Break(Terminate::Escape); return Break(Analyzer::Terminate::Escape);
} else if (isUnevaluated(tok)) { } else if (isUnevaluated(tok)) {
if (out) if (out)
*out = tok->link(); *out = tok->link();
@ -103,7 +104,7 @@ struct ForwardTraversal {
// Skip lambdas // Skip lambdas
} else if (T* lambdaEndToken = findLambdaEndToken(tok)) { } else if (T* lambdaEndToken = findLambdaEndToken(tok)) {
if (checkScope(lambdaEndToken).isModified()) if (checkScope(lambdaEndToken).isModified())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
if (out) if (out)
*out = lambdaEndToken->next(); *out = lambdaEndToken->next();
// Skip class scope // Skip class scope
@ -152,7 +153,7 @@ struct ForwardTraversal {
if (!checkThen && !checkElse) { if (!checkThen && !checkElse) {
// Stop if the value is conditional // Stop if the value is conditional
if (!traverseUnknown && analyzer->isConditional() && stopUpdates()) { if (!traverseUnknown && analyzer->isConditional() && stopUpdates()) {
return Break(Terminate::Conditional); return Break(Analyzer::Terminate::Conditional);
} }
checkThen = true; checkThen = true;
checkElse = true; checkElse = true;
@ -180,12 +181,12 @@ struct ForwardTraversal {
if (!action.isNone() && !analyzeOnly) if (!action.isNone() && !analyzeOnly)
analyzer->update(tok, action, Analyzer::Direction::Forward); analyzer->update(tok, action, Analyzer::Direction::Forward);
if (action.isInconclusive() && !analyzer->lowerToInconclusive()) if (action.isInconclusive() && !analyzer->lowerToInconclusive())
return Break(Terminate::Inconclusive); return Break(Analyzer::Terminate::Inconclusive);
if (action.isInvalid()) if (action.isInvalid())
return Break(Terminate::Modified); return Break(Analyzer::Terminate::Modified);
if (action.isWrite() && !action.isRead()) if (action.isWrite() && !action.isRead())
// Analysis of this write will continue separately // Analysis of this write will continue separately
return Break(Terminate::Modified); return Break(Analyzer::Terminate::Modified);
return Progress::Continue; return Progress::Continue;
} }
@ -320,13 +321,13 @@ struct ForwardTraversal {
if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) { if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) {
ForwardTraversal ft2 = fork(true); ForwardTraversal ft2 = fork(true);
ft2.updateScope(branch.endBlock); ft2.updateScope(branch.endBlock);
if (ft2.terminate == Terminate::Escape) { if (ft2.terminate == Analyzer::Terminate::Escape) {
branch.escape = true; branch.escape = true;
branch.escapeUnknown = false; branch.escapeUnknown = false;
} }
} }
} else { } else {
if (ft1.front().terminate == Terminate::Escape) { if (ft1.front().terminate == Analyzer::Terminate::Escape) {
branch.escape = true; branch.escape = true;
branch.escapeUnknown = false; branch.escapeUnknown = false;
} }
@ -387,10 +388,10 @@ struct ForwardTraversal {
actions |= allAnalysis; actions |= allAnalysis;
if (allAnalysis.isInconclusive()) { if (allAnalysis.isInconclusive()) {
if (!analyzer->lowerToInconclusive()) if (!analyzer->lowerToInconclusive())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} else if (allAnalysis.isModified()) { } else if (allAnalysis.isModified()) {
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} }
bool checkThen = true; bool checkThen = true;
bool checkElse = false; bool checkElse = false;
@ -409,9 +410,9 @@ struct ForwardTraversal {
return Break(); return Break();
// If loop re-enters then it could be modified again // If loop re-enters then it could be modified again
if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok)) if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok))
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
if (allAnalysis.isIncremental()) if (allAnalysis.isIncremental())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} else { } else {
std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, allAnalysis.isModified()); std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, allAnalysis.isModified());
bool forkContinue = true; bool forkContinue = true;
@ -425,9 +426,9 @@ struct ForwardTraversal {
if (allAnalysis.isModified() || !forkContinue) { if (allAnalysis.isModified() || !forkContinue) {
// TODO: Dont bail on missing condition // TODO: Dont bail on missing condition
if (!condTok) if (!condTok)
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
if (analyzer->isConditional() && stopUpdates()) if (analyzer->isConditional() && stopUpdates())
return Break(Terminate::Conditional); return Break(Analyzer::Terminate::Conditional);
analyzer->assume(condTok, false); analyzer->assume(condTok, false);
} }
if (forkContinue) { if (forkContinue) {
@ -437,7 +438,7 @@ struct ForwardTraversal {
} }
} }
if (allAnalysis.isIncremental()) if (allAnalysis.isIncremental())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} }
return Progress::Continue; return Progress::Continue;
} }
@ -451,7 +452,7 @@ struct ForwardTraversal {
Progress updateRange(Token* start, const Token* end, int depth = 20) { Progress updateRange(Token* start, const Token* end, int depth = 20) {
forked = false; forked = false;
if (depth < 0) if (depth < 0)
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
std::size_t i = 0; std::size_t i = 0;
for (Token* tok = start; tok && tok != end; tok = tok->next()) { for (Token* tok = start; tok && tok != end; tok = tok->next()) {
Token* next = nullptr; Token* next = nullptr;
@ -485,13 +486,13 @@ struct ForwardTraversal {
return Break(); return Break();
tok = skipTo(tok, scopeEndToken, end); tok = skipTo(tok, scopeEndToken, end);
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
// TODO: Don't break, instead move to the outer scope // TODO: Don't break, instead move to the outer scope
if (!tok) if (!tok)
return Break(); return Break();
} else if (Token::Match(tok, "%name% :") || tok->str() == "case") { } else if (Token::Match(tok, "%name% :") || tok->str() == "case") {
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} else if (tok->link() && tok->str() == "}") { } else if (tok->link() && tok->str() == "}") {
const Scope* scope = tok->scope(); const Scope* scope = tok->scope();
if (!scope) if (!scope)
@ -505,7 +506,7 @@ struct ForwardTraversal {
return Break(); return Break();
if (!condTok->hasKnownIntValue() || inLoop) { if (!condTok->hasKnownIntValue() || inLoop) {
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} else if (condTok->values().front().intvalue == inElse) { } else if (condTok->values().front().intvalue == inElse) {
return Break(); return Break();
} }
@ -524,7 +525,7 @@ struct ForwardTraversal {
tok = tok->linkAt(2); tok = tok->linkAt(2);
} else if (scope->type == Scope::eTry) { } else if (scope->type == Scope::eTry) {
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} else if (scope->type == Scope::eLambda) { } else if (scope->type == Scope::eLambda) {
return Break(); return Break();
} else if (scope->type == Scope::eDo && Token::simpleMatch(tok, "} while (")) { } else if (scope->type == Scope::eDo && Token::simpleMatch(tok, "} while (")) {
@ -595,32 +596,32 @@ struct ForwardTraversal {
return Break(); return Break();
if (thenBranch.isDead() && elseBranch.isDead()) { if (thenBranch.isDead() && elseBranch.isDead()) {
if (thenBranch.isModified() && elseBranch.isModified()) if (thenBranch.isModified() && elseBranch.isModified())
return Break(Terminate::Modified); return Break(Analyzer::Terminate::Modified);
if (thenBranch.isConclusiveEscape() && elseBranch.isConclusiveEscape()) if (thenBranch.isConclusiveEscape() && elseBranch.isConclusiveEscape())
return Break(Terminate::Escape); return Break(Analyzer::Terminate::Escape);
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} }
// Conditional return // Conditional return
if (thenBranch.isEscape() && !hasElse) { if (thenBranch.isEscape() && !hasElse) {
if (!thenBranch.isConclusiveEscape()) { if (!thenBranch.isConclusiveEscape()) {
if (!analyzer->lowerToInconclusive()) if (!analyzer->lowerToInconclusive())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} else if (thenBranch.check) { } else if (thenBranch.check) {
return Break(); return Break();
} else { } else {
if (analyzer->isConditional() && stopUpdates()) if (analyzer->isConditional() && stopUpdates())
return Break(Terminate::Conditional); return Break(Analyzer::Terminate::Conditional);
analyzer->assume(condTok, false); analyzer->assume(condTok, false);
} }
} }
if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) { if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) {
if (!analyzer->lowerToInconclusive()) if (!analyzer->lowerToInconclusive())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
} else if (thenBranch.isModified() || elseBranch.isModified()) { } else if (thenBranch.isModified() || elseBranch.isModified()) {
if (!hasElse && analyzer->isConditional() && stopUpdates()) if (!hasElse && analyzer->isConditional() && stopUpdates())
return Break(Terminate::Conditional); return Break(Analyzer::Terminate::Conditional);
if (!analyzer->lowerToPossible()) if (!analyzer->lowerToPossible())
return Break(Terminate::Bail); return Break(Analyzer::Terminate::Bail);
analyzer->assume(condTok, elseBranch.isModified()); analyzer->assume(condTok, elseBranch.isModified());
} }
} }
@ -742,19 +743,16 @@ struct ForwardTraversal {
}; };
Analyzer::Action valueFlowGenericForward(Token* start, Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings* settings)
const Token* end,
const ValuePtr<Analyzer>& a,
const Settings* settings)
{ {
ForwardTraversal ft{a, settings}; ForwardTraversal ft{a, settings};
ft.updateRange(start, end); ft.updateRange(start, end);
return ft.actions; return {ft.actions, ft.terminate};
} }
Analyzer::Action valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings) Analyzer::Result valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings)
{ {
ForwardTraversal ft{a, settings}; ForwardTraversal ft{a, settings};
ft.updateRecursive(start); ft.updateRecursive(start);
return ft.actions; return {ft.actions, ft.terminate};
} }

View File

@ -25,11 +25,11 @@ class Settings;
class Token; class Token;
template <class T> class ValuePtr; template <class T> class ValuePtr;
Analyzer::Action valueFlowGenericForward(Token* start, Analyzer::Result valueFlowGenericForward(Token* start,
const Token* end, const Token* end,
const ValuePtr<Analyzer>& a, const ValuePtr<Analyzer>& a,
const Settings* settings); const Settings* settings);
Analyzer::Action valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings); Analyzer::Result valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings);
#endif #endif

View File

@ -1664,12 +1664,12 @@ static void valueFlowGlobalStaticVar(TokenList *tokenList, const Settings *setti
} }
} }
static Analyzer::Action valueFlowForwardVariable(Token* const startToken, static Analyzer::Result valueFlowForwardVariable(Token* const startToken,
const Token* const endToken, const Token* const endToken,
const Variable* const var, const Variable* const var,
std::list<ValueFlow::Value> values, std::list<ValueFlow::Value> values,
TokenList* const tokenlist, TokenList* const tokenlist,
const Settings* const settings); const Settings* const settings);
static void valueFlowReverse(TokenList* tokenlist, static void valueFlowReverse(TokenList* tokenlist,
Token* tok, Token* tok,
@ -2407,28 +2407,32 @@ static std::vector<const Variable*> getAliasesFromValues(std::list<ValueFlow::Va
return aliases; return aliases;
} }
static Analyzer::Action valueFlowForwardVariable(Token* const startToken, static Analyzer::Result valueFlowForwardVariable(Token* const startToken,
const Token* const endToken, const Token* const endToken,
const Variable* const var, const Variable* const var,
std::list<ValueFlow::Value> values, std::list<ValueFlow::Value> values,
std::vector<const Variable*> aliases, std::vector<const Variable*> aliases,
TokenList* const tokenlist, TokenList* const tokenlist,
const Settings* const settings) const Settings* const settings)
{ {
Analyzer::Action actions; Analyzer::Action actions;
Analyzer::Terminate terminate = Analyzer::Terminate::None;
for (ValueFlow::Value& v : values) { for (ValueFlow::Value& v : values) {
VariableAnalyzer a(var, v, aliases, tokenlist); VariableAnalyzer a(var, v, aliases, tokenlist);
actions |= valueFlowGenericForward(startToken, endToken, a, settings); Analyzer::Result r = valueFlowGenericForward(startToken, endToken, a, settings);
actions |= r.action;
if (terminate == Analyzer::Terminate::None)
terminate = r.terminate;
} }
return actions; return {actions, terminate};
} }
static Analyzer::Action valueFlowForwardVariable(Token* const startToken, static Analyzer::Result valueFlowForwardVariable(Token* const startToken,
const Token* const endToken, const Token* const endToken,
const Variable* const var, const Variable* const var,
std::list<ValueFlow::Value> values, std::list<ValueFlow::Value> values,
TokenList* const tokenlist, TokenList* const tokenlist,
const Settings* const settings) const Settings* const settings)
{ {
auto aliases = getAliasesFromValues(values); auto aliases = getAliasesFromValues(values);
return valueFlowForwardVariable( return valueFlowForwardVariable(
@ -2527,19 +2531,23 @@ struct OppositeExpressionAnalyzer : ExpressionAnalyzer {
} }
}; };
static Analyzer::Action valueFlowForwardExpression(Token* startToken, static Analyzer::Result valueFlowForwardExpression(Token* startToken,
const Token* endToken, const Token* endToken,
const Token* exprTok, const Token* exprTok,
const std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& values,
const TokenList* const tokenlist, const TokenList* const tokenlist,
const Settings* settings) const Settings* settings)
{ {
Analyzer::Action actions; Analyzer::Action actions;
Analyzer::Terminate terminate = Analyzer::Terminate::None;
for (const ValueFlow::Value& v : values) { for (const ValueFlow::Value& v : values) {
ExpressionAnalyzer a(exprTok, v, tokenlist); ExpressionAnalyzer a(exprTok, v, tokenlist);
actions |= valueFlowGenericForward(startToken, endToken, a, settings); Analyzer::Result r = valueFlowGenericForward(startToken, endToken, a, settings);
actions |= r.action;
if (terminate == Analyzer::Terminate::None)
terminate = r.terminate;
} }
return actions; return {actions, terminate};
} }
static const Token* parseBinaryIntOp(const Token* expr, MathLib::bigint& known) static const Token* parseBinaryIntOp(const Token* expr, MathLib::bigint& known)
@ -2614,12 +2622,12 @@ ValuePtr<Analyzer> makeAnalyzer(Token* exprTok, const ValueFlow::Value& value, c
} }
} }
static Analyzer::Action valueFlowForward(Token* startToken, static Analyzer::Result valueFlowForward(Token* startToken,
const Token* endToken, const Token* endToken,
const Token* exprTok, const Token* exprTok,
std::list<ValueFlow::Value> values, std::list<ValueFlow::Value> values,
TokenList* const tokenlist, TokenList* const tokenlist,
const Settings* settings) const Settings* settings)
{ {
const Token* expr = solveExprValues(exprTok, values); const Token* expr = solveExprValues(exprTok, values);
if (expr->variable()) { if (expr->variable()) {
@ -4285,12 +4293,12 @@ struct ConditionHandler {
Condition() : vartok(nullptr), true_values(), false_values(), inverted(false) {} Condition() : vartok(nullptr), true_values(), false_values(), inverted(false) {}
}; };
virtual bool forward(Token* start, virtual Analyzer::Result forward(Token* start,
const Token* stop, const Token* stop,
const Token* exprTok, const Token* exprTok,
const std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& values,
TokenList* tokenlist, TokenList* tokenlist,
const Settings* settings) const = 0; const Settings* settings) const = 0;
virtual void reverse(Token* start, virtual void reverse(Token* start,
const Token* endToken, const Token* endToken,
@ -4577,9 +4585,6 @@ struct ConditionHandler {
return; return;
} }
// start token of conditional code
Token* startTokens[] = {nullptr, nullptr};
// if astParent is "!" we need to invert codeblock // if astParent is "!" we need to invert codeblock
{ {
const Token* tok2 = tok; const Token* tok2 = tok;
@ -4593,6 +4598,9 @@ struct ConditionHandler {
} }
} }
bool deadBranch[] = {false, false};
// start token of conditional code
Token* startTokens[] = {nullptr, nullptr};
// determine startToken(s) // determine startToken(s)
if (Token::simpleMatch(top->link(), ") {")) if (Token::simpleMatch(top->link(), ") {"))
startTokens[0] = top->link()->next(); startTokens[0] = top->link()->next();
@ -4608,12 +4616,13 @@ struct ConditionHandler {
std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues); std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues);
valueFlowSetConditionToKnown(tok, values, i == 0); valueFlowSetConditionToKnown(tok, values, i == 0);
// TODO: The endToken should not be startTokens[i]->link() in the valueFlowForwardVariable call Analyzer::Result r =
if (forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings)) 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; changeBlock = i;
changeKnownToPossible(values); changeKnownToPossible(values);
} }
// TODO: Values changed in noreturn blocks should not bail
if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) { if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) {
if (settings->debugwarnings) if (settings->debugwarnings)
bailout(tokenlist, bailout(tokenlist,
@ -4627,12 +4636,13 @@ struct ConditionHandler {
// After conditional code.. // After conditional code..
if (Token::simpleMatch(top->link(), ") {")) { if (Token::simpleMatch(top->link(), ") {")) {
Token* after = top->link()->linkAt(1); Token* after = top->link()->linkAt(1);
bool dead_if = deadBranch[0];
bool dead_else = deadBranch[1];
const Token* unknownFunction = nullptr; const Token* unknownFunction = nullptr;
const bool isWhile = if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while ("))
tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while ("); dead_if = !isBreakScope(after);
bool dead_if = (!isBreakScope(after) && isWhile) || else if (!dead_if)
(isReturnScope(after, &settings->library, &unknownFunction) && !isWhile); dead_if = isReturnScope(after, &settings->library, &unknownFunction);
bool dead_else = false;
if (!dead_if && unknownFunction) { if (!dead_if && unknownFunction) {
if (settings->debugwarnings) if (settings->debugwarnings)
@ -4643,7 +4653,8 @@ struct ConditionHandler {
if (Token::simpleMatch(after, "} else {")) { if (Token::simpleMatch(after, "} else {")) {
after = after->linkAt(2); after = after->linkAt(2);
unknownFunction = nullptr; unknownFunction = nullptr;
dead_else = isReturnScope(after, &settings->library, &unknownFunction); if (!dead_else)
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
if (!dead_else && unknownFunction) { if (!dead_else && unknownFunction) {
if (settings->debugwarnings) if (settings->debugwarnings)
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope"); bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
@ -4717,13 +4728,14 @@ static void valueFlowCondition(const ValuePtr<ConditionHandler>& handler,
} }
struct SimpleConditionHandler : ConditionHandler { struct SimpleConditionHandler : ConditionHandler {
virtual bool forward(Token* start, virtual Analyzer::Result forward(Token* start,
const Token* stop, const Token* stop,
const Token* exprTok, const Token* exprTok,
const std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& values,
TokenList* tokenlist, TokenList* tokenlist,
const Settings* settings) const OVERRIDE { const Settings* settings) const OVERRIDE
return valueFlowForward(start->next(), stop, exprTok, values, tokenlist, settings).isModified(); {
return valueFlowForward(start->next(), stop, exprTok, values, tokenlist, settings);
} }
virtual void reverse(Token* start, virtual void reverse(Token* start,
@ -6081,19 +6093,19 @@ struct ContainerVariableAnalyzer : VariableAnalyzer {
} }
}; };
static Analyzer::Action valueFlowContainerForward(Token* tok, static Analyzer::Result valueFlowContainerForward(Token* tok,
const Token* endToken, const Token* endToken,
const Variable* var, const Variable* var,
ValueFlow::Value value, ValueFlow::Value value,
TokenList* tokenlist) TokenList* tokenlist)
{ {
ContainerVariableAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist); ContainerVariableAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist);
return valueFlowGenericForward(tok, endToken, a, tokenlist->getSettings()); return valueFlowGenericForward(tok, endToken, a, tokenlist->getSettings());
} }
static Analyzer::Action valueFlowContainerForward(Token* tok, static Analyzer::Result valueFlowContainerForward(Token* tok,
const Variable* var, const Variable* var,
ValueFlow::Value value, ValueFlow::Value value,
TokenList* tokenlist) TokenList* tokenlist)
{ {
const Token * endOfVarScope = nullptr; const Token * endOfVarScope = nullptr;
if (var->isLocal() || var->isArgument()) if (var->isLocal() || var->isArgument())
@ -6506,19 +6518,20 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold
} }
struct ContainerConditionHandler : ConditionHandler { struct ContainerConditionHandler : ConditionHandler {
virtual bool forward(Token* start, virtual Analyzer::Result forward(Token* start,
const Token* stop, const Token* stop,
const Token* exprTok, const Token* exprTok,
const std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& values,
TokenList* tokenlist, TokenList* tokenlist,
const Settings*) const OVERRIDE { const Settings*) const OVERRIDE
{
// TODO: Forward multiple values // TODO: Forward multiple values
if (values.empty()) if (values.empty())
return false; return {};
const Variable* var = exprTok->variable(); const Variable* var = exprTok->variable();
if (!var) if (!var)
return false; return {};
return valueFlowContainerForward(start->next(), stop, var, values.front(), tokenlist).isModified(); return valueFlowContainerForward(start->next(), stop, var, values.front(), tokenlist);
} }
virtual void reverse(Token* start, virtual void reverse(Token* start,

View File

@ -2718,6 +2718,29 @@ private:
" return x;\n" " return x;\n"
"}\n"; "}\n";
ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0)); ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0));
code = "int g(int x) {\n"
" switch (x) {\n"
" case 1:\n"
" return 1;\n"
" default:\n"
" return 2;\n"
" }\n"
"}\n"
"void f(int x) {\n"
" if (x == 3)\n"
" x = g(0);\n"
" int a = x;\n"
"}\n";
ASSERT_EQUALS(false, testValueOfX(code, 12U, 3));
code = "int g(int x) { throw 0; }\n"
"void f(int x) {\n"
" if (x == 3)\n"
" x = g(0);\n"
" int a = x;\n"
"}\n";
ASSERT_EQUALS(true, testValueOfXImpossible(code, 5U, 3));
} }
void valueFlowAfterConditionExpr() { void valueFlowAfterConditionExpr() {