Coverage Report

Created: 2023-09-25 06:15

/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
}