Coverage Report

Created: 2023-09-25 06:15

/src/cppcheck/lib/checkunusedvar.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
20
//---------------------------------------------------------------------------
21
#include "checkunusedvar.h"
22
23
#include "astutils.h"
24
#include "errortypes.h"
25
#include "fwdanalysis.h"
26
#include "library.h"
27
#include "preprocessor.h"
28
#include "settings.h"
29
#include "symboldatabase.h"
30
#include "token.h"
31
#include "tokenize.h"
32
#include "tokenlist.h"
33
#include "utils.h"
34
#include "valueflow.h"
35
36
#include <algorithm>
37
#include <list>
38
#include <set>
39
#include <utility>
40
#include <vector>
41
//---------------------------------------------------------------------------
42
43
// Register this check class (by creating a static instance of it)
44
namespace {
45
    CheckUnusedVar instance;
46
}
47
48
static const struct CWE CWE563(563U);   // Assignment to Variable without Use ('Unused Variable')
49
static const struct CWE CWE665(665U);   // Improper Initialization
50
51
/** Is scope a raii class scope */
52
static bool isRaiiClassScope(const Scope *classScope)
53
0
{
54
0
    return classScope && classScope->getDestructor() != nullptr;
55
0
}
56
57
/** Is ValueType a raii class? */
58
static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn = true)
59
1.90k
{
60
1.90k
    if (!cpp)
61
0
        return false;
62
63
1.90k
    if (!valueType)
64
393
        return defaultReturn;
65
66
1.50k
    if ((valueType->smartPointerType && isRaiiClassScope(valueType->smartPointerType->classScope)) || (!valueType->smartPointerType && valueType->type == ValueType::Type::SMART_POINTER))
67
0
        return true;
68
69
1.50k
    switch (valueType->type) {
70
0
    case ValueType::Type::UNKNOWN_TYPE:
71
0
    case ValueType::Type::NONSTD:
72
0
        return defaultReturn;
73
74
0
    case ValueType::Type::RECORD:
75
0
        if (isRaiiClassScope(valueType->typeScope))
76
0
            return true;
77
0
        return defaultReturn;
78
79
0
    case ValueType::Type::POD:
80
0
    case ValueType::Type::SMART_POINTER:
81
0
    case ValueType::Type::CONTAINER:
82
0
    case ValueType::Type::ITERATOR:
83
0
    case ValueType::Type::VOID:
84
41
    case ValueType::Type::BOOL:
85
41
    case ValueType::Type::CHAR:
86
41
    case ValueType::Type::SHORT:
87
41
    case ValueType::Type::WCHAR_T:
88
1.50k
    case ValueType::Type::INT:
89
1.50k
    case ValueType::Type::LONG:
90
1.50k
    case ValueType::Type::LONGLONG:
91
1.50k
    case ValueType::Type::UNKNOWN_INT:
92
1.50k
    case ValueType::Type::FLOAT:
93
1.50k
    case ValueType::Type::DOUBLE:
94
1.50k
    case ValueType::Type::LONGDOUBLE:
95
1.50k
        return false;
96
1.50k
    }
97
98
0
    return defaultReturn;
99
1.50k
}
100
101
/**
102
 * @brief This class is used create a list of variables within a function.
103
 */
104
class Variables {
105
public:
106
    enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer, none };
107
108
    /** Store information about variable usage */
109
    class VariableUsage {
110
    public:
111
        explicit VariableUsage(const Variable *var = nullptr,
112
                               VariableType type = standard,
113
                               bool read = false,
114
                               bool write = false,
115
                               bool modified = false,
116
                               bool allocateMemory = false) :
117
            _var(var),
118
            _lastAccess(var ? var->nameToken() : nullptr),
119
            mType(type),
120
            _read(read),
121
            _write(write),
122
            _modified(modified),
123
0
            _allocateMemory(allocateMemory) {}
124
125
        /** variable is used.. set both read+write */
126
0
        void use() {
127
0
            _read = true;
128
0
            _write = true;
129
0
        }
130
131
        /** is variable unused? */
132
0
        bool unused() const {
133
0
            return (!_read && !_write);
134
0
        }
135
136
        std::set<nonneg int> _aliases;
137
        std::set<const Scope*> _assignments;
138
139
        const Variable* _var;
140
        const Token* _lastAccess;
141
        VariableType mType;
142
        bool _read;
143
        bool _write;
144
        bool _modified; // read/modify/write
145
        bool _allocateMemory;
146
    };
147
148
0
    void clear() {
149
0
        mVarUsage.clear();
150
0
    }
151
3.47k
    const std::map<nonneg int, VariableUsage> &varUsage() const {
152
3.47k
        return mVarUsage;
153
3.47k
    }
154
    void addVar(const Variable *var, VariableType type, bool write_);
155
    void allocateMemory(nonneg int varid, const Token* tok);
156
    void read(nonneg int varid, const Token* tok);
157
    void readAliases(nonneg int varid, const Token* tok);
158
    void readAll(nonneg int varid, const Token* tok);
159
    void write(nonneg int varid, const Token* tok);
160
    void writeAliases(nonneg int varid, const Token* tok);
161
    void writeAll(nonneg int varid, const Token* tok);
162
    void use(nonneg int varid, const Token* tok);
163
    void modified(nonneg int varid, const Token* tok);
164
    VariableUsage *find(nonneg int varid);
165
    void alias(nonneg int varid1, nonneg int varid2, bool replace);
166
0
    void erase(nonneg int varid) {
167
0
        mVarUsage.erase(varid);
168
0
    }
169
    void eraseAliases(nonneg int varid);
170
    void eraseAll(nonneg int varid);
171
    void clearAliases(nonneg int varid);
172
173
private:
174
175
    std::map<nonneg int, VariableUsage> mVarUsage;
176
};
177
178
179
/**
180
 * Alias the 2 given variables. Either replace the existing aliases if
181
 * they exist or merge them.  You would replace an existing alias when this
182
 * assignment is in the same scope as the previous assignment.  You might
183
 * merge the aliases when this assignment is in a different scope from the
184
 * previous assignment depending on the relationship of the 2 scopes.
185
 */
186
void Variables::alias(nonneg int varid1, nonneg int varid2, bool replace)
187
0
{
188
0
    VariableUsage *var1 = find(varid1);
189
0
    VariableUsage *var2 = find(varid2);
190
191
0
    if (!var1 || !var2)
192
0
        return;
193
194
    // alias to self
195
0
    if (varid1 == varid2) {
196
0
        var1->use();
197
0
        return;
198
0
    }
199
200
0
    if (replace) {
201
        // remove var1 from all aliases
202
0
        for (std::set<nonneg int>::const_iterator i = var1->_aliases.cbegin(); i != var1->_aliases.cend(); ++i) {
203
0
            VariableUsage *temp = find(*i);
204
205
0
            if (temp)
206
0
                temp->_aliases.erase(var1->_var->declarationId());
207
0
        }
208
209
        // remove all aliases from var1
210
0
        var1->_aliases.clear();
211
0
    }
212
213
    // var1 gets all var2s aliases
214
0
    for (std::set<nonneg int>::const_iterator i = var2->_aliases.cbegin(); i != var2->_aliases.cend(); ++i) {
215
0
        if (*i != varid1)
216
0
            var1->_aliases.insert(*i);
217
0
    }
218
219
    // var2 is an alias of var1
220
0
    var2->_aliases.insert(varid1);
221
0
    var1->_aliases.insert(varid2);
222
223
0
    if (var2->mType == Variables::pointer) {
224
0
        var2->_read = true;
225
0
    }
226
0
}
227
228
void Variables::clearAliases(nonneg int varid)
229
0
{
230
0
    VariableUsage *usage = find(varid);
231
232
0
    if (usage) {
233
        // remove usage from all aliases
234
0
        std::set<nonneg int>::const_iterator i;
235
236
0
        for (i = usage->_aliases.cbegin(); i != usage->_aliases.cend(); ++i) {
237
0
            VariableUsage *temp = find(*i);
238
239
0
            if (temp)
240
0
                temp->_aliases.erase(usage->_var->declarationId());
241
0
        }
242
243
        // remove all aliases from usage
244
0
        usage->_aliases.clear();
245
0
    }
246
0
}
247
248
void Variables::eraseAliases(nonneg int varid)
249
0
{
250
0
    VariableUsage *usage = find(varid);
251
252
0
    if (usage) {
253
0
        for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases)
254
0
            erase(*aliases);
255
0
    }
256
0
}
257
258
void Variables::eraseAll(nonneg int varid)
259
0
{
260
0
    eraseAliases(varid);
261
0
    erase(varid);
262
0
}
263
264
void Variables::addVar(const Variable *var,
265
                       VariableType type,
266
                       bool write_)
