Fix 10314: Possible nullPointerRedundantCheck false positive (#3298)
This commit is contained in:
parent
5922d5178b
commit
dd178c3ad9
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1664,7 +1664,7 @@ 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,
|
||||||
|
@ -2407,7 +2407,7 @@ 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,
|
||||||
|
@ -2416,14 +2416,18 @@ static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||||
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,
|
||||||
|
@ -2527,7 +2531,7 @@ 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,
|
||||||
|
@ -2535,11 +2539,15 @@ static Analyzer::Action valueFlowForwardExpression(Token* startToken,
|
||||||
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,7 +2622,7 @@ 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,
|
||||||
|
@ -4285,7 +4293,7 @@ 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,
|
||||||
|
@ -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,6 +4653,7 @@ struct ConditionHandler {
|
||||||
if (Token::simpleMatch(after, "} else {")) {
|
if (Token::simpleMatch(after, "} else {")) {
|
||||||
after = after->linkAt(2);
|
after = after->linkAt(2);
|
||||||
unknownFunction = nullptr;
|
unknownFunction = nullptr;
|
||||||
|
if (!dead_else)
|
||||||
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
|
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
|
||||||
if (!dead_else && unknownFunction) {
|
if (!dead_else && unknownFunction) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
|
@ -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,7 +6093,7 @@ 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,
|
||||||
|
@ -6090,7 +6102,7 @@ static Analyzer::Action valueFlowContainerForward(Token* tok,
|
||||||
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)
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue