Skip to content

Commit c860de8

Browse files
authored
Fix issue 8143: valueFlowCondition: before and inside while (#3045)
1 parent d80f2fb commit c860de8

6 files changed

Lines changed: 149 additions & 39 deletions

File tree

lib/astutils.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,24 @@ const Token* findAstNode(const Token* ast, const std::function<bool(const Token*
8181
return result;
8282
}
8383

84+
const Token* findExpression(MathLib::bigint exprid,
85+
const Token* start,
86+
const Token* end,
87+
const std::function<bool(const Token*)>& pred)
88+
{
89+
if (!precedes(start, end))
90+
return nullptr;
91+
if (exprid == 0)
92+
return nullptr;
93+
for (const Token* tok = start; tok != end; tok = tok->next()) {
94+
if (tok->exprId() != exprid)
95+
continue;
96+
if (pred(tok))
97+
return tok;
98+
}
99+
return nullptr;
100+
}
101+
84102
static void astFlattenRecursive(const Token *tok, std::vector<const Token *> *result, const char* op, nonneg int depth = 0)
85103
{
86104
++depth;

lib/astutils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <vector>
2929

3030
#include "errortypes.h"
31+
#include "mathlib.h"
3132
#include "utils.h"
3233

3334
class Function;
@@ -52,6 +53,10 @@ void visitAstNodes(const Token *ast, std::function<ChildrenToVisit(const Token *
5253
void visitAstNodes(Token *ast, std::function<ChildrenToVisit(Token *)> visitor);
5354

5455
const Token* findAstNode(const Token* ast, const std::function<bool(const Token*)>& pred);
56+
const Token* findExpression(MathLib::bigint exprid,
57+
const Token* start,
58+
const Token* end,
59+
const std::function<bool(const Token*)>& pred);
5560

5661
std::vector<const Token*> astFlatten(const Token* tok, const char* op);
5762

lib/reverseanalyzer.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ struct ReverseTraversal {
116116
return nullptr;
117117
}
118118

119-
void traverse(Token* start) {
120-
for (Token* tok = start->previous(); tok; tok = tok->previous()) {
119+
void traverse(Token* start, const Token* end = nullptr)
120+
{
121+
if (start == end)
122+
return;
123+
for (Token* tok = start->previous(); tok != end; tok = tok->previous()) {
121124
if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction ||
122125
tok->scope()->type == Scope::ScopeType::eLambda))) {
123126
break;
@@ -171,7 +174,7 @@ struct ReverseTraversal {
171174
assignTok->astOperand2()->scope()->bodyEnd,
172175
a,
173176
settings);
174-
valueFlowGenericReverse(assignTok->astOperand1()->previous(), a, settings);
177+
valueFlowGenericReverse(assignTok->astOperand1()->previous(), end, a, settings);
175178
}
176179
}
177180
}
@@ -284,3 +287,9 @@ void valueFlowGenericReverse(Token* start, const ValuePtr<Analyzer>& a, const Se
284287
ReverseTraversal rt{a, settings};
285288
rt.traverse(start);
286289
}
290+
291+
void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings* settings)
292+
{
293+
ReverseTraversal rt{a, settings};
294+
rt.traverse(start, end);
295+
}

lib/reverseanalyzer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ template <class T>
2626
class ValuePtr;
2727

2828
void valueFlowGenericReverse(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings);
29+
void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings* settings);
2930

3031
#endif

lib/valueflow.cpp

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2432,6 +2432,7 @@ static void valueFlowReverse(TokenList* tokenlist,
24322432
}
24332433

24342434
static void valueFlowReverse(Token* tok,
2435+
const Token* const endToken,
24352436
const Token* const varToken,
24362437
const std::list<ValueFlow::Value>& values,
24372438
TokenList* tokenlist,
@@ -2442,16 +2443,25 @@ static void valueFlowReverse(Token* tok,
24422443
auto aliases = getAliasesFromValues(values);
24432444
for (const ValueFlow::Value& v : values) {
24442445
VariableAnalyzer a(var, v, aliases, tokenlist);
2445-
valueFlowGenericReverse(tok, a, settings);
2446+
valueFlowGenericReverse(tok, endToken, a, settings);
24462447
}
24472448
} else {
24482449
for (const ValueFlow::Value& v : values) {
24492450
ExpressionAnalyzer a(varToken, v, tokenlist);
2450-
valueFlowGenericReverse(tok, a, settings);
2451+
valueFlowGenericReverse(tok, endToken, a, settings);
24512452
}
24522453
}
24532454
}
24542455

2456+
static void valueFlowReverse(Token* tok,
2457+
const Token* const varToken,
2458+
const std::list<ValueFlow::Value>& values,
2459+
TokenList* tokenlist,
2460+
const Settings* settings)
2461+
{
2462+
valueFlowReverse(tok, nullptr, varToken, values, tokenlist, settings);
2463+
}
2464+
24552465
std::string lifetimeType(const Token *tok, const ValueFlow::Value *val)
24562466
{
24572467
std::string result;
@@ -4058,6 +4068,7 @@ struct ConditionHandler {
40584068
const Settings* settings) const = 0;
40594069

40604070
virtual void reverse(Token* start,
4071+
const Token* endToken,
40614072
const Token* exprTok,
40624073
const std::list<ValueFlow::Value>& values,
40634074
TokenList* tokenlist,
@@ -4139,6 +4150,8 @@ struct ConditionHandler {
41394150

41404151
if (Token::Match(top, "%assign%"))
41414152
return;
4153+
if (Token::Match(cond.vartok->astParent(), "%assign%|++|--"))
4154+
return;
41424155

41434156
if (Token::simpleMatch(tok->astParent(), "?") && tok->astParent()->isExpandedMacro()) {
41444157
if (settings->debugwarnings)
@@ -4159,6 +4172,23 @@ struct ConditionHandler {
41594172
return;
41604173
}
41614174

4175+
std::list<ValueFlow::Value> values = cond.true_values;
4176+
if (cond.true_values != cond.false_values)
4177+
values.insert(values.end(), cond.false_values.begin(), cond.false_values.end());
4178+
4179+
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
4180+
if (Token::Match(tok, "<|>")) {
4181+
values.remove_if([](const ValueFlow::Value& v) {
4182+
if (v.isIntValue())
4183+
return v.intvalue != 0;
4184+
return false;
4185+
});
4186+
if (cond.vartok->valueType() && cond.vartok->valueType()->sign != ValueType::Sign::UNSIGNED)
4187+
return;
4188+
}
4189+
if (values.empty())
4190+
return;
4191+
41624192
// bailout: for/while-condition, variable is changed in while loop
41634193
if (Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) {
41644194

@@ -4177,40 +4207,31 @@ struct ConditionHandler {
41774207
}
41784208

41794209
// Variable changed in loop code
4180-
if (Token::Match(top->previous(), "for|while (")) {
4181-
const Token* const start = top;
4182-
const Token* const block = top->link()->next();
4183-
const Token* const end = block->link();
4184-
4185-
if (isExpressionChanged(cond.vartok, start, end, settings, tokenlist->isCPP())) {
4186-
if (settings->debugwarnings)
4187-
bailout(tokenlist,
4188-
errorLogger,
4189-
tok,
4190-
"variable '" + cond.vartok->expressionString() + "' used in loop");
4191-
return;
4210+
const Token* const start = top;
4211+
const Token* const block = top->link()->next();
4212+
const Token* const end = block->link();
4213+
4214+
if (isExpressionChanged(cond.vartok, start, end, settings, tokenlist->isCPP())) {
4215+
// If its reassigned in loop then analyze from the end
4216+
if (!Token::Match(tok, "%assign%|++|--") &&
4217+
findExpression(cond.vartok->exprId(), start, end, [&](const Token* tok2) {
4218+
return Token::Match(tok2->astParent(), "%assign%") && astIsLHS(tok2);
4219+
})) {
4220+
// Start at the end of the loop body
4221+
Token* bodyTok = top->link()->next();
4222+
reverse(bodyTok->link(), bodyTok, cond.vartok, values, tokenlist, settings);
41924223
}
4224+
if (settings->debugwarnings)
4225+
bailout(tokenlist,
4226+
errorLogger,
4227+
tok,
4228+
"variable '" + cond.vartok->expressionString() + "' used in loop");
4229+
return;
41934230
}
41944231
}
41954232

4196-
std::list<ValueFlow::Value> values = cond.true_values;
4197-
if (cond.true_values != cond.false_values)
4198-
values.insert(values.end(), cond.false_values.begin(), cond.false_values.end());
4199-
4200-
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
4201-
if (Token::Match(tok, "<|>")) {
4202-
values.remove_if([](const ValueFlow::Value& v) {
4203-
if (v.isIntValue())
4204-
return v.intvalue != 0;
4205-
return false;
4206-
});
4207-
if (cond.vartok->valueType() && cond.vartok->valueType()->sign != ValueType::Sign::UNSIGNED)
4208-
return;
4209-
}
4210-
if (values.empty())
4211-
return;
42124233
Token* startTok = tok->astParent() ? tok->astParent() : tok->previous();
4213-
reverse(startTok, cond.vartok, values, tokenlist, settings);
4234+
reverse(startTok, nullptr, cond.vartok, values, tokenlist, settings);
42144235
});
42154236
}
42164237

@@ -4453,11 +4474,13 @@ struct SimpleConditionHandler : ConditionHandler {
44534474
}
44544475

44554476
virtual void reverse(Token* start,
4477+
const Token* endToken,
44564478
const Token* exprTok,
44574479
const std::list<ValueFlow::Value>& values,
44584480
TokenList* tokenlist,
4459-
const Settings* settings) const OVERRIDE {
4460-
return valueFlowReverse(start, exprTok, values, tokenlist, settings);
4481+
const Settings* settings) const OVERRIDE
4482+
{
4483+
return valueFlowReverse(start, endToken, exprTok, values, tokenlist, settings);
44614484
}
44624485

44634486
virtual Condition parse(const Token* tok, const Settings*) const OVERRIDE {
@@ -5803,6 +5826,7 @@ static Analyzer::Action valueFlowContainerForward(Token* tok,
58035826
}
58045827

58055828
static void valueFlowContainerReverse(Token* tok,
5829+
const Token* const endToken,
58065830
const Token* const varToken,
58075831
const std::list<ValueFlow::Value>& values,
58085832
TokenList* tokenlist,
@@ -5812,10 +5836,19 @@ static void valueFlowContainerReverse(Token* tok,
58125836
auto aliases = getAliasesFromValues(values);
58135837
for (const ValueFlow::Value& value : values) {
58145838
ContainerVariableAnalyzer a(var, value, aliases, tokenlist);
5815-
valueFlowGenericReverse(tok, a, settings);
5839+
valueFlowGenericReverse(tok, endToken, a, settings);
58165840
}
58175841
}
58185842

5843+
static void valueFlowContainerReverse(Token* tok,
5844+
const Token* const varToken,
5845+
const std::list<ValueFlow::Value>& values,
5846+
TokenList* tokenlist,
5847+
const Settings* settings)
5848+
{
5849+
valueFlowContainerReverse(tok, nullptr, varToken, values, tokenlist, settings);
5850+
}
5851+
58195852
static bool isContainerSizeChanged(const Token *tok, int depth)
58205853
{
58215854
if (!tok)
@@ -6162,15 +6195,17 @@ struct ContainerConditionHandler : ConditionHandler {
61626195
}
61636196

61646197
virtual void reverse(Token* start,
6198+
const Token* endTok,
61656199
const Token* exprTok,
61666200
const std::list<ValueFlow::Value>& values,
61676201
TokenList* tokenlist,
6168-
const Settings* settings) const OVERRIDE {
6202+
const Settings* settings) const OVERRIDE
6203+
{
61696204
if (values.empty())
61706205
return;
61716206
if (!exprTok->variable())
61726207
return;
6173-
return valueFlowContainerReverse(start, exprTok, values, tokenlist, settings);
6208+
return valueFlowContainerReverse(start, endTok, exprTok, values, tokenlist, settings);
61746209
}
61756210

61766211
virtual Condition parse(const Token* tok, const Settings*) const OVERRIDE {

test/testnullpointer.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class TestNullPointer : public TestFixture {
109109
TEST_CASE(nullpointer66); // #10024
110110
TEST_CASE(nullpointer67); // #10062
111111
TEST_CASE(nullpointer68);
112+
TEST_CASE(nullpointer69); // #8143
112113
TEST_CASE(nullpointer_addressOf); // address of
113114
TEST_CASE(nullpointerSwitch); // #2626
114115
TEST_CASE(nullpointer_cast); // #4692
@@ -2135,6 +2136,47 @@ class TestNullPointer : public TestFixture {
21352136
ASSERT_EQUALS("", errout.str());
21362137
}
21372138

2139+
void nullpointer69()
2140+
{
2141+
check("void f(const Scope *scope) {\n"
2142+
" if (scope->definedType) {}\n"
2143+
" while (scope) {\n"
2144+
" scope = scope->nestedIn;\n"
2145+
" enumerator = scope->findEnumerator();\n"
2146+
" }\n"
2147+
"}\n");
2148+
ASSERT_EQUALS(
2149+
"[test.cpp:3] -> [test.cpp:5]: (warning) Either the condition 'scope' is redundant or there is possible null pointer dereference: scope.\n",
2150+
errout.str());
2151+
2152+
check("void f(const Scope *scope) {\n"
2153+
" if (scope->definedType) {}\n"
2154+
" while (scope && scope->nestedIn) {\n"
2155+
" if (scope->type == Scope::eFunction && scope->functionOf)\n"
2156+
" scope = scope->functionOf;\n"
2157+
" else\n"
2158+
" scope = scope->nestedIn;\n"
2159+
" enumerator = scope->findEnumerator();\n"
2160+
" }\n"
2161+
"}\n");
2162+
ASSERT_EQUALS(
2163+
"[test.cpp:3] -> [test.cpp:8]: (warning) Either the condition 'scope' is redundant or there is possible null pointer dereference: scope.\n",
2164+
errout.str());
2165+
2166+
check("struct a {\n"
2167+
" a *b() const;\n"
2168+
" void c();\n"
2169+
"};\n"
2170+
"void d() {\n"
2171+
" for (a *e;;) {\n"
2172+
" e->b()->c();\n"
2173+
" while (e)\n"
2174+
" e = e->b();\n"
2175+
" }\n"
2176+
"}\n");
2177+
ASSERT_EQUALS("", errout.str());
2178+
}
2179+
21382180
void nullpointer_addressOf() { // address of
21392181
check("void f() {\n"
21402182
" struct X *x = 0;\n"

0 commit comments

Comments
 (0)