267
0
{
268
0
    if (var->declarationId() > 0) {
269
0
        mVarUsage.insert(std::make_pair(var->declarationId(), VariableUsage(var, type, false, write_, false)));
270
0
    }
271
0
}
272
273
void Variables::allocateMemory(nonneg int varid, const Token* tok)
274
0
{
275
0
    VariableUsage *usage = find(varid);
276
277
0
    if (usage) {
278
0
        usage->_allocateMemory = true;
279
0
        usage->_lastAccess = tok;
280
0
    }
281
0
}
282
283
void Variables::read(nonneg int varid, const Token* tok)
284
12.8k
{
285
12.8k
    VariableUsage *usage = find(varid);
286
287
12.8k
    if (usage) {
288
0
        usage->_read = true;
289
0
        if (tok)
290
0
            usage->_lastAccess = tok;
291
0
    }
292
12.8k
}
293
294
void Variables::readAliases(nonneg int varid, const Token* tok)
295
9.72k
{
296
9.72k
    const VariableUsage *usage = find(varid);
297
298
9.72k
    if (usage) {
299
0
        for (nonneg int const aliases : usage->_aliases) {
300
0
            VariableUsage *aliased = find(aliases);
301
302
0
            if (aliased) {
303
0
                aliased->_read = true;
304
0
                aliased->_lastAccess = tok;
305
0
            }
306
0
        }
307
0
    }
308
9.72k
}
309
310
void Variables::readAll(nonneg int varid, const Token* tok)
311
9.72k
{
312
9.72k
    read(varid, tok);
313
9.72k
    readAliases(varid, tok);
314
9.72k
}
315
316
void Variables::write(nonneg int varid, const Token* tok)
317
2.29k
{
318
2.29k
    VariableUsage *usage = find(varid);
319
320
2.29k
    if (usage) {
321
0
        usage->_write = true;
322
0
        if (!usage->_var->isStatic() && !Token::simpleMatch(tok->next(), "= 0 ;"))
323
0
            usage->_read = false;
324
0
        usage->_lastAccess = tok;
325
0
    }
326
2.29k
}
327
328
void Variables::writeAliases(nonneg int varid, const Token* tok)
329
0
{
330
0
    VariableUsage *usage = find(varid);
331
332
0
    if (usage) {
333
0
        for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) {
334
0
            VariableUsage *aliased = find(*aliases);
335
336
0
            if (aliased) {
337
0
                aliased->_write = true;
338
0
                aliased->_lastAccess = tok;
339
0
            }
340
0
        }
341
0
    }
342
0
}
343
344
void Variables::writeAll(nonneg int varid, const Token* tok)
345
0
{
346
0
    write(varid, tok);
347
0
    writeAliases(varid, tok);
348
0
}
349
350
void Variables::use(nonneg int varid, const Token* tok)
351
3.13k
{
352
3.13k
    VariableUsage *usage = find(varid);
353
354
3.13k
    if (usage) {
355
0
        usage->use();
356
0
        usage->_lastAccess = tok;
357
358
0
        for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) {
359
0
            VariableUsage *aliased = find(*aliases);
360
361
0
            if (aliased) {
362
0
                aliased->use();
363
0
                aliased->_lastAccess = tok;
364
0
            }
365
0
        }
366
0
    }
367
3.13k
}
368
369
void Variables::modified(nonneg int varid, const Token* tok)
370
0
{
371
0
    VariableUsage *usage = find(varid);
372
373
0
    if (usage) {
374
0
        if (!usage->_var->isStatic())
375
0
            usage->_read = false;
376
0
        usage->_modified = true;
377
0
        usage->_lastAccess = tok;
378
379
0
        for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) {
380
0
            VariableUsage *aliased = find(*aliases);
381
382
0
            if (aliased) {
383
0
                aliased->_modified = true;
384
0
                aliased->_lastAccess = tok;
385
0
            }
386
0
        }
387
0
    }
