Skip to content

Commit e6632d9

Browse files
authored
Fix 11983: False positive: uninitialized variable (#5443)
1 parent 11e5c11 commit e6632d9

10 files changed

Lines changed: 318 additions & 99 deletions

File tree

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ validateRules:
456456

457457
###### Build
458458

459-
$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h
459+
$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h
460460
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp
461461

462462
$(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h
@@ -468,7 +468,7 @@ $(libcppdir)/symboldatabase.o: lib/symboldatabase.cpp lib/astutils.h lib/color.h
468468
$(libcppdir)/analyzerinfo.o: lib/analyzerinfo.cpp externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/path.h lib/platform.h lib/utils.h
469469
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/analyzerinfo.cpp
470470

471-
$(libcppdir)/astutils.o: lib/astutils.cpp lib/astutils.h lib/check.h lib/checkclass.h lib/config.h lib/errortypes.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h
471+
$(libcppdir)/astutils.o: lib/astutils.cpp lib/astutils.h lib/check.h lib/checkclass.h lib/config.h lib/errortypes.h lib/findtoken.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h
472472
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/astutils.cpp
473473

474474
$(libcppdir)/check.o: lib/check.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h

lib/astutils.cpp

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "config.h"
2424
#include "errortypes.h"
25+
#include "findtoken.h"
2526
#include "infer.h"
2627
#include "library.h"
2728
#include "mathlib.h"
@@ -2867,7 +2868,14 @@ bool isThisChanged(const Token* start, const Token* end, int indirect, const Set
28672868
return false;
28682869
}
28692870

2870-
bool isExpressionChanged(const Token* expr, const Token* start, const Token* end, const Settings* settings, bool cpp, int depth)
2871+
template<class Find>
2872+
bool isExpressionChangedImpl(const Token* expr,
2873+
const Token* start,
2874+
const Token* end,
2875+
const Settings* settings,
2876+
bool cpp,
2877+
int depth,
2878+
Find find)
28712879
{
28722880
if (depth < 0)
28732881
return true;
@@ -2888,7 +2896,7 @@ bool isExpressionChanged(const Token* expr, const Token* start, const Token* end
28882896
}
28892897

28902898
if (tok->exprId() > 0) {
2891-
for (const Token* tok2 = start; tok2 != end; tok2 = tok2->next()) {
2899+
const Token* result = find(start, end, [&](const Token* tok2) {
28922900
int indirect = 0;
28932901
if (const ValueType* vt = tok->valueType()) {
28942902
indirect = vt->pointer;
@@ -2898,13 +2906,55 @@ bool isExpressionChanged(const Token* expr, const Token* start, const Token* end
28982906
for (int i = 0; i <= indirect; ++i)
28992907
if (isExpressionChangedAt(tok, tok2, i, global, settings, cpp, depth))
29002908
return true;
2901-
}
2909+
return false;
2910+
});
2911+
if (result)
2912+
return true;
29022913
}
29032914
return false;
29042915
});
29052916
return result;
29062917
}
29072918

2919+
struct ExpressionChangedSimpleFind {
2920+
template<class F>
2921+
const Token* operator()(const Token* start, const Token* end, F f) const
2922+
{
2923+
return findToken(start, end, f);
2924+
}
2925+
};
2926+
2927+
struct ExpressionChangedSkipDeadCode {
2928+
const Library* library;
2929+
const std::function<std::vector<MathLib::bigint>(const Token* tok)>* evaluate;
2930+
ExpressionChangedSkipDeadCode(const Library* library,
2931+
const std::function<std::vector<MathLib::bigint>(const Token* tok)>& evaluate)
2932+
: library(library), evaluate(&evaluate)
2933+
{}
2934+
template<class F>
2935+
const Token* operator()(const Token* start, const Token* end, F f) const
2936+
{
2937+
return findTokenSkipDeadCode(library, start, end, f, *evaluate);
2938+
}
2939+
};
2940+
2941+
bool isExpressionChanged(const Token* expr, const Token* start, const Token* end, const Settings* settings, bool cpp, int depth)
2942+
{
2943+
return isExpressionChangedImpl(expr, start, end, settings, cpp, depth, ExpressionChangedSimpleFind{});
2944+
}
2945+
2946+
bool isExpressionChangedSkipDeadCode(const Token* expr,
2947+
const Token* start,
2948+
const Token* end,
2949+
const Settings* settings,
2950+
bool cpp,
2951+
const std::function<std::vector<MathLib::bigint>(const Token* tok)>& evaluate,
2952+
int depth)
2953+
{
2954+
return isExpressionChangedImpl(
2955+
expr, start, end, settings, cpp, depth, ExpressionChangedSkipDeadCode{&settings->library, evaluate});
2956+
}
2957+
29082958
const Token* getArgumentStart(const Token* ftok)
29092959
{
29102960
const Token* tok = ftok;

lib/astutils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ CPPCHECKLIB bool isExpressionChanged(const Token* expr,
345345
bool cpp,
346346
int depth = 20);
347347

348+
bool isExpressionChangedSkipDeadCode(const Token* expr,
349+
const Token* start,
350+
const Token* end,
351+
const Settings* settings,
352+
bool cpp,
353+
const std::function<std::vector<MathLib::bigint>(const Token* tok)>& evaluate,
354+
int depth = 20);
355+
348356
bool isExpressionChangedAt(const Token* expr,
349357
const Token* tok,
350358
int indirect,

lib/cppcheck.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
<ClInclude Include="ctu.h" />
129129
<ClInclude Include="errorlogger.h" />
130130
<ClInclude Include="errortypes.h" />
131+
<ClInclude Include="findtoken.h" />
131132
<ClInclude Include="forwardanalyzer.h" />
132133
<ClInclude Include="fwdanalysis.h" />
133134
<ClInclude Include="importproject.h" />

lib/findtoken.h

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Cppcheck - A tool for static C/C++ code analysis
3+
* Copyright (C) 2007-2023 Cppcheck team.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
//---------------------------------------------------------------------------
20+
#ifndef findtokenH
21+
#define findtokenH
22+
//---------------------------------------------------------------------------
23+
24+
#include <functional>
25+
#include <stack>
26+
#include <string>
27+
#include <type_traits>
28+
#include <vector>
29+
30+
#include "config.h"
31+
#include "errortypes.h"
32+
#include "library.h"
33+
#include "smallvector.h"
34+
#include "symboldatabase.h"
35+
#include "token.h"
36+
37+
inline std::vector<MathLib::bigint> evaluateKnownValues(const Token* tok)
38+
{
39+
if (!tok->hasKnownIntValue())
40+
return {};
41+
return {tok->getKnownIntValue()};
42+
}
43+
44+
template<class T, class Predicate, class Found, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
45+
void findTokensImpl(T* start, const Token* end, const Predicate& pred, Found found)
46+
{
47+
for (T* tok = start; precedes(tok, end); tok = tok->next()) {
48+
if (pred(tok)) {
49+
if (found(tok))
50+
break;
51+
}
52+
}
53+
}
54+
55+
template<class T, class Predicate, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
56+
std::vector<T*> findTokens(T* start, const Token* end, const Predicate& pred)
57+
{
58+
std::vector<T*> result;
59+
findTokensImpl(start, end, pred, [&](T* tok) {
60+
result.push_back(tok);
61+
return false;
62+
});
63+
return result;
64+
}
65+
66+
template<class T, class Predicate, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
67+
T* findToken(T* start, const Token* end, const Predicate& pred)
68+
{
69+
T* result = nullptr;
70+
findTokensImpl(start, end, pred, [&](T* tok) {
71+
result = tok;
72+
return true;
73+
});
74+
return result;
75+
}
76+
77+
template<class T,
78+
class Predicate,
79+
class Found,
80+
class Evaluate,
81+
REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
82+
bool findTokensSkipDeadCodeImpl(const Library* library,
83+
T* start,
84+
const Token* end,
85+
const Predicate& pred,
86+
Found found,
87+
const Evaluate& evaluate)
88+
{
89+
for (T* tok = start; precedes(tok, end); tok = tok->next()) {
90+
if (pred(tok)) {
91+
if (found(tok))
92+
return true;
93+
}
94+
if (Token::Match(tok, "if|for|while (") && Token::simpleMatch(tok->next()->link(), ") {")) {
95+
const Token* condTok = getCondTok(tok);
96+
if (!condTok)
97+
continue;
98+
auto result = evaluate(condTok);
99+
if (result.empty())
100+
continue;
101+
if (findTokensSkipDeadCodeImpl(library, tok->next(), tok->linkAt(1), pred, found, evaluate))
102+
return true;
103+
T* thenStart = tok->linkAt(1)->next();
104+
T* elseStart = nullptr;
105+
if (Token::simpleMatch(thenStart->link(), "} else {"))
106+
elseStart = thenStart->link()->tokAt(2);
107+
108+
int r = result.front();
109+
if (r == 0) {
110+
if (elseStart) {
111+
if (findTokensSkipDeadCodeImpl(library, elseStart, elseStart->link(), pred, found, evaluate))
112+
return true;
113+
if (isReturnScope(elseStart->link(), library))
114+
return true;
115+
tok = elseStart->link();
116+
} else {
117+
tok = thenStart->link();
118+
}
119+
} else {
120+
if (findTokensSkipDeadCodeImpl(library, thenStart, thenStart->link(), pred, found, evaluate))
121+
return true;
122+
if (isReturnScope(thenStart->link(), library))
123+
return true;
124+
tok = thenStart->link();
125+
}
126+
} else if (Token::Match(tok->astParent(), "&&|?|%oror%") && astIsLHS(tok)) {
127+
auto result = evaluate(tok);
128+
if (result.empty())
129+
continue;
130+
const bool cond = result.front() != 0;
131+
T* next = nullptr;
132+
if ((cond && Token::simpleMatch(tok->astParent(), "||")) ||
133+
(!cond && Token::simpleMatch(tok->astParent(), "&&"))) {
134+
next = nextAfterAstRightmostLeaf(tok->astParent());
135+
} else if (Token::simpleMatch(tok->astParent(), "?")) {
136+
T* colon = tok->astParent()->astOperand2();
137+
if (!cond) {
138+
next = colon;
139+
} else {
140+
if (findTokensSkipDeadCodeImpl(library, tok->astParent()->next(), colon, pred, found, evaluate))
141+
return true;
142+
next = nextAfterAstRightmostLeaf(colon);
143+
}
144+
}
145+
if (next)
146+
tok = next;
147+
} else if (Token::simpleMatch(tok, "} else {")) {
148+
const Token* condTok = getCondTokFromEnd(tok);
149+
if (!condTok)
150+
continue;
151+
auto result = evaluate(condTok);
152+
if (result.empty())
153+
continue;
154+
if (isReturnScope(tok->link(), library))
155+
return true;
156+
int r = result.front();
157+
if (r != 0) {
158+
tok = tok->linkAt(2);
159+
}
160+
} else if (Token::simpleMatch(tok, "[") && Token::Match(tok->link(), "] (|{")) {
161+
T* afterCapture = tok->link()->next();
162+
if (Token::simpleMatch(afterCapture, "(") && afterCapture->link())
163+
tok = afterCapture->link()->next();
164+
else
165+
tok = afterCapture;
166+
}
167+
}
168+
return false;
169+
}
170+
171+
template<class T, class Predicate, class Evaluate, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
172+
std::vector<T*> findTokensSkipDeadCode(const Library* library,
173+
T* start,
174+
const Token* end,
175+
const Predicate& pred,
176+
const Evaluate& evaluate)
177+
{
178+
std::vector<T*> result;
179+
findTokensSkipDeadCodeImpl(
180+
library,
181+
start,
182+
end,
183+
pred,
184+
[&](T* tok) {
185+
result.push_back(tok);
186+
return false;
187+
},
188+
evaluate);
189+
return result;
190+
}
191+
192+
template<class T, class Predicate, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
193+
std::vector<T*> findTokensSkipDeadCode(const Library* library, T* start, const Token* end, const Predicate& pred)
194+
{
195+
return findTokensSkipDeadCode(library, start, end, pred, &evaluateKnownValues);
196+
}
197+
198+
template<class T, class Predicate, class Evaluate, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
199+
T* findTokenSkipDeadCode(const Library* library, T* start, const Token* end, const Predicate& pred, const Evaluate& evaluate)
200+
{
201+
T* result = nullptr;
202+
findTokensSkipDeadCodeImpl(
203+
library,
204+
start,
205+
end,
206+
pred,
207+
[&](T* tok) {
208+
result = tok;
209+
return true;
210+
},
211+
evaluate);
212+
return result;
213+
}
214+
215+
template<class T, class Predicate, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
216+
T* findTokenSkipDeadCode(const Library* library, T* start, const Token* end, const Predicate& pred)
217+
{
218+
return findTokenSkipDeadCode(library, start, end, pred, &evaluateKnownValues);
219+
}
220+
221+
#endif // findtokenH

lib/lib.pri

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ HEADERS += $${PWD}/analyzer.h \
4141
$${PWD}/ctu.h \
4242
$${PWD}/errorlogger.h \
4343
$${PWD}/errortypes.h \
44+
$${PWD}/findtoken.h \
4445
$${PWD}/forwardanalyzer.h \
4546
$${PWD}/fwdanalysis.h \
4647
$${PWD}/importproject.h \

lib/programmemory.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,18 @@ void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty)
486486

487487
void ProgramMemoryState::removeModifiedVars(const Token* tok)
488488
{
489+
ProgramMemory pm = state;
490+
auto eval = [&](const Token* cond) -> std::vector<MathLib::bigint> {
491+
if (conditionIsTrue(cond, pm))
492+
return {1};
493+
if (conditionIsFalse(cond, pm))
494+
return {0};
495+
return {};
496+
};
489497
state.erase_if([&](const ExprIdToken& e) {
490498
const Token* start = origins[e.getExpressionId()];
491499
const Token* expr = e.tok;
492-
if (!expr || isExpressionChanged(expr, start, tok, settings, true)) {
500+
if (!expr || isExpressionChangedSkipDeadCode(expr, start, tok, settings, true, eval)) {
493501
origins.erase(e.getExpressionId());
494502
return true;
495503
}

0 commit comments

Comments
 (0)