/src/cppcheck/lib/fwdanalysis.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "fwdanalysis.h" |
20 | | |
21 | | #include "astutils.h" |
22 | | #include "config.h" |
23 | | #include "library.h" |
24 | | #include "symboldatabase.h" |
25 | | #include "token.h" |
26 | | #include "vfvalue.h" |
27 | | |
28 | | #include <list> |
29 | | #include <map> |
30 | | #include <string> |
31 | | #include <utility> |
32 | | |
33 | | static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set<nonneg int> &exprVarIds, bool local) |
34 | 0 | { |
35 | 0 | for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { |
36 | 0 | if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) |
37 | | // TODO: this is a quick bailout |
38 | 0 | return false; |
39 | 0 | if (tok->varId() == 0 || exprVarIds.find(tok->varId()) == exprVarIds.end()) |
40 | 0 | continue; |
41 | 0 | const Token *parent = tok; |
42 | 0 | while (parent->astParent() && !parent->astParent()->isAssignmentOp() && parent->astParent()->tokType() != Token::Type::eIncDecOp) { |
43 | 0 | if (parent->str() == "," || parent->isUnaryOp("&")) |
44 | | // TODO: This is a quick bailout |
45 | 0 | return false; |
46 | 0 | parent = parent->astParent(); |
47 | 0 | } |
48 | 0 | if (parent->astParent()) { |
49 | 0 | if (parent->astParent()->tokType() == Token::Type::eIncDecOp) |
50 | 0 | return false; |
51 | 0 | if (parent->astParent()->isAssignmentOp() && parent == parent->astParent()->astOperand1()) |
52 | 0 | return false; |
53 | 0 | } |
54 | 0 | } |
55 | 0 | return true; |
56 | 0 | } |
57 | | |
58 | | static bool hasFunctionCall(const Token *tok) |
59 | 1.05k | { |
60 | 1.05k | if (!tok) |
61 | 608 | return false; |
62 | 450 | if (Token::Match(tok, "%name% (")) |
63 | | // todo, const/pure function? |
64 | 0 | return true; |
65 | 450 | return hasFunctionCall(tok->astOperand1()) || hasFunctionCall(tok->astOperand2()); |
66 | 450 | } |
67 | | |
68 | | static bool hasGccCompoundStatement(const Token *tok) |
69 | 835 | { |
70 | 835 | if (!tok) |
71 | 482 | return false; |
72 | 353 | if (tok->str() == "{" && Token::simpleMatch(tok->previous(), "( {")) |
73 | 0 | return true; |
74 | 353 | return hasGccCompoundStatement(tok->astOperand1()) || hasGccCompoundStatement(tok->astOperand2()); |
75 | 353 | } |
76 | | |
77 | | static bool nonLocal(const Variable* var, bool deref) |
78 | 2.23k | { |
79 | 2.23k | return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern(); |
80 | 2.23k | } |
81 | | |
82 | | static bool hasVolatileCastOrVar(const Token *expr) |
83 | 2.99k | { |
84 | 2.99k | bool ret = false; |
85 | 2.99k | visitAstNodes(expr, |
86 | 3.08k | [&ret](const Token *tok) { |
87 | 3.08k | if (tok->variable() && tok->variable()->isVolatile()) |
88 | 0 | ret = true; |
89 | 3.08k | else if (Token::simpleMatch(tok, "( volatile")) |
90 | 0 | ret = true; |
91 | 3.08k | return ret ? ChildrenToVisit::none : ChildrenToVisit::op1_and_op2; |
92 | 3.08k | }); |
93 | 2.99k | return ret; |
94 | 2.99k | } |
95 | | |
96 | | struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set<nonneg int> &exprVarIds, bool local, bool inInnerClass, int depth) |
97 | 1.97k | { |
98 | | // Parse the given tokens |
99 | 1.97k | if (++depth > 1000) |
100 | 0 | return Result(Result::Type::BAILOUT); |
101 | | |
102 | 12.3k | for (const Token* tok = startToken; precedes(tok, endToken); tok = tok->next()) { |
103 | 11.4k | if (Token::simpleMatch(tok, "try {")) { |
104 | | // TODO: handle try |
105 | 0 | return Result(Result::Type::BAILOUT); |
106 | 0 | } |
107 | | |
108 | 11.4k | if (Token::simpleMatch(tok, "break ;")) { |
109 | 0 | return Result(Result::Type::BREAK, tok); |
110 | 0 | } |
111 | | |
112 | 11.4k | if (Token::simpleMatch(tok, "goto")) |
113 | 0 | return Result(Result::Type::BAILOUT); |
114 | | |
115 | 11.4k | if (!inInnerClass && tok->str() == "{" && tok->scope()->isClassOrStruct()) { |
116 | | // skip returns from local class definition |
117 | 0 | FwdAnalysis::Result result = checkRecursive(expr, tok, tok->link(), exprVarIds, local, true, depth); |
118 | 0 | if (result.type != Result::Type::NONE) |
119 | 0 | return result; |
120 | 0 | tok=tok->link(); |
121 | 0 | } |
122 | | |
123 | 11.4k | if (tok->str() == "continue") |
124 | | // TODO |
125 | 0 | return Result(Result::Type::BAILOUT); |
126 | | |
127 | 11.4k | if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { |
128 | 0 | tok = lambdaEndToken; |
129 | 0 | const Result lambdaResult = checkRecursive(expr, lambdaEndToken->link()->next(), lambdaEndToken, exprVarIds, local, inInnerClass, depth); |
130 | 0 | if (lambdaResult.type == Result::Type::READ || lambdaResult.type == Result::Type::BAILOUT) |
131 | 0 | return lambdaResult; |
132 | 0 | } |
133 | | |
134 | 11.4k | if (Token::Match(tok, "return|throw")) { |
135 | | // TODO: Handle these better |
136 | | // Is expr variable used in expression? |
137 | | |
138 | 632 | const Token* opTok = tok->astOperand1(); |
139 | 632 | if (!opTok) |
140 | 0 | opTok = tok->next(); |
141 | 632 | std::pair<const Token*, const Token*> startEndTokens = opTok->findExpressionStartEndTokens(); |
142 | 632 | FwdAnalysis::Result result = |
143 | 632 | checkRecursive(expr, startEndTokens.first, startEndTokens.second->next(), exprVarIds, local, true, depth); |
144 | 632 | if (result.type != Result::Type::NONE) |
145 | 53 | return result; |
146 | | |
147 | | // #9167: if the return is inside an inner class, it does not tell us anything |
148 | 579 | if (!inInnerClass) { |
149 | 579 | if (!local && mWhat == What::Reassign) |
150 | 579 | return Result(Result::Type::BAILOUT); |
151 | | |
152 | 0 | return Result(Result::Type::RETURN); |
153 | 579 | } |
154 | 579 | } |
155 | | |
156 | 10.8k | if (tok->str() == "}") { |
157 | | // Known value => possible value |
158 | 237 | if (tok->scope() == expr->scope()) |
159 | 163 | mValueFlowKnown = false; |
160 | | |
161 | 237 | if (tok->scope()->isLoopScope()) { |
162 | | // check condition |
163 | 78 | const Token *conditionStart = nullptr; |
164 | 78 | const Token *conditionEnd = nullptr; |
165 | 78 | if (Token::simpleMatch(tok->link()->previous(), ") {")) { |
166 | 78 | conditionEnd = tok->link()->previous(); |
167 | 78 | conditionStart = conditionEnd->link(); |
168 | 78 | } else if (Token::simpleMatch(tok->link()->previous(), "do {") && Token::simpleMatch(tok, "} while (")) { |
169 | 0 | conditionStart = tok->tokAt(2); |
170 | 0 | conditionEnd = conditionStart->link(); |
171 | 0 | } |
172 | 78 | if (conditionStart && conditionEnd) { |
173 | 78 | bool used = false; |
174 | 631 | for (const Token *condTok = conditionStart; condTok != conditionEnd; condTok = condTok->next()) { |
175 | 570 | if (exprVarIds.find(condTok->varId()) != exprVarIds.end()) { |
176 | 17 | used = true; |
177 | 17 | break; |
178 | 17 | } |
179 | 570 | } |
180 | 78 | if (used) |
181 | 17 | return Result(Result::Type::BAILOUT); |
182 | 78 | } |
183 | | |
184 | | // check loop body again.. |
185 | 61 | const struct FwdAnalysis::Result &result = checkRecursive(expr, tok->link(), tok, exprVarIds, local, inInnerClass, depth); |
186 | 61 | if (result.type == Result::Type::BAILOUT || result.type == Result::Type::READ) |
187 | 10 | return result; |
188 | 61 | } |
189 | 237 | } |
190 | | |
191 | 10.8k | if (Token::simpleMatch(tok, "else {")) |
192 | 70 | tok = tok->linkAt(1); |
193 | | |
194 | 10.8k | if (Token::simpleMatch(tok, "asm (")) |
195 | 0 | return Result(Result::Type::BAILOUT); |
196 | | |
197 | 10.8k | if (mWhat == What::ValueFlow && (Token::Match(tok, "while|for (") || Token::simpleMatch(tok, "do {"))) { |
198 | 0 | const Token *bodyStart = nullptr; |
199 | 0 | const Token *conditionStart = nullptr; |
200 | 0 | if (Token::simpleMatch(tok, "do {")) { |
201 | 0 | bodyStart = tok->next(); |
202 | 0 | if (Token::simpleMatch(bodyStart->link(), "} while (")) |
203 | 0 | conditionStart = bodyStart->link()->tokAt(2); |
204 | 0 | } else { |
205 | 0 | conditionStart = tok->next(); |
206 | 0 | if (Token::simpleMatch(conditionStart->link(), ") {")) |
207 | 0 | bodyStart = conditionStart->link()->next(); |
208 | 0 | } |
209 | |
|
210 | 0 | if (!bodyStart || !conditionStart) |
211 | 0 | return Result(Result::Type::BAILOUT); |
212 | | |
213 | | // Is expr changed in condition? |
214 | 0 | if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) |
215 | 0 | return Result(Result::Type::BAILOUT); |
216 | | |
217 | | // Is expr changed in loop body? |
218 | 0 | if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) |
219 | 0 | return Result(Result::Type::BAILOUT); |
220 | 0 | } |
221 | | |
222 | 10.8k | if (mWhat == What::ValueFlow && Token::simpleMatch(tok, "if (") && Token::simpleMatch(tok->linkAt(1), ") {")) { |
223 | 0 | const Token *bodyStart = tok->linkAt(1)->next(); |
224 | 0 | const Token *conditionStart = tok->next(); |
225 | 0 | const Token *condTok = conditionStart->astOperand2(); |
226 | 0 | if (condTok->hasKnownIntValue()) { |
227 | 0 | const bool cond = condTok->values().front().intvalue; |
228 | 0 | if (cond) { |
229 | 0 | FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); |
230 | 0 | if (result.type != Result::Type::NONE) |
231 | 0 | return result; |
232 | 0 | } else if (Token::simpleMatch(bodyStart->link(), "} else {")) { |
233 | 0 | bodyStart = bodyStart->link()->tokAt(2); |
234 | 0 | FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); |
235 | 0 | if (result.type != Result::Type::NONE) |
236 | 0 | return result; |
237 | 0 | } |
238 | 0 | } |
239 | 0 | tok = bodyStart->link(); |
240 | 0 | if (isReturnScope(tok, &mLibrary)) |
241 | 0 | return Result(Result::Type::BAILOUT); |
242 | 0 | if (Token::simpleMatch(tok, "} else {")) |
243 | 0 | tok = tok->linkAt(2); |
244 | 0 | if (!tok) |
245 | 0 | return Result(Result::Type::BAILOUT); |
246 | | |
247 | | // Is expr changed in condition? |
248 | 0 | if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) |
249 | 0 | return Result(Result::Type::BAILOUT); |
250 | | |
251 | | // Is expr changed in condition body? |
252 | 0 | if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) |
253 | 0 | return Result(Result::Type::BAILOUT); |
254 | 0 | } |
255 | | |
256 | 10.8k | if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { |
257 | | // TODO: this is a quick bailout |
258 | 0 | return Result(Result::Type::BAILOUT); |
259 | 0 | } |
260 | | |
261 | 10.8k | if (mWhat == What::Reassign && |
262 | 10.8k | Token::simpleMatch(tok, ";") && |
263 | 10.8k | Token::simpleMatch(tok->astParent(), ";") && |
264 | 10.8k | Token::simpleMatch(tok->astParent()->astParent(), "(") && |
265 | 10.8k | Token::simpleMatch(tok->astParent()->astParent()->previous(), "for (") && |
266 | 10.8k | !isUnchanged(tok, tok->astParent()->astParent()->link(), exprVarIds, local)) |
267 | | // TODO: This is a quick bailout to avoid FP #9420, there are false negatives (TODO_ASSERT_EQUALS) |
268 | 0 | return Result(Result::Type::BAILOUT); |
269 | | |
270 | 10.8k | if (expr->isName() && Token::Match(tok, "%name% (") && tok->str().find('<') != std::string::npos && tok->str().find(expr->str()) != std::string::npos) |
271 | 0 | return Result(Result::Type::BAILOUT); |
272 | | |
273 | 10.8k | if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { |
274 | 409 | const Token *parent = tok; |
275 | 409 | bool other = false; |
276 | 409 | bool same = tok->astParent() && isSameExpression(mCpp, false, expr, tok, mLibrary, true, false, nullptr); |
277 | 409 | while (!same && Token::Match(parent->astParent(), "*|.|::|[|(|%cop%")) { |
278 | 0 | parent = parent->astParent(); |
279 | 0 | if (parent->str() == "(" && !parent->isCast()) |
280 | 0 | break; |
281 | 0 | if (isSameExpression(mCpp, false, expr, parent, mLibrary, true, false, nullptr)) { |
282 | 0 | same = true; |
283 | 0 | if (mWhat == What::ValueFlow) { |
284 | 0 | KnownAndToken v; |
285 | 0 | v.known = mValueFlowKnown; |
286 | 0 | v.token = parent; |
287 | 0 | mValueFlow.push_back(v); |
288 | 0 | } |
289 | 0 | } |
290 | 0 | if (Token::Match(parent, ". %var%") && parent->next()->varId() && exprVarIds.find(parent->next()->varId()) == exprVarIds.end() && |
291 | 0 | isSameExpression(mCpp, false, expr->astOperand1(), parent->astOperand1(), mLibrary, true, false, nullptr)) { |
292 | 0 | other = true; |
293 | 0 | break; |
294 | 0 | } |
295 | 0 | } |
296 | 409 | if (mWhat != What::ValueFlow && same && Token::simpleMatch(parent->astParent(), "[") && parent == parent->astParent()->astOperand2()) { |
297 | 0 | return Result(Result::Type::READ); |
298 | 0 | } |
299 | 409 | if (other) |
300 | 0 | continue; |
301 | 409 | if (Token::simpleMatch(parent->astParent(), "=") && parent == parent->astParent()->astOperand1()) { |
302 | 158 | if (!local && hasFunctionCall(parent->astParent()->astOperand2())) { |
303 | | // TODO: this is a quick bailout |
304 | 0 | return Result(Result::Type::BAILOUT); |
305 | 0 | } |
306 | 158 | if (hasOperand(parent->astParent()->astOperand2(), expr)) { |
307 | 29 | if (mWhat == What::Reassign) |
308 | 29 | return Result(Result::Type::READ); |
309 | 0 | continue; |
310 | 29 | } |
311 | 129 | const auto startEnd = parent->astParent()->astOperand2()->findExpressionStartEndTokens(); |
312 | 373 | for (const Token* tok2 = startEnd.first; tok2 != startEnd.second; tok2 = tok2->next()) { |
313 | 244 | if (tok2->tokType() == Token::eLambda) |
314 | 0 | return Result(Result::Type::BAILOUT); |
315 | | // TODO: analyze usage in lambda |
316 | 244 | } |
317 | | // ({ .. }) |
318 | 129 | if (hasGccCompoundStatement(parent->astParent()->astOperand2())) |
319 | 0 | return Result(Result::Type::BAILOUT); |
320 | 129 | const bool reassign = isSameExpression(mCpp, false, expr, parent, mLibrary, false, false, nullptr); |
321 | 129 | if (reassign) |
322 | 129 | return Result(Result::Type::WRITE, parent->astParent()); |
323 | 0 | return Result(Result::Type::READ); |
324 | 129 | } |
325 | 251 | if (mWhat == What::Reassign && parent->valueType() && parent->valueType()->pointer && Token::Match(parent->astParent(), "%assign%") && parent == parent->astParent()->astOperand1()) |
326 | 0 | return Result(Result::Type::READ); |
327 | | |
328 | 251 | if (Token::Match(parent->astParent(), "%assign%") && !parent->astParent()->astParent() && parent == parent->astParent()->astOperand1()) { |
329 | 0 | if (mWhat == What::Reassign) |
330 | 0 | return Result(Result::Type::BAILOUT, parent->astParent()); |
331 | 0 | if (mWhat == What::UnusedValue && (!parent->valueType() || parent->valueType()->reference != Reference::None)) |
332 | 0 | return Result(Result::Type::BAILOUT, parent->astParent()); |
333 | 0 | continue; |
334 | 0 | } |
335 | 251 | if (mWhat == What::UnusedValue && parent->isUnaryOp("&") && Token::Match(parent->astParent(), "[,(]")) { |
336 | | // Pass variable to function the writes it |
337 | 0 | const Token *ftok = parent->astParent(); |
338 | 0 | while (Token::simpleMatch(ftok, ",")) |
339 | 0 | ftok = ftok->astParent(); |
340 | 0 | if (ftok && Token::Match(ftok->previous(), "%name% (")) { |
341 | 0 | const std::vector<const Token *> args = getArguments(ftok); |
342 | 0 | int argnr = 0; |
343 | 0 | while (argnr < args.size() && args[argnr] != parent) |
344 | 0 | argnr++; |
345 | 0 | if (argnr < args.size()) { |
346 | 0 | const Library::Function* functionInfo = mLibrary.getFunction(ftok->astOperand1()); |
347 | 0 | if (functionInfo) { |
348 | 0 | const auto it = functionInfo->argumentChecks.find(argnr + 1); |
349 | 0 | if (it != functionInfo->argumentChecks.end() && it->second.direction == Library::ArgumentChecks::Direction::DIR_OUT) |
350 | 0 | continue; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | } |
354 | 0 | return Result(Result::Type::BAILOUT, parent->astParent()); |
355 | 0 | } |
356 | | // TODO: this is a quick bailout |
357 | 251 | return Result(Result::Type::BAILOUT, parent->astParent()); |
358 | 251 | } |
359 | | |
360 | 10.4k | if (Token::Match(tok, ")|do {")) { |
361 | 282 | if (tok->str() == ")" && Token::simpleMatch(tok->link()->previous(), "switch (")) |
362 | | // TODO: parse switch |
363 | 0 | return Result(Result::Type::BAILOUT); |
364 | 282 | const Result &result1 = checkRecursive(expr, tok->tokAt(2), tok->linkAt(1), exprVarIds, local, inInnerClass, depth); |
365 | 282 | if (result1.type == Result::Type::READ || result1.type == Result::Type::BAILOUT) |
366 | 78 | return result1; |
367 | 204 | if (mWhat == What::UnusedValue && result1.type == Result::Type::WRITE && expr->variable() && expr->variable()->isReference()) |
368 | 0 | return result1; |
369 | 204 | if (mWhat == What::ValueFlow && result1.type == Result::Type::WRITE) |
370 | 0 | mValueFlowKnown = false; |
371 | 204 | if (mWhat == What::Reassign && result1.type == Result::Type::BREAK) { |
372 | 0 | const Token *scopeEndToken = findNextTokenFromBreak(result1.token); |
373 | 0 | if (scopeEndToken) { |
374 | 0 | const Result &result2 = checkRecursive(expr, scopeEndToken->next(), endToken, exprVarIds, local, inInnerClass, depth); |
375 | 0 | if (result2.type == Result::Type::BAILOUT) |
376 | 0 | return result2; |
377 | 0 | } |
378 | 0 | } |
379 | 204 | if (Token::simpleMatch(tok->linkAt(1), "} else {")) { |
380 | 62 | const Token *elseStart = tok->linkAt(1)->tokAt(2); |
381 | 62 | const Result &result2 = checkRecursive(expr, elseStart, elseStart->link(), exprVarIds, local, inInnerClass, depth); |
382 | 62 | if (mWhat == What::ValueFlow && result2.type == Result::Type::WRITE) |
383 | 0 | mValueFlowKnown = false; |
384 | 62 | if (result2.type == Result::Type::READ || result2.type == Result::Type::BAILOUT) |
385 | 0 | return result2; |
386 | 62 | if (result1.type == Result::Type::WRITE && result2.type == Result::Type::WRITE) |
387 | 0 | return result1; |
388 | 62 | tok = elseStart->link(); |
389 | 142 | } else { |
390 | 142 | tok = tok->linkAt(1); |
391 | 142 | } |
392 | 204 | } |
393 | 10.4k | } |
394 | | |
395 | 829 | return Result(Result::Type::NONE); |
396 | 1.97k | } |
397 | | |
398 | | bool FwdAnalysis::isGlobalData(const Token *expr) const |
399 | 2.20k | { |
400 | 2.20k | return ::isGlobalData(expr, mCpp); |
401 | 2.20k | } |
402 | | |
403 | | std::set<nonneg int> FwdAnalysis::getExprVarIds(const Token* expr, bool* localOut, bool* unknownVarIdOut) const |
404 | 2.99k | { |
405 | | // all variable ids in expr. |
406 | 2.99k | std::set<nonneg int> exprVarIds; |
407 | 2.99k | bool local = true; |
408 | 2.99k | bool unknownVarId = false; |
409 | 2.99k | visitAstNodes(expr, |
410 | 3.08k | [&](const Token *tok) { |
411 | 3.08k | if (tok->str() == "[" && mWhat == What::UnusedValue) |
412 | 0 | return ChildrenToVisit::op1; |
413 | 3.08k | if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") { |
414 | | // unknown variable |
415 | 785 | unknownVarId = true; |
416 | 785 | return ChildrenToVisit::none; |
417 | 785 | } |
418 | 2.30k | if (tok->varId() > 0) { |
419 | 2.23k | exprVarIds.insert(tok->varId()); |
420 | 2.23k | if (!Token::simpleMatch(tok->previous(), ".")) { |
421 | 2.23k | const Variable *var = tok->variable(); |
422 | 2.23k | if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && !isGlobalData(var->nameToken()->next()->astOperand2())) |
423 | 0 | return ChildrenToVisit::none; |
424 | 2.23k | const bool deref = tok->astParent() && (tok->astParent()->isUnaryOp("*") || (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); |
425 | 2.23k | local &= !nonLocal(tok->variable(), deref); |
426 | 2.23k | } |
427 | 2.23k | } |
428 | 2.30k | return ChildrenToVisit::op1_and_op2; |
429 | 2.30k | }); |
430 | 2.99k | if (localOut) |
431 | 2.99k | *localOut = local; |
432 | 2.99k | if (unknownVarIdOut) |
433 | 2.99k | *unknownVarIdOut = unknownVarId; |
434 | 2.99k | return exprVarIds; |
435 | 2.99k | } |
436 | | |
437 | | FwdAnalysis::Result FwdAnalysis::check(const Token* expr, const Token* startToken, const Token* endToken) |
438 | 2.99k | { |
439 | | // all variable ids in expr. |
440 | 2.99k | bool local = true; |
441 | 2.99k | bool unknownVarId = false; |
442 | 2.99k | std::set<nonneg int> exprVarIds = getExprVarIds(expr, &local, &unknownVarId); |
443 | | |
444 | 2.99k | if (unknownVarId) |
445 | 785 | return Result(FwdAnalysis::Result::Type::BAILOUT); |
446 | | |
447 | 2.20k | if (mWhat == What::Reassign && isGlobalData(expr)) |
448 | 938 | local = false; |
449 | | |
450 | | // In unused values checking we do not want to check assignments to |
451 | | // global data. |
452 | 2.20k | if (mWhat == What::UnusedValue && isGlobalData(expr)) |
453 | 1.26k | return Result(FwdAnalysis::Result::Type::BAILOUT); |
454 | | |
455 | 938 | Result result = checkRecursive(expr, startToken, endToken, exprVarIds, local, false); |
456 | | |
457 | | // Break => continue checking in outer scope |
458 | 938 | while (mWhat!=What::ValueFlow && result.type == FwdAnalysis::Result::Type::BREAK) { |
459 | 0 | const Token *scopeEndToken = findNextTokenFromBreak(result.token); |
460 | 0 | if (!scopeEndToken) |
461 | 0 | break; |
462 | 0 | result = checkRecursive(expr, scopeEndToken->next(), endToken, exprVarIds, local, false); |
463 | 0 | } |
464 | | |
465 | 938 | return result; |
466 | 2.20k | } |
467 | | |
468 | | bool FwdAnalysis::hasOperand(const Token *tok, const Token *lhs) const |
469 | 9.93k | { |
470 | 9.93k | if (!tok) |
471 | 5.47k | return false; |
472 | 4.45k | if (isSameExpression(mCpp, false, tok, lhs, mLibrary, false, false, nullptr)) |
473 | 236 | return true; |
474 | 4.22k | return hasOperand(tok->astOperand1(), lhs) || hasOperand(tok->astOperand2(), lhs); |
475 | 4.45k | } |
476 | | |
477 | | const Token *FwdAnalysis::reassign(const Token *expr, const Token *startToken, const Token *endToken) |
478 | 1.32k | { |
479 | 1.32k | if (hasVolatileCastOrVar(expr)) |
480 | 0 | return nullptr; |
481 | 1.32k | mWhat = What::Reassign; |
482 | 1.32k | Result result = check(expr, startToken, endToken); |
483 | 1.32k | return result.type == FwdAnalysis::Result::Type::WRITE ? result.token : nullptr; |
484 | 1.32k | } |
485 | | |
486 | | bool FwdAnalysis::unusedValue(const Token *expr, const Token *startToken, const Token *endToken) |
487 | 1.66k | { |
488 | 1.66k | if (isEscapedAlias(expr)) |
489 | 0 | return false; |
490 | 1.66k | if (hasVolatileCastOrVar(expr)) |
491 | 0 | return false; |
492 | 1.66k | if (Token::simpleMatch(expr, "[") && astIsContainerView(expr->astOperand1())) |
493 | 0 | return false; |
494 | 1.66k | mWhat = What::UnusedValue; |
495 | 1.66k | Result result = check(expr, startToken, endToken); |
496 | 1.66k | return (result.type == FwdAnalysis::Result::Type::NONE || result.type == FwdAnalysis::Result::Type::RETURN) && !possiblyAliased(expr, startToken); |
497 | 1.66k | } |
498 | | |
499 | | bool FwdAnalysis::possiblyAliased(const Token *expr, const Token *startToken) const |
500 | 0 | { |
501 | 0 | if (expr->isUnaryOp("*") && !expr->astOperand1()->isUnaryOp("&")) |
502 | 0 | return true; |
503 | | |
504 | 0 | const bool macro = false; |
505 | 0 | const bool pure = false; |
506 | 0 | const bool followVar = false; |
507 | 0 | for (const Token *tok = startToken; tok; tok = tok->previous()) { |
508 | |
|
509 | 0 | if (Token::Match(tok, "%name% (") && !Token::Match(tok, "if|while|for")) { |
510 | | // Is argument passed by reference? |
511 | 0 | const std::vector<const Token*> args = getArguments(tok); |
512 | 0 | for (int argnr = 0; argnr < args.size(); ++argnr) { |
513 | 0 | if (!Token::Match(args[argnr], "%name%|.|::")) |
514 | 0 | continue; |
515 | 0 | if (tok->function() && tok->function()->getArgumentVar(argnr) && !tok->function()->getArgumentVar(argnr)->isReference() && !tok->function()->isConst()) |
516 | 0 | continue; |
517 | 0 | for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { |
518 | 0 | if (isSameExpression(mCpp, macro, subexpr, args[argnr], mLibrary, pure, followVar)) { |
519 | 0 | const Scope* scope = expr->scope(); // if there is no other variable, assume no aliasing |
520 | 0 | if (scope->varlist.size() > 1) |
521 | 0 | return true; |
522 | 0 | } |
523 | 0 | } |
524 | 0 | } |
525 | 0 | continue; |
526 | 0 | } |
527 | | |
528 | 0 | const Token *addrOf = nullptr; |
529 | 0 | if (Token::Match(tok, "& %name% =")) |
530 | 0 | addrOf = tok->tokAt(2)->astOperand2(); |
531 | 0 | else if (tok->isUnaryOp("&")) |
532 | 0 | addrOf = tok->astOperand1(); |
533 | 0 | else if (Token::simpleMatch(tok, "std :: ref (")) |
534 | 0 | addrOf = tok->tokAt(3)->astOperand2(); |
535 | 0 | else if (tok->valueType() && tok->valueType()->pointer && |
536 | 0 | (Token::Match(tok, "%var% = %var% ;") || Token::Match(tok, "%var% {|( %var% }|)")) && |
537 | 0 | Token::Match(expr->previous(), "%varid% [", tok->tokAt(2)->varId())) |
538 | 0 | addrOf = tok->tokAt(2); |
539 | 0 | else |
540 | 0 | continue; |
541 | | |
542 | 0 | for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { |
543 | 0 | if (subexpr != addrOf && isSameExpression(mCpp, macro, subexpr, addrOf, mLibrary, pure, followVar)) |
544 | 0 | return true; |
545 | 0 | } |
546 | 0 | } |
547 | 0 | return false; |
548 | 0 | } |
549 | | |
550 | | bool FwdAnalysis::isEscapedAlias(const Token* expr) |
551 | 1.66k | { |
552 | 3.38k | for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { |
553 | 1.72k | for (const ValueFlow::Value &val : subexpr->values()) { |
554 | 62 | if (!val.isLocalLifetimeValue()) |
555 | 62 | continue; |
556 | 0 | const Variable* var = val.tokvalue->variable(); |
557 | 0 | if (!var) |
558 | 0 | continue; |
559 | 0 | if (!var->isLocal()) |
560 | 0 | return true; |
561 | 0 | if (var->isArgument()) |
562 | 0 | return true; |
563 | |
|
564 | 0 | } |
565 | 1.72k | } |
566 | 1.66k | return false; |
567 | 1.66k | } |