388
0
}
389
390
Variables::VariableUsage *Variables::find(nonneg int varid)
391
34.6k
{
392
34.6k
    if (varid) {
393
31.5k
        const std::map<nonneg int, VariableUsage>::iterator i = mVarUsage.find(varid);
394
31.5k
        if (i != mVarUsage.end())
395
0
            return &i->second;
396
31.5k
    }
397
34.6k
    return nullptr;
398
34.6k
}
399
400
static const Token* doAssignment(Variables &variables, const Token *tok, bool dereference, const Scope *scope)
401
2.10k
{
402
    // a = a + b;
403
2.10k
    if (Token::Match(tok, "%var% = %var% !!;")) {
404
418
        const Token* rhsVarTok = tok->tokAt(2);
405
418
        if (tok->varId() == rhsVarTok->varId()) {
406
251
            return rhsVarTok;
407
251
        }
408
418
    }
409
410
1.85k
    if (Token::Match(tok, "%var% %assign%") && tok->strAt(1) != "=")
411
0
        return tok->next();
412
413
1.85k
    const Token* const tokOld = tok;
414
415
    // check for aliased variable
416
1.85k
    const nonneg int varid1 = tok->varId();
417
1.85k
    Variables::VariableUsage *var1 = variables.find(varid1);
418
419
1.85k
    if (var1) {
420
        // jump behind '='
421
0
        tok = tok->next();
422
0
        while (!tok->isAssignmentOp()) {
423
0
            if (tok->varId())
424
0
                variables.read(tok->varId(), tok);
425
0
            tok = tok->next();
426
0
        }
427
0
        tok = tok->next();
428
429
0
        if (Token::Match(tok, "( const| struct|union| %type% * ) ( ("))
430
0
            tok = tok->link()->next();
431
432
0
        if (Token::Match(tok, "( [(<] const| struct|union| %type% *| [>)]"))
433
0
            tok = tok->next();
434
435
0
        if (Token::Match(tok, "(| &| %name%") ||
436
0
            (Token::Match(tok->next(), "< const| struct|union| %type% *| > ( &| %name%"))) {
437
0
            bool addressOf = false;
438
439
0
            if (Token::Match(tok, "%var% ."))
440
0
                variables.use(tok->varId(), tok);   // use = read + write
441
442
            // check for C style cast
443
0
            if (tok->str() == "(") {
444
0
                tok = tok->next();
445
0
                if (tok->str() == "const")
446
0
                    tok = tok->next();
447
448
0
                if (Token::Match(tok, "struct|union"))
449
0
                    tok = tok->next();
450
451
0
                while ((tok->isName() && tok->varId() == 0) || (tok->str() == "*") || (tok->str() == ")"))
452
0
                    tok = tok->next();
453
454
0
                if (tok->str() == "&") {
455
0
                    addressOf = true;
456
0
                    tok = tok->next();
457
0
                } else if (tok->str() == "(") {
458
0
                    tok = tok->next();
459
0
                    if (tok->str() == "&") {
460
0
                        addressOf = true;
461
0
                        tok = tok->next();
462
0
                    }
463
0
                } else if (Token::Match(tok, "%cop% %var%")) {
464
0
                    variables.read(tok->next()->varId(), tok);
465
0
                }
466
0
            }
467
468
            // check for C++ style cast
469
0
            else if (tok->str().find("cast") != std::string::npos &&
470
0
                     tok->strAt(1) == "<") {
471
0
                tok = tok->tokAt(2);
472
0
                if (tok->str() == "const")
473
0
                    tok = tok->next();
474
475
0
                if (Token::Match(tok, "struct|union"))
476
0
                    tok = tok->next();
477
478
0
                tok = tok->next();
479
0
                if (tok->str() == "*")
480
0
                    tok = tok->next();
481
482
0
                tok = tok->tokAt(2);
483
0
                if (!tok)
484
0
                    return tokOld;
485
0
                if (tok->str() == "&") {
486
0
                    addressOf = true;
487
0
                    tok = tok->next();
488
0
                }
489
0
            }
490
491
            // no cast, no ?
492
0
            else if (!Token::Match(tok, "%name% ?")) {
493
0
                if (tok->str() == "&") {
494
0
                    addressOf = true;
495
0
                    tok = tok->next();
496
0
                } else if (tok->str() == "new")
497
0
                    return tokOld;
498
0
            }
499
500
            // check if variable is local
501
0
            const nonneg int varid2 = tok->varId();
502
0
            const Variables::VariableUsage* var2 = variables.find(varid2);
503
504
0
            if (var2) { // local variable (alias or read it)
505
0
                if (var1->mType == Variables::pointer || var1->mType == Variables::pointerArray) {
506
0
                    if (dereference)
507
0
                        variables.read(varid2, tok);
508
0
                    else {
509
0
                        if (addressOf ||
510
0
                            var2->mType == Variables::array ||
511
0
                            var2->mType == Variables::pointer) {
512
0
                            bool replace = true;
513
514
                            // pointerArray => don't replace
515
0
                            if (var1->mType == Variables::pointerArray)
516
0
                                replace = false;
517
518
                            // check if variable declared in same scope
519
0
                            else if (scope == var1->_var->scope())
520
0
                                replace = true;
521
522
                            // not in same scope as declaration
523
0
                            else {
524
                                // no other assignment in this scope
525
0
                                if (var1->_assignments.find(scope) == var1->_assignments.end() ||
526
0
                                    scope->type == Scope::eSwitch) {
527
                                    // nothing to replace
528
                                    // cppcheck-suppress duplicateBranch - remove when TODO below is address
529
0
                                    if (var1->_assignments.empty())
530
0
                                        replace = false;
531
532
                                    // this variable has previous assignments
533
0
                                    else {
534
                                        // TODO: determine if existing aliases should be replaced or merged
535
0
                                        replace = false;
536
0
                                    }
537
0
                                }
538
539
                                // assignment in this scope
540
0
                                else {
541
                                    // replace when only one other assignment, merge them otherwise
542
0
                                    replace = (var1->_assignments.size() == 1);
543
0
                                }
544
0
                            }
545
546
0
                            variables.alias(varid1, varid2, replace);
547
0
                        } else if (tok->strAt(1) == "?") {
548
0
                            if (var2->mType == Variables::reference)
549
0
                                variables.readAliases(varid2, tok);
550
0
                            else
551
0
                                variables.read(varid2, tok);
552
0
                        } else {
553
0
                            variables.readAll(varid2, tok);
554
0
                        }
555
0
                    }
556
0
                } else if (var1->mType == Variables::reference) {
557
0
                    variables.alias(varid1, varid2, true);
558
0
                } else if (var1->mType == Variables::standard && addressOf) {
559
0
                    variables.alias(varid1, varid2, true);
560
0
                } else {
561
0
                    if ((var2->mType == Variables::pointer || var2->mType == Variables::pointerArray) && tok->strAt(1) == "[")
562
0
                        variables.readAliases(varid2, tok);
563
564
0
                    variables.read(varid2, tok);
565
0
                }
566
0
            } else { // not a local variable (or an unsupported local variable)
567
0
                if (var1->mType == Variables::pointer && !dereference) {
568
                    // check if variable declaration is in this scope
569
0
                    if (var1->_var->scope() == scope) {
570
                        // If variable is used in RHS then "use" variable
571
0
                        for (const Token *rhs = tok; rhs && rhs->str() != ";"; rhs = rhs->next()) {
572
0
                            if (rhs->varId() == varid1) {
573
0
                                variables.use(varid1, tok);
574
0
                                break;
575
0
                            }
576
0
                        }
577
0
                        variables.clearAliases(varid1);
578
0
                    } else {
579
                        // no other assignment in this scope
580
0
                        if (var1->_assignments.find(scope) == var1->_assignments.end()) {
581
                            /**
582
                             * @todo determine if existing aliases should be discarded
583
                             */
584
0
                        }
585
586
                        // this assignment replaces the last assignment in this scope
587
0
                        else {
588
                            // aliased variables in a larger scope are not supported
589
                            // remove all aliases
590
0
                            variables.clearAliases(varid1);
591
0
                        }
592
0
                    }
593
0
                }
594
0
            }
595
0
        } else
596
0
            tok = tokOld;
597
598
0
        var1->_assignments.insert(scope);
599
0
    }
600
601
    // check for alias to struct member
602
    // char c[10]; a.b = c;
603
1.85k
    else if (Token::Match(tok->tokAt(-2), "%name% .")) {
604
0
        const Token *rhsVarTok = tok->tokAt(2);
605
0
        if (rhsVarTok && rhsVarTok->varId()) {
606
0
            const nonneg int varid2 = rhsVarTok->varId();
607
0
            const Variables::VariableUsage *var2 = variables.find(varid2);
608
609
            // struct member aliased to local variable
610
0
            if (var2 && (var2->mType == Variables::array ||
611
0
                         var2->mType == Variables::pointer)) {
612
                // erase aliased variable and all variables that alias it
613
                // to prevent false positives
614
0
                variables.eraseAll(varid2);
615
0
            }
616
0
        }
617
0
    }
618
619
    // Possible pointer alias
620
1.85k
    else if (Token::Match(tok, "%name% = %name% ;")) {
621
562
        const nonneg int varid2 = tok->tokAt(2)->varId();
622
562
        const Variables::VariableUsage *var2 = variables.find(varid2);
623
562
        if (var2 && (var2->mType == Variables::array ||
624
0
                     var2->mType == Variables::pointer)) {
625
0
            variables.use(varid2,tok);
626
0
        }
627
562
    }
628
629
1.85k
    return tok;
630
1.85k
}
631
632
static bool isPartOfClassStructUnion(const Token* tok)
633
0
{
634
0
    for (; tok; tok = tok->previous()) {
635
0
        if (tok->str() == "}" || tok->str() == ")")
636
0
            tok = tok->link();
637
0
        else if (tok->str() == "(")
638
0
            return false;
639
0
        else if (tok->str() == "{") {
640
0
            return (tok->strAt(-1) == "struct" || tok->strAt(-2) == "struct" || tok->strAt(-1) == "class" || tok->strAt(-2) == "class" || tok->strAt(-1) == "union" || tok->strAt(-2) == "union");
641
0
        }
642
0
    }
643
0
    return false;
644
0
}
645
646
static bool isVarDecl(const Token *tok)
647
10.4k
{
648
10.4k
    return tok && tok->variable() && tok->variable()->nameToken() == tok;
649
10.4k
}
650
651
// Skip [ .. ]
652
static const Token * skipBrackets(const Token *tok)
653
0
{
654
0
    while (tok && tok->str() == "[")
655
0
        tok = tok->link()->next();
656
0
    return tok;
657
0
}
658
659
660
// Skip [ .. ] . x
661
static const Token * skipBracketsAndMembers(const Token *tok)
662
2.10k
{
663
2.10k
    while (tok) {
664
2.10k
        if (tok->str() == "[")
665
0
            tok = tok->link()->next();
666
2.10k
        else if (Token::Match(tok, ". %name%"))
667
0
            tok = tok->tokAt(2);
668
2.10k
        else
669
2.10k
            break;
670
2.10k
    }
671
2.10k
    return tok;
672
2.10k
}
673
674
static void useFunctionArgs(const Token *tok, Variables& variables)
675
1.38k
{
676
    // TODO: Match function args to see if they are const or not. Assume that const data is not written.
677
1.38k
    if (!tok)
678
0
        return;
679
1.38k
    if (tok->str() == ",") {
680
0
        useFunctionArgs(tok->astOperand1(), variables);
681
0
        useFunctionArgs(tok->astOperand2(), variables);
682
1.38k
    } else if (Token::Match(tok, "[+:]") && (!tok->valueType() || tok->valueType()->pointer)) {
683
0
        useFunctionArgs(tok->astOperand1(), variables);
684
0
        useFunctionArgs(tok->astOperand2(), variables);
685
1.38k
    } else if (tok->variable() && tok->variable()->isArray()) {
686
0
        variables.use(tok->varId(), tok);
687
0
    }
688
1.38k
}
689
690
//---------------------------------------------------------------------------
691
// Usage of function variables
692
//---------------------------------------------------------------------------
693
void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables)
694
3.58k
{
695
    // Find declarations if the scope is executable..
696
3.58k
    if (scope->isExecutable()) {
697
        // Find declarations
698
3.58k
        for (std::list<Variable>::const_iterator i = scope->varlist.cbegin(); i != scope->varlist.cend(); ++i) {
699
0
            if (i->isThrow() || i->isExtern())
700
0
                continue;
701
0
            Variables::VariableType type = Variables::none;
702
0
            if (i->isArray() && (i->nameToken()->previous()->str() == "*" || i->nameToken()->strAt(-2) == "*"))
703
0
                type = Variables::pointerArray;
704
0
            else if (i->isArray() && i->nameToken()->previous()->str() == "&")
705
0
                type = Variables::referenceArray;
706
0
            else if (i->isArray())
707
0
                type = Variables::array;
708
0
            else if (i->isReference() && !(i->valueType() && i->valueType()->type == ValueType::UNKNOWN_TYPE && Token::simpleMatch(i->typeStartToken(), "auto")))
709
0
                type = Variables::reference;
710
0
            else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*")
711
0
                type = Variables::pointerPointer;
712
0
            else if (i->isPointerToArray())
713
0
                type = Variables::pointerPointer;
714
0
            else if (i->isPointer())
715
0
                type = Variables::pointer;
716
0
            else if (mTokenizer->isC() ||
717
0
                     i->typeEndToken()->isStandardType() ||
718
0
                     isRecordTypeWithoutSideEffects(i->type()) ||
719
0
                     mSettings->library.detectContainer(i->typeStartToken()) ||
720
0
                     i->isStlType())
721
0
                type = Variables::standard;
722
0
            if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken()))
723
0
                continue;
724
0
            const Token* defValTok = i->nameToken()->next();
725
0
            if (Token::Match(i->nameToken()->previous(), "* %var% ) (")) // function pointer. Jump behind parameter list.
726
0
                defValTok = defValTok->linkAt(1)->next();
727
0
            for (; defValTok; defValTok = defValTok->next()) {
728
0
                if (defValTok->str() == "[")
729
0
                    defValTok = defValTok->link();
730
0
                else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=" || defValTok->str() == ":") {
731
0
                    variables.addVar(&*i, type, true);
732
0
                    break;
733
0
                } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") {
734
0
                    variables.addVar(&*i, type, i->isStatic() && i->scope()->type != Scope::eFunction);
735
0
                    break;
736
0
                }
737
0
            }
738
0
            if (i->isArray() && i->isClass() && // Array of class/struct members. Initialized by ctor except for std::array
739
0
                !(i->isStlType() && i->valueType() && i->valueType()->containerTypeToken && i->valueType()->containerTypeToken->isStandardType()))
740
0
                variables.write(i->declarationId(), i->nameToken());
741
0
            if (i->isArray() && Token::Match(i->nameToken(), "%name% [ %var% ]")) // Array index variable read.
742
0
                variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken());
743
744
0
            if (defValTok && defValTok->next()) {
745
                // simple assignment "var = 123"
746
0
                if (defValTok->str() == "=" && defValTok->next()->str() != "{") {
747
0
                    doAssignment(variables, i->nameToken(), false, scope);
748
0
                } else {
749
                    // could be "var = {...}" OR "var{...}" (since C++11)
750
0
                    const Token* tokBraceStart = nullptr;
751
0
                    if (Token::simpleMatch(defValTok, "= {")) {
752
                        // "var = {...}"
753
0
                        tokBraceStart = defValTok->next();
754
0
                    } else if (defValTok->str() == "{") {
755
                        // "var{...}"
756
0
                        tokBraceStart = defValTok;
757
0
                    }
758
0
                    if (tokBraceStart) {
759
0
                        for (const Token* tok = tokBraceStart->next(); tok && tok != tokBraceStart->link(); tok = tok->next()) {
760
0
                            if (tok->varId()) {
761
                                // Variables used to initialize the array read.
762
0
                                variables.read(tok->varId(), i->nameToken());
763
0
                            }
764
0
                        }
765
0
                    }
766
0
                }
767
0
            }
768
0
        }
769
3.58k
    }
770
771
    // Check variable usage
772
3.58k
    const Token *tok;
773
3.58k
    if (scope->type == Scope::eFunction)
774
1.73k
        tok = scope->bodyStart->next();
775
1.84k
    else
776
1.84k
        tok = scope->classDef->next();
777
49.5k
    for (; tok && tok != scope->bodyEnd; tok = tok->next()) {
778
46.0k
        if (tok->str() == "{" && tok != scope->bodyStart && !tok->previous()->varId()) {
779
2.35k
            if (std::any_of(scope->nestedList.cbegin(), scope->nestedList.cend(), [&](const Scope* s) {
780
2.35k
                return s->bodyStart == tok;
781
2.35k
            })) {
782
1.84k
                checkFunctionVariableUsage_iterateScopes(tok->scope(), variables); // Scan child scope
783
1.84k
                tok = tok->link();
784
1.84k
            }
785
1.84k
            if (!tok)
786
0
                break;
787
1.84k
        }
788
789
46.0k
        if (Token::Match(tok, "asm ( %str% )")) {
790
0
            variables.clear();
791
0
            break;
792
0
        }
793
794
        // templates
795
46.0k
        if (tok->isName() && endsWith(tok->str(), '>')) {
796
            // TODO: This is a quick fix to handle when constants are used
797
            // as template parameters. Try to handle this better, perhaps
798
            // only remove constants.
799
0
            variables.clear();
800
0
        }
801
802
46.0k
        else if (Token::Match(tok->previous(), "[;{}]")) {
803
13.2k
            for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) {
804
13.2k
                if (tok2->varId()) {
805
                    // Is this a variable declaration?
806
497
                    const Variable *var = tok2->variable();
807
497
                    if (!var || var->nameToken() != tok2)
808
497
                        continue;
809
810
                    // Mark template parameters used in declaration as use..
811
0
                    if (tok2->strAt(-1) == ">") {
812
0
                        for (const Token *tok3 = tok; tok3 != tok2; tok3 = tok3->next()) {
813
0
                            if (tok3->varId())
814
0
                                variables.use(tok3->varId(), tok3);
815
0
                        }
816
0
                    }
817
818
                    // Skip variable declaration..
819
0
                    tok = tok2->next();
820
0
                    if (Token::Match(tok, "( %name% )")) // Simple initialization through copy ctor
821
0
                        tok = tok->next();
822
0
                    else if (Token::Match(tok, "= %var% ;")) { // Simple initialization
823
0
                        tok = tok->next();
824
0
                        if (!var->isReference())
825
0
                            variables.read(tok->varId(), tok);
826
0
                    } else if (tok->str() == "[" && Token::simpleMatch(skipBrackets(tok),"= {")) {
827
0
                        const Token * const rhs1 = skipBrackets(tok)->next();
828
0
                        for (const Token *rhs = rhs1->link(); rhs != rhs1; rhs = rhs->previous()) {
829
0
                            if (rhs->varId())
830
0
                                variables.readAll(rhs->varId(), rhs);
831
0
                        }
832
0
                    } else if (var->typeEndToken()->str() == ">") // Be careful with types like std::vector
833
0
                        tok = tok->previous();
834
0
                    break;
835
497
                }
836
12.7k
                if (Token::Match(tok2, "[;({=]"))
837
7.14k
                    break;
838
12.7k
            }
839
7.14k
        }
840
        // Freeing memory (not considered "using" the pointer if it was also allocated in this function)
841
46.0k
        if ((Token::Match(tok, "%name% ( %var% )") && mSettings->library.getDeallocFuncInfo(tok)) ||
842
46.0k
            (mTokenizer->isCPP() && (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")))) {
843
0
            nonneg int varid = 0;
844
0
            if (tok->str() != "delete") {
845
0
                const Token *varTok = tok->tokAt(2);
846
0
                varid = varTok->varId();
847
0
                tok = varTok->next();
848
0
            } else if (tok->strAt(1) == "[") {
849
0
                const Token *varTok = tok->tokAt(3);
850
0
                varid = varTok->varId();
851
0
                tok = varTok;
852
0
            } else {
853
0
                varid = tok->next()->varId();
854
0
                tok = tok->next();
855
0
            }
856
857
0
            const Variables::VariableUsage *const var = variables.find(varid);
858
0
            if (var) {
859
0
                if (!var->_aliases.empty())
860
0
                    variables.use(varid, tok);
861
0
                else if (!var->_allocateMemory)
862
0
                    variables.readAll(varid, tok);
863
0
            }
864
0
        }
865
866
46.0k
        else if (Token::Match(tok, "return|throw")) {
867
5.18k
            for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
868
5.18k
                if (tok2->varId())
869
615
                    variables.readAll(tok2->varId(), tok);
870
4.57k
                else if (tok2->str() == ";")
871
1.91k
                    break;
872
5.18k
            }
873
1.91k
        }
874
875
        // assignment
876
44.0k
        else if (Token::Match(tok, "*| ++|--| %name% ++|--| %assign%") ||
877
44.0k
                 Token::Match(tok, "*| ( const| %type% *| ) %name% %assign%")) {
878
2.10k
            bool dereference = false;
879
2.10k
            bool pre = false;
880
2.10k
            bool post = false;
881
882
2.10k
            if (tok->str() == "*") {
883
0
                dereference = true;
884
0
                tok = tok->next();
885
0
            }
886
887
2.10k
            if (Token::Match(tok, "( const| %type% *| ) %name% %assign%"))
888
0
                tok = tok->link()->next();
889
890
2.10k
            else if (tok->str() == "(")
891
0
                tok = tok->next();
892
893
2.10k
            if (tok->tokType() == Token::eIncDecOp) {
894
0
                pre = true;
895
0
                tok = tok->next();
896
0
            }
897
898
2.10k
            if (tok->next()->tokType() == Token::eIncDecOp)
899
0
                post = true;
900
901
2.10k
            const nonneg int varid1 = tok->varId();
902
2.10k
            const Token * const start = tok;
903
904
            // assignment in while head..
905
2.10k
            bool inwhile = false;
906
2.10k
            {
907
2.10k
                const Token *parent = tok->astParent();
908
5.28k
                while (parent) {
909
3.34k
                    if (Token::simpleMatch(parent->previous(), "while (")) {
910
166
                        inwhile = true;
911
166
                        break;
912
166
                    }
913
3.17k
                    parent = parent->astParent();
914
3.17k
                }
915
2.10k
            }
916
917
2.10k
            tok = doAssignment(variables, tok, dereference, scope);
918
919
2.10k
            if (tok && tok->isAssignmentOp() && tok->str() != "=") {
920
0
                variables.use(varid1, tok);
921
0
                if (Token::Match(tok, "%assign% %name%")) {
922
0
                    tok = tok->next();
923
0
                    variables.read(tok->varId(), tok);
924
0
                }
925
0
            }
926
927
2.10k
            if (pre || post)
928
0
                variables.use(varid1, tok);
929
930
2.10k
            if (dereference) {
931
0
                const Variables::VariableUsage *const var = variables.find(varid1);
932
0
                if (var && var->mType == Variables::array)
933
0
                    variables.write(varid1, tok);
934
0
                variables.writeAliases(varid1, tok);
935
0
                variables.read(varid1, tok);
936
2.10k
            } else {
937
2.10k
                const Variables::VariableUsage *const var = variables.find(varid1);
938
2.10k
                if (var && (inwhile || start->strAt(-1) == ",")) {
939
0
                    variables.use(varid1, tok);
940
2.10k
                } else if (var && var->mType == Variables::reference) {
941
0
                    variables.writeAliases(varid1, tok);
942
0
                    variables.read(varid1, tok);
943
0
                }
944
                // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
945
2.10k
                else if (var && var->mType == Variables::pointer &&
946
2.10k
                         Token::Match(start, "%name% =") &&
947
2.10k
                         findAllocFuncCallToken(start->next()->astOperand2(), mSettings->library)) {
948
949
0
                    const Token *allocFuncCallToken = findAllocFuncCallToken(start->next()->astOperand2(), mSettings->library);
950
0
                    const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken);
951
952
0
                    bool allocateMemory = !allocFunc || Library::ismemory(allocFunc->groupId);
953
954
0
                    if (allocFuncCallToken->str() == "new") {
955
0
                        const Token *type = allocFuncCallToken->next();
956
957
                        // skip nothrow
958
0
                        if (mTokenizer->isCPP() && (Token::simpleMatch(type, "( nothrow )") ||
959
0
                                                    Token::simpleMatch(type, "( std :: nothrow )")))
960
0
                            type = type->link()->next();
961
962
                        // is it a user defined type?
963
0
                        if (!type->isStandardType()) {
964
0
                            const Variable *variable = start->variable();
965
0
                            if (!variable || !isRecordTypeWithoutSideEffects(variable->type()))
966
0
                                allocateMemory = false;
967
0
                        }
968
0
                    }
969
970
0
                    if (allocateMemory)
971
0
                        variables.allocateMemory(varid1, tok);
972
0
                    else
973
0
                        variables.write(varid1, tok);
974
2.10k
                } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) {
975
0
                    variables.read(varid1, tok);
976
0
                    variables.write(varid1, start);
977
2.10k
                } else {
978
2.10k
                    variables.write(varid1, tok);
979
2.10k
                }
980
2.10k
            }
981
982
2.10k
            const Variables::VariableUsage * const var2 = variables.find(tok->varId());
983
2.10k
            if (var2) {
984
0
                if (var2->mType == Variables::reference) {
985
0
                    variables.writeAliases(tok->varId(), tok);
986
0
                    variables.read(tok->varId(), tok);
987
0
                } else if (tok->varId() != varid1 && Token::Match(tok, "%name% .|["))
988
0
                    variables.read(tok->varId(), tok);
989
0
                else if (tok->varId() != varid1 &&
990
0
                         var2->mType == Variables::standard &&
991
0
                         tok->strAt(-1) != "&")
992
0
                    variables.use(tok->varId(), tok);
993
0
            }
994
995
2.10k
            const Token * const equal = skipBracketsAndMembers(tok->next());
996
997
            // checked for chained assignments
998
2.10k
            if (tok != start && equal && equal->str() == "=") {
999
15
                const nonneg int varId = tok->varId();
1000
15
                const Variables::VariableUsage * const var = variables.find(varId);
1001
1002
15
                if (var && var->mType != Variables::reference) {
1003
0
                    variables.read(varId,tok);
1004
0
                }
1005
1006
15
                tok = tok->previous();
1007
15
            }
1008
2.10k
        }
1009
1010
        // assignment
1011
41.9k
        else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) ||
1012
41.9k
                 (tok->isUnaryOp("*") && Token::simpleMatch(tok->astParent(), "=") && Token::simpleMatch(tok->astOperand1(), "+"))) {
1013
0
            const Token *eq = tok;
1014
0
            while (eq && !eq->isAssignmentOp())
1015
0
                eq = eq->astParent();
1016
1017
0
            const bool deref = eq && eq->astOperand1() && eq->astOperand1()->valueType() && eq->astOperand1()->valueType()->pointer == 0U;
1018
1019
0
            if (tok->str() == "*") {
1020
0
                tok = tok->tokAt(2);
1021
0
                if (tok->str() == "(")
1022
0
                    tok = tok->link()->next();
1023
0
            }
1024
1025
0
            const nonneg int varid = tok->varId();
1026
0
            const Variables::VariableUsage *var = variables.find(varid);
1027
1028
0
            if (var) {
1029
                // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
1030
0
                if (var->mType == Variables::pointer &&
1031
0
                    ((mTokenizer->isCPP() && Token::simpleMatch(skipBrackets(tok->next()), "= new")) ||
1032
0
                     (Token::Match(skipBrackets(tok->next()), "= %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2))))) {
1033
0
                    variables.allocateMemory(varid, tok);
1034
0
                } else if (var->mType == Variables::pointer || var->mType == Variables::reference) {
1035
0
                    variables.read(varid, tok);
1036
0
                    variables.writeAliases(varid, tok);
1037
0
                } else if (var->mType == Variables::pointerArray) {
1038
0
                    tok = doAssignment(variables, tok, deref, scope);
1039
0
                } else
1040
0
                    variables.writeAll(varid, tok);
1041
0
            }
1042
0
        }
1043
1044
41.9k
        else if (mTokenizer->isCPP() && Token::Match(tok, "[;{}] %var% <<")) {
1045
0
            variables.erase(tok->next()->varId());
1046
0
        }
1047
1048
41.9k
        else if (Token::Match(tok, "& %var%")) {
1049
184
            if (tok->astOperand2()) { // bitop
1050
184
                variables.read(tok->next()->varId(), tok);
1051
184
            } else // addressof
1052
0
                variables.use(tok->next()->varId(), tok); // use = read + write
1053
41.8k
        } else if (Token::Match(tok, ">>|>>= %name%")) {
1054
0
            if (isLikelyStreamRead(mTokenizer->isCPP(), tok))
1055
0
                variables.use(tok->next()->varId(), tok); // use = read + write
1056
0
            else
1057
0
                variables.read(tok->next()->varId(), tok);
1058
41.8k
        } else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) {
1059
0
            variables.read(tok->varId(), tok);
1060
41.8k
        } else if (isLikelyStreamRead(mTokenizer->isCPP(),tok->previous())) {
1061
0
            variables.use(tok->varId(), tok);
1062
0
        }
1063
1064
        // function parameter
1065
41.8k
        else if (Token::Match(tok, "[(,] %var% [")) {
1066
0
            variables.use(tok->next()->varId(), tok);   // use = read + write
1067
41.8k
        } else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") {
1068
206
            variables.use(tok->next()->varId(), tok);   // use = read + write
1069
41.6k
        } else if (Token::Match(tok, "[(,] & %var% [,)]")) {
1070
0
            variables.eraseAll(tok->tokAt(2)->varId());
1071
41.6k
        } else if (Token::Match(tok, "[(,] (") &&
1072
41.6k
                   Token::Match(tok->next()->link(), ") %var% [,)[]")) {
1073
0
            variables.use(tok->next()->link()->next()->varId(), tok);   // use = read + write
1074
41.6k
        } else if (Token::Match(tok, "[(,] *| *| %var%")) {
1075
1.20k
            const Token* vartok = tok->next();
1076
1.20k
            while (vartok->str() == "*")
1077
0
                vartok = vartok->next();
1078
1.20k
            if (!(vartok->variable() && vartok == vartok->variable()->nameToken()) &&
1079
1.20k
                !(tok->str() == "(" && !Token::Match(tok->previous(), "%name%")))
1080
855
                variables.use(vartok->varId(), vartok);
1081
1.20k
        }
1082
1083
        // function
1084
40.3k
        else if (Token::Match(tok, "%name% (")) {
1085
1.38k
            if (tok->varId() && !tok->function()) // operator()
1086
0
                variables.use(tok->varId(), tok);
1087
1.38k
            else
1088
1.38k
                variables.read(tok->varId(), tok);
1089
1.38k
            useFunctionArgs(tok->next()->astOperand2(), variables);
1090
39.0k
        } else if (Token::Match(tok, "std :: ref ( %var% )")) {
1091
0
            variables.eraseAll(tok->tokAt(4)->varId());
1092
0
        }
1093
1094
39.0k
        else if (Token::Match(tok->previous(), "[{,] %var% [,}]")) {
1095
0
            variables.read(tok->varId(), tok);
1096
0
        }
1097
1098
39.0k
        else if (tok->varId() && Token::Match(tok, "%var% .")) {
1099
0
            variables.use(tok->varId(), tok);   // use = read + write
1100
0
        }
1101
1102
39.0k
        else if (tok->str() == ":" && (!tok->valueType() || tok->valueType()->pointer)) {
1103
0
            if (tok->astOperand1())
1104
0
                variables.use(tok->astOperand1()->varId(), tok->astOperand1());
1105
0
            if (tok->astOperand2())
1106
0
                variables.use(tok->astOperand2()->varId(), tok->astOperand2());
1107
0
        }
1108
1109
39.0k
        else if (tok->isExtendedOp() && tok->next() && tok->next()->varId() && tok->strAt(2) != "=" && !isVarDecl(tok->next())) {
1110
3.18k
            variables.readAll(tok->next()->varId(), tok);
1111
3.18k
        }
1112
1113
35.8k
        else if (tok->varId() && !isVarDecl(tok) && tok->next() && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) {
1114
5.93k
            if (Token::Match(tok->tokAt(-2), "%name% ( %var% [,)]") &&
1115
5.93k
                !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference()))
1116
11
                variables.use(tok->varId(), tok);
1117
5.92k
            else
1118
5.92k
                variables.readAll(tok->varId(), tok);
1119
5.93k
        }
1120
1121
29.8k
        else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) {
1122
0
            variables.readAll(tok->varId(), tok);
1123
0
        }
1124
1125
        // ++|--
1126
29.8k
        else if (tok->next() && tok->next()->tokType() == Token::eIncDecOp && tok->next()->astOperand1() && tok->next()->astOperand1()->varId()) {
1127
2.05k
            if (tok->next()->astParent())
1128
2.05k
                variables.use(tok->next()->astOperand1()->varId(), tok);
1129
0
            else
1130
0
                variables.modified(tok->next()->astOperand1()->varId(), tok);
1131
2.05k
        }
1132
1133
27.8k
        else if (tok->isAssignmentOp()) {
1134
8.78k
            for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
1135
7.28k
                if (tok2->varId()) {
1136
1.78k
                    if (tok2->strAt(1) == "=")
1137
188
                        variables.write(tok2->varId(), tok);
1138
1.59k
                    else if (tok2->next() && tok2->next()->isAssignmentOp())
1139
0
                        variables.use(tok2->varId(), tok);
1140
1.59k
                    else
1141
1.59k
                        variables.read(tok2->varId(), tok);
1142
1.78k
                }
1143
7.28k
            }
1144
26.3k
        } else if (tok->variable() && tok->variable()->isClass() && tok->variable()->type() &&
1145
26.3k
                   (tok->variable()->type()->needInitialization == Type::NeedInitialization::False) &&
1146
26.3k
                   tok->next()->str() == ";") {
1147
0
            variables.write(tok->varId(), tok);
1148
0
        }
1149
46.0k
    }
1150
3.58k
}
1151
1152
void CheckUnusedVar::checkFunctionVariableUsage()
1153
1.36k
{
1154
1.36k
    if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->checkLibrary)
1155
0
        return;
1156
1157
1.36k
    logChecker("CheckUnusedVar::checkFunctionVariableUsage"); // style
1158
1159
    // Parse all executing scopes..
1160
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1161
1162
1.36k
    auto reportLibraryCfgError = [this](const Token* tok, const std::string& typeName) {
1163
0
        if (mSettings->checkLibrary) {
1164
0
            reportError(tok,
1165
0
                        Severity::information,
1166
0
                        "checkLibraryCheckType",
1167
0
                        "--check-library: Provide <type-checks><unusedvar> configuration for " + typeName);
1168
0
        }
1169
0
    };
1170
1171
    // only check functions
1172
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
1173
        // Bailout when there are lambdas or inline functions
1174
        // TODO: Handle lambdas and inline functions properly
1175
1.73k
        if (scope->hasInlineOrLambdaFunction())
1176
0
            continue;
1177
1178
37.6k
        for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1179
35.9k
            if (findLambdaEndToken(tok))
1180
                // todo: handle lambdas
1181
0
                break;
1182
35.9k
            if (Token::simpleMatch(tok, "try {"))
1183
                // todo: check try blocks
1184
0
                tok = tok->linkAt(1);
1185
35.9k
            const Token *varDecl = nullptr;
1186
35.9k
            if (tok->variable() && tok->variable()->nameToken() == tok) {
1187
0
                const Token * eq = tok->next();
1188
0
                while (Token::simpleMatch(eq, "["))
1189
0
                    eq = eq->link()->next();
1190
0
                if (Token::simpleMatch(eq, ") (") && Token::simpleMatch(eq->linkAt(1), ") ="))
1191
0
                    eq = eq->linkAt(1)->next();
1192
0
                if (Token::simpleMatch(eq, "=")) {
1193
0
                    varDecl = tok;
1194
0
                    tok = eq;
1195
0
                }
1196
0
            }
1197
            // not assignment/initialization/increment => continue
1198
35.9k
            const bool isAssignment = tok->isAssignmentOp() && tok->astOperand1();
1199
35.9k
            const bool isInitialization = (Token::Match(tok, "%var% (|{") && tok->variable() && tok->variable()->nameToken() == tok);
1200
35.9k
            const bool isIncrementOrDecrement = (tok->tokType() == Token::Type::eIncDecOp);
1201
35.9k
            if (!isAssignment && !isInitialization && !isIncrementOrDecrement)
1202
32.5k
                continue;
1203
1204
3.34k
            if (isInitialization && Token::Match(tok, "%var% { }")) // don't warn for trivial initialization
1205
0
                continue;
1206
1207
3.34k
            if (isIncrementOrDecrement && tok->astParent() && precedes(tok, tok->astOperand1()))
1208
1.40k
                continue;
1209
1210
1.93k
            if (tok->str() == "=" && !(tok->valueType() && tok->valueType()->pointer) && isRaiiClass(tok->valueType(), mTokenizer->isCPP(), false))
1211
0
                continue;
1212
1213
1.93k
            const bool isPointer = tok->valueType() && (tok->valueType()->pointer || tok->valueType()->type == ValueType::SMART_POINTER);
1214
1215
1.93k
            if (tok->isName()) {
1216
0
                if (isRaiiClass(tok->valueType(), mTokenizer->isCPP(), false))
1217
0
                    continue;
1218
0
                tok = tok->next();
1219
0
            }
1220
1.93k
            if (!isInitialization && tok->astParent() && !tok->astParent()->isAssignmentOp() && tok->str() != "(") {
1221
343
                const Token *parent = tok->astParent();
1222
407
                while (Token::Match(parent, "%oror%|%comp%|!|&&"))
1223
64
                    parent = parent->astParent();
1224
343
                if (!parent)
1225
0
                    continue;
1226
343
                if (!Token::simpleMatch(parent->previous(), "if ("))
1227
272
                    continue;
1228
343
            }
1229
            // Do not warn about assignment with NULL
1230
1.66k
            if (isPointer && isNullOperand(tok->astOperand2()))
1231
0
                continue;
1232
1233
1.66k
            if (!tok->astOperand1())
1234
2
                continue;
1235
1236
1.66k
            const Token *iteratorToken = tok->astOperand1();
1237
1.66k
            while (Token::Match(iteratorToken, "[.*]"))
1238
0
                iteratorToken = iteratorToken->astOperand1();
1239
1.66k
            if (iteratorToken && iteratorToken->variable() && iteratorToken->variable()->typeEndToken()->str().find("iterator") != std::string::npos)
1240
0
                continue;
1241
1242
1.66k
            const Token *op1tok = tok->astOperand1();
1243
1.66k
            while (Token::Match(op1tok, ".|[|*"))
1244
0
                op1tok = op1tok->astOperand1();
1245
1246
            // Assignment in macro => do not warn
1247
1.66k
            if (isAssignment && tok->isExpandedMacro() && op1tok && op1tok->isExpandedMacro())
1248
0
                continue;
1249
1250
1.66k
            const Variable *op1Var = op1tok ? op1tok->variable() : nullptr;
1251
1.66k
            if (!op1Var && Token::Match(tok, "(|{") && tok->previous() && tok->previous()->variable())
1252
0
                op1Var = tok->previous()->variable();
1253
1.66k
            std::string bailoutTypeName;
1254
1.66k
            if (op1Var) {
1255
1.24k
                if (op1Var->isReference() && op1Var->nameToken() != tok->astOperand1())
1256
                    // todo: check references
1257
0
                    continue;
1258
1259
1.24k
                if (op1Var->isStatic())
1260
                    // todo: check static variables
1261
0
                    continue;
1262
1263
1.24k
                if (op1Var->nameToken()->isAttributeUnused())
1264
0
                    continue;
1265
1266
                // Avoid FP for union..
1267
1.24k
                if (op1Var->type() && op1Var->type()->isUnionType())
1268
0
                    continue;
1269
1270
                // Bailout for unknown template classes, we have no idea what side effects such assignments have
1271
1.24k
                if (mTokenizer->isCPP() &&
1272
1.24k
                    op1Var->isClass() &&
1273
1.24k
                    (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) {
1274
                    // Check in the library if we should bailout or not..
1275
0
                    std::string typeName = op1Var->getTypeName();
1276
0
                    if (startsWith(typeName, "::"))
1277
0
                        typeName.erase(typeName.begin(), typeName.begin() + 2);
1278
0
                    switch (mSettings->library.getTypeCheck("unusedvar", typeName)) {
1279
0
                    case Library::TypeCheck::def:
1280
0
                        bailoutTypeName = typeName;
1281
0
                        break;
1282
0
                    case Library::TypeCheck::check:
1283
0
                        break;
1284
0
                    case Library::TypeCheck::suppress:
1285
0
                    case Library::TypeCheck::checkFiniteLifetime:
1286
0
                        continue;
1287
0
                    }
1288
0
                }
1289
1.24k
            }
1290
1291
            // Is there a redundant assignment?
1292
1.66k
            const Token *start = tok->findExpressionStartEndTokens().second->next();
1293
1294
1.66k
            const Token *expr = varDecl ? varDecl : tok->astOperand1();
1295
1296
1.66k
            if (isInitialization)
1297
0
                expr = tok->previous();
1298
1299
            // Is variable in lhs a union member?
1300
1.66k
            if (tok->previous() && tok->previous()->variable() && tok->previous()->variable()->nameToken()->scope()->type == Scope::eUnion)
1301
0
                continue;
1302
1303
1.66k
            FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library);
1304
1.66k
            const Token* scopeEnd = ValueFlow::getEndOfExprScope(expr, scope, /*smallest*/ false);
1305
1.66k
            if (fwdAnalysis.unusedValue(expr, start, scopeEnd)) {
1306
0
                if (!bailoutTypeName.empty()) {
1307
0
                    if (bailoutTypeName != "auto")
1308
0
                        reportLibraryCfgError(tok, bailoutTypeName);
1309
0
                    continue;
1310
0
                }
1311
1312
                // warn
1313
0
                if (!expr->variable() || !expr->variable()->isMaybeUnused())
1314
0
                    unreadVariableError(tok, expr->expressionString(), false);
1315
0
            }
1316
1.66k
        }
1317
1318
        // varId, usage {read, write, modified}
1319
1.73k
        Variables variables;
1320
1321
1.73k
        checkFunctionVariableUsage_iterateScopes(scope, variables);
1322
1323
1324
        // Check usage of all variables in the current scope..
1325
1.73k
        for (std::map<nonneg int, Variables::VariableUsage>::const_iterator it = variables.varUsage().cbegin();
1326
1.73k
             it != variables.varUsage().cend();
1327
1.73k
             ++it) {
1328
0
            const Variables::VariableUsage &usage = it->second;
1329
1330
            // variable has been marked as unused so ignore it
1331
0
            if (usage._var->nameToken()->isAttributeUnused() || usage._var->nameToken()->isAttributeUsed())
1332
0
                continue;
1333
1334
            // skip things that are only partially implemented to prevent false positives
1335
0
            if (usage.mType == Variables::pointerPointer ||
1336
0
                usage.mType == Variables::pointerArray ||
1337
0
                usage.mType == Variables::referenceArray)
1338
0
                continue;
1339
1340
0
            const std::string &varname = usage._var->name();
1341
0
            const Variable* var = symbolDatabase->getVariableFromVarId(it->first);
1342
1343
            // variable has had memory allocated for it, but hasn't done
1344
            // anything with that memory other than, perhaps, freeing it
1345
0
            if (usage.unused() && !usage._modified && usage._allocateMemory)
1346
0
                allocatedButUnusedVariableError(usage._lastAccess, varname);
1347
1348
            // variable has not been written, read, or modified
1349
0
            else if (usage.unused() && !usage._modified) {
1350
0
                if (!usage._var->isMaybeUnused()) {
1351
0
                    unusedVariableError(usage._var->nameToken(), varname);
1352
0
                }
1353
0
            }
1354
            // variable has not been written but has been modified
1355
0
            else if (usage._modified && !usage._write && !usage._allocateMemory && var && !var->isStlType()) {
1356
0
                if (var->isStatic()) // static variables are initialized by default
1357
0
                    continue;
1358
0
                unassignedVariableError(usage._var->nameToken(), varname);
1359
0
            }
1360
            // variable has been read but not written
1361
0
            else if (!usage._write && !usage._allocateMemory && var && !var->isStlType() && !isEmptyType(var->type()))
1362
0
                unassignedVariableError(usage._var->nameToken(), varname);
1363
0
            else if (!usage._var->isMaybeUnused() && !usage._modified && !usage._read && var) {
1364
0
                const Token* vnt = var->nameToken();
1365
0
                bool error = false;
1366
0
                if (vnt->next()->isSplittedVarDeclEq() || (!var->isReference() && vnt->next()->str() == "=")) {
1367
0
                    const Token* nextStmt = vnt->tokAt(2);
1368
0
                    if (nextStmt->isExpandedMacro()) {
1369
0
                        const Token* parent = nextStmt;
1370
0
                        while (parent->astParent() && parent == parent->astParent()->astOperand1())
1371
0
                            parent = parent->astParent();
1372
0
                        if (parent->isAssignmentOp() && parent->isExpandedMacro())
1373
0
                            continue;
1374
0
                    }
1375
0
                    while (nextStmt && nextStmt->str() != ";")
1376
0
                        nextStmt = nextStmt->next();
1377
0
                    error = precedes(usage._lastAccess, nextStmt);
1378
0
                }
1379
0
                if (error) {
1380
0
                    if (mTokenizer->isCPP() && var->isClass() &&
1381
0
                        (!var->valueType() || var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) {
1382
0
                        const std::string typeName = var->getTypeName();
1383
0
                        switch (mSettings->library.getTypeCheck("unusedvar", typeName)) {
1384
0
                        case Library::TypeCheck::def:
1385
0
                            reportLibraryCfgError(vnt, typeName);
1386
0
                            break;
1387
0
                        case Library::TypeCheck::check:
1388
0
                            break;
1389
0
                        case Library::TypeCheck::suppress:
1390
0
                        case Library::TypeCheck::checkFiniteLifetime:
1391
0
                            error = false;
1392
0
                        }
1393
0
                    }
1394
0
                    if (error)
1395
0
                        unreadVariableError(vnt, varname, false);
1396
0
                }
1397
0
            }
1398
0
        }
1399
1.73k
    }
1400
1.36k
}
1401
1402
void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname)
1403
0
{
1404
0
    if (!mSettings->severity.isEnabled(Severity::style))
1405
0
        return;
1406
1407
0
    reportError(tok, Severity::style, "unusedVariable", "$symbol:" + varname + "\nUnused variable: $symbol", CWE563, Certainty::normal);
1408
0
}
1409
1410
void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname)
1411
0
{
1412
0
    if (!mSettings->severity.isEnabled(Severity::style))
1413
0
        return;
1414
1415
0
    reportError(tok, Severity::style, "unusedAllocatedMemory", "$symbol:" + varname + "\nVariable '$symbol' is allocated memory that is never used.", CWE563, Certainty::normal);
1416
0
}
1417
1418
void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified)
1419
0
{
1420
0
    if (!mSettings->severity.isEnabled(Severity::style))
1421
0
        return;
1422
1423
0
    if (modified)
1424
0
        reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is modified but its new value is never used.", CWE563, Certainty::normal);
1425
0
    else
1426
0
        reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is assigned a value that is never used.", CWE563, Certainty::normal);
1427
0
}
1428
1429
void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname)
1430
0
{
1431
0
    if (!mSettings->severity.isEnabled(Severity::style))
1432
0
        return;
1433
1434
0
    reportError(tok, Severity::style, "unassignedVariable", "$symbol:" + varname + "\nVariable '$symbol' is not assigned a value.", CWE665, Certainty::normal);
1435
0
}
1436
1437
//---------------------------------------------------------------------------
1438
// Check that all struct members are used
1439
//---------------------------------------------------------------------------
1440
void CheckUnusedVar::checkStructMemberUsage()
1441
1.36k
{
1442
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
1443
0
        return;
1444
1445
1.36k
    logChecker("CheckUnusedVar::checkStructMemberUsage"); // style
1446
1447
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1448
1449
4.94k
    for (const Scope &scope : symbolDatabase->scopeList) {
1450
4.94k
        if (scope.type != Scope::eStruct && scope.type != Scope::eClass && scope.type != Scope::eUnion)
1451
4.94k
            continue;
1452
1453
0
        if (scope.bodyStart->fileIndex() != 0 || scope.className.empty())
1454
0
            continue;
1455
1456
0
        if (scope.classDef->isExpandedMacro())
1457
0
            continue;
1458
1459
        // Packed struct => possibly used by lowlevel code. Struct members might be required by hardware.
1460
0
        if (scope.bodyEnd->isAttributePacked())
1461
0
            continue;
1462
0
        if (const Preprocessor *preprocessor = mTokenizer->getPreprocessor()) {
1463
0
            const auto& directives = preprocessor->getDirectives();
1464
0
            const bool isPacked = std::any_of(directives.cbegin(), directives.cend(), [&](const Directive& d) {
1465
0
                return d.linenr < scope.bodyStart->linenr() && d.str == "#pragma pack(1)" && d.file == mTokenizer->list.getFiles().front();
1466
0
            });
1467
0
            if (isPacked)
1468
0
                continue;
1469
0
        }
1470
1471
        // Bail out for template struct, members might be used in non-matching instantiations
1472
0
        if (scope.className.find('<') != std::string::npos)
1473
0
            continue;
1474
1475
        // bail out if struct is inherited
1476
0
        const bool isInherited = std::any_of(symbolDatabase->scopeList.cbegin(), symbolDatabase->scopeList.cend(), [&](const Scope& derivedScope) {
1477
0
            const Type* dType = derivedScope.definedType;
1478
0
            return dType && std::any_of(dType->derivedFrom.cbegin(), dType->derivedFrom.cend(), [&](const Type::BaseInfo& derivedFrom) {
1479
0
                return derivedFrom.type == scope.definedType && derivedFrom.access != AccessControl::Private;
1480
0
            });
1481
0
        });
1482
1483
        // bail out for extern/global struct
1484
0
        bool bailout = false;
1485
0
        for (const Variable* var : symbolDatabase->variableList()) {
1486
0
            if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope.className) {
1487
0
                bailout = true;
1488
0
                break;
1489
0
            }
1490
0
            if (bailout)
1491
0
                break;
1492
0
        }
1493
0
        if (bailout)
1494
0
            continue;
1495
1496
        // Bail out if some data is casted to struct..
1497
0
        const std::string castPattern("( struct| " + scope.className + " * ) &| %name%");
1498
0
        if (Token::findmatch(scope.bodyEnd, castPattern.c_str()))
1499
0
            continue;
1500
1501
        // (struct S){..}
1502
0
        const std::string initPattern("( struct| " + scope.className + " ) {");
1503
0
        if (Token::findmatch(scope.bodyEnd, initPattern.c_str()))
1504
0
            continue;
1505
1506
        // Bail out if struct is used in sizeof..
1507
0
        for (const Token *tok = scope.bodyEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) {
1508
0
            tok = tok->tokAt(2);
1509
0
            if (Token::Match(tok, ("struct| " + scope.className).c_str())) {
1510
0
                bailout = true;
1511
0
                break;
1512
0
            }
1513
0
        }
1514
0
        if (bailout)
1515
0
            continue;
1516
1517
0
        for (const Variable &var : scope.varlist) {
1518
            // only warn for variables without side effects
1519
0
            if (!var.typeStartToken()->isStandardType() && !var.isPointer() && !astIsContainer(var.nameToken()) && !isRecordTypeWithoutSideEffects(var.type()))
1520
0
                continue;
1521
0
            if (isInherited && !var.isPrivate())
1522
0
                continue;
1523
1524
            // Check if the struct member variable is used anywhere in the file
1525
0
            bool use = false;
1526
0
            for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
1527
0
                if (Token::Match(tok, ". %name%") && !tok->next()->variable() && !tok->next()->function() && tok->next()->str() == var.name()) {
1528
                    // not known => assume variable is used
1529
0
                    use = true;
1530
0
                    break;
1531
0
                }
1532
0
                if (tok->variable() != &var)
1533
0
                    continue;
1534
0
                if (tok != var.nameToken()) {
1535
0
                    use = true;
1536
0
                    break;
1537
0
                }
1538
0
            }
1539
0
            if (!use) {
1540
0
                std::string prefix = "struct";
1541
0
                if (scope.type == Scope::ScopeType::eClass)
1542
0
                    prefix = "class";
1543
0
                else if (scope.type == Scope::ScopeType::eUnion)
1544
0
                    prefix = "union";
1545
0
                unusedStructMemberError(var.nameToken(), scope.className, var.name(), prefix);
1546
0
            }
1547
0
        }
1548
0
    }
1549
1.36k
}
1550
1551
void CheckUnusedVar::unusedStructMemberError(const Token* tok, const std::string& structname, const std::string& varname, const std::string& prefix)
1552
0
{
1553
0
    reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + " member '$symbol' is never used.", CWE563, Certainty::normal);
1554
0
}
1555
1556
bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
1557
0
{
1558
    // a type that has no side effects (no constructors and no members with constructors)
1559
    /** @todo false negative: check constructors for side effects */
1560
0
    const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert(
1561
0
        std::pair<const Type *,bool>(type,false));         //Initialize with side effects for possible recursions
1562
0
    bool & withoutSideEffects = found.first->second;
1563
0
    if (!found.second)
1564
0
        return withoutSideEffects;
1565
1566
    // unknown types are assumed to have side effects
1567
0
    if (!type || !type->classScope)
1568
0
        return (withoutSideEffects = false);
1569
1570
    // Non-empty constructors => possible side effects
1571
0
    for (const Function& f : type->classScope->functionList) {
1572
0
        if (!f.isConstructor() && !f.isDestructor())
1573
0
            continue;
1574
0
        if (f.argDef && Token::simpleMatch(f.argDef->link(), ") ="))
1575
0
            continue; // ignore default/deleted constructors
1576
0
        const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }"));
1577
1578
0
        const Token* nextToken = f.argDef->link();
1579
0
        if (Token::simpleMatch(nextToken, ") :")) {
1580
            // validating initialization list
1581
0
            nextToken = nextToken->next(); // goto ":"
1582
1583
0
            for (const Token *initListToken = nextToken; Token::Match(initListToken, "[:,] %var% [({]"); initListToken = initListToken->linkAt(2)->next()) {
1584
0
                const Token* varToken = initListToken->next();
1585
0
                const Variable* variable = varToken->variable();
1586
0
                if (variable && !isVariableWithoutSideEffects(*variable)) {
1587
0
                    return withoutSideEffects = false;
1588
0
                }
1589
1590
0
                const Token* valueEnd = initListToken->linkAt(2);
1591
0
                for (const Token* valueToken = initListToken->tokAt(3); valueToken != valueEnd; valueToken = valueToken->next()) {
1592
0
                    const Variable* initValueVar = valueToken->variable();
1593
0
                    if (initValueVar && !isVariableWithoutSideEffects(*initValueVar)) {
1594
0
                        return withoutSideEffects = false;
1595
0
                    }
1596
0
                    if ((valueToken->tokType() == Token::Type::eName) ||
1597
0
                        (valueToken->tokType() == Token::Type::eLambda) ||
1598
0
                        (valueToken->tokType() == Token::Type::eOther)) {
1599
0
                        return withoutSideEffects = false;
1600
0
                    }
1601
0
                    const Function* initValueFunc = valueToken->function();
1602
0
                    if (initValueFunc && !isFunctionWithoutSideEffects(*initValueFunc, valueToken,
1603
0
                                                                       std::list<const Function*> {})) {
1604
0
                        return withoutSideEffects = false;
1605
0
                    }
1606
0
                }
1607
0
            }
1608
0
        }
1609
1610
0
        if (!emptyBody)
1611
0
            return (withoutSideEffects = false);
1612
0
    }
1613
1614
    // Derived from type that has side effects?
1615
0
    if (std::any_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& derivedFrom) {
1616
0
        return !isRecordTypeWithoutSideEffects(derivedFrom.type);
1617
0
    }))
1618
0
        return (withoutSideEffects = false);
1619
1620
    // Is there a member variable with possible side effects
1621
0
    for (const Variable& var : type->classScope->varlist) {
1622
0
        withoutSideEffects = isVariableWithoutSideEffects(var);
1623
0
        if (!withoutSideEffects) {
1624
0
            return withoutSideEffects;
1625
0
        }
1626
0
    }
1627
1628
1629
0
    return (withoutSideEffects = true);
1630
0
}
1631
1632
bool CheckUnusedVar::isVariableWithoutSideEffects(const Variable& var)
1633
0
{
1634
0
    if (var.isPointer())
1635
0
        return true;
1636
1637
0
    const Type* variableType = var.type();
1638
0
    if (variableType) {
1639
0
        if (!isRecordTypeWithoutSideEffects(variableType))
1640
0
            return false;
1641
0
    } else {
1642
0
        if (WRONG_DATA(!var.valueType(), var.typeStartToken()))
1643
0
            return false;
1644
0
        const ValueType::Type valueType = var.valueType()->type;
1645
0
        if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD))
1646
0
            return false;
1647
0
    }
1648
1649
0
    return true;
1650
0
}
1651
1652
bool CheckUnusedVar::isEmptyType(const Type* type)
1653
0
{
1654
    // a type that has no variables and no constructor
1655
1656
0
    const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsEmptyTypeMap.insert(
1657
0
        std::pair<const Type *,bool>(type,false));
1658
0
    bool & emptyType=found.first->second;
1659
0
    if (!found.second)
1660
0
        return emptyType;
1661
1662
0
    if (type && type->classScope && type->classScope->numConstructors == 0 &&
1663
0
        (type->classScope->varlist.empty())) {
1664
0
        return (emptyType = std::all_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& bi) {
1665
0
            return isEmptyType(bi.type);
1666
0
        }));
1667
0
    }
1668
1669
    // unknown types are assumed to be nonempty
1670
0
    return (emptyType = false);
1671
0
}
1672
1673
bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken,
1674
                                                  std::list<const Function*> checkedFuncs)
1675
0
{
1676
    // no body to analyze
1677
0
    if (!func.hasBody()) {
1678
0
        return false;
1679
0
    }
1680
1681
0
    for (const Token* argsToken = functionUsageToken->next(); !Token::simpleMatch(argsToken, ")"); argsToken = argsToken->next()) {
1682
0
        const Variable* argVar = argsToken->variable();
1683
0
        if (argVar && argVar->isGlobal()) {
1684
0
            return false; // TODO: analyze global variable usage
1685
0
        }
1686
0
    }
1687
1688
0
    bool sideEffectReturnFound = false;
1689
0
    std::set<const Variable*> pointersToGlobals;
1690
0
    for (const Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd;
1691
0
         bodyToken = bodyToken->next()) {
1692
        // check variable inside function body
1693
0
        const Variable* bodyVariable = bodyToken->variable();
1694
0
        if (bodyVariable) {
1695
0
            if (!isVariableWithoutSideEffects(*bodyVariable)) {
1696
0
                return false;
1697
0
            }
1698
            // check if global variable is changed
1699
0
            if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end())) {
1700
0
                const int indirect = bodyVariable->isArray() ? bodyVariable->dimensions().size() : bodyVariable->isPointer();
1701
0
                if (isVariableChanged(bodyToken, indirect, mSettings, mTokenizer->isCPP())) {
1702
0
                    return false;
1703
0
                }
1704
                // check if pointer to global variable assigned to another variable (another_var = &global_var)
1705
0
                if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) {
1706
0
                    const Token* assigned_var_token = bodyToken->tokAt(-3);
1707
0
                    if (assigned_var_token && assigned_var_token->variable()) {
1708
0
                        pointersToGlobals.insert(assigned_var_token->variable());
1709
0
                    }
1710
0
                }
1711
0
            }
1712
0
        }
1713
1714
        // check nested function
1715
0
        const Function* bodyFunction = bodyToken->function();
1716
0
        if (bodyFunction) {
1717
0
            if (std::find(checkedFuncs.cbegin(), checkedFuncs.cend(), bodyFunction) != checkedFuncs.cend()) { // recursion found
1718
0
                continue;
1719
0
            }
1720
0
            checkedFuncs.push_back(bodyFunction);
1721
0
            if (!isFunctionWithoutSideEffects(*bodyFunction, bodyToken, checkedFuncs)) {
1722
0
                return false;
1723
0
            }
1724
0
        }
1725
1726
        // check returned value
1727
0
        if (Token::simpleMatch(bodyToken, "return")) {
1728
0
            const Token* returnValueToken = bodyToken->next();
1729
            // TODO: handle complex return expressions
1730
0
            if (!Token::simpleMatch(returnValueToken->next(), ";")) {
1731
0
                sideEffectReturnFound = true;
1732
0
                continue;
1733
0
            }
1734
            // simple one-token return
1735
0
            const Variable* returnVariable = returnValueToken->variable();
1736
0
            if (returnValueToken->isLiteral() ||
1737
0
                (returnVariable && isVariableWithoutSideEffects(*returnVariable))) {
1738
0
                continue;
1739
0
            }
1740
0
            sideEffectReturnFound = true;
1741
0
        }
1742
1743
        // unknown name
1744
0
        if (bodyToken->isNameOnly()) {
1745
0
            return false;
1746
0
        }
1747
0
    }
1748
1749
0
    return !sideEffectReturnFound;
1750
0
}