Coverage Report

Created: 2023-09-25 06:15

/src/cppcheck/lib/checkother.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 "checkother.h"
22
23
#include "astutils.h"
24
#include "fwdanalysis.h"
25
#include "library.h"
26
#include "mathlib.h"
27
#include "platform.h"
28
#include "settings.h"
29
#include "standards.h"
30
#include "symboldatabase.h"
31
#include "token.h"
32
#include "tokenize.h"
33
#include "tokenlist.h"
34
#include "utils.h"
35
#include "valueflow.h"
36
#include "vfvalue.h"
37
38
#include <algorithm> // find_if()
39
#include <cctype>
40
#include <list>
41
#include <map>
42
#include <sstream>
43
#include <utility>
44
#include <numeric>
45
46
//---------------------------------------------------------------------------
47
48
// Register this check class (by creating a static instance of it)
49
namespace {
50
    CheckOther instance;
51
}
52
53
static const struct CWE CWE128(128U);   // Wrap-around Error
54
static const struct CWE CWE131(131U);   // Incorrect Calculation of Buffer Size
55
static const struct CWE CWE197(197U);   // Numeric Truncation Error
56
static const struct CWE CWE362(362U);   // Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')
57
static const struct CWE CWE369(369U);   // Divide By Zero
58
static const struct CWE CWE398(398U);   // Indicator of Poor Code Quality
59
static const struct CWE CWE475(475U);   // Undefined Behavior for Input to API
60
static const struct CWE CWE561(561U);   // Dead Code
61
static const struct CWE CWE563(563U);   // Assignment to Variable without Use ('Unused Variable')
62
static const struct CWE CWE570(570U);   // Expression is Always False
63
static const struct CWE CWE571(571U);   // Expression is Always True
64
static const struct CWE CWE672(672U);   // Operation on a Resource after Expiration or Release
65
static const struct CWE CWE628(628U);   // Function Call with Incorrectly Specified Arguments
66
static const struct CWE CWE683(683U);   // Function Call With Incorrect Order of Arguments
67
static const struct CWE CWE704(704U);   // Incorrect Type Conversion or Cast
68
static const struct CWE CWE758(758U);   // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
69
static const struct CWE CWE768(768U);   // Incorrect Short Circuit Evaluation
70
static const struct CWE CWE783(783U);   // Operator Precedence Logic Error
71
72
//----------------------------------------------------------------------------------
73
// The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value.
74
// If this return value is stored in a character variable and then compared
75
// to EOF, which is an integer, the comparison maybe be false.
76
//
77
// Reference:
78
// - Ticket #160
79
// - http://www.cplusplus.com/reference/cstdio/fgetc/
80
// - http://www.cplusplus.com/reference/cstdio/getc/
81
// - http://www.cplusplus.com/reference/cstdio/getchar/
82
// - http://www.cplusplus.com/reference/cstdio/ungetc/ ...
83
//----------------------------------------------------------------------------------
84
void CheckOther::checkCastIntToCharAndBack()
85
1.36k
{
86
1.36k
    if (!mSettings->severity.isEnabled(Severity::warning))
87
0
        return;
88
89
1.36k
    logChecker("CheckOther::checkCastIntToCharAndBack"); // warning
90
91
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
92
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
93
1.73k
        std::map<int, std::string> vars;
94
35.9k
        for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
95
            // Quick check to see if any of the matches below have any chances
96
34.1k
            if (!Token::Match(tok, "%var%|EOF %comp%|="))
97
31.5k
                continue;
98
2.62k
            if (Token::Match(tok, "%var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) {
99
0
                const Variable *var = tok->variable();
100
0
                if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) {
101
0
                    vars[tok->varId()] = tok->strAt(2);
102
0
                }
103
2.62k
            } else if (Token::Match(tok, "EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) {
104
0
                tok = tok->tokAt(3);
105
0
                const Variable *var = tok->variable();
106
0
                if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) {
107
0
                    checkCastIntToCharAndBackError(tok, tok->strAt(2));
108
0
                }
109
2.62k
            } else if (mTokenizer->isCPP() && (Token::Match(tok, "EOF %comp% ( %var% = std :: cin . get (") || Token::Match(tok, "EOF %comp% ( %var% = cin . get ("))) {
110
0
                tok = tok->tokAt(3);
111
0
                const Variable *var = tok->variable();
112
0
                if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) {
113
0
                    checkCastIntToCharAndBackError(tok, "cin.get");
114
0
                }
115
2.62k
            } else if (mTokenizer->isCPP() && (Token::Match(tok, "%var% = std :: cin . get (") || Token::Match(tok, "%var% = cin . get ("))) {
116
0
                const Variable *var = tok->variable();
117
0
                if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) {
118
0
                    vars[tok->varId()] = "cin.get";
119
0
                }
120
2.62k
            } else if (Token::Match(tok, "%var% %comp% EOF")) {
121
0
                if (vars.find(tok->varId()) != vars.end()) {
122
0
                    checkCastIntToCharAndBackError(tok, vars[tok->varId()]);
123
0
                }
124
2.62k
            } else if (Token::Match(tok, "EOF %comp% %var%")) {
125
0
                tok = tok->tokAt(2);
126
0
                if (vars.find(tok->varId()) != vars.end()) {
127
0
                    checkCastIntToCharAndBackError(tok, vars[tok->varId()]);
128
0
                }
129
0
            }
130
2.62k
        }
131
1.73k
    }
132
1.36k
}
133
134
void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName)
135
0
{
136
0
    reportError(
137
0
        tok,
138
0
        Severity::warning,
139
0
        "checkCastIntToCharAndBack",
140
0
        "$symbol:" + strFunctionName + "\n"
141
0
        "Storing $symbol() return value in char variable and then comparing with EOF.\n"
142
0
        "When saving $symbol() return value in char variable there is loss of precision. "
143
0
        " When $symbol() returns EOF this value is truncated. Comparing the char "
144
0
        "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = $symbol());\" "
145
0
        "loops forever on some compilers/platforms and on other compilers/platforms it will stop "
146
0
        "when the file contains a matching character.", CWE197, Certainty::normal
147
0
        );
148
0
}
149
150
151
//---------------------------------------------------------------------------
152
// Clarify calculation precedence for ternary operators.
153
//---------------------------------------------------------------------------
154
void CheckOther::clarifyCalculation()
155
1.36k
{
156
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
157
0
        return;
158
159
1.36k
    logChecker("CheckOther::clarifyCalculation"); // style
160
161
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
162
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
163
35.9k
        for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
164
            // ? operator where lhs is arithmetical expression
165
34.1k
            if (tok->str() != "?" || !tok->astOperand1() || !tok->astOperand1()->isCalculation())
166
34.1k
                continue;
167
0
            if (!tok->astOperand1()->isArithmeticalOp() && tok->astOperand1()->tokType() != Token::eBitOp)
168
0
                continue;
169
170
            // non-pointer calculation in lhs and pointer in rhs => no clarification is needed
171
0
            if (tok->astOperand1()->isBinaryOp() && Token::Match(tok->astOperand1(), "%or%|&|%|*|/") && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->pointer > 0)
172
0
                continue;
173
174
            // bit operation in lhs and char literals in rhs => probably no mistake
175
0
            if (tok->astOperand1()->tokType() == Token::eBitOp && Token::Match(tok->astOperand2()->astOperand1(), "%char%") && Token::Match(tok->astOperand2()->astOperand2(), "%char%"))
176
0
                continue;
177
178
            // 2nd operand in lhs has known integer value => probably no mistake
179
0
            if (tok->astOperand1()->isBinaryOp() && tok->astOperand1()->astOperand2()->hasKnownIntValue()) {
180
0
                const Token *op = tok->astOperand1()->astOperand2();
181
0
                if (op->isNumber())
182
0
                    continue;
183
0
                if (op->valueType() && op->valueType()->isEnum())
184
0
                    continue;
185
0
            }
186
187
            // Is code clarified by parentheses already?
188
0
            const Token *tok2 = tok->astOperand1();
189
0
            for (; tok2; tok2 = tok2->next()) {
190
0
                if (tok2->str() == "(")
191
0
                    tok2 = tok2->link();
192
0
                else if (tok2->str() == ")")
193
0
                    break;
194
0
                else if (tok2->str() == "?") {
195
0
                    clarifyCalculationError(tok, tok->astOperand1()->str());
196
0
                    break;
197
0
                }
198
0
            }
199
0
        }
200
1.73k
    }
201
1.36k
}
202
203
void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op)
204
0
{
205
    // suspicious calculation
206
0
    const std::string calc("'a" + op + "b?c:d'");
207
208
    // recommended calculation #1
209
0
    const std::string s1("'(a" + op + "b)?c:d'");
210
211
    // recommended calculation #2
212
0
    const std::string s2("'a" + op + "(b?c:d)'");
213
214
0
    reportError(tok,
215
0
                Severity::style,
216
0
                "clarifyCalculation",
217
0
                "Clarify calculation precedence for '" + op + "' and '?'.\n"
218
0
                "Suspicious calculation. Please use parentheses to clarify the code. "
219
0
                "The code '" + calc + "' should be written as either '" + s1 + "' or '" + s2 + "'.", CWE783, Certainty::normal);
220
0
}
221
222
//---------------------------------------------------------------------------
223
// Clarify (meaningless) statements like *foo++; with parentheses.
224
//---------------------------------------------------------------------------
225
void CheckOther::clarifyStatement()
226
1.36k
{
227
1.36k
    if (!mSettings->severity.isEnabled(Severity::warning))
228
0
        return;
229
230
1.36k
    logChecker("CheckOther::clarifyStatement"); // warning
231
232
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
233
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
234
37.6k
        for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
235
35.9k
            if (tok->astOperand1() && Token::Match(tok, "* %name%")) {
236
153
                const Token *tok2 = tok->previous();
237
238
153
                while (tok2 && tok2->str() == "*")
239
0
                    tok2 = tok2->previous();
240
241
153
                if (tok2 && !tok2->astParent() && Token::Match(tok2, "[{};]")) {
242
0
                    tok2 = tok->astOperand1();
243
0
                    if (Token::Match(tok2, "++|-- [;,]"))
244
0
                        clarifyStatementError(tok2);
245
0
                }
246
153
            }
247
35.9k
        }
248
1.73k
    }
249
1.36k
}
250
251
void CheckOther::clarifyStatementError(const Token *tok)
252
0
{
253
0
    reportError(tok, Severity::warning, "clarifyStatement", "In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n"
254
0
                "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. "
255
0
                "Thus, the dereference is meaningless. Did you intend to write '(*A)++;'?", CWE783, Certainty::normal);
256
0
}
257
258
//---------------------------------------------------------------------------
259
// Check for suspicious occurrences of 'if(); {}'.
260
//---------------------------------------------------------------------------
261
void CheckOther::checkSuspiciousSemicolon()
262
1.36k
{
263
1.36k
    if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning))
264
0
        return;
265
266
1.36k
    const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
267
268
1.36k
    logChecker("CheckOther::checkSuspiciousSemicolon"); // warning,inconclusive
269
270
    // Look for "if(); {}", "for(); {}" or "while(); {}"
271
4.94k
    for (const Scope &scope : symbolDatabase->scopeList) {
272
4.94k
        if (scope.type == Scope::eIf || scope.type == Scope::eElse || scope.type == Scope::eWhile || scope.type == Scope::eFor) {
273
            // Ensure the semicolon is at the same line number as the if/for/while statement
274
            // and the {..} block follows it without an extra empty line.
275
1.84k
            if (Token::simpleMatch(scope.bodyStart, "{ ; } {") &&
276
1.84k
                scope.bodyStart->previous()->linenr() == scope.bodyStart->tokAt(2)->linenr() &&
277
1.84k
                scope.bodyStart->linenr()+1 >= scope.bodyStart->tokAt(3)->linenr() &&
278
1.84k
                !scope.bodyStart->tokAt(3)->isExpandedMacro()) {
279
0
                suspiciousSemicolonError(scope.classDef);
280
0
            }
281
1.84k
        }
282
4.94k
    }
283
1.36k
}
284
285
void CheckOther::suspiciousSemicolonError(const Token* tok)
286
0
{
287
0
    reportError(tok, Severity::warning, "suspiciousSemicolon",
288
0
                "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", CWE398, Certainty::normal);
289
0
}
290
291
292
//---------------------------------------------------------------------------
293
// For C++ code, warn if C-style casts are used on pointer types
294
//---------------------------------------------------------------------------
295
void CheckOther::warningOldStylePointerCast()
296
1.36k
{
297
    // Only valid on C++ code
298
1.36k
    if (!mSettings->severity.isEnabled(Severity::style) || !mTokenizer->isCPP())
299
0
        return;
300
301
1.36k
    logChecker("CheckOther::warningOldStylePointerCast"); // style,c++
302
303
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
304
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
305
1.73k
        const Token* tok;
306
1.73k
        if (scope->function && scope->function->isConstructor())
307
0
            tok = scope->classDef;
308
1.73k
        else
309
1.73k
            tok = scope->bodyStart;
310
37.6k
        for (; tok && tok != scope->bodyEnd; tok = tok->next()) {
311
            // Old style pointer casting..
312
35.9k
            if (!Token::Match(tok, "( const|volatile| const|volatile|class|struct| %type% * *| *| const|&| ) (| %name%|%num%|%bool%|%char%|%str%|&"))
313
35.9k
                continue;
314
0
            if (Token::Match(tok->previous(), "%type%"))
315
0
                continue;
316
317
            // skip first "const" in "const Type* const"
318
0
            while (Token::Match(tok->next(), "const|volatile|class|struct"))
319
0
                tok = tok->next();
320
0
            const Token* typeTok = tok->next();
321
            // skip second "const" in "const Type* const"
322
0
            if (tok->strAt(3) == "const")
323
0
                tok = tok->next();
324
325
0
            const Token *p = tok->tokAt(4);
326
0
            if (p->hasKnownIntValue() && p->values().front().intvalue==0) // Casting nullpointers is safe
327
0
                continue;
328
329
0
            if (typeTok->tokType() == Token::eType || typeTok->tokType() == Token::eName)
330
0
                cstyleCastError(tok);
331
0
        }
332
1.73k
    }
333
1.36k
}
334
335
void CheckOther::cstyleCastError(const Token *tok)
336
0
{
337
0
    reportError(tok, Severity::style, "cstyleCast",
338
0
                "C-style pointer casting\n"
339
0
                "C-style pointer casting detected. C++ offers four different kinds of casts as replacements: "
340
0
                "static_cast, const_cast, dynamic_cast and reinterpret_cast. A C-style cast could evaluate to "
341
0
                "any of those automatically, thus it is considered safer if the programmer explicitly states "
342
0
                "which kind of cast is expected.", CWE398, Certainty::normal);
343
0
}
344
345
//---------------------------------------------------------------------------
346
// float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation
347
//---------------------------------------------------------------------------
348
349
void CheckOther::invalidPointerCast()
350
1.36k
{
351
1.36k
    if (!mSettings->severity.isEnabled(Severity::portability))
352
0
        return;
353
354
1.36k
    logChecker("CheckOther::invalidPointerCast"); // portability
355
356
1.36k
    const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
357
1.36k
    const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
358
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
359
35.9k
        for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
360
34.1k
            const Token* toTok = nullptr;
361
34.1k
            const Token* fromTok = nullptr;
362
            // Find cast
363
34.1k
            if (Token::Match(tok, "( const|volatile| const|volatile| %type% %type%| const| * )")) {
364
0
                toTok = tok;
365
0
                fromTok = tok->astOperand1();
366
34.1k
            } else if (Token::simpleMatch(tok, "reinterpret_cast <") && tok->linkAt(1)) {
367
0
                toTok = tok->linkAt(1)->next();
368
0
                fromTok = toTok->astOperand2();
369
0
            }
370
34.1k
            if (!fromTok)
371
34.1k
                continue;
372
373
0
            const ValueType* fromType = fromTok->valueType();
374
0
            const ValueType* toType = toTok->valueType();
375
0
            if (!fromType || !toType || !fromType->pointer || !toType->pointer)
376
0
                continue;
377
378
0
            if (fromType->type != toType->type && fromType->type >= ValueType::Type::BOOL && toType->type >= ValueType::Type::BOOL && (toType->type != ValueType::Type::CHAR || printInconclusive)) {
379
0
                if (toType->isIntegral() && fromType->isIntegral())
380
0
                    continue;
381
382
0
                invalidPointerCastError(tok, fromType->str(), toType->str(), toType->type == ValueType::Type::CHAR, toType->isIntegral());
383
0
            }
384
0
        }
385
1.73k
    }
386
1.36k
}
387
388
389
void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt)
390
0
{
391
0
    if (toIsInt) { // If we cast something to int*, this can be useful to play with its binary data representation
392
0
        reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + " to " + to + " is not portable due to different binary data representations on different platforms.", CWE704, inconclusive ? Certainty::inconclusive : Certainty::normal);
393
0
    } else
394
0
        reportError(tok, Severity::portability, "invalidPointerCast", "Casting between " + from + " and " + to + " which have an incompatible binary data representation.", CWE704, Certainty::normal);
395
0
}
396
397
398
//---------------------------------------------------------------------------
399
// Detect redundant assignments: x = 0; x = 4;
400
//---------------------------------------------------------------------------
401
402
void CheckOther::checkRedundantAssignment()
403
1.36k
{
404
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
405
0
        return;
406
407
1.36k
    logChecker("CheckOther::checkRedundantAssignment"); // style
408
409
1.36k
    const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
410
1.73k
    for (const Scope *scope : symbolDatabase->functionScopes) {
411
1.73k
        if (!scope->bodyStart)
412
0
            continue;
413
35.9k
        for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
414
34.1k
            if (Token::simpleMatch(tok, "] ("))
415
                // todo: handle lambdas
416
0
                break;
417
34.1k
            if (Token::simpleMatch(tok, "try {"))
418
                // todo: check try blocks
419
0
                tok = tok->linkAt(1);
420
34.1k
            if ((tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) && tok->astOperand1()) {
421
3.34k
                if (tok->astParent())
422
1.80k
                    continue;
423
424
                // Do not warn about redundant initialization when rhs is trivial
425
                // TODO : do not simplify the variable declarations
426
1.53k
                bool isInitialization = false;
427
1.53k
                if (Token::Match(tok->tokAt(-2), "; %var% =") && tok->tokAt(-2)->isSplittedVarDeclEq()) {
428
0
                    isInitialization = true;
429
0
                    bool trivial = true;
430
0
                    visitAstNodes(tok->astOperand2(),
431
0
                                  [&](const Token *rhs) {
432
0
                        if (Token::simpleMatch(rhs, "{ 0 }"))
433
0
                            return ChildrenToVisit::none;
434
0
                        if (Token::Match(rhs, "%str%|%num%|%name%") && !rhs->varId())
435
0
                            return ChildrenToVisit::none;
436
0
                        if (Token::Match(rhs, ":: %name%") && rhs->hasKnownIntValue())
437
0
                            return ChildrenToVisit::none;
438
0
                        if (rhs->isCast())
439
0
                            return ChildrenToVisit::op2;
440
0
                        trivial = false;
441
0
                        return ChildrenToVisit::done;
442
0
                    });
443
0
                    if (trivial)
444
0
                        continue;
445
0
                }
446
447
1.53k
                const Token* rhs = tok->astOperand2();
448
                // Do not warn about assignment with 0 / NULL
449
1.53k
                if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs))
450
4
                    continue;
451
452
1.53k
                if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference())
453
                    // todo: check references
454
0
                    continue;
455
456
1.53k
                if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic())
457
                    // todo: check static variables
458
0
                    continue;
459
460
1.53k
                bool inconclusive = false;
461
1.53k
                if (mTokenizer->isCPP() && tok->astOperand1()->valueType()) {
462
                    // If there is a custom assignment operator => this is inconclusive
463
1.14k
                    if (tok->astOperand1()->valueType()->typeScope) {
464
0
                        const std::string op = "operator" + tok->str();
465
0
                        const std::list<Function>& fList = tok->astOperand1()->valueType()->typeScope->functionList;
466
0
                        inconclusive = std::any_of(fList.cbegin(), fList.cend(), [&](const Function& f) {
467
0
                            return f.name() == op;
468
0
                        });
469
0
                    }
470
                    // assigning a smart pointer has side effects
471
1.14k
                    if (tok->astOperand1()->valueType()->type == ValueType::SMART_POINTER)
472
0
                        break;
473
1.14k
                }
474
1.53k
                if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive))
475
0
                    continue;
476
477
1.53k
                FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library);
478
1.53k
                if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1()))
479
207
                    continue;
480
481
                // Is there a redundant assignment?
482
1.32k
                const Token *start;
483
1.32k
                if (tok->isAssignmentOp())
484
1.32k
                    start = tok->next();
485
0
                else
486
0
                    start = tok->findExpressionStartEndTokens().second->next();
487
488
                // Get next assignment..
489
1.32k
                const Token *nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd);
490
491
1.32k
                if (!nextAssign)
492
1.26k
                    continue;
493
494
                // there is redundant assignment. Is there a case between the assignments?
495
62
                bool hasCase = false;
496
468
                for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) {
497
407
                    if (tok2->str() == "break" || tok2->str() == "return")
498
1
                        break;
499
406
                    if (tok2->str() == "case") {
500
0
                        hasCase = true;
501
0
                        break;
502
0
                    }
503
406
                }
504
505
                // warn
506
62
                if (hasCase)
507
0
                    redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString());
508
62
                else if (isInitialization)
509
0
                    redundantInitializationError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive);
510
62
                else
511
62
                    redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive);
512
62
            }
513
34.1k
        }
514
1.73k
    }
515
1.36k
}
516
517
void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var)
518
0
{
519
0
    const std::list<const Token *> callstack = { tok1, tok2 };
520
0
    reportError(callstack, Severity::performance, "redundantCopy",
521
0
                "$symbol:" + var + "\n"
522
0
                "Buffer '$symbol' is being written before its old content has been used.", CWE563, Certainty::normal);
523
0
}
524
525
void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive)
526
62
{
527
62
    const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is assigned"), ErrorPathItem(tok2, var + " is overwritten") };
528
62
    if (inconclusive)
529
0
        reportError(errorPath, Severity::style, "redundantAssignment",
530
0
                    "$symbol:" + var + "\n"
531
0
                    "Variable '$symbol' is reassigned a value before the old one has been used if variable is no semaphore variable.\n"
532
0
                    "Variable '$symbol' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", CWE563, Certainty::inconclusive);
533
62
    else
534
62
        reportError(errorPath, Severity::style, "redundantAssignment",
535
62
                    "$symbol:" + var + "\n"
536
62
                    "Variable '$symbol' is reassigned a value before the old one has been used.", CWE563, Certainty::normal);
537
62
}
538
539
void CheckOther::redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive)
540
0
{
541
0
    const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is initialized"), ErrorPathItem(tok2, var + " is overwritten") };
542
0
    reportError(errorPath, Severity::style, "redundantInitialization",
543
0
                "$symbol:" + var + "\nRedundant initialization for '$symbol'. The initialized value is overwritten before it is read.",
544
0
                CWE563,
545
0
                inconclusive ? Certainty::inconclusive : Certainty::normal);
546
0
}
547
548
void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var)
549
0
{
550
0
    const ErrorPath errorPath = { ErrorPathItem(tok1, "$symbol is assigned"), ErrorPathItem(tok2, "$symbol is overwritten") };
551
0
    reportError(errorPath, Severity::style, "redundantAssignInSwitch",
552
0
                "$symbol:" + var + "\n"
553
0
                "Variable '$symbol' is reassigned a value before the old one has been used. 'break;' missing?", CWE563, Certainty::normal);
554
0
}
555
556
557
//---------------------------------------------------------------------------
558
//    switch (x)
559
//    {
560
//        case 2:
561
//            y = a;        // <- this assignment is redundant
562
//        case 3:
563
//            y = b;        // <- case 2 falls through and sets y twice
564
//    }
565
//---------------------------------------------------------------------------
566
static inline bool isFunctionOrBreakPattern(const Token *tok)
567
0
{
568
0
    return Token::Match(tok, "%name% (") || Token::Match(tok, "break|continue|return|exit|goto|throw");
569
0
}
570
571
void CheckOther::redundantBitwiseOperationInSwitchError()
572
1.36k
{
573
1.36k
    if (!mSettings->severity.isEnabled(Severity::warning))
574
0
        return;
575
576
1.36k
    logChecker("CheckOther::redundantBitwiseOperationInSwitch"); // warning
577
578
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
579
580
    // Find the beginning of a switch. E.g.:
581
    //   switch (var) { ...
582
4.94k
    for (const Scope &switchScope : symbolDatabase->scopeList) {
583
4.94k
        if (switchScope.type != Scope::eSwitch || !switchScope.bodyStart)
584
4.94k
            continue;
585
586
        // Check the contents of the switch statement
587
0
        std::map<int, const Token*> varsWithBitsSet;
588
0
        std::map<int, std::string> bitOperations;
589
590
0
        for (const Token *tok2 = switchScope.bodyStart->next(); tok2 != switchScope.bodyEnd; tok2 = tok2->next()) {
591
0
            if (tok2->str() == "{") {
592
                // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.:
593
                //   case 3: b = 1;
594
                //   case 4: if (a) { b = 2; }    // Doesn't make the b=1 redundant because it's conditional
595
0
                if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) {
596
0
                    const Token* endOfConditional = tok2->link();
597
0
                    for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) {
598
0
                        if (tok3->varId() != 0) {
599
0
                            varsWithBitsSet.erase(tok3->varId());
600
0
                            bitOperations.erase(tok3->varId());
601
0
                        } else if (isFunctionOrBreakPattern(tok3)) {
602
0
                            varsWithBitsSet.clear();
603
0
                            bitOperations.clear();
604
0
                        }
605
0
                    }
606
0
                    tok2 = endOfConditional;
607
0
                }
608
0
            }
609
610
            // Variable assignment. Report an error if it's assigned to twice before a break. E.g.:
611
            //    case 3: b = 1;    // <== redundant
612
            //    case 4: b = 2;
613
614
0
            if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;")) {
615
0
                varsWithBitsSet.erase(tok2->varId());
616
0
                bitOperations.erase(tok2->varId());
617
0
            }
618
619
            // Bitwise operation. Report an error if it's performed twice before a break. E.g.:
620
            //    case 3: b |= 1;    // <== redundant
621
            //    case 4: b |= 1;
622
0
            else if (Token::Match(tok2->previous(), ";|{|}|: %var% %assign% %num% ;") &&
623
0
                     (tok2->strAt(1) == "|=" || tok2->strAt(1) == "&=") &&
624
0
                     Token::Match(tok2->next()->astOperand2(), "%num%")) {
625
0
                const std::string bitOp = tok2->strAt(1)[0] + tok2->strAt(2);
626
0
                const std::map<int, const Token*>::const_iterator i2 = varsWithBitsSet.find(tok2->varId());
627
628
                // This variable has not had a bit operation performed on it yet, so just make a note of it
629
0
                if (i2 == varsWithBitsSet.end()) {
630
0
                    varsWithBitsSet[tok2->varId()] = tok2;
631
0
                    bitOperations[tok2->varId()] = bitOp;
632
0
                }
633
634
                // The same bit operation has been performed on the same variable twice, so report an error
635
0
                else if (bitOperations[tok2->varId()] == bitOp)
636
0
                    redundantBitwiseOperationInSwitchError(i2->second, i2->second->str());
637
638
                // A different bit operation was performed on the variable, so clear it
639
0
                else {
640
0
                    varsWithBitsSet.erase(tok2->varId());
641
0
                    bitOperations.erase(tok2->varId());
642
0
                }
643
0
            }
644
645
            // Bitwise operation. Report an error if it's performed twice before a break. E.g.:
646
            //    case 3: b = b | 1;    // <== redundant
647
            //    case 4: b = b | 1;
648
0
            else if (Token::Match(tok2->previous(), ";|{|}|: %var% = %name% %or%|& %num% ;") &&
649
0
                     tok2->varId() == tok2->tokAt(2)->varId()) {
650
0
                const std::string bitOp = tok2->strAt(3) + tok2->strAt(4);
651
0
                const std::map<int, const Token*>::const_iterator i2 = varsWithBitsSet.find(tok2->varId());
652
653
                // This variable has not had a bit operation performed on it yet, so just make a note of it
654
0
                if (i2 == varsWithBitsSet.end()) {
655
0
                    varsWithBitsSet[tok2->varId()] = tok2;
656
0
                    bitOperations[tok2->varId()] = bitOp;
657
0
                }
658
659
                // The same bit operation has been performed on the same variable twice, so report an error
660
0
                else if (bitOperations[tok2->varId()] == bitOp)
661
0
                    redundantBitwiseOperationInSwitchError(i2->second, i2->second->str());
662
663
                // A different bit operation was performed on the variable, so clear it
664
0
                else {
665
0
                    varsWithBitsSet.erase(tok2->varId());
666
0
                    bitOperations.erase(tok2->varId());
667
0
                }
668
0
            }
669
670
            // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.:
671
            //    case 3: b = 1;
672
            //    case 4: b++;
673
0
            else if (tok2->varId() != 0 && tok2->strAt(1) != "|" && tok2->strAt(1) != "&") {
674
0
                varsWithBitsSet.erase(tok2->varId());
675
0
                bitOperations.erase(tok2->varId());
676
0
            }
677
678
            // Reset our record of assignments if there is a break or function call. E.g.:
679
            //    case 3: b = 1; break;
680
0
            if (isFunctionOrBreakPattern(tok2)) {
681
0
                varsWithBitsSet.clear();
682
0
                bitOperations.clear();
683
0
            }
684
0
        }
685
0
    }
686
1.36k
}
687
688
void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname)
689
0
{
690
0
    reportError(tok, Severity::style,
691
0
                "redundantBitwiseOperationInSwitch",
692
0
                "$symbol:" + varname + "\n"
693
0
                "Redundant bitwise operation on '$symbol' in 'switch' statement. 'break;' missing?");
694
0
}
695
696
697
//---------------------------------------------------------------------------
698
// Check for statements like case A||B: in switch()
699
//---------------------------------------------------------------------------
700
void CheckOther::checkSuspiciousCaseInSwitch()
701
1.36k
{
702
1.36k
    if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning))
703
0
        return;
704
705
1.36k
    logChecker("CheckOther::checkSuspiciousCaseInSwitch"); // warning,inconclusive
706
707
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
708
709
4.94k
    for (const Scope & scope : symbolDatabase->scopeList) {
710
4.94k
        if (scope.type != Scope::eSwitch)
711
4.94k
            continue;
712
713
0
        for (const Token* tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) {
714
0
            if (tok->str() == "case") {
715
0
                const Token* finding = nullptr;
716
0
                for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) {
717
0
                    if (tok2->str() == ":")
718
0
                        break;
719
0
                    if (Token::Match(tok2, "[;}{]"))
720
0
                        break;
721
722
0
                    if (tok2->str() == "?")
723
0
                        finding = nullptr;
724
0
                    else if (Token::Match(tok2, "&&|%oror%"))
725
0
                        finding = tok2;
726
0
                }
727
0
                if (finding)
728
0
                    suspiciousCaseInSwitchError(finding, finding->str());
729
0
            }
730
0
        }
731
0
    }
732
1.36k
}
733
734
void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString)
735
0
{
736
0
    reportError(tok, Severity::warning, "suspiciousCase",
737
0
                "Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n"
738
0
                "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, Certainty::inconclusive);
739
0
}
740
741
//---------------------------------------------------------------------------
742
//    Find consecutive return, break, continue, goto or throw statements. e.g.:
743
//        break; break;
744
//    Detect dead code, that follows such a statement. e.g.:
745
//        return(0); foo();
746
//---------------------------------------------------------------------------
747
void CheckOther::checkUnreachableCode()
748
1.36k
{
749
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
750
0
        return;
751
752
1.36k
    logChecker("CheckOther::checkUnreachableCode"); // style
753
754
1.36k
    const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
755
1.36k
    const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
756
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
757
19.0k
        for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
758
17.3k
            const Token* secondBreak = nullptr;
759
17.3k
            const Token* labelName = nullptr;
760
17.3k
            if (tok->link() && Token::Match(tok, "(|[|<"))
761
1.55k
                tok = tok->link();
762
15.7k
            else if (Token::Match(tok, "break|continue ;"))
763
0
                secondBreak = tok->tokAt(2);
764
15.7k
            else if (Token::Match(tok, "[;{}:] return|throw") && tok->next()->isKeyword()) {
765
1.91k
                if (Token::simpleMatch(tok->astParent(), "?"))
766
0
                    continue;
767
1.91k
                tok = tok->next(); // tok should point to return or throw
768
4.81k
                for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
769
4.81k
                    if (tok2->str() == "(" || tok2->str() == "{")
770
113
                        tok2 = tok2->link();
771
4.81k
                    if (tok2->str() == ";") {
772
1.91k
                        secondBreak = tok2->next();
773
1.91k
                        break;
774
1.91k
                    }
775
4.81k
                }
776
13.8k
            } else if (Token::Match(tok, "goto %any% ;")) {
777
0
                secondBreak = tok->tokAt(3);
778
0
                labelName = tok->next();
779
13.8k
            } else if (Token::Match(tok, "%name% (") && mSettings->library.isnoreturn(tok) && !Token::Match(tok->next()->astParent(), "?|:")) {
780
0
                if ((!tok->function() || (tok->function()->token != tok && tok->function()->tokenDef != tok)) && tok->linkAt(1)->strAt(1) != "{")
781
0
                    secondBreak = tok->linkAt(1)->tokAt(2);
782
0
                if (Token::simpleMatch(secondBreak, "return")) {
783
                    // clarification for tools that function returns
784
0
                    continue;
785
0
                }
786
0
            }
787
788
            // Statements follow directly, no line between them. (#3383)
789
            // TODO: Try to find a better way to avoid false positives due to preprocessor configurations.
790
17.3k
            const bool inconclusive = secondBreak && (secondBreak->linenr() - 1 > secondBreak->previous()->linenr());
791
792
17.3k
            if (secondBreak && (printInconclusive || !inconclusive)) {
793
1.91k
                if (Token::Match(secondBreak, "continue|goto|throw|return") && secondBreak->isKeyword()) {
794
0
                    duplicateBreakError(secondBreak, inconclusive);
795
0
                    tok = Token::findmatch(secondBreak, "[}:]");
796
1.91k
                } else if (secondBreak->str() == "break") { // break inside switch as second break statement should not issue a warning
797
0
                    if (tok->str() == "break") // If the previous was a break, too: Issue warning
798
0
                        duplicateBreakError(secondBreak, inconclusive);
799
0
                    else {
800
0
                        if (tok->scope()->type != Scope::eSwitch) // Check, if the enclosing scope is a switch
801
0
                            duplicateBreakError(secondBreak, inconclusive);
802
0
                    }
803
0
                    tok = Token::findmatch(secondBreak, "[}:]");
804
1.91k
                } else if (!Token::Match(secondBreak, "return|}|case|default") && secondBreak->strAt(1) != ":") { // TODO: No bailout for unconditional scopes
805
                    // If the goto label is followed by a loop construct in which the label is defined it's quite likely
806
                    // that the goto jump was intended to skip some code on the first loop iteration.
807
0
                    bool labelInFollowingLoop = false;
808
0
                    if (labelName && Token::Match(secondBreak, "while|do|for")) {
809
0
                        const Token *scope2 = Token::findsimplematch(secondBreak, "{");
810
0
                        if (scope2) {
811
0
                            for (const Token *tokIter = scope2; tokIter != scope2->link() && tokIter; tokIter = tokIter->next()) {
812
0
                                if (Token::Match(tokIter, "[;{}] %any% :") && labelName->str() == tokIter->strAt(1)) {
813
0
                                    labelInFollowingLoop = true;
814
0
                                    break;
815
0
                                }
816
0
                            }
817
0
                        }
818
0
                    }
819
820
                    // hide FP for statements that just hide compiler warnings about unused function arguments
821
0
                    bool silencedCompilerWarningOnly = false;
822
0
                    const Token *silencedWarning = secondBreak;
823
0
                    for (;;) {
824
0
                        if (Token::Match(silencedWarning, "( void ) %name% ;")) {
825
0
                            silencedWarning = silencedWarning->tokAt(5);
826
0
                            continue;
827
0
                        }
828
0
                        if (silencedWarning && silencedWarning == scope->bodyEnd)
829
0
                            silencedCompilerWarningOnly = true;
830
0
                        break;
831
0
                    }
832
0
                    if (silencedWarning)
833
0
                        secondBreak = silencedWarning;
834
835
0
                    if (!labelInFollowingLoop && !silencedCompilerWarningOnly)
836
0
                        unreachableCodeError(secondBreak, tok, inconclusive);
837
0
                    tok = Token::findmatch(secondBreak, "[}:]");
838
1.91k
                } else if (secondBreak->scope() && secondBreak->scope()->isLoopScope() && secondBreak->str() == "}" && tok->str() == "continue") {
839
0
                    redundantContinueError(tok);
840
0
                    tok = secondBreak;
841
0
                } else
842
1.91k
                    tok = secondBreak;
843
844
1.91k
                if (!tok)
845
0
                    break;
846
1.91k
                tok = tok->previous(); // Will be advanced again by for loop
847
1.91k
            }
848
17.3k
        }
849
1.73k
    }
850
1.36k
}
851
852
void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive)
853
0
{
854
0
    reportError(tok, Severity::style, "duplicateBreak",
855
0
                "Consecutive return, break, continue, goto or throw statements are unnecessary.\n"
856
0
                "Consecutive return, break, continue, goto or throw statements are unnecessary. "
857
0
                "The second statement can never be executed, and so should be removed.", CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal);
858
0
}
859
860
void CheckOther::unreachableCodeError(const Token *tok, const Token* noreturn, bool inconclusive)
861
0
{
862
0
    std::string msg = "Statements following ";
863
0
    if (noreturn && (noreturn->function() || mSettings->library.isnoreturn(noreturn)))
864
0
        msg += "noreturn function '" + noreturn->str() + "()'";
865
0
    else if (noreturn && noreturn->isKeyword())
866
0
        msg += "'" + noreturn->str() + "'";
867
0
    else
868
0
        msg += "return, break, continue, goto or throw";
869
0
    msg += " will never be executed.";
870
0
    reportError(tok, Severity::style, "unreachableCode",
871
0
                msg, CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal);
872
0
}
873
874
void CheckOther::redundantContinueError(const Token *tok)
875
0
{
876
0
    reportError(tok, Severity::style, "redundantContinue",
877
0
                "'continue' is redundant since it is the last statement in a loop.", CWE561, Certainty::normal);
878
0
}
879
880
0
static bool isSimpleExpr(const Token* tok, const Variable* var, const Settings* settings) {
881
0
    if (!tok)
882
0
        return false;
883
0
    if (tok->isNumber() || tok->tokType() == Token::eString || tok->tokType() == Token::eChar || tok->isBoolean())
884
0
        return true;
885
0
    bool needsCheck = tok->varId() > 0;
886
0
    if (!needsCheck) {
887
0
        const Token* ftok = tok->previous();
888
0
        if (Token::Match(ftok, "%name% (") &&
889
0
            ((ftok->function() && ftok->function()->isConst()) || settings->library.isFunctionConst(ftok->str(), /*pure*/ true)))
890
0
            needsCheck = true;
891
0
    }
892
0
    return (needsCheck && !isExpressionChanged(tok, tok->astParent(), var->scope()->bodyEnd, settings, true));
893
0
}
894
895
//---------------------------------------------------------------------------
896
// Check scope of variables..
897
//---------------------------------------------------------------------------
898
void CheckOther::checkVariableScope()
899
1.36k
{
900
1.36k
    if (mSettings->clang)
901
0
        return;
902
903
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
904
0
        return;
905
906
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
907
908
    // In C it is common practice to declare local variables at the
909
    // start of functions.
910
1.36k
    if (mSettings->daca && mTokenizer->isC())
911
0
        return;
912
913
1.36k
    logChecker("CheckOther::checkVariableScope"); // style,notclang
914
915
8.16k
    for (const Variable* var : symbolDatabase->variableList()) {
916
8.16k
        if (!var || !var->isLocal() || var->isConst())
917
8.16k
            continue;
918
919
0
        const bool isPtrOrRef = var->isPointer() || var->isReference();
920
0
        const bool isSimpleType = var->typeStartToken()->isStandardType() || var->typeStartToken()->isEnumType() || (mTokenizer->isC() && var->type() && var->type()->isStructType());
921
0
        if (!isPtrOrRef && !isSimpleType && !astIsContainer(var->nameToken()))
922
0
            continue;
923
924
0
        if (mTokenizer->hasIfdef(var->nameToken(), var->scope()->bodyEnd))
925
0
            continue;
926
927
        // reference of range for loop variable..
928
0
        if (Token::Match(var->nameToken()->previous(), "& %var% = %var% .")) {
929
0
            const Token *otherVarToken = var->nameToken()->tokAt(2);
930
0
            const Variable *otherVar = otherVarToken->variable();
931
0
            if (otherVar && Token::Match(otherVar->nameToken(), "%var% :") &&
932
0
                otherVar->nameToken()->next()->astParent() &&
933
0
                Token::simpleMatch(otherVar->nameToken()->next()->astParent()->previous(), "for ("))
934
0
                continue;
935
0
        }
936
937
0
        bool forHead = false; // Don't check variables declared in header of a for loop
938
0
        for (const Token* tok = var->typeStartToken(); tok; tok = tok->previous()) {
939
0
            if (tok->str() == "(") {
940
0
                forHead = true;
941
0
                break;
942
0
            }
943
0
            if (Token::Match(tok, "[;{}]"))
944
0
                break;
945
0
        }
946
0
        if (forHead)
947
0
            continue;
948
949
0
        const Token* tok = var->nameToken()->next();
950
0
        bool isConstructor = false;
951
0
        if (Token::Match(tok, "; %varid% =", var->declarationId())) { // bailout for assignment
952
0
            tok = tok->tokAt(2)->astOperand2();
953
0
            if (!isSimpleExpr(tok, var, mSettings))
954
0
                continue;
955
0
        }
956
0
        else if (Token::Match(tok, "{|(")) { // bailout for constructor
957
0
            isConstructor = true;
958
0
            const Token* argTok = tok->astOperand2();
959
0
            bool bail = false;
960
0
            while (argTok) {
961
0
                if (Token::simpleMatch(argTok, ",")) {
962
0
                    if (!isSimpleExpr(argTok->astOperand2(), var, mSettings)) {
963
0
                        bail = true;
964
0
                        break;
965
0
                    }
966
0
                } else if (argTok->str() != "." && !isSimpleExpr(argTok, var, mSettings)) {
967
0
                    bail = true;
968
0
                    break;
969
0
                }
970
0
                argTok = argTok->astOperand1();
971
0
            }
972
0
            if (bail)
973
0
                continue;
974
0
        }
975
        // bailout if initialized with function call that has possible side effects
976
0
        if (!isConstructor && Token::Match(tok, "[(=]") && Token::simpleMatch(tok->astOperand2(), "("))
977
0
            continue;
978
0
        bool reduce = true;
979
0
        bool used = false; // Don't warn about unused variables
980
0
        for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) {
981
0
            if (tok->str() == "{" && tok->scope() != tok->previous()->scope() && !tok->isExpandedMacro() && !isWithinScope(tok, var, Scope::ScopeType::eLambda)) {
982
0
                if (used) {
983
0
                    bool used2 = false;
984
0
                    if (!checkInnerScope(tok, var, used2) || used2) {
985
0
                        reduce = false;
986
0
                        break;
987
0
                    }
988
0
                } else if (!checkInnerScope(tok, var, used)) {
989
0
                    reduce = false;
990
0
                    break;
991
0
                }
992
993
0
                tok = tok->link();
994
995
                // parse else if blocks..
996
0
            } else if (Token::simpleMatch(tok, "else { if (") && Token::simpleMatch(tok->linkAt(3), ") {")) {
997
0
                const Token *endif = tok->linkAt(3)->linkAt(1);
998
0
                bool elseif = false;
999
0
                if (Token::simpleMatch(endif, "} }"))
1000
0
                    elseif = true;
1001
0
                else if (Token::simpleMatch(endif, "} else {") && Token::simpleMatch(endif->linkAt(2),"} }"))
1002
0
                    elseif = true;
1003
0
                if (elseif && Token::findmatch(tok->next(), "%varid%", tok->linkAt(1), var->declarationId())) {
1004
0
                    reduce = false;
1005
0
                    break;
1006
0
                }
1007
0
            } else if (tok->varId() == var->declarationId() || tok->str() == "goto") {
1008
0
                reduce = false;
1009
0
                break;
1010
0
            }
1011
0
        }
1012
1013
0
        if (reduce && used)
1014
0
            variableScopeError(var->nameToken(), var->name());
1015
0
    }
1016
1.36k
}
1017
1018
bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) const
1019
0
{
1020
0
    const Scope* scope = tok->next()->scope();
1021
0
    bool loopVariable = scope->isLoopScope();
1022
0
    bool noContinue = true;
1023
0
    const Token* forHeadEnd = nullptr;
1024
0
    const Token* end = tok->link();
1025
0
    if (scope->type == Scope::eUnconditional && (tok->strAt(-1) == ")" || tok->previous()->isName())) // Might be an unknown macro like BOOST_FOREACH
1026
0
        loopVariable = true;
1027
1028
0
    if (scope->type == Scope::eDo) {
1029
0
        end = end->linkAt(2);
1030
0
    } else if (loopVariable && tok->strAt(-1) == ")") {
1031
0
        tok = tok->linkAt(-1); // Jump to opening ( of for/while statement
1032
0
    } else if (scope->type == Scope::eSwitch) {
1033
0
        for (const Scope* innerScope : scope->nestedList) {
1034
0
            if (used) {
1035
0
                bool used2 = false;
1036
0
                if (!checkInnerScope(innerScope->bodyStart, var, used2) || used2) {
1037
0
                    return false;
1038
0
                }
1039
0
            } else if (!checkInnerScope(innerScope->bodyStart, var, used)) {
1040
0
                return false;
1041
0
            }
1042
0
        }
1043
0
    }
1044
1045
0
    bool bFirstAssignment=false;
1046
0
    for (; tok && tok != end; tok = tok->next()) {
1047
0
        if (tok->str() == "goto")
1048
0
            return false;
1049
0
        if (tok->str() == "continue")
1050
0
            noContinue = false;
1051
1052
0
        if (Token::simpleMatch(tok, "for ("))
1053
0
            forHeadEnd = tok->linkAt(1);
1054
0
        if (tok == forHeadEnd)
1055
0
            forHeadEnd = nullptr;
1056
1057
0
        if (loopVariable && noContinue && tok->scope() == scope && !forHeadEnd && scope->type != Scope::eSwitch && Token::Match(tok, "%varid% =", var->declarationId())) { // Assigned in outer scope.
1058
0
            loopVariable = false;
1059
0
            std::pair<const Token*, const Token*> range = tok->next()->findExpressionStartEndTokens();
1060
0
            if (range.first)
1061
0
                range.first = range.first->next();
1062
0
            const Token* exprTok = findExpression(var->nameToken()->exprId(), range.first, range.second, [&](const Token* tok2) {
1063
0
                return tok2->varId() == var->declarationId();
1064
0
            });
1065
0
            if (exprTok) {
1066
0
                tok = exprTok;
1067
0
                loopVariable = true;
1068
0
            }
1069
0
        }
1070
1071
0
        if (loopVariable && Token::Match(tok, "%varid% !!=", var->declarationId())) // Variable used in loop
1072
0
            return false;
1073
1074
0
        if (Token::Match(tok, "& %varid%", var->declarationId())) // Taking address of variable
1075
0
            return false;
1076
1077
0
        if (Token::Match(tok, "%varid% =", var->declarationId())) {
1078
0
            if (!bFirstAssignment && var->isInit() && Token::findmatch(tok->tokAt(2), "%varid%", Token::findsimplematch(tok->tokAt(3), ";"), var->declarationId()))
1079
0
                return false;
1080
0
            bFirstAssignment = true;
1081
0
        }
1082
1083
0
        if (!bFirstAssignment && Token::Match(tok, "* %varid%", var->declarationId())) // dereferencing means access to previous content
1084
0
            return false;
1085
1086
0
        if (Token::Match(tok, "= %varid%", var->declarationId()) && (var->isArray() || var->isPointer() || (var->valueType() && var->valueType()->container))) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope
1087
0
            return false;
1088
1089
0
        if (tok->varId() == var->declarationId()) {
1090
0
            used = true;
1091
0
            if (scope == tok->scope()) {
1092
0
                if (scope->type == Scope::eSwitch)
1093
0
                    return false; // Used in outer switch scope - unsafe or impossible to reduce scope
1094
1095
0
                if (scope->bodyStart && scope->bodyStart->isSimplifiedScope())
1096
0
                    return false; // simplified if/for/switch init statement
1097
0
            }
1098
0
            if (var->isArrayOrPointer()) {
1099
0
                int argn{};
1100
0
                if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { // var passed to function?
1101
0
                    if (ftok->next()->astParent()) { // return value used?
1102
0
                        if (ftok->function() && Function::returnsPointer(ftok->function()))
1103
0
                            return false;
1104
0
                        const std::string ret = mSettings->library.returnValueType(ftok); // assume that var is returned
1105
0
                        if (!ret.empty() && ret.back() == '*')
1106
0
                            return false;
1107
0
                    }
1108
0
                }
1109
0
            }
1110
0
        }
1111
0
    }
1112
1113
0
    return true;
1114
0
}
1115
1116
void CheckOther::variableScopeError(const Token *tok, const std::string &varname)
1117
0
{
1118
0
    reportError(tok,
1119
0
                Severity::style,
1120
0
                "variableScope",
1121
0
                "$symbol:" + varname + "\n"
1122
0
                "The scope of the variable '$symbol' can be reduced.\n"
1123
0
                "The scope of the variable '$symbol' can be reduced. Warning: Be careful "
1124
0
                "when fixing this message, especially when there are inner loops. Here is an "
1125
0
                "example where cppcheck will write that the scope for 'i' can be reduced:\n"
1126
0
                "void f(int x)\n"
1127
0
                "{\n"
1128
0
                "    int i = 0;\n"
1129
0
                "    if (x) {\n"
1130
0
                "        // it's safe to move 'int i = 0;' here\n"
1131
0
                "        for (int n = 0; n < 10; ++n) {\n"
1132
0
                "            // it is possible but not safe to move 'int i = 0;' here\n"
1133
0
                "            do_something(&i);\n"
1134
0
                "        }\n"
1135
0
                "    }\n"
1136
0
                "}\n"
1137
0
                "When you see this message it is always safe to reduce the variable scope 1 level.", CWE398, Certainty::normal);
1138
0
}
1139
1140
//---------------------------------------------------------------------------
1141
// Comma in return statement: return a+1, b++;. (experimental)
1142
//---------------------------------------------------------------------------
1143
void CheckOther::checkCommaSeparatedReturn()
1144
1.36k
{
1145
    // This is experimental for now. See #5076
1146
1.36k
    if ((true) || !mSettings->severity.isEnabled(Severity::style)) // NOLINT(readability-simplify-boolean-expr)
1147
1.36k
        return;
1148
1149
    // logChecker
1150
1151
0
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
1152
0
        if (tok->str() == "return") {
1153
0
            tok = tok->next();
1154
0
            while (tok && tok->str() != ";") {
1155
0
                if (tok->link() && Token::Match(tok, "[([{<]"))
1156
0
                    tok = tok->link();
1157
1158
0
                if (!tok->isExpandedMacro() && tok->str() == "," && tok->linenr() != tok->next()->linenr())
1159
0
                    commaSeparatedReturnError(tok);
1160
1161
0
                tok = tok->next();
1162
0
            }
1163
            // bailout: missing semicolon (invalid code / bad tokenizer)
1164
0
            if (!tok)
1165
0
                break;
1166
0
        }
1167
0
    }
1168
0
}
1169
1170
void CheckOther::commaSeparatedReturnError(const Token *tok)
1171
0
{
1172
0
    reportError(tok,
1173
0
                Severity::style,
1174
0
                "commaSeparatedReturn",
1175
0
                "Comma is used in return statement. The comma can easily be misread as a ';'.\n"
1176
0
                "Comma is used in return statement. When comma is used in a return statement it can "
1177
0
                "easily be misread as a semicolon. For example in the code below the value "
1178
0
                "of 'b' is returned if the condition is true, but it is easy to think that 'a+1' is "
1179
0
                "returned:\n"
1180
0
                "    if (x)\n"
1181
0
                "        return a + 1,\n"
1182
0
                "    b++;\n"
1183
0
                "However it can be useful to use comma in macros. Cppcheck does not warn when such a "
1184
0
                "macro is then used in a return statement, it is less likely such code is misunderstood.", CWE398, Certainty::normal);
1185
0
}
1186
1187
//---------------------------------------------------------------------------
1188
// Check for function parameters that should be passed by const reference
1189
//---------------------------------------------------------------------------
1190
static int estimateSize(const Type* type, const Settings* settings, const SymbolDatabase* symbolDatabase, int recursionDepth = 0)
1191
0
{
1192
0
    if (recursionDepth > 20)
1193
0
        return 0;
1194
1195
0
    int cumulatedSize = 0;
1196
0
    const bool isUnion = type->classScope->type == Scope::ScopeType::eUnion;
1197
0
    const auto accumulateSize = [](int& cumulatedSize, int size, bool isUnion) -> void {
1198
0
        if (isUnion)
1199
0
            cumulatedSize = std::max(cumulatedSize, size);
1200
0
        else
1201
0
            cumulatedSize += size;
1202
0
    };
1203
0
    for (const Variable&var : type->classScope->varlist) {
1204
0
        int size = 0;
1205
0
        if (var.isStatic())
1206
0
            continue;
1207
0
        if (var.isPointer() || var.isReference())
1208
0
            size = settings->platform.sizeof_pointer;
1209
0
        else if (var.type() && var.type()->classScope)
1210
0
            size = estimateSize(var.type(), settings, symbolDatabase, recursionDepth+1);
1211
0
        else if (var.valueType() && var.valueType()->type == ValueType::Type::CONTAINER)
1212
0
            size = 3 * settings->platform.sizeof_pointer; // Just guess
1213
0
        else
1214
0
            size = symbolDatabase->sizeOfType(var.typeStartToken());
1215
1216
0
        if (var.isArray())
1217
0
            size *= std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), 1, [](int v, const Dimension& d) {
1218
0
                return v *= d.num;
1219
0
            });
1220
1221
0
        accumulateSize(cumulatedSize, size, isUnion);
1222
0
    }
1223
0
    return std::accumulate(type->derivedFrom.cbegin(), type->derivedFrom.cend(), cumulatedSize, [&](int v, const Type::BaseInfo& baseInfo) {
1224
0
        if (baseInfo.type && baseInfo.type->classScope)
1225
0
            v += estimateSize(baseInfo.type, settings, symbolDatabase, recursionDepth + 1);
1226
0
        return v;
1227
0
    });
1228
0
}
1229
1230
0
static bool isConstRangeBasedFor(const Token* tok) {
1231
0
    if (astIsRangeBasedForDecl(tok)) {
1232
0
        const Variable* loopVar = tok->astParent()->astOperand1()->variable();
1233
0
        return loopVar && (!loopVar->isReference() || loopVar->isConst());
1234
0
    }
1235
0
    return false;
1236
0
}
1237
1238
static bool canBeConst(const Variable *var, const Settings* settings)
1239
6.80k
{
1240
6.80k
    if (!var->scope())
1241
0
        return false;
1242
6.80k
    {
1243
        // check initializer list. If variable is moved from it can't be const.
1244
6.80k
        const Function* func_scope = var->scope()->function;
1245
6.80k
        if (func_scope && func_scope->type == Function::Type::eConstructor) {
1246
            //could be initialized in initializer list
1247
0
            const Token* init = func_scope->arg->link()->next();
1248
0
            if (init->str() == "noexcept") {
1249
0
                init = init->next();
1250
0
                if (init->link())
1251
0
                    init = init->link()->next();
1252
0
            }
1253
0
            if (init->str() == ":") {
1254
0
                for (const Token* tok2 = func_scope->arg->link()->next()->next(); tok2 != var->scope()->bodyStart; tok2 = tok2->next()) {
1255
0
                    if (tok2->varId() != var->declarationId())
1256
0
                        continue;
1257
0
                    const Token* parent = tok2->astParent();
1258
0
                    if (parent && Token::simpleMatch(parent->previous(), "move ("))
1259
0
                        return false;
1260
0
                }
1261
0
            }
1262
0
        }
1263
6.80k
    }
1264
6.80k
    for (const Token* tok2 = var->scope()->bodyStart; tok2 != var->scope()->bodyEnd; tok2 = tok2->next()) {
1265
0
        if (tok2->varId() != var->declarationId())
1266
0
            continue;
1267
1268
0
        const Token* parent = tok2->astParent();
1269
0
        while (Token::simpleMatch(parent, "["))
1270
0
            parent = parent->astParent();
1271
0
        if (!parent)
1272
0
            continue;
1273
0
        if (Token::simpleMatch(tok2->next(), ";") && tok2->next()->isSplittedVarDeclEq()) {
1274
0
            tok2 = tok2->tokAt(2);
1275
0
            tok2 = Token::findsimplematch(tok2, ";");
1276
0
            continue;
1277
0
        }
1278
0
        if (parent->str() == "<<" || isLikelyStreamRead(true, parent)) {
1279
0
            if (parent->str() == "<<" && parent->astOperand1() == tok2)
1280
0
                return false;
1281
0
            if (parent->str() == ">>" && parent->astOperand2() == tok2)
1282
0
                return false;
1283
0
        } else if (parent->str() == "," || parent->str() == "(") { // function argument
1284
0
            const Token* tok3 = tok2->previous();
1285
0
            int argNr = 0;
1286
0
            while (tok3 && tok3->str() != "(") {
1287
0
                if (tok3->link() && Token::Match(tok3, ")|]|}|>"))
1288
0
                    tok3 = tok3->link();
1289
0
                else if (tok3->link())
1290
0
                    break;
1291
0
                else if (tok3->str() == ";")
1292
0
                    break;
1293
0
                else if (tok3->str() == ",")
1294
0
                    argNr++;
1295
0
                tok3 = tok3->previous();
1296
0
            }
1297
0
            if (!tok3 || tok3->str() != "(")
1298
0
                return false;
1299
0
            const Token* functionTok = tok3->astOperand1();
1300
0
            if (!functionTok)
1301
0
                return false;
1302
0
            const Function* tokFunction = functionTok->function();
1303
0
            if (!tokFunction && functionTok->str() == "." && (functionTok = functionTok->astOperand2()))
1304
0
                tokFunction = functionTok->function();
1305
0
            if (tokFunction) {
1306
0
                const Variable* argVar = tokFunction->getArgumentVar(argNr);
1307
0
                if (!argVar || (!argVar->isConst() && argVar->isReference()))
1308
0
                    return false;
1309
0
            }
1310
0
            else if (!settings->library.isFunctionConst(functionTok))
1311
0
                return false;
1312
0
        } else if (parent->isUnaryOp("&")) {
1313
            // TODO: check how pointer is used
1314
0
            return false;
1315
0
        } else if (parent->isConstOp() ||
1316
0
                   (parent->astOperand2() && settings->library.isFunctionConst(parent->astOperand2())))
1317
0
            continue;
1318
0
        else if (parent->isAssignmentOp()) {
1319
0
            const Token* assignee = parent->astOperand1();
1320
0
            while (Token::simpleMatch(assignee, "["))
1321
0
                assignee = assignee->astOperand1();
1322
0
            if (assignee == tok2)
1323
0
                return false;
1324
0
            const Variable* assignedVar = assignee ? assignee->variable() : nullptr;
1325
0
            if (assignedVar &&
1326
0
                !assignedVar->isConst() &&
1327
0
                assignedVar->isReference() &&
1328
0
                assignedVar->nameToken() == parent->astOperand1())
1329
0
                return false;
1330
0
        } else if (Token::Match(tok2, "%var% . %name% (")) {
1331
0
            const Function* func = tok2->tokAt(2)->function();
1332
0
            if (func && (func->isConst() || func->isStatic()))
1333
0
                continue;
1334
0
            return false;
1335
0
        } else if (isConstRangeBasedFor(tok2))
1336
0
            continue;
1337
0
        else
1338
0
            return false;
1339
0
    }
1340
1341
6.80k
    return true;
1342
6.80k
}
1343
1344
void CheckOther::checkPassByReference()
1345
1.36k
{
1346
1.36k
    if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC())
1347
0
        return;
1348
1349
1.36k
    logChecker("CheckOther::checkPassByReference"); // performance,c++
1350
1351
1.36k
    const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
1352
1353
8.16k
    for (const Variable* var : symbolDatabase->variableList()) {
1354
8.16k
        if (!var || !var->isArgument() || !var->isClass() || var->isPointer() || var->isArray() || var->isReference() || var->isEnumType())
1355
8.16k
            continue;
1356
1357
0
        if (var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...")
1358
0
            continue; // references could not be used as va_start parameters (#5824)
1359
1360
0
        const Token * const varDeclEndToken = var->declEndToken();
1361
0
        if ((varDeclEndToken && varDeclEndToken->isExternC()) ||
1362
0
            (var->scope() && var->scope()->function && var->scope()->function->tokenDef && var->scope()->function->tokenDef->isExternC()))
1363
0
            continue; // references cannot be used in functions in extern "C" blocks
1364
1365
0
        bool inconclusive = false;
1366
1367
0
        const bool isContainer = var->valueType() && var->valueType()->type == ValueType::Type::CONTAINER && var->valueType()->container && !var->valueType()->container->view;
1368
0
        if (!isContainer) {
1369
0
            if (var->type() && !var->type()->isEnumType()) { // Check if type is a struct or class.
1370
                // Ensure that it is a large object.
1371
0
                if (!var->type()->classScope)
1372
0
                    inconclusive = true;
1373
0
                else if (estimateSize(var->type(), mSettings, symbolDatabase) <= 2 * mSettings->platform.sizeof_pointer)
1374
0
                    continue;
1375
0
            }
1376
0
            else
1377
0
                continue;
1378
0
        }
1379
1380
0
        if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive))
1381
0
            continue;
1382
1383
0
        const bool isConst = var->isConst();
1384
0
        if (isConst) {
1385
0
            passedByValueError(var->nameToken(), var->name(), inconclusive);
1386
0
            continue;
1387
0
        }
1388
1389
        // Check if variable could be const
1390
0
        if (!var->scope() || var->scope()->function->isImplicitlyVirtual())
1391
0
            continue;
1392
1393
0
        if (canBeConst(var, mSettings)) {
1394
0
            passedByValueError(var->nameToken(), var->name(), inconclusive);
1395
0
        }
1396
0
    }
1397
1.36k
}
1398
1399
void CheckOther::passedByValueError(const Token *tok, const std::string &parname, bool inconclusive)
1400
0
{
1401
0
    reportError(tok, Severity::performance, "passedByValue",
1402
0
                "$symbol:" + parname + "\n"
1403
0
                "Function parameter '$symbol' should be passed by const reference.\n"
1404
0
                "Parameter '$symbol' is passed by value. It could be passed "
1405
0
                "as a const reference which is usually faster and recommended in C++.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
1406
0
}
1407
1408
static bool isUnusedVariable(const Variable *var)
1409
0
{
1410
0
    if (!var)
1411
0
        return false;
1412
0
    if (!var->scope())
1413
0
        return false;
1414
0
    const Token *start = var->declEndToken();
1415
0
    if (!start)
1416
0
        return false;
1417
0
    if (Token::Match(start, "; %varid% =", var->declarationId()))
1418
0
        start = start->tokAt(2);
1419
0
    return !Token::findmatch(start->next(), "%varid%", var->scope()->bodyEnd, var->declarationId());
1420
0
}
1421
1422
static bool isVariableMutableInInitializer(const Token* start, const Token * end, nonneg int varid)
1423
0
{
1424
0
    if (!start)
1425
0
        return false;
1426
0
    if (!end)
1427
0
        return false;
1428
0
    for (const Token *tok = start; tok != end; tok = tok->next()) {
1429
0
        if (tok->varId() != varid)
1430
0
            continue;
1431
0
        if (tok->astParent()) {
1432
0
            const Token * memberTok = tok->astParent()->previous();
1433
0
            if (Token::Match(memberTok, "%var% (") && memberTok->variable()) {
1434
0
                const Variable * memberVar = memberTok->variable();
1435
0
                if (memberVar->isClass())
1436
                    //TODO: check if the called constructor could live with a const variable
1437
                    // pending that, assume the worst (that it can't)
1438
0
                    return true;
1439
0
                if (!memberVar->isReference())
1440
0
                    continue;
1441
0
                if (memberVar->isConst())
1442
0
                    continue;
1443
0
            }
1444
0
        }
1445
0
        return true;
1446
0
    }
1447
0
    return false;
1448
0
}
1449
1450
void CheckOther::checkConstVariable()
1451
1.36k
{
1452
1.36k
    if (!mSettings->severity.isEnabled(Severity::style) || mTokenizer->isC())
1453
0
        return;
1454
1455
1.36k
    const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase();
1456
1457
8.16k
    for (const Variable *var : symbolDatabase->variableList()) {
1458
8.16k
        if (!var)
1459
1.36k
            continue;
1460
6.80k
        if (!var->isReference())
1461
6.80k
            continue;
1462
0
        if (var->isRValueReference())
1463
0
            continue;
1464
0
        if (var->isPointer())
1465
0
            continue;
1466
0
        if (var->isConst())
1467
0
            continue;
1468
0
        const Scope* scope = var->scope();
1469
0
        if (!scope)
1470
0
            continue;
1471
0
        const Function* function = scope->function;
1472
0
        if (!function && !scope->isLocal())
1473
0
            continue;
1474
0
        if (function && var->isArgument()) {
1475
0
            if (function->isImplicitlyVirtual() || function->templateDef)
1476
0
                continue;
1477
0
            if (isUnusedVariable(var))
1478
0
                continue;
1479
0
            if (function->isConstructor() && isVariableMutableInInitializer(function->constructorMemberInitialization(), scope->bodyStart, var->declarationId()))
1480
0
                continue;
1481
0
        }
1482
0
        if (var->isGlobal())
1483
0
            continue;
1484
0
        if (var->isStatic())
1485
0
            continue;
1486
0
        if (var->isArray() && !var->isStlType())
1487
0
            continue;
1488
0
        if (var->isEnumType())
1489
0
            continue;
1490
0
        if (var->isVolatile())
1491
0
            continue;
1492
0
        if (isStructuredBindingVariable(var)) // TODO: check all bound variables
1493
0
            continue;
1494
0
        if (isVariableChanged(var, mSettings, mTokenizer->isCPP()))
1495
0
            continue;
1496
0
        const bool hasFunction = function != nullptr;
1497
0
        if (!hasFunction) {
1498
0
            const Scope* functionScope = scope;
1499
0
            do {
1500
0
                functionScope = functionScope->nestedIn;
1501
0
            } while (functionScope && !(function = functionScope->function));
1502
0
        }
1503
0
        if (function && (Function::returnsReference(function) || Function::returnsPointer(function)) && !Function::returnsConst(function)) {
1504
0
            std::vector<const Token*> returns = Function::findReturns(function);
1505
0
            if (std::any_of(returns.cbegin(), returns.cend(), [&](const Token* retTok) {
1506
0
                if (retTok->varId() == var->declarationId())
1507
0
                    return true;
1508
0
                while (retTok && retTok->isCast())
1509
0
                    retTok = retTok->astOperand2();
1510
0
                while (Token::simpleMatch(retTok, "."))
1511
0
                    retTok = retTok->astOperand2();
1512
0
                if (Token::simpleMatch(retTok, "&"))
1513
0
                    retTok = retTok->astOperand1();
1514
0
                return ValueFlow::hasLifetimeToken(getParentLifetime(retTok), var->nameToken());
1515
0
            }))
1516
0
                continue;
1517
0
        }
1518
        // Skip if another non-const variable is initialized with this variable
1519
0
        {
1520
            //Is it the right side of an initialization of a non-const reference
1521
0
            bool usedInAssignment = false;
1522
0
            for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) {
1523
0
                if (Token::Match(tok, "& %var% = %varid%", var->declarationId())) {
1524
0
                    const Variable* refvar = tok->next()->variable();
1525
0
                    if (refvar && !refvar->isConst() && refvar->nameToken() == tok->next()) {
1526
0
                        usedInAssignment = true;
1527
0
                        break;
1528
0
                    }
1529
0
                }
1530
0
                if (tok->isUnaryOp("&") && Token::Match(tok, "& %varid%", var->declarationId())) {
1531
0
                    const Token* opTok = tok->astParent();
1532
0
                    int argn = -1;
1533
0
                    if (opTok && opTok->isUnaryOp("!"))
1534
0
                        continue;
1535
0
                    if (opTok && (opTok->isComparisonOp() || opTok->isAssignmentOp() || opTok->isCalculation())) {
1536
0
                        if (opTok->isComparisonOp() || opTok->isCalculation()) {
1537
0
                            if (opTok->astOperand1() != tok)
1538
0
                                opTok = opTok->astOperand1();
1539
0
                            else
1540
0
                                opTok = opTok->astOperand2();
1541
0
                        }
1542
0
                        if (opTok && opTok->valueType() && var->valueType() && opTok->valueType()->isConst(var->valueType()->pointer))
1543
0
                            continue;
1544
0
                    } else if (const Token* ftok = getTokenArgumentFunction(tok, argn)) {
1545
0
                        bool inconclusive{};
1546
0
                        if (var->valueType() && !isVariableChangedByFunctionCall(ftok, var->valueType()->pointer, var->declarationId(), mSettings, &inconclusive) && !inconclusive)
1547
0
                            continue;
1548
0
                    }
1549
0
                    usedInAssignment = true;
1550
0
                    break;
1551
0
                }
1552
0
                if (astIsRangeBasedForDecl(tok) && Token::Match(tok->astParent()->astOperand2(), "%varid%", var->declarationId())) {
1553
0
                    const Variable* refvar = tok->astParent()->astOperand1()->variable();
1554
0
                    if (refvar && refvar->isReference() && !refvar->isConst()) {
1555
0
                        usedInAssignment = true;
1556
0
                        break;
1557
0
                    }
1558
0
                }
1559
0
            }
1560
0
            if (usedInAssignment)
1561
0
                continue;
1562
0
        }
1563
        // Skip if we ever cast this variable to a pointer/reference to a non-const type
1564
0
        {
1565
0
            bool castToNonConst = false;
1566
0
            for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) {
1567
0
                if (tok->isCast()) {
1568
0
                    if (!tok->valueType()) {
1569
0
                        castToNonConst = true; // safe guess
1570
0
                        break;
1571
0
                    }
1572
0
                    const bool isConst = tok->valueType()->isConst(tok->valueType()->pointer);
1573
0
                    if (!isConst) {
1574
0
                        castToNonConst = true;
1575
0
                        break;
1576
0
                    }
1577
0
                }
1578
0
            }
1579
0
            if (castToNonConst)
1580
0
                continue;
1581
0
        }
1582
1583
0
        constVariableError(var, hasFunction ? function : nullptr);
1584
0
    }
1585
1.36k
}
1586
1587
static const Token* getVariableChangedStart(const Variable* p)
1588
0
{
1589
0
    if (p->isArgument())
1590
0
        return p->scope()->bodyStart;
1591
0
    const Token* start = p->nameToken()->next();
1592
0
    if (start->isSplittedVarDeclEq())
1593
0
        start = start->tokAt(3);
1594
0
    return start;
1595
0
}
1596
1597
void CheckOther::checkConstPointer()
1598
1.36k
{
1599
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
1600
0
        return;
1601
1602
1.36k
    logChecker("CheckOther::checkConstPointer"); // style
1603
1604
1.36k
    std::vector<const Variable*> pointers, nonConstPointers;
1605
93.5k
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
1606
92.2k
        const Variable* const var = tok->variable();
1607
92.2k
        if (!var)
1608
72.4k
            continue;
1609
19.8k
        if (!var->isLocal() && !var->isArgument())
1610
19.8k
            continue;
1611
0
        const Token* const nameTok = var->nameToken();
1612
        // declarations of (static) pointers are (not) split up, array declarations are never split up
1613
0
        if (tok == nameTok && (!var->isStatic() || Token::simpleMatch(nameTok->next(), "[")) &&
1614
0
            !astIsRangeBasedForDecl(nameTok))
1615
0
            continue;
1616
0
        const ValueType* const vt = tok->valueType();
1617
0
        if (!vt)
1618
0
            continue;
1619
0
        if ((vt->pointer != 1 && !(vt->pointer == 2 && var->isArray())) || (vt->constness & 1))
1620
0
            continue;
1621
0
        if (var->typeStartToken()->isTemplateArg())
1622
0
            continue;
1623
0
        if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), var) != nonConstPointers.cend())
1624
0
            continue;
1625
0
        pointers.emplace_back(var);
1626
0
        const Token* parent = tok->astParent();
1627
0
        enum Deref { NONE, DEREF, MEMBER } deref = NONE;
1628
0
        bool hasIncDec = false;
1629
0
        if (parent && (parent->isUnaryOp("*") || (hasIncDec = parent->isIncDecOp() && parent->astParent() && parent->astParent()->isUnaryOp("*"))))
1630
0
            deref = DEREF;
1631
0
        else if (Token::simpleMatch(parent, "[") && parent->astOperand1() == tok && tok != nameTok)
1632
0
            deref = DEREF;
1633
0
        else if (Token::Match(parent, "%op%") && Token::simpleMatch(parent->astParent(), "."))
1634
0
            deref = DEREF;
1635
0
        else if (Token::simpleMatch(parent, "."))
1636
0
            deref = MEMBER;
1637
0
        else if (astIsRangeBasedForDecl(tok))
1638
0
            continue;
1639
0
        if (deref != NONE) {
1640
0
            const Token* gparent = parent->astParent();
1641
0
            if (deref == MEMBER) {
1642
0
                if (!gparent)
1643
0
                    continue;
1644
0
                if (parent->astOperand2()) {
1645
0
                    if (parent->astOperand2()->function() && parent->astOperand2()->function()->isConst())
1646
0
                        continue;
1647
0
                    if (mSettings->library.isFunctionConst(parent->astOperand2()))
1648
0
                        continue;
1649
0
                }
1650
0
            }
1651
0
            if (Token::Match(gparent, "%cop%") && !gparent->isUnaryOp("&") && !gparent->isUnaryOp("*"))
1652
0
                continue;
1653
0
            if (hasIncDec) {
1654
0
                parent = gparent;
1655
0
                gparent = gparent ? gparent->astParent() : nullptr;
1656
0
            }
1657
0
            int argn = -1;
1658
0
            if (Token::simpleMatch(gparent, "return")) {
1659
0
                const Function* function = gparent->scope()->function;
1660
0
                if (function && (!Function::returnsReference(function) || Function::returnsConst(function)))
1661
0
                    continue;
1662
0
            }
1663
0
            else if (Token::Match(gparent, "%assign%") && parent == gparent->astOperand2()) {
1664
0
                bool takingRef = false, nonConstPtrAssignment = false;
1665
0
                const Token *lhs = gparent->astOperand1();
1666
0
                if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs->variable()->nameToken() == lhs)
1667
0
                    takingRef = true;
1668
0
                if (lhs && lhs->valueType() && lhs->valueType()->pointer && (lhs->valueType()->constness & 1) == 0 &&
1669
0
                    parent->valueType() && parent->valueType()->pointer)
1670
0
                    nonConstPtrAssignment = true;
1671
0
                if (!takingRef && !nonConstPtrAssignment)
1672
0
                    continue;
1673
0
            } else if (Token::simpleMatch(gparent, "[") && gparent->astOperand2() == parent)
1674
0
                continue;
1675
0
            else if (const Token* ftok = getTokenArgumentFunction(parent, argn)) {
1676
0
                bool inconclusive{};
1677
0
                if (!isVariableChangedByFunctionCall(ftok->next(), vt->pointer, var->declarationId(), mSettings, &inconclusive) && !inconclusive)
1678
0
                    continue;
1679
0
            }
1680
0
        } else {
1681
0
            int argn = -1;
1682
0
            if (Token::Match(parent, "%oror%|%comp%|&&|?|!|-"))
1683
0
                continue;
1684
0
            if (Token::simpleMatch(parent, "(") && Token::Match(parent->astOperand1(), "if|while"))
1685
0
                continue;
1686
0
            if (Token::simpleMatch(parent, "=") && parent->astOperand1() == tok)
1687
0
                continue;
1688
0
            if (const Token* ftok = getTokenArgumentFunction(tok, argn)) {
1689
0
                if (ftok->function()) {
1690
0
                    const bool isCastArg = parent->isCast() && !ftok->function()->getOverloadedFunctions().empty(); // assume that cast changes the called function
1691
0
                    if (!isCastArg) {
1692
0
                        const Variable* argVar = ftok->function()->getArgumentVar(argn);
1693
0
                        if (argVar && argVar->valueType() && argVar->valueType()->isConst(vt->pointer)) {
1694
0
                            bool inconclusive{};
1695
0
                            if (!isVariableChangedByFunctionCall(ftok, vt->pointer, var->declarationId(), mSettings, &inconclusive) && !inconclusive)
1696
0
                                continue;
1697
0
                        }
1698
0
                    }
1699
0
                } else {
1700
0
                    const auto dir = mSettings->library.getArgDirection(ftok, argn + 1);
1701
0
                    if (dir == Library::ArgumentChecks::Direction::DIR_IN)
1702
0
                        continue;
1703
0
                }
1704
0
            }
1705
0
            else if (Token::simpleMatch(parent, "(")) {
1706
0
                if (parent->isCast() && parent->valueType() && var->valueType() && parent->valueType()->isConst(var->valueType()->pointer))
1707
0
                    continue;
1708
0
            }
1709
0
        }
1710
0
        nonConstPointers.emplace_back(var);
1711
0
    }
1712
1.36k
    for (const Variable *p: pointers) {
1713
0
        if (p->isArgument()) {
1714
0
            if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(true) || p->scope()->function->hasVirtualSpecifier())
1715
0
                continue;
1716
0
        }
1717
0
        if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), p) == nonConstPointers.cend()) {
1718
0
            const Token *start = getVariableChangedStart(p);
1719
0
            const int indirect = p->isArray() ? p->dimensions().size() : 1;
1720
0
            if (isVariableChanged(start, p->scope()->bodyEnd, indirect, p->declarationId(), false, mSettings, mTokenizer->isCPP()))
1721
0
                continue;
1722
0
            if (p->isArgument() && p->typeStartToken() && p->typeStartToken()->isSimplifiedTypedef() && !(Token::simpleMatch(p->typeEndToken(), "*") && !p->typeEndToken()->isSimplifiedTypedef()))
1723
0
                continue;
1724
0
            constVariableError(p, p->isArgument() ? p->scope()->function : nullptr);
1725
0
        }
1726
0
    }
1727
1.36k
}
1728
void CheckOther::constVariableError(const Variable *var, const Function *function)
1729
0
{
1730
0
    if (!var) {
1731
0
        reportError(nullptr, Severity::style, "constParameter", "Parameter 'x' can be declared with const");
1732
0
        reportError(nullptr, Severity::style, "constVariable",  "Variable 'x' can be declared with const");
1733
0
        reportError(nullptr, Severity::style, "constParameterReference", "Parameter 'x' can be declared with const");
1734
0
        reportError(nullptr, Severity::style, "constVariableReference", "Variable 'x' can be declared with const");
1735
0
        reportError(nullptr, Severity::style, "constParameterPointer", "Parameter 'x' can be declared with const");
1736
0
        reportError(nullptr, Severity::style, "constVariablePointer", "Variable 'x' can be declared with const");
1737
0
        reportError(nullptr, Severity::style, "constParameterCallback", "Parameter 'x' can be declared with const, however it seems that 'f' is a callback function.");
1738
0
        return;
1739
0
    }
1740
1741
0
    const std::string vartype(var->isArgument() ? "Parameter" : "Variable");
1742
0
    const std::string varname(var->name());
1743
0
    const std::string ptrRefArray = var->isArray() ? "const array" : (var->isPointer() ? "pointer to const" : "reference to const");
1744
1745
0
    ErrorPath errorPath;
1746
0
    std::string id = "const" + vartype;
1747
0
    std::string message = "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared as " + ptrRefArray;
1748
0
    errorPath.emplace_back(var->nameToken(), message);
1749
0
    if (var->isArgument() && function && function->functionPointerUsage) {
1750
0
        errorPath.emplace_front(function->functionPointerUsage, "You might need to cast the function pointer here");
1751
0
        id += "Callback";
1752
0
        message += ". However it seems that '" + function->name() + "' is a callback function, if '$symbol' is declared with const you might also need to cast function pointer(s).";
1753
0
    } else if (var->isReference()) {
1754
0
        id += "Reference";
1755
0
    } else if (var->isPointer() && !var->isArray()) {
1756
0
        id += "Pointer";
1757
0
    }
1758
1759
0
    reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal);
1760
0
}
1761
1762
//---------------------------------------------------------------------------
1763
// Check usage of char variables..
1764
//---------------------------------------------------------------------------
1765
1766
void CheckOther::checkCharVariable()
1767
1.36k
{
1768
1.36k
    const bool warning = mSettings->severity.isEnabled(Severity::warning);
1769
1.36k
    const bool portability = mSettings->severity.isEnabled(Severity::portability);
1770
1.36k
    if (!warning && !portability)
1771
0
        return;
1772
1773
1.36k
    logChecker("CheckOther::checkCharVariable"); // warning,portability
1774
1775
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1776
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
1777
37.6k
        for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1778
35.9k
            if (Token::Match(tok, "%var% [")) {
1779
0
                if (!tok->variable())
1780
0
                    continue;
1781
0
                if (!tok->variable()->isArray() && !tok->variable()->isPointer())
1782
0
                    continue;
1783
0
                const Token *index = tok->next()->astOperand2();
1784
0
                if (warning && tok->variable()->isArray() && astIsSignedChar(index) && index->getValueGE(0x80, mSettings))
1785
0
                    signedCharArrayIndexError(tok);
1786
0
                if (portability && astIsUnknownSignChar(index) && index->getValueGE(0x80, mSettings))
1787
0
                    unknownSignCharArrayIndexError(tok);
1788
35.9k
            } else if (warning && Token::Match(tok, "[&|^]") && tok->isBinaryOp()) {
1789
781
                bool warn = false;
1790
781
                if (astIsSignedChar(tok->astOperand1())) {
1791
0
                    const ValueFlow::Value *v1 = tok->astOperand1()->getValueLE(-1, mSettings);
1792
0
                    const ValueFlow::Value *v2 = tok->astOperand2()->getMaxValue(false);
1793
0
                    if (!v1)
1794
0
                        v1 = tok->astOperand1()->getValueGE(0x80, mSettings);
1795
0
                    if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100))
1796
0
                        warn = true;
1797
781
                } else if (astIsSignedChar(tok->astOperand2())) {
1798
0
                    const ValueFlow::Value *v1 = tok->astOperand2()->getValueLE(-1, mSettings);
1799
0
                    const ValueFlow::Value *v2 = tok->astOperand1()->getMaxValue(false);
1800
0
                    if (!v1)
1801
0
                        v1 = tok->astOperand2()->getValueGE(0x80, mSettings);
1802
0
                    if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100))
1803
0
                        warn = true;
1804
0
                }
1805
1806
                // is the result stored in a short|int|long?
1807
781
                if (warn && Token::simpleMatch(tok->astParent(), "=")) {
1808
0
                    const Token *lhs = tok->astParent()->astOperand1();
1809
0
                    if (lhs && lhs->valueType() && lhs->valueType()->type >= ValueType::Type::SHORT)
1810
0
                        charBitOpError(tok); // This is an error..
1811
0
                }
1812
781
            }
1813
35.9k
        }
1814
1.73k
    }
1815
1.36k
}
1816
1817
void CheckOther::signedCharArrayIndexError(const Token *tok)
1818
0
{
1819
0
    reportError(tok,
1820
0
                Severity::warning,
1821
0
                "signedCharArrayIndex",
1822
0
                "Signed 'char' type used as array index.\n"
1823
0
                "Signed 'char' type used as array index. If the value "
1824
0
                "can be greater than 127 there will be a buffer underflow "
1825
0
                "because of sign extension.", CWE128, Certainty::normal);
1826
0
}
1827
1828
void CheckOther::unknownSignCharArrayIndexError(const Token *tok)
1829
0
{
1830
0
    reportError(tok,
1831
0
                Severity::portability,
1832
0
                "unknownSignCharArrayIndex",
1833
0
                "'char' type used as array index.\n"
1834
0
                "'char' type used as array index. Values greater than 127 will be "
1835
0
                "treated depending on whether 'char' is signed or unsigned on target platform.", CWE758, Certainty::normal);
1836
0
}
1837
1838
void CheckOther::charBitOpError(const Token *tok)
1839
0
{
1840
0
    reportError(tok,
1841
0
                Severity::warning,
1842
0
                "charBitOp",
1843
0
                "When using 'char' variables in bit operations, sign extension can generate unexpected results.\n"
1844
0
                "When using 'char' variables in bit operations, sign extension can generate unexpected results. For example:\n"
1845
0
                "    char c = 0x80;\n"
1846
0
                "    int i = 0 | c;\n"
1847
0
                "    if (i & 0x8000)\n"
1848
0
                "        printf(\"not expected\");\n"
1849
0
                "The \"not expected\" will be printed on the screen.", CWE398, Certainty::normal);
1850
0
}
1851
1852
//---------------------------------------------------------------------------
1853
// Incomplete statement..
1854
//---------------------------------------------------------------------------
1855
1856
static bool isType(const Token * tok, bool unknown)
1857
0
{
1858
0
    if (tok && (tok->isStandardType() || (!tok->isKeyword() && Token::Match(tok, "%type%")) || tok->str() == "auto"))
1859
0
        return true;
1860
0
    if (Token::simpleMatch(tok, "::"))
1861
0
        return isType(tok->astOperand2(), unknown);
1862
0
    if (Token::simpleMatch(tok, "<") && tok->link())
1863
0
        return true;
1864
0
    if (unknown && Token::Match(tok, "%name% !!("))
1865
0
        return true;
1866
0
    return false;
1867
0
}
1868
1869
static bool isVarDeclOp(const Token* tok)
1870
0
{
1871
0
    if (!tok)
1872
0
        return false;
1873
0
    const Token * vartok = tok->astOperand2();
1874
0
    if (vartok && vartok->variable() && vartok->variable()->nameToken() == vartok)
1875
0
        return true;
1876
0
    const Token * typetok = tok->astOperand1();
1877
0
    return isType(typetok, vartok && vartok->varId() != 0);
1878
0
}
1879
1880
static bool isBracketAccess(const Token* tok)
1881
3.45k
{
1882
3.45k
    if (!Token::simpleMatch(tok, "[") || !tok->astOperand1())
1883
3.45k
        return false;
1884
0
    tok = tok->astOperand1();
1885
0
    if (tok->str() == ".")
1886
0
        tok = tok->astOperand2();
1887
0
    while (Token::simpleMatch(tok, "["))
1888
0
        tok = tok->astOperand1();
1889
0
    if (!tok || !tok->variable())
1890
0
        return false;
1891
0
    return tok->variable()->nameToken() != tok;
1892
0
}
1893
1894
3.45k
static bool isConstant(const Token* tok) {
1895
3.45k
    return Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL");
1896
3.45k
}
1897
1898
static bool isConstStatement(const Token *tok, bool cpp)
1899
3.45k
{
1900
3.45k
    if (!tok)
1901
0
        return false;
1902
3.45k
    if (tok->isExpandedMacro())
1903
0
        return false;
1904
3.45k
    if (tok->varId() != 0)
1905
0
        return true;
1906
3.45k
    if (isConstant(tok))
1907
0
        return true;
1908
3.45k
    if (Token::Match(tok, "*|&|&&") &&
1909
3.45k
        (Token::Match(tok->previous(), "::|.|const|volatile|restrict") || isVarDeclOp(tok)))
1910
0
        return false;
1911
3.45k
    if (Token::Match(tok, "<<|>>") && !astIsIntegral(tok, false))
1912
0
        return false;
1913
3.45k
    if (tok->astTop() && Token::simpleMatch(tok->astTop()->astOperand1(), "delete"))
1914
0
        return false;
1915
3.45k
    if (Token::Match(tok, "&&|%oror%"))
1916
0
        return isConstStatement(tok->astOperand1(), cpp) && isConstStatement(tok->astOperand2(), cpp);
1917
3.45k
    if (Token::Match(tok, "!|~|%cop%") && (tok->astOperand1() || tok->astOperand2()))
1918
0
        return true;
1919
3.45k
    if (Token::simpleMatch(tok->previous(), "sizeof ("))
1920
0
        return true;
1921
3.45k
    if (isCPPCast(tok)) {
1922
0
        if (Token::simpleMatch(tok->astOperand1(), "dynamic_cast") && Token::simpleMatch(tok->astOperand1()->linkAt(1)->previous(), "& >"))
1923
0
            return false;
1924
0
        return isWithoutSideEffects(cpp, tok) && isConstStatement(tok->astOperand2(), cpp);
1925
0
    }
1926
3.45k
    if (tok->isCast() && tok->next() && tok->next()->isStandardType())
1927
0
        return isWithoutSideEffects(cpp, tok->astOperand1()) && isConstStatement(tok->astOperand1(), cpp);
1928
3.45k
    if (Token::simpleMatch(tok, "."))
1929
0
        return isConstStatement(tok->astOperand2(), cpp);
1930
3.45k
    if (Token::simpleMatch(tok, ",")) {
1931
0
        if (tok->astParent()) // warn about const statement on rhs at the top level
1932
0
            return isConstStatement(tok->astOperand1(), cpp) && isConstStatement(tok->astOperand2(), cpp);
1933
1934
0
        const Token* lml = previousBeforeAstLeftmostLeaf(tok); // don't warn about matrix/vector assignment (e.g. Eigen)
1935
0
        if (lml)
1936
0
            lml = lml->next();
1937
0
        const Token* stream = lml;
1938
0
        while (stream && Token::Match(stream->astParent(), ".|[|(|*"))
1939
0
            stream = stream->astParent();
1940
0
        return (!stream || !isLikelyStream(cpp, stream)) && isConstStatement(tok->astOperand2(), cpp);
1941
0
    }
1942
3.45k
    if (Token::simpleMatch(tok, "?") && Token::simpleMatch(tok->astOperand2(), ":")) // ternary operator
1943
0
        return isConstStatement(tok->astOperand1(), cpp) && isConstStatement(tok->astOperand2()->astOperand1(), cpp) && isConstStatement(tok->astOperand2()->astOperand2(), cpp);
1944
3.45k
    if (isBracketAccess(tok) && isWithoutSideEffects(cpp, tok->astOperand1(), /*checkArrayAccess*/ true, /*checkReference*/ false)) {
1945
0
        if (Token::simpleMatch(tok->astParent(), "["))
1946
0
            return isConstStatement(tok->astOperand2(), cpp) && isConstStatement(tok->astParent(), cpp);
1947
0
        return isConstStatement(tok->astOperand2(), cpp);
1948
0
    }
1949
3.45k
    return false;
1950
3.45k
}
1951
1952
static bool isVoidStmt(const Token *tok)
1953
0
{
1954
0
    if (Token::simpleMatch(tok, "( void"))
1955
0
        return true;
1956
0
    if (isCPPCast(tok) && tok->astOperand1() && Token::Match(tok->astOperand1()->next(), "< void *| >"))
1957
0
        return true;
1958
0
    const Token *tok2 = tok;
1959
0
    while (tok2->astOperand1())
1960
0
        tok2 = tok2->astOperand1();
1961
0
    if (Token::simpleMatch(tok2->previous(), ")") && Token::simpleMatch(tok2->previous()->link(), "( void"))
1962
0
        return true;
1963
0
    if (Token::simpleMatch(tok2, "( void"))
1964
0
        return true;
1965
0
    return Token::Match(tok2->previous(), "delete|throw|return");
1966
0
}
1967
1968
static bool isConstTop(const Token *tok)
1969
37.6k
{
1970
37.6k
    if (!tok)
1971
0
        return false;
1972
37.6k
    if (!tok->astParent())
1973
18.7k
        return true;
1974
18.9k
    if (Token::simpleMatch(tok->astParent(), ";") &&
1975
18.9k
        Token::Match(tok->astTop()->previous(), "for|if (") && Token::simpleMatch(tok->astTop()->astOperand2(), ";")) {
1976
0
        if (Token::simpleMatch(tok->astParent()->astParent(), ";"))
1977
0
            return tok->astParent()->astOperand2() == tok;
1978
0
        return tok->astParent()->astOperand1() == tok;
1979
0
    }
1980
18.9k
    if (Token::simpleMatch(tok, "[")) {
1981
0
        const Token* bracTok = tok;
1982
0
        while (Token::simpleMatch(bracTok->astParent(), "["))
1983
0
            bracTok = bracTok->astParent();
1984
0
        if (!bracTok->astParent())
1985
0
            return true;
1986
0
    }
1987
18.9k
    if (tok->str() == "," && tok->astParent()->isAssignmentOp())
1988
0
        return true;
1989
18.9k
    return false;
1990
18.9k
}
1991
1992
void CheckOther::checkIncompleteStatement()
1993
1.36k
{
1994
1.36k
    if (!mSettings->severity.isEnabled(Severity::warning))
1995
0
        return;
1996
1997
1.36k
    logChecker("CheckOther::checkIncompleteStatement"); // warning
1998
1999
93.5k
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
2000
92.2k
        const Scope *scope = tok->scope();
2001
92.2k
        if (scope && !scope->isExecutable())
2002
54.5k
            continue;
2003
37.6k
        if (!isConstTop(tok))
2004
18.9k
            continue;
2005
18.7k
        if (tok->str() == "," && Token::simpleMatch(tok->astTop()->previous(), "for ("))
2006
0
            continue;
2007
2008
        // Do not warn for statement when both lhs and rhs has side effects:
2009
        //   dostuff() || x=213;
2010
18.7k
        if (Token::Match(tok, "%oror%|&&")) {
2011
0
            bool warn = false;
2012
0
            visitAstNodes(tok, [&warn](const Token *child) {
2013
0
                if (Token::Match(child, "%oror%|&&"))
2014
0
                    return ChildrenToVisit::op1_and_op2;
2015
0
                if (child->isAssignmentOp())
2016
0
                    return ChildrenToVisit::none;
2017
0
                if (child->tokType() == Token::Type::eIncDecOp)
2018
0
                    return ChildrenToVisit::none;
2019
0
                if (Token::Match(child->previous(), "%name% ("))
2020
0
                    return ChildrenToVisit::none;
2021
0
                warn = true;
2022
0
                return ChildrenToVisit::done;
2023
0
            });
2024
0
            if (!warn)
2025
0
                continue;
2026
0
        }
2027
2028
18.7k
        const Token *rtok = nextAfterAstRightmostLeaf(tok);
2029
18.7k
        if (!Token::simpleMatch(tok->astParent(), ";") && !Token::simpleMatch(rtok, ";") &&
2030
18.7k
            !Token::Match(tok->previous(), ";|}|{ %any% ;") &&
2031
18.7k
            !(mTokenizer->isCPP() && tok->isCast() && !tok->astParent()) &&
2032
18.7k
            !Token::simpleMatch(tok->tokAt(-2), "for (") &&
2033
18.7k
            !Token::Match(tok->tokAt(-1), "%var% [") &&
2034
18.7k
            !(tok->str() == "," && tok->astParent() && tok->astParent()->isAssignmentOp()))
2035
15.2k
            continue;
2036
        // Skip statement expressions
2037
3.45k
        if (Token::simpleMatch(rtok, "; } )"))
2038
0
            continue;
2039
3.45k
        if (!isConstStatement(tok, mTokenizer->isCPP()))
2040
3.45k
            continue;
2041
0
        if (isVoidStmt(tok))
2042
0
            continue;
2043
0
        if (mTokenizer->isCPP() && tok->str() == "&" && !(tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isIntegral()))
2044
            // Possible archive
2045
0
            continue;
2046
0
        const bool inconclusive = tok->isConstOp();
2047
0
        if (mSettings->certainty.isEnabled(Certainty::inconclusive) || !inconclusive)
2048
0
            constStatementError(tok, tok->isNumber() ? "numeric" : "string", inconclusive);
2049
0
    }
2050
1.36k
}
2051
2052
void CheckOther::constStatementError(const Token *tok, const std::string &type, bool inconclusive)
2053
0
{
2054
0
    const Token *valueTok = tok;
2055
0
    while (valueTok && valueTok->isCast())
2056
0
        valueTok = valueTok->astOperand2() ? valueTok->astOperand2() : valueTok->astOperand1();
2057
2058
0
    std::string msg;
2059
0
    if (Token::simpleMatch(tok, "=="))
2060
0
        msg = "Found suspicious equality comparison. Did you intend to assign a value instead?";
2061
0
    else if (Token::Match(tok, ",|!|~|%cop%"))
2062
0
        msg = "Found suspicious operator '" + tok->str() + "', result is not used.";
2063
0
    else if (Token::Match(tok, "%var%"))
2064
0
        msg = "Unused variable value '" + tok->str() + "'";
2065
0
    else if (isConstant(valueTok)) {
2066
0
        std::string typeStr("string");
2067
0
        if (valueTok->isNumber())
2068
0
            typeStr = "numeric";
2069
0
        else if (valueTok->isBoolean())
2070
0
            typeStr = "bool";
2071
0
        else if (valueTok->tokType() == Token::eChar)
2072
0
            typeStr = "character";
2073
0
        else if (isNullOperand(valueTok))
2074
0
            typeStr = "NULL";
2075
0
        msg = "Redundant code: Found a statement that begins with " + typeStr + " constant.";
2076
0
    }
2077
0
    else if (!tok)
2078
0
        msg = "Redundant code: Found a statement that begins with " + type + " constant.";
2079
0
    else if (tok->isCast() && tok->tokType() == Token::Type::eExtendedOp) {
2080
0
        msg = "Redundant code: Found unused cast ";
2081
0
        msg += valueTok ? "of expression '" + valueTok->expressionString() + "'." : "expression.";
2082
0
    }
2083
0
    else if (tok->str() == "?" && tok->tokType() == Token::Type::eExtendedOp)
2084
0
        msg = "Redundant code: Found unused result of ternary operator.";
2085
0
    else if (tok->str() == "." && tok->tokType() == Token::Type::eOther)
2086
0
        msg = "Redundant code: Found unused member access.";
2087
0
    else if (tok->str() == "[" && tok->tokType() == Token::Type::eExtendedOp)
2088
0
        msg = "Redundant code: Found unused array access.";
2089
0
    else if (mSettings->debugwarnings) {
2090
0
        reportError(tok, Severity::debug, "debug", "constStatementError not handled.");
2091
0
        return;
2092
0
    }
2093
0
    reportError(tok, Severity::warning, "constStatement", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
2094
0
}
2095
2096
//---------------------------------------------------------------------------
2097
// Detect division by zero.
2098
//---------------------------------------------------------------------------
2099
void CheckOther::checkZeroDivision()
2100
1.36k
{
2101
1.36k
    logChecker("CheckOther::checkZeroDivision");
2102
2103
93.5k
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
2104
92.2k
        if (!tok->astOperand2() || !tok->astOperand1())
2105
78.0k
            continue;
2106
14.2k
        if (tok->str() != "%" && tok->str() != "/" && tok->str() != "%=" && tok->str() != "/=")
2107
13.6k
            continue;
2108
557
        if (!tok->valueType() || !tok->valueType()->isIntegral())
2109
237
            continue;
2110
320
        if (tok->scope() && tok->scope()->type == Scope::eEnum) // don't warn for compile-time error
2111
0
            continue;
2112
2113
        // Value flow..
2114
320
        const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL);
2115
320
        if (value && mSettings->isEnabled(value, false))
2116
4
            zerodivError(tok, value);
2117
320
    }
2118
1.36k
}
2119
2120
void CheckOther::zerodivError(const Token *tok, const ValueFlow::Value *value)
2121
4
{
2122
4
    if (!tok && !value) {
2123
0
        reportError(tok, Severity::error, "zerodiv", "Division by zero.", CWE369, Certainty::normal);
2124
0
        reportError(tok, Severity::warning, "zerodivcond", ValueFlow::eitherTheConditionIsRedundant(nullptr) + " or there is division by zero.", CWE369, Certainty::normal);
2125
0
        return;
2126
0
    }
2127
2128
4
    const ErrorPath errorPath = getErrorPath(tok, value, "Division by zero");
2129
2130
4
    std::ostringstream errmsg;
2131
4
    if (value->condition) {
2132
0
        const int line = tok ? tok->linenr() : 0;
2133
0
        errmsg << ValueFlow::eitherTheConditionIsRedundant(value->condition)
2134
0
               << " or there is division by zero at line " << line << ".";
2135
0
    } else
2136
4
        errmsg << "Division by zero.";
2137
2138
4
    reportError(errorPath,
2139
4
                value->errorSeverity() ? Severity::error : Severity::warning,
2140
4
                value->condition ? "zerodivcond" : "zerodiv",
2141
4
                errmsg.str(), CWE369, value->isInconclusive() ? Certainty::inconclusive : Certainty::normal);
2142
4
}
2143
2144
//---------------------------------------------------------------------------
2145
// Check for NaN (not-a-number) in an arithmetic expression, e.g.
2146
// double d = 1.0 / 0.0 + 100.0;
2147
//---------------------------------------------------------------------------
2148
2149
void CheckOther::checkNanInArithmeticExpression()
2150
1.36k
{
2151
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
2152
0
        return;
2153
1.36k
    logChecker("CheckOther::checkNanInArithmeticExpression"); // style
2154
93.5k
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
2155
92.2k
        if (tok->str() != "/")
2156
91.9k
            continue;
2157
269
        if (!Token::Match(tok->astParent(), "[+-]"))
2158
226
            continue;
2159
43
        if (Token::simpleMatch(tok->astOperand2(), "0.0"))
2160
0
            nanInArithmeticExpressionError(tok);
2161
43
    }
2162
1.36k
}
2163
2164
void CheckOther::nanInArithmeticExpressionError(const Token *tok)
2165
0
{
2166
0
    reportError(tok, Severity::style, "nanInArithmeticExpression",
2167
0
                "Using NaN/Inf in a computation.\n"
2168
0
                "Using NaN/Inf in a computation. "
2169
0
                "Although nothing bad really happens, it is suspicious.", CWE369, Certainty::normal);
2170
0
}
2171
2172
//---------------------------------------------------------------------------
2173
// Creating instance of classes which are destroyed immediately
2174
//---------------------------------------------------------------------------
2175
void CheckOther::checkMisusedScopedObject()
2176
1.36k
{
2177
    // Skip this check for .c files
2178
1.36k
    if (mTokenizer->isC())
2179
0
        return;
2180
2181
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
2182
0
        return;
2183
2184
1.36k
    logChecker("CheckOther::checkMisusedScopedObject"); // style,c++
2185
2186
35.9k
    auto getConstructorTok = [](const Token* tok, std::string& typeStr) -> const Token* {
2187
35.9k
        if (!Token::Match(tok, "[;{}] %name%") || tok->next()->isKeyword())
2188
34.3k
            return nullptr;
2189
1.53k
        tok = tok->next();
2190
1.53k
        typeStr.clear();
2191
1.53k
        while (Token::Match(tok, "%name% ::")) {
2192
0
            typeStr += tok->str();
2193
0
            typeStr += "::";
2194
0
            tok = tok->tokAt(2);
2195
0
        }
2196
1.53k
        typeStr += tok->str();
2197
1.53k
        const Token* endTok = tok;
2198
1.53k
        if (Token::Match(endTok, "%name% <"))
2199
0
            endTok = endTok->linkAt(1);
2200
1.53k
        if (Token::Match(endTok, "%name%|> (|{") && Token::Match(endTok->linkAt(1), ")|} ;") &&
2201
1.53k
            !Token::simpleMatch(endTok->next()->astParent(), ";")) { // for loop condition
2202
0
            return tok;
2203
0
        }
2204
1.53k
        return nullptr;
2205
1.53k
    };
2206
2207
1.36k
    auto isLibraryConstructor = [&](const Token* tok, const std::string& typeStr) -> bool {
2208
0
        const Library::TypeCheck typeCheck = mSettings->library.getTypeCheck("unusedvar", typeStr);
2209
0
        if (typeCheck == Library::TypeCheck::check || typeCheck == Library::TypeCheck::checkFiniteLifetime)
2210
0
            return true;
2211
0
        return mSettings->library.detectContainerOrIterator(tok);
2212
0
    };
2213
2214
1.36k
    const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
2215
1.36k
    std::string typeStr;
2216
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
2217
37.6k
        for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
2218
35.9k
            const Token* ctorTok = getConstructorTok(tok, typeStr);
2219
35.9k
            if (ctorTok && (((ctorTok->type() || ctorTok->isStandardType() || (ctorTok->function() && ctorTok->function()->isConstructor())) // TODO: The rhs of || should be removed; It is a workaround for a symboldatabase bug
2220
0
                             && (!ctorTok->function() || ctorTok->function()->isConstructor()) // // is not a function on this scope or is function in this scope and it's a ctor
2221
0
                             && ctorTok->str() != "void") || isLibraryConstructor(tok->next(), typeStr))) {
2222
0
                const Token* parTok = ctorTok->next();
2223
0
                if (Token::simpleMatch(parTok, "<") && parTok->link())
2224
0
                    parTok = parTok->link()->next();
2225
0
                if (const Token* arg = parTok->astOperand2()) {
2226
0
                    if (!isConstStatement(arg, mTokenizer->isCPP()))
2227
0
                        continue;
2228
0
                    if (parTok->str() == "(") {
2229
0
                        if (arg->varId() && !(arg->variable() && arg->variable()->nameToken() != arg))
2230
0
                            continue;
2231
0
                        const Token* rml = nextAfterAstRightmostLeaf(arg);
2232
0
                        if (rml && rml->previous() && rml->previous()->varId())
2233
0
                            continue;
2234
0
                    }
2235
0
                }
2236
0
                tok = tok->next();
2237
0
                misusedScopeObjectError(ctorTok, typeStr);
2238
0
                tok = tok->next();
2239
0
            }
2240
35.9k
            if (tok->isAssignmentOp() && Token::simpleMatch(tok->astOperand1(), "(") && tok->astOperand1()->astOperand1()) {
2241
0
                if (const Function* ftok = tok->astOperand1()->astOperand1()->function()) {
2242
0
                    if (ftok->retType && Token::Match(ftok->retType->classDef, "class|struct|union") && !Function::returnsReference(ftok, /*unknown*/ false, /*includeRValueRef*/ true))
2243
0
                        misusedScopeObjectError(tok->next(), ftok->retType->name(), /*isAssignment*/ true);
2244
0
                }
2245
0
            }
2246
35.9k
        }
2247
1.73k
    }
2248
1.36k
}
2249
2250
void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname, bool isAssignment)
2251
0
{
2252
0
    std::string msg = "Instance of '$symbol' object is destroyed immediately";
2253
0
    msg += isAssignment ? ", assignment has no effect." : ".";
2254
0
    reportError(tok, Severity::style,
2255
0
                "unusedScopedObject",
2256
0
                "$symbol:" + varname + "\n" +
2257
0
                msg, CWE563, Certainty::normal);
2258
0
}
2259
2260
static const Token * getSingleExpressionInBlock(const Token * tok)
2261
314
{
2262
314
    if (!tok)
2263
0
        return nullptr;
2264
314
    const Token * top = tok->astTop();
2265
314
    if (!top)
2266
0
        return nullptr;
2267
314
    const Token * nextExpression = nextAfterAstRightmostLeaf(top);
2268
314
    if (!Token::simpleMatch(nextExpression, "; }"))
2269
258
        return nullptr;
2270
56
    return top;
2271
314
}
2272
2273
//-----------------------------------------------------------------------------
2274
// check for duplicate code in if and else branches
2275
// if (a) { b = true; } else { b = true; }
2276
//-----------------------------------------------------------------------------
2277
void CheckOther::checkDuplicateBranch()
2278
1.36k
{
2279
    // This is inconclusive since in practice most warnings are noise:
2280
    // * There can be unfixed low-priority todos. The code is fine as it
2281
    //   is but it could be possible to enhance it. Writing a warning
2282
    //   here is noise since the code is fine (see cppcheck, abiword, ..)
2283
    // * There can be overspecified code so some conditions can't be true
2284
    //   and their conditional code is a duplicate of the condition that
2285
    //   is always true just in case it would be false. See for instance
2286
    //   abiword.
2287
1.36k
    if (!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive))
2288
0
        return;
2289
2290
1.36k
    logChecker("CheckOther::checkDuplicateBranch"); // style,inconclusive
2291
2292
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
2293
2294
4.94k
    for (const Scope & scope : symbolDatabase->scopeList) {
2295
4.94k
        if (scope.type != Scope::eIf)
2296
4.02k
            continue;
2297
2298
        // check all the code in the function for if (..) else
2299
919
        if (Token::simpleMatch(scope.bodyEnd, "} else {")) {
2300
            // Make sure there are no macros (different macros might be expanded
2301
            // to the same code)
2302
457
            bool macro = false;
2303
7.75k
            for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd->linkAt(2); tok = tok->next()) {
2304
7.30k
                if (tok->isExpandedMacro()) {
2305
0
                    macro = true;
2306
0
                    break;
2307
0
                }
2308
7.30k
            }
2309
457
            if (macro)
2310
0
                continue;
2311
2312
457
            const Token * const tokIf = scope.bodyStart->next();
2313
457
            const Token * const tokElse = scope.bodyEnd->tokAt(3);
2314
2315
            // compare first tok before stringifying the whole blocks
2316
457
            const std::string tokIfStr = tokIf->stringify(false, true, false);
2317
457
            if (tokIfStr.empty())
2318
0
                continue;
2319
2320
457
            const std::string tokElseStr = tokElse->stringify(false, true, false);
2321
2322
457
            if (tokIfStr == tokElseStr) {
2323
                // save if branch code
2324
199
                const std::string branch1 = tokIf->stringifyList(scope.bodyEnd);
2325
2326
199
                if (branch1.empty())
2327
195
                    continue;
2328
2329
                // save else branch code
2330
4
                const std::string branch2 = tokElse->stringifyList(scope.bodyEnd->linkAt(2));
2331
2332
                // check for duplicates
2333
4
                if (branch1 == branch2) {
2334
0
                    duplicateBranchError(scope.classDef, scope.bodyEnd->next(), ErrorPath{});
2335
0
                    continue;
2336
0
                }
2337
4
            }
2338
2339
            // check for duplicates using isSameExpression
2340
262
            const Token * branchTop1 = getSingleExpressionInBlock(tokIf);
2341
262
            if (!branchTop1)
2342
210
                continue;
2343
52
            const Token * branchTop2 = getSingleExpressionInBlock(tokElse);
2344
52
            if (!branchTop2)
2345
48
                continue;
2346
4
            if (branchTop1->str() != branchTop2->str())
2347
2
                continue;
2348
2
            ErrorPath errorPath;
2349
2
            if (isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand1(), branchTop2->astOperand1(), mSettings->library, true, true, &errorPath) &&
2350
2
                isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand2(), branchTop2->astOperand2(), mSettings->library, true, true, &errorPath))
2351
0
                duplicateBranchError(scope.classDef, scope.bodyEnd->next(), errorPath);
2352
2
        }
2353
919
    }
2354
1.36k
}
2355
2356
void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors)
2357
0
{
2358
0
    errors.emplace_back(tok2, "");
2359
0
    errors.emplace_back(tok1, "");
2360
2361
0
    reportError(errors, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n"
2362
0
                "Finding the same code in an 'if' and related 'else' branch is suspicious and "
2363
0
                "might indicate a cut and paste or logic error. Please examine this code "
2364
0
                "carefully to determine if it is correct.", CWE398, Certainty::inconclusive);
2365
0
}
2366
2367
2368
//-----------------------------------------------------------------------------
2369
// Check for a free() of an invalid address
2370
// char* p = malloc(100);
2371
// free(p + 10);
2372
//-----------------------------------------------------------------------------
2373
void CheckOther::checkInvalidFree()
2374
1.36k
{
2375
1.36k
    std::map<int, bool> inconclusive;
2376
1.36k
    std::map<int, std::string> allocation;
2377
2378
1.36k
    logChecker("CheckOther::checkInvalidFree");
2379
2380
1.36k
    const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
2381
1.36k
    const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
2382
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
2383
35.9k
        for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
2384
2385
            // Keep track of which variables were assigned addresses to newly-allocated memory
2386
34.1k
            if ((mTokenizer->isCPP() && Token::Match(tok, "%var% = new")) ||
2387
34.1k
                (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) {
2388
0
                allocation.insert(std::make_pair(tok->varId(), tok->strAt(2)));
2389
0
                inconclusive.insert(std::make_pair(tok->varId(), false));
2390
0
            }
2391
2392
            // If a previously-allocated pointer is incremented or decremented, any subsequent
2393
            // free involving pointer arithmetic may or may not be invalid, so we should only
2394
            // report an inconclusive result.
2395
34.1k
            else if (Token::Match(tok, "%var% = %name% +|-") &&
2396
34.1k
                     tok->varId() == tok->tokAt(2)->varId() &&
2397
34.1k
                     allocation.find(tok->varId()) != allocation.end()) {
2398
0
                if (printInconclusive)
2399
0
                    inconclusive[tok->varId()] = true;
2400
0
                else {
2401
0
                    allocation.erase(tok->varId());
2402
0
                    inconclusive.erase(tok->varId());
2403
0
                }
2404
0
            }
2405
2406
            // If a previously-allocated pointer is assigned a completely new value,
2407
            // we can't know if any subsequent free() on that pointer is valid or not.
2408
34.1k
            else if (Token::Match(tok, "%var% =")) {
2409
1.51k
                allocation.erase(tok->varId());
2410
1.51k
                inconclusive.erase(tok->varId());
2411
1.51k
            }
2412
2413
            // If a variable that was previously assigned a newly-allocated memory location is
2414
            // added or subtracted from when used to free the memory, report an error.
2415
32.6k
            else if ((Token::Match(tok, "%name% ( %any% +|-") && mSettings->library.getDeallocFuncInfo(tok)) ||
2416
32.6k
                     Token::Match(tok, "delete [ ] ( %any% +|-") ||
2417
32.6k
                     Token::Match(tok, "delete %any% +|- %any%")) {
2418
2419
0
                const int varIndex = tok->strAt(1) == "(" ? 2 :
2420
0
                                     tok->strAt(3) == "(" ? 4 : 1;
2421
0
                const int var1 = tok->tokAt(varIndex)->varId();
2422
0
                const int var2 = tok->tokAt(varIndex + 2)->varId();
2423
0
                const std::map<int, bool>::const_iterator alloc1 = inconclusive.find(var1);
2424
0
                const std::map<int, bool>::const_iterator alloc2 = inconclusive.find(var2);
2425
0
                if (alloc1 != inconclusive.end()) {
2426
0
                    invalidFreeError(tok, allocation[var1], alloc1->second);
2427
0
                } else if (alloc2 != inconclusive.end()) {
2428
0
                    invalidFreeError(tok, allocation[var2], alloc2->second);
2429
0
                }
2430
0
            }
2431
2432
            // If the previously-allocated variable is passed in to another function
2433
            // as a parameter, it might be modified, so we shouldn't report an error
2434
            // if it is later used to free memory
2435
32.6k
            else if (Token::Match(tok, "%name% (") && !mSettings->library.isFunctionConst(tok->str(), true)) {
2436
1.46k
                const Token* tok2 = Token::findmatch(tok->next(), "%var%", tok->linkAt(1));
2437
4.48k
                while (tok2 != nullptr) {
2438
3.02k
                    allocation.erase(tok->varId());
2439
3.02k
                    inconclusive.erase(tok2->varId());
2440
3.02k
                    tok2 = Token::findmatch(tok2->next(), "%var%", tok->linkAt(1));
2441
3.02k
                }
2442
1.46k
            }
2443
34.1k
        }
2444
1.73k
    }
2445
1.36k
}
2446
2447
void CheckOther::invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive)
2448
0
{
2449
0
    std::string alloc = allocation;
2450
0
    if (alloc != "new")
2451
0
        alloc += "()";
2452
0
    std::string deallocated = (alloc == "new") ? "deleted" : "freed";
2453
0
    reportError(tok, Severity::error, "invalidFree", "Mismatching address is " + deallocated + ". The address you get from " + alloc + " must be " + deallocated + " without offset.", CWE(0U), inconclusive ? Certainty::inconclusive : Certainty::normal);
2454
0
}
2455
2456
2457
//---------------------------------------------------------------------------
2458
// check for the same expression on both sides of an operator
2459
// (x == x), (x && x), (x || x)
2460
// (x.y == x.y), (x.y && x.y), (x.y || x.y)
2461
//---------------------------------------------------------------------------
2462
2463
namespace {
2464
    bool notconst(const Function* func)
2465
1.73k
    {
2466
1.73k
        return !func->isConst();
2467
1.73k
    }
2468
2469
    void getConstFunctions(const SymbolDatabase *symbolDatabase, std::list<const Function*> &constFunctions)
2470
1.36k
    {
2471
4.94k
        for (const Scope &scope : symbolDatabase->scopeList) {
2472
            // only add const functions that do not have a non-const overloaded version
2473
            // since it is pretty much impossible to tell which is being called.
2474
4.94k
            using StringFunctionMap = std::map<std::string, std::list<const Function*>>;
2475
4.94k
            StringFunctionMap functionsByName;
2476
4.94k
            for (const Function &func : scope.functionList) {
2477
1.73k
                functionsByName[func.tokenDef->str()].push_back(&func);
2478
1.73k
            }
2479
4.94k
            for (std::pair<const std::string, std::list<const Function*>>& it : functionsByName) {
2480
1.73k
                const std::list<const Function*>::const_iterator nc = std::find_if(it.second.cbegin(), it.second.cend(), notconst);
2481
1.73k
                if (nc == it.second.cend()) {
2482
                    // ok to add all of them
2483
0
                    constFunctions.splice(constFunctions.end(), it.second);
2484
0
                }
2485
1.73k
            }
2486
4.94k
        }
2487
1.36k
    }
2488
}
2489
2490
void CheckOther::checkDuplicateExpression()
2491
1.36k
{
2492
1.36k
    const bool styleEnabled = mSettings->severity.isEnabled(Severity::style);
2493
1.36k
    const bool warningEnabled = mSettings->severity.isEnabled(Severity::warning);
2494
1.36k
    if (!styleEnabled && !warningEnabled)
2495
0
        return;
2496
2497
1.36k
    logChecker("CheckOther::checkDuplicateExpression"); // style,warning
2498
2499
    // Parse all executing scopes..
2500
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
2501
2502
1.36k
    std::list<const Function*> constFunctions;
2503
1.36k
    getConstFunctions(symbolDatabase, constFunctions);
2504
2505
1.36k
    const bool cpp = mTokenizer->isCPP();
2506
2507
1.73k
    for (const Scope *scope : symbolDatabase->functionScopes) {
2508
37.6k
        for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
2509
35.9k
            if (tok->str() == "=" && Token::Match(tok->astOperand1(), "%var%")) {
2510
1.46k
                const Token * endStatement = Token::findsimplematch(tok, ";");
2511
1.46k
                if (Token::Match(endStatement, "; %type% %var% ;")) {
2512
25
                    endStatement = endStatement->tokAt(4);
2513
25
                }
2514
1.46k
                if (Token::Match(endStatement, "%var% %assign%")) {
2515
0
                    const Token * nextAssign = endStatement->tokAt(1);
2516
0
                    const Token * var1 = tok->astOperand1();
2517
0
                    const Token * var2 = nextAssign->astOperand1();
2518
0
                    if (var1 && var2 &&
2519
0
                        Token::Match(var1->previous(), ";|{|} %var%") &&
2520
0
                        Token::Match(var2->previous(), ";|{|} %var%") &&
2521
0
                        var2->valueType() && var1->valueType() &&
2522
0
                        var2->valueType()->originalTypeName == var1->valueType()->originalTypeName &&
2523
0
                        var2->valueType()->pointer == var1->valueType()->pointer &&
2524
0
                        var2->valueType()->constness == var1->valueType()->constness &&
2525
0
                        var2->varId() != var1->varId() && (
2526
0
                            tok->astOperand2()->isArithmeticalOp() ||
2527
0
                            tok->astOperand2()->str() == "." ||
2528
0
                            Token::Match(tok->astOperand2()->previous(), "%name% (")
2529
0
                            ) &&
2530
0
                        tok->next()->tokType() != Token::eType &&
2531
0
                        isSameExpression(cpp, true, tok->next(), nextAssign->next(), mSettings->library, true, false) &&
2532
0
                        isSameExpression(cpp, true, tok->astOperand2(), nextAssign->astOperand2(), mSettings->library, true, false) &&
2533
0
                        tok->astOperand2()->expressionString() == nextAssign->astOperand2()->expressionString()) {
2534
0
                        bool differentDomain = false;
2535
0
                        const Scope * varScope = var1->scope() ? var1->scope() : scope;
2536
0
                        for (const Token *assignTok = Token::findsimplematch(var2, ";"); assignTok && assignTok != varScope->bodyEnd; assignTok = assignTok->next()) {
2537
0
                            if (!Token::Match(assignTok, "%assign%|%comp%"))
2538
0
                                continue;
2539
0
                            if (!assignTok->astOperand1())
2540
0
                                continue;
2541
0
                            if (!assignTok->astOperand2())
2542
0
                                continue;
2543
2544
0
                            if (assignTok->astOperand1()->varId() != var1->varId() &&
2545
0
                                assignTok->astOperand1()->varId() != var2->varId() &&
2546
0
                                !isSameExpression(cpp,
2547
0
                                                  true,
2548
0
                                                  tok->astOperand2(),
2549
0
                                                  assignTok->astOperand1(),
2550
0
                                                  mSettings->library,
2551
0
                                                  true,
2552
0
                                                  true))
2553
0
                                continue;
2554
0
                            if (assignTok->astOperand2()->varId() != var1->varId() &&
2555
0
                                assignTok->astOperand2()->varId() != var2->varId() &&
2556
0
                                !isSameExpression(cpp,
2557
0
                                                  true,
2558
0
                                                  tok->astOperand2(),
2559
0
                                                  assignTok->astOperand2(),
2560
0
                                                  mSettings->library,
2561
0
                                                  true,
2562
0
                                                  true))
2563
0
                                continue;
2564
0
                            differentDomain = true;
2565
0
                            break;
2566
0
                        }
2567
0
                        if (!differentDomain && !isUniqueExpression(tok->astOperand2()))
2568
0
                            duplicateAssignExpressionError(var1, var2, false);
2569
0
                        else if (mSettings->certainty.isEnabled(Certainty::inconclusive))
2570
0
                            duplicateAssignExpressionError(var1, var2, true);
2571
0
                    }
2572
0
                }
2573
1.46k
            }
2574
35.9k
            auto isInsideLambdaCaptureList = [](const Token* tok) {
2575
7.69k
                const Token* parent = tok->astParent();
2576
7.69k
                while (Token::simpleMatch(parent, ","))
2577
0
                    parent = parent->astParent();
2578
7.69k
                return isLambdaCaptureList(parent);
2579
7.69k
            };
2580
35.9k
            ErrorPath errorPath;
2581
35.9k
            if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>|+=|*=|<<=|>>=") && !isInsideLambdaCaptureList(tok)) {
2582
7.69k
                if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true))
2583
144
                    continue;
2584
7.54k
                const bool pointerDereference = (tok->astOperand1() && tok->astOperand1()->isUnaryOp("*")) ||
2585
7.54k
                                                (tok->astOperand2() && tok->astOperand2()->isUnaryOp("*"));
2586
7.54k
                const bool followVar = (!isConstVarExpression(tok) || Token::Match(tok, "%comp%|%oror%|&&")) && !pointerDereference;
2587
7.54k
                if (isSameExpression(cpp,
2588
7.54k
                                     true,
2589
7.54k
                                     tok->astOperand1(),
2590
7.54k
                                     tok->astOperand2(),
2591
7.54k
                                     mSettings->library,
2592
7.54k
                                     true,
2593
7.54k
                                     followVar,
2594
7.54k
                                     &errorPath)) {
2595
548
                    if (isWithoutSideEffects(cpp, tok->astOperand1())) {
2596
548
                        const Token* loopTok = isInLoopCondition(tok);
2597
548
                        if (!loopTok || !isExpressionChanged(tok, tok, loopTok->link()->next()->link(), mSettings, cpp)) {
2598
547
                            const bool assignment = tok->str() == "=";
2599
547
                            if (assignment && warningEnabled)
2600
163
                                selfAssignmentError(tok, tok->astOperand1()->expressionString());
2601
384
                            else if (styleEnabled) {
2602
384
                                if (cpp && mSettings->standards.cpp >= Standards::CPP11 && tok->str() == "==") {
2603
78
                                    const Token* parent = tok->astParent();
2604
91
                                    while (parent && parent->astParent()) {
2605
13
                                        parent = parent->astParent();
2606
13
                                    }
2607
78
                                    if (parent && parent->previous() && parent->previous()->str() == "static_assert") {
2608
0
                                        continue;
2609
0
                                    }
2610
78
                                }
2611
384
                                duplicateExpressionError(tok->astOperand1(), tok->astOperand2(), tok, errorPath);
2612
384
                            }
2613
547
                        }
2614
548
                    }
2615
7.00k
                } else if (tok->str() == "=" && Token::simpleMatch(tok->astOperand2(), "=") &&
2616
7.00k
                           isSameExpression(cpp,
2617
56
                                            false,
2618
56
                                            tok->astOperand1(),
2619
56
                                            tok->astOperand2()->astOperand1(),
2620
56
                                            mSettings->library,
2621
56
                                            true,
2622
56
                                            false)) {
2623
15
                    if (warningEnabled && isWithoutSideEffects(cpp, tok->astOperand1())) {
2624
15
                        selfAssignmentError(tok, tok->astOperand1()->expressionString());
2625
15
                    }
2626
6.98k
                } else if (styleEnabled &&
2627
6.98k
                           isOppositeExpression(cpp,
2628
6.98k
                                                tok->astOperand1(),
2629
6.98k
                                                tok->astOperand2(),
2630
6.98k
                                                mSettings->library,
2631
6.98k
                                                false,
2632
6.98k
                                                true,
2633
6.98k
                                                &errorPath) &&
2634
6.98k
                           !Token::Match(tok, "=|-|-=|/|/=") &&
2635
6.98k
                           isWithoutSideEffects(cpp, tok->astOperand1())) {
2636
0
                    oppositeExpressionError(tok, errorPath);
2637
6.98k
                } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative
2638
6.32k
                    if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() &&
2639
6.32k
                        isSameExpression(cpp,
2640
124
                                         true,
2641
124
                                         tok->astOperand2(),
2642
124
                                         tok->astOperand1()->astOperand2(),
2643
124
                                         mSettings->library,
2644
124
                                         true,
2645
124
                                         followVar,
2646
124
                                         &errorPath) &&
2647
6.32k
                        isWithoutSideEffects(cpp, tok->astOperand2()))
2648
11
                        duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath);
2649
6.31k
                    else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library, cpp)) {
2650
3.30k
                        auto checkDuplicate = [&](const Token* exp1, const Token* exp2, const Token* ast1) {
2651
176
                            if (isSameExpression(cpp, true, exp1, exp2, mSettings->library, true, true, &errorPath) &&
2652
176
                                isWithoutSideEffects(cpp, exp1) &&
2653
176
                                isWithoutSideEffects(cpp, ast1->astOperand2()))
2654
5
                                duplicateExpressionError(exp1, exp2, tok, errorPath, /*hasMultipleExpr*/ true);
2655
176
                        };
2656
3.30k
                        const Token *ast1 = tok->astOperand1();
2657
3.40k
                        while (ast1 && tok->str() == ast1->str()) { // chain of identical operators
2658
93
                            checkDuplicate(ast1->astOperand2(), tok->astOperand2(), ast1);
2659
93
                            if (ast1->astOperand1() && ast1->astOperand1()->str() != tok->str()) // check first condition in the chain
2660
83
                                checkDuplicate(ast1->astOperand1(), tok->astOperand2(), ast1);
2661
93
                            ast1 = ast1->astOperand1();
2662
93
                        }
2663
3.30k
                    }
2664
6.32k
                }
2665
28.2k
            } else if (styleEnabled && tok->astOperand1() && tok->astOperand2() && tok->str() == ":" && tok->astParent() && tok->astParent()->str() == "?") {
2666
0
                if (!tok->astOperand1()->values().empty() && !tok->astOperand2()->values().empty() && isEqualKnownValue(tok->astOperand1(), tok->astOperand2()) &&
2667
0
                    !isVariableChanged(tok->astParent(), /*indirect*/ 0, mSettings, cpp) &&
2668
0
                    isConstStatement(tok->astOperand1(), cpp) && isConstStatement(tok->astOperand2(), cpp))
2669
0
                    duplicateValueTernaryError(tok);
2670
0
                else if (isSameExpression(cpp, true, tok->astOperand1(), tok->astOperand2(), mSettings->library, false, true, &errorPath))
2671
0
                    duplicateExpressionTernaryError(tok, errorPath);
2672
0
            }
2673
35.9k
        }
2674
1.73k
    }
2675
1.36k
}
2676
2677
void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors)
2678
0
{
2679
0
    errors.emplace_back(opTok, "");
2680
2681
0
    const std::string& op = opTok ? opTok->str() : "&&";
2682
2683
0
    reportError(errors, Severity::style, "oppositeExpression", "Opposite expression on both sides of \'" + op + "\'.\n"
2684
0
                "Finding the opposite expression on both sides of an operator is suspicious and might "
2685
0
                "indicate a cut and paste or logic error. Please examine this code carefully to "
2686
0
                "determine if it is correct.", CWE398, Certainty::normal);
2687
0
}
2688
2689
void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr)
2690
400
{
2691
400
    errors.emplace_back(opTok, "");
2692
2693
400
    const std::string& expr1 = tok1 ? tok1->expressionString() : "x";
2694
400
    const std::string& expr2 = tok2 ? tok2->expressionString() : "x";
2695
2696
400
    const std::string& op = opTok ? opTok->str() : "&&";
2697
400
    std::string msg = "Same expression " + (hasMultipleExpr ? "\'" + expr1 + "\'" + " found multiple times in chain of \'" + op + "\' operators" : "on both sides of \'" + op + "\'");
2698
400
    const char *id = "duplicateExpression";
2699
400
    if (expr1 != expr2 && (!opTok || !opTok->isArithmeticalOp())) {
2700
0
        id = "knownConditionTrueFalse";
2701
0
        std::string exprMsg = "The comparison \'" + expr1 + " " + op +  " " + expr2 + "\' is always ";
2702
0
        if (Token::Match(opTok, "==|>=|<="))
2703
0
            msg = exprMsg + "true";
2704
0
        else if (Token::Match(opTok, "!=|>|<"))
2705
0
            msg = exprMsg + "false";
2706
0
        if (!Token::Match(tok1, "%num%|NULL|nullptr") && !Token::Match(tok2, "%num%|NULL|nullptr"))
2707
0
            msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value";
2708
0
    }
2709
2710
400
    reportError(errors, Severity::style, id, msg +
2711
400
                (std::string(".\nFinding the same expression ") + (hasMultipleExpr ? "more than once in a condition" : "on both sides of an operator")) +
2712
400
                " is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to "
2713
400
                "determine if it is correct.", CWE398, Certainty::normal);
2714
400
}
2715
2716
void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive)
2717
0
{
2718
0
    const std::list<const Token *> toks = { tok2, tok1 };
2719
2720
0
    const std::string& var1 = tok1 ? tok1->str() : "x";
2721
0
    const std::string& var2 = tok2 ? tok2->str() : "x";
2722
2723
0
    reportError(toks, Severity::style, "duplicateAssignExpression",
2724
0
                "Same expression used in consecutive assignments of '" + var1 + "' and '" + var2 + "'.\n"
2725
0
                "Finding variables '" + var1 + "' and '" + var2 + "' that are assigned the same expression "
2726
0
                "is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to "
2727
0
                "determine if it is correct.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
2728
0
}
2729
2730
void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors)
2731
0
{
2732
0
    errors.emplace_back(tok, "");
2733
0
    reportError(errors, Severity::style, "duplicateExpressionTernary", "Same expression in both branches of ternary operator.\n"
2734
0
                "Finding the same expression in both branches of ternary operator is suspicious as "
2735
0
                "the same code is executed regardless of the condition.", CWE398, Certainty::normal);
2736
0
}
2737
2738
void CheckOther::duplicateValueTernaryError(const Token *tok)
2739
0
{
2740
0
    reportError(tok, Severity::style, "duplicateValueTernary", "Same value in both branches of ternary operator.\n"
2741
0
                "Finding the same value in both branches of ternary operator is suspicious as "
2742
0
                "the same code is executed regardless of the condition.", CWE398, Certainty::normal);
2743
0
}
2744
2745
void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname)
2746
178
{
2747
178
    reportError(tok, Severity::warning,
2748
178
                "selfAssignment",
2749
178
                "$symbol:" + varname + "\n"
2750
178
                "Redundant assignment of '$symbol' to itself.", CWE398, Certainty::normal);
2751
178
}
2752
2753
//-----------------------------------------------------------------------------
2754
// Check is a comparison of two variables leads to condition, which is
2755
// always true or false.
2756
// For instance: int a = 1; if(isless(a,a)){...}
2757
// In this case isless(a,a) always evaluates to false.
2758
//
2759
// Reference:
2760
// - http://www.cplusplus.com/reference/cmath/
2761
//-----------------------------------------------------------------------------
2762
void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse()
2763
1.36k
{
2764
1.36k
    if (!mSettings->severity.isEnabled(Severity::warning))
2765
0
        return;
2766
2767
1.36k
    logChecker("CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse"); // warning
2768
2769
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
2770
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
2771
35.9k
        for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
2772
34.1k
            if (tok->isName() && Token::Match(tok, "isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% )")) {
2773
0
                const int varidLeft = tok->tokAt(2)->varId();// get the left varid
2774
0
                const int varidRight = tok->tokAt(4)->varId();// get the right varid
2775
                // compare varids: if they are not zero but equal
2776
                // --> the comparison function is called with the same variables
2777
0
                if (varidLeft == varidRight) {
2778
0
                    const std::string& functionName = tok->str(); // store function name
2779
0
                    const std::string& varNameLeft = tok->strAt(2); // get the left variable name
2780
0
                    if (functionName == "isgreater" || functionName == "isless" || functionName == "islessgreater") {
2781
                        // e.g.: isgreater(x,x) --> (x)>(x) --> false
2782
0
                        checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, false);
2783
0
                    } else { // functionName == "isgreaterequal" || functionName == "islessequal"
2784
                        // e.g.: isgreaterequal(x,x) --> (x)>=(x) --> true
2785
0
                        checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, true);
2786
0
                    }
2787
0
                }
2788
0
            }
2789
34.1k
        }
2790
1.73k
    }
2791
1.36k
}
2792
void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result)
2793
0
{
2794
0
    const std::string strResult = bool_to_string(result);
2795
0
    const struct CWE cweResult = result ? CWE571 : CWE570;
2796
2797
0
    reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse",
2798
0
                "$symbol:" + functionName + "\n"
2799
0
                "Comparison of two identical variables with $symbol(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n"
2800
0
                "The function $symbol is designed to compare two variables. Calling this function with one variable (" + varName + ") "
2801
0
                "for both parameters leads to a statement which is always " + strResult + ".", cweResult, Certainty::normal);
2802
0
}
2803
2804
//---------------------------------------------------------------------------
2805
// Check testing sign of unsigned variables and pointers.
2806
//---------------------------------------------------------------------------
2807
void CheckOther::checkSignOfUnsignedVariable()
2808
1.36k
{
2809
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
2810
0
        return;
2811
2812
1.36k
    logChecker("CheckOther::checkSignOfUnsignedVariable"); // style
2813
2814
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
2815
2816
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
2817
        // check all the code in the function
2818
35.9k
        for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
2819
34.1k
            const ValueFlow::Value *zeroValue = nullptr;
2820
34.1k
            const Token *nonZeroExpr = nullptr;
2821
34.1k
            if (comparisonNonZeroExpressionLessThanZero(tok, &zeroValue, &nonZeroExpr)) {
2822
0
                const ValueType* vt = nonZeroExpr->valueType();
2823
0
                if (vt->pointer)
2824
0
                    pointerLessThanZeroError(tok, zeroValue);
2825
0
                else
2826
0
                    unsignedLessThanZeroError(tok, zeroValue, nonZeroExpr->expressionString());
2827
34.1k
            } else if (testIfNonZeroExpressionIsPositive(tok, &zeroValue, &nonZeroExpr)) {
2828
0
                const ValueType* vt = nonZeroExpr->valueType();
2829
0
                if (vt->pointer)
2830
0
                    pointerPositiveError(tok, zeroValue);
2831
0
                else
2832
0
                    unsignedPositiveError(tok, zeroValue, nonZeroExpr->expressionString());
2833
0
            }
2834
34.1k
        }
2835
1.73k
    }
2836
1.36k
}
2837
2838
bool CheckOther::comparisonNonZeroExpressionLessThanZero(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr)
2839
34.2k
{
2840
34.2k
    if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2())
2841
32.2k
        return false;
2842
2843
2.05k
    const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0);
2844
2.05k
    const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0);
2845
2846
2.05k
    if (Token::Match(tok, "<|<=") && v2 && v2->isKnown()) {
2847
14
        *zeroValue = v2;
2848
14
        *nonZeroExpr = tok->astOperand1();
2849
2.03k
    } else if (Token::Match(tok, ">|>=") && v1 && v1->isKnown()) {
2850
22
        *zeroValue = v1;
2851
22
        *nonZeroExpr = tok->astOperand2();
2852
2.01k
    } else {
2853
2.01k
        return false;
2854
2.01k
    }
2855
2856
36
    const ValueType* vt = (*nonZeroExpr)->valueType();
2857
36
    return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED);
2858
2.05k
}
2859
2860
bool CheckOther::testIfNonZeroExpressionIsPositive(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr)
2861
34.2k
{
2862
34.2k
    if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2())
2863
32.2k
        return false;
2864
2865
2.05k
    const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0);
2866
2.05k
    const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0);
2867
2868
2.05k
    if (Token::simpleMatch(tok, ">=") && v2 && v2->isKnown()) {
2869
1
        *zeroValue = v2;
2870
1
        *nonZeroExpr = tok->astOperand1();
2871
2.04k
    } else if (Token::simpleMatch(tok, "<=") && v1 && v1->isKnown()) {
2872
7
        *zeroValue = v1;
2873
7
        *nonZeroExpr = tok->astOperand2();
2874
2.04k
    } else {
2875
2.04k
        return false;
2876
2.04k
    }
2877
2878
8
    const ValueType* vt = (*nonZeroExpr)->valueType();
2879
8
    return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED);
2880
2.05k
}
2881
2882
void CheckOther::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value * v, const std::string &varname)
2883
0
{
2884
0
    reportError(getErrorPath(tok, v, "Unsigned less than zero"), Severity::style, "unsignedLessThanZero",
2885
0
                "$symbol:" + varname + "\n"
2886
0
                "Checking if unsigned expression '$symbol' is less than zero.\n"
2887
0
                "The unsigned expression '$symbol' will never be negative so it "
2888
0
                "is either pointless or an error to check if it is.", CWE570, Certainty::normal);
2889
0
}
2890
2891
void CheckOther::pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v)
2892
0
{
2893
0
    reportError(getErrorPath(tok, v, "Pointer less than zero"), Severity::style, "pointerLessThanZero",
2894
0
                "A pointer can not be negative so it is either pointless or an error to check if it is.", CWE570, Certainty::normal);
2895
0
}
2896
2897
void CheckOther::unsignedPositiveError(const Token *tok, const ValueFlow::Value * v, const std::string &varname)
2898
0
{
2899
0
    reportError(getErrorPath(tok, v, "Unsigned positive"), Severity::style, "unsignedPositive",
2900
0
                "$symbol:" + varname + "\n"
2901
0
                "Unsigned expression '$symbol' can't be negative so it is unnecessary to test it.", CWE570, Certainty::normal);
2902
0
}
2903
2904
void CheckOther::pointerPositiveError(const Token *tok, const ValueFlow::Value * v)
2905
0
{
2906
0
    reportError(getErrorPath(tok, v, "Pointer positive"), Severity::style, "pointerPositive",
2907
0
                "A pointer can not be negative so it is either pointless or an error to check if it is not.", CWE570, Certainty::normal);
2908
0
}
2909
2910
/* check if a constructor in given class scope takes a reference */
2911
static bool constructorTakesReference(const Scope * const classScope)
2912
0
{
2913
0
    return std::any_of(classScope->functionList.begin(), classScope->functionList.end(), [&](const Function& constructor) {
2914
0
        if (constructor.isConstructor()) {
2915
0
            for (int argnr = 0U; argnr < constructor.argCount(); argnr++) {
2916
0
                const Variable * const argVar = constructor.getArgumentVar(argnr);
2917
0
                if (argVar && argVar->isReference()) {
2918
0
                    return true;
2919
0
                }
2920
0
            }
2921
0
        }
2922
0
        return false;
2923
0
    });
2924
0
}
2925
2926
//---------------------------------------------------------------------------
2927
// This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &".
2928
// In most scenarios, "const A & a = getA()" will be more efficient.
2929
//---------------------------------------------------------------------------
2930
void CheckOther::checkRedundantCopy()
2931
1.36k
{
2932
1.36k
    if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC() || !mSettings->certainty.isEnabled(Certainty::inconclusive))
2933
0
        return;
2934
2935
1.36k
    logChecker("CheckOther::checkRedundantCopy"); // c++,performance,inconclusive
2936
2937
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
2938
2939
8.16k
    for (const Variable* var : symbolDatabase->variableList()) {
2940
8.16k
        if (!var || var->isReference() || (!var->isConst() && !canBeConst(var, mSettings)) || var->isPointer() || (!var->type() && !var->isStlType())) // bailout if var is of standard type, if it is a pointer or non-const
2941
8.16k
            continue;
2942
2943
0
        const Token* startTok = var->nameToken();
2944
0
        if (startTok->strAt(1) == "=") // %type% %name% = ... ;
2945
0
            ;
2946
0
        else if (Token::Match(startTok->next(), "(|{") && var->isClass() && var->typeScope()) {
2947
            // Object is instantiated. Warn if constructor takes arguments by value.
2948
0
            if (constructorTakesReference(var->typeScope()))
2949
0
                continue;
2950
0
        } else if (Token::simpleMatch(startTok->next(), ";") && startTok->next()->isSplittedVarDeclEq()) {
2951
0
            startTok = startTok->tokAt(2);
2952
0
        } else
2953
0
            continue;
2954
2955
0
        const Token* tok = startTok->next()->astOperand2();
2956
0
        if (!tok)
2957
0
            continue;
2958
0
        if (!Token::Match(tok->previous(), "%name% ("))
2959
0
            continue;
2960
0
        if (!Token::Match(tok->link(), ") )| ;")) // bailout for usage like "const A a = getA()+3"
2961
0
            continue;
2962
2963
0
        const Token* dot = tok->astOperand1();
2964
0
        if (Token::simpleMatch(dot, ".")) {
2965
0
            if (dot->astOperand1() && isVariableChanged(dot->astOperand1()->variable(), mSettings, mTokenizer->isCPP()))
2966
0
                continue;
2967
0
            if (isTemporary(/*cpp*/ true, dot, &mSettings->library, /*unknown*/ true))
2968
0
                continue;
2969
0
        }
2970
0
        if (exprDependsOnThis(tok->previous()))
2971
0
            continue;
2972
2973
0
        const Function* func = tok->previous()->function();
2974
0
        if (func && func->tokenDef->strAt(-1) == "&") {
2975
0
            const Scope* fScope = func->functionScope;
2976
0
            if (fScope && fScope->bodyEnd && Token::Match(fScope->bodyEnd->tokAt(-3), "return %var% ;")) {
2977
0
                const Token* varTok = fScope->bodyEnd->tokAt(-2);
2978
0
                if (varTok->variable() && !varTok->variable()->isGlobal())
2979
0
                    redundantCopyError(startTok, startTok->str());
2980
0
            }
2981
0
        }
2982
0
    }
2983
1.36k
}
2984
2985
void CheckOther::redundantCopyError(const Token *tok,const std::string& varname)
2986
0
{
2987
0
    reportError(tok, Severity::performance, "redundantCopyLocalConst",
2988
0
                "$symbol:" + varname + "\n"
2989
0
                "Use const reference for '$symbol' to avoid unnecessary data copying.\n"
2990
0
                "The const variable '$symbol' is assigned a copy of the data. You can avoid "
2991
0
                "the unnecessary data copying by converting '$symbol' to const reference.",
2992
0
                CWE398,
2993
0
                Certainty::inconclusive); // since #5618 that check became inconclusive
2994
0
}
2995
2996
//---------------------------------------------------------------------------
2997
// Checking for shift by negative values
2998
//---------------------------------------------------------------------------
2999
3000
static bool isNegative(const Token *tok, const Settings *settings)
3001
0
{
3002
0
    return tok->valueType() && tok->valueType()->sign == ValueType::SIGNED && tok->getValueLE(-1LL, settings);
3003
0
}
3004
3005
void CheckOther::checkNegativeBitwiseShift()
3006
1.36k
{
3007
1.36k
    const bool portability = mSettings->severity.isEnabled(Severity::portability);
3008
3009
1.36k
    logChecker("CheckOther::checkNegativeBitwiseShift");
3010
3011
93.5k
    for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) {
3012
92.2k
        if (!tok->astOperand1() || !tok->astOperand2())
3013
78.0k
            continue;
3014
3015
14.2k
        if (!Token::Match(tok, "<<|>>|<<=|>>="))
3016
14.2k
            continue;
3017
3018
        // don't warn if lhs is a class. this is an overloaded operator then
3019
0
        if (mTokenizer->isCPP()) {
3020
0
            const ValueType * lhsType = tok->astOperand1()->valueType();
3021
0
            if (!lhsType || !lhsType->isIntegral())
3022
0
                continue;
3023
0
        }
3024
3025
        // bailout if operation is protected by ?:
3026
0
        bool ternary = false;
3027
0
        for (const Token *parent = tok; parent; parent = parent->astParent()) {
3028
0
            if (Token::Match(parent, "?|:")) {
3029
0
                ternary = true;
3030
0
                break;
3031
0
            }
3032
0
        }
3033
0
        if (ternary)
3034
0
            continue;
3035
3036
        // Get negative rhs value. preferably a value which doesn't have 'condition'.
3037
0
        if (portability && isNegative(tok->astOperand1(), mSettings))
3038
0
            negativeBitwiseShiftError(tok, 1);
3039
0
        else if (isNegative(tok->astOperand2(), mSettings))
3040
0
            negativeBitwiseShiftError(tok, 2);
3041
0
    }
3042
1.36k
}
3043
3044
3045
void CheckOther::negativeBitwiseShiftError(const Token *tok, int op)
3046
0
{
3047
0
    if (op == 1)
3048
        // LHS - this is used by intention in various software, if it
3049
        // is used often in a project and works as expected then this is
3050
        // a portability issue
3051
0
        reportError(tok, Severity::portability, "shiftNegativeLHS", "Shifting a negative value is technically undefined behaviour", CWE758, Certainty::normal);
3052
0
    else // RHS
3053
0
        reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value is undefined behaviour", CWE758, Certainty::normal);
3054
0
}
3055
3056
//---------------------------------------------------------------------------
3057
// Check for incompletely filled buffers.
3058
//---------------------------------------------------------------------------
3059
void CheckOther::checkIncompleteArrayFill()
3060
1.36k
{
3061
1.36k
    if (!mSettings->certainty.isEnabled(Certainty::inconclusive))
3062
0
        return;
3063
1.36k
    const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
3064
1.36k
    const bool printPortability = mSettings->severity.isEnabled(Severity::portability);
3065
1.36k
    if (!printPortability && !printWarning)
3066
0
        return;
3067
3068
1.36k
    logChecker("CheckOther::checkIncompleteArrayFill"); // warning,portability,inconclusive
3069
3070
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3071
3072
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
3073
35.9k
        for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
3074
34.1k
            if (Token::Match(tok, "memset|memcpy|memmove (") && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )")) {
3075
0
                const Token* tok2 = tok->tokAt(2);
3076
0
                if (tok2->str() == "::")
3077
0
                    tok2 = tok2->next();
3078
0
                while (Token::Match(tok2, "%name% ::|."))
3079
0
                    tok2 = tok2->tokAt(2);
3080
0
                if (!Token::Match(tok2, "%var% ,"))
3081
0
                    continue;
3082
3083
0
                const Variable *var = tok2->variable();
3084
0
                if (!var || !var->isArray() || var->dimensions().empty() || !var->dimension(0))
3085
0
                    continue;
3086
3087
0
                if (MathLib::toLongNumber(tok->linkAt(1)->strAt(-1)) == var->dimension(0)) {
3088
0
                    int size = mTokenizer->sizeOfType(var->typeStartToken());
3089
0
                    if (size == 0 && var->valueType()->pointer)
3090
0
                        size = mSettings->platform.sizeof_pointer;
3091
0
                    else if (size == 0 && var->type())
3092
0
                        size = estimateSize(var->type(), mSettings, symbolDatabase);
3093
0
                    const Token* tok3 = tok->next()->astOperand2()->astOperand1()->astOperand1();
3094
0
                    if ((size != 1 && size != 100 && size != 0) || var->isPointer()) {
3095
0
                        if (printWarning)
3096
0
                            incompleteArrayFillError(tok, tok3->expressionString(), tok->str(), false);
3097
0
                    } else if (var->valueType()->type == ValueType::Type::BOOL && printPortability) // sizeof(bool) is not 1 on all platforms
3098
0
                        incompleteArrayFillError(tok, tok3->expressionString(), tok->str(), true);
3099
0
                }
3100
0
            }
3101
34.1k
        }
3102
1.73k
    }
3103
1.36k
}
3104
3105
void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean)
3106
0
{
3107
0
    if (boolean)
3108
0
        reportError(tok, Severity::portability, "incompleteArrayFill",
3109
0
                    "$symbol:" + buffer + "\n"
3110
0
                    "$symbol:" + function + "\n"
3111
0
                    "Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n"
3112
0
                    "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive);
3113
0
    else
3114
0
        reportError(tok, Severity::warning, "incompleteArrayFill",
3115
0
                    "$symbol:" + buffer + "\n"
3116
0
                    "$symbol:" + function + "\n"
3117
0
                    "Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n"
3118
0
                    "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive);
3119
0
}
3120
3121
//---------------------------------------------------------------------------
3122
// Detect NULL being passed to variadic function.
3123
//---------------------------------------------------------------------------
3124
3125
void CheckOther::checkVarFuncNullUB()
3126
1.36k
{
3127
1.36k
    if (!mSettings->severity.isEnabled(Severity::portability))
3128
0
        return;
3129
3130
1.36k
    logChecker("CheckOther::checkVarFuncNullUB"); // portability
3131
3132
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3133
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
3134
37.6k
        for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
3135
            // Is NULL passed to a function?
3136
35.9k
            if (Token::Match(tok,"[(,] NULL [,)]")) {
3137
                // Locate function name in this function call.
3138
0
                const Token *ftok = tok;
3139
0
                int argnr = 1;
3140
0
                while (ftok && ftok->str() != "(") {
3141
0
                    if (ftok->str() == ")")
3142
0
                        ftok = ftok->link();
3143
0
                    else if (ftok->str() == ",")
3144
0
                        ++argnr;
3145
0
                    ftok = ftok->previous();
3146
0
                }
3147
0
                ftok = ftok ? ftok->previous() : nullptr;
3148
0
                if (ftok && ftok->isName()) {
3149
                    // If this is a variadic function then report error
3150
0
                    const Function *f = ftok->function();
3151
0
                    if (f && f->argCount() <= argnr) {
3152
0
                        const Token *tok2 = f->argDef;
3153
0
                        tok2 = tok2 ? tok2->link() : nullptr; // goto ')'
3154
0
                        if (tok2 && Token::simpleMatch(tok2->tokAt(-1), "..."))
3155
0
                            varFuncNullUBError(tok);
3156
0
                    }
3157
0
                }
3158
0
            }
3159
35.9k
        }
3160
1.73k
    }
3161
1.36k
}
3162
3163
void CheckOther::varFuncNullUBError(const Token *tok)
3164
0
{
3165
0
    reportError(tok,
3166
0
                Severity::portability,
3167
0
                "varFuncNullUB",
3168
0
                "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n"
3169
0
                "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n"
3170
0
                "The C99 standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined.\n"
3171
0
                "The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL.\n"
3172
0
                "In practice on common architectures, this will cause real crashes if sizeof(int) != sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int.\n"
3173
0
                "To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \"ERROR\", the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \"ERROR\" output go away.\n"
3174
0
                "#include <stdarg.h>\n"
3175
0
                "#include <stdio.h>\n"
3176
0
                "\n"
3177
0
                "void f(char *s, ...) {\n"
3178
0
                "    va_list ap;\n"
3179
0
                "    va_start(ap,s);\n"
3180
0
                "    for (;;) {\n"
3181
0
                "        char *p = va_arg(ap,char*);\n"
3182
0
                "        printf(\"%018p, %s\\n\", p, (long)p & 255 ? p : \"\");\n"
3183
0
                "        if(!p) break;\n"
3184
0
                "    }\n"
3185
0
                "    va_end(ap);\n"
3186
0
                "}\n"
3187
0
                "\n"
3188
0
                "void g() {\n"
3189
0
                "    char *s2 = \"x\";\n"
3190
0
                "    char *s3 = \"ERROR\";\n"
3191
0
                "\n"
3192
0
                "    // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64\n"
3193
0
                "    f(\"first\", s2, s2, s2, s2, s2, 0, s3, (char*)0);\n"
3194
0
                "}\n"
3195
0
                "\n"
3196
0
                "void h() {\n"
3197
0
                "    int i;\n"
3198
0
                "    volatile unsigned char a[1000];\n"
3199
0
                "    for (i = 0; i<sizeof(a); i++)\n"
3200
0
                "        a[i] = -1;\n"
3201
0
                "}\n"
3202
0
                "\n"
3203
0
                "int main() {\n"
3204
0
                "    h();\n"
3205
0
                "    g();\n"
3206
0
                "    return 0;\n"
3207
0
                "}", CWE475, Certainty::normal);
3208
0
}
3209
3210
void CheckOther::checkRedundantPointerOp()
3211
1.36k
{
3212
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
3213
0
        return;
3214
3215
1.36k
    logChecker("CheckOther::checkRedundantPointerOp"); // style
3216
3217
93.5k
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
3218
92.2k
        if (tok->isExpandedMacro() && tok->str() == "(")
3219
0
            tok = tok->link();
3220
3221
92.2k
        bool addressOfDeref{};
3222
92.2k
        if (tok->isUnaryOp("&") && tok->astOperand1()->isUnaryOp("*"))
3223
0
            addressOfDeref = true;
3224
92.2k
        else if (tok->isUnaryOp("*") && tok->astOperand1()->isUnaryOp("&"))
3225
0
            addressOfDeref = false;
3226
92.2k
        else
3227
92.2k
            continue;
3228
3229
        // variable
3230
0
        const Token *varTok = tok->astOperand1()->astOperand1();
3231
0
        if (!varTok || varTok->isExpandedMacro())
3232
0
            continue;
3233
3234
0
        if (!addressOfDeref) { // dereference of address
3235
0
            if (tok->isExpandedMacro())
3236
0
                continue;
3237
0
            if (varTok->valueType() && varTok->valueType()->pointer && varTok->valueType()->reference == Reference::LValue)
3238
0
                continue;
3239
0
        }
3240
3241
0
        const Variable *var = varTok->variable();
3242
0
        if (!var || (addressOfDeref && !var->isPointer()))
3243
0
            continue;
3244
3245
0
        redundantPointerOpError(tok, var->name(), false, addressOfDeref);
3246
0
    }
3247
1.36k
}
3248
3249
void CheckOther::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive, bool addressOfDeref)
3250
0
{
3251
0
    std::string msg = "$symbol:" + varname + "\nRedundant pointer operation on '$symbol' - it's already a ";
3252
0
    msg += addressOfDeref ? "pointer." : "variable.";
3253
0
    reportError(tok, Severity::style, "redundantPointerOp", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
3254
0
}
3255
3256
void CheckOther::checkInterlockedDecrement()
3257
1.36k
{
3258
1.36k
    if (!mSettings->platform.isWindows()) {
3259
1.36k
        return;
3260
1.36k
    }
3261
3262
0
    logChecker("CheckOther::checkInterlockedDecrement"); // windows-platform
3263
3264
0
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
3265
0
        if (tok->isName() && Token::Match(tok, "InterlockedDecrement ( & %name% ) ; if ( %name%|!|0")) {
3266
0
            const Token* interlockedVarTok = tok->tokAt(3);
3267
0
            const Token* checkStartTok =  interlockedVarTok->tokAt(5);
3268
0
            if ((Token::Match(checkStartTok, "0 %comp% %name% )") && checkStartTok->strAt(2) == interlockedVarTok->str()) ||
3269
0
                (Token::Match(checkStartTok, "! %name% )") && checkStartTok->strAt(1) == interlockedVarTok->str()) ||
3270
0
                (Token::Match(checkStartTok, "%name% )") && checkStartTok->str() == interlockedVarTok->str()) ||
3271
0
                (Token::Match(checkStartTok, "%name% %comp% 0 )") && checkStartTok->str() == interlockedVarTok->str())) {
3272
0
                raceAfterInterlockedDecrementError(checkStartTok);
3273
0
            }
3274
0
        } else if (Token::Match(tok, "if ( ::| InterlockedDecrement ( & %name%")) {
3275
0
            const Token* condEnd = tok->next()->link();
3276
0
            const Token* funcTok = tok->tokAt(2);
3277
0
            const Token* firstAccessTok = funcTok->str() == "::" ? funcTok->tokAt(4) : funcTok->tokAt(3);
3278
0
            if (condEnd && condEnd->next() && condEnd->next()->link()) {
3279
0
                const Token* ifEndTok = condEnd->next()->link();
3280
0
                if (Token::Match(ifEndTok, "} return %name%")) {
3281
0
                    const Token* secondAccessTok = ifEndTok->tokAt(2);
3282
0
                    if (secondAccessTok->str() == firstAccessTok->str()) {
3283
0
                        raceAfterInterlockedDecrementError(secondAccessTok);
3284
0
                    }
3285
0
                } else if (Token::Match(ifEndTok, "} else { return %name%")) {
3286
0
                    const Token* secondAccessTok = ifEndTok->tokAt(4);
3287
0
                    if (secondAccessTok->str() == firstAccessTok->str()) {
3288
0
                        raceAfterInterlockedDecrementError(secondAccessTok);
3289
0
                    }
3290
0
                }
3291
0
            }
3292
0
        }
3293
0
    }
3294
0
}
3295
3296
void CheckOther::raceAfterInterlockedDecrementError(const Token* tok)
3297
0
{
3298
0
    reportError(tok, Severity::error, "raceAfterInterlockedDecrement",
3299
0
                "Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.", CWE362, Certainty::normal);
3300
0
}
3301
3302
void CheckOther::checkUnusedLabel()
3303
1.36k
{
3304
1.36k
    if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->severity.isEnabled(Severity::warning))
3305
0
        return;
3306
3307
1.36k
    logChecker("CheckOther::checkUnusedLabel"); // style,warning
3308
3309
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3310
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
3311
1.73k
        const bool hasIfdef = mTokenizer->hasIfdef(scope->bodyStart, scope->bodyEnd);
3312
37.6k
        for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
3313
35.9k
            if (!tok->scope()->isExecutable())
3314
0
                tok = tok->scope()->bodyEnd;
3315
3316
35.9k
            if (Token::Match(tok, "{|}|; %name% :") && !tok->tokAt(1)->isKeyword()) {
3317
0
                const std::string tmp("goto " + tok->strAt(1));
3318
0
                if (!Token::findsimplematch(scope->bodyStart->next(), tmp.c_str(), tmp.size(), scope->bodyEnd->previous()))
3319
0
                    unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch, hasIfdef);
3320
0
            }
3321
35.9k
        }
3322
1.73k
    }
3323
1.36k
}
3324
3325
void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef)
3326
0
{
3327
0
    if (tok && !mSettings->severity.isEnabled(inSwitch ? Severity::warning : Severity::style))
3328
0
        return;
3329
3330
0
    std::string id = "unusedLabel";
3331
0
    if (inSwitch)
3332
0
        id += "Switch";
3333
0
    if (hasIfdef)
3334
0
        id += "Configuration";
3335
3336
0
    std::string msg = "$symbol:" + (tok ? tok->str() : emptyString) + "\nLabel '$symbol' is not used.";
3337
0
    if (hasIfdef)
3338
0
        msg += " There is #if in function body so the label might be used in code that is removed by the preprocessor.";
3339
0
    if (inSwitch)
3340
0
        msg += " Should this be a 'case' of the enclosing switch()?";
3341
3342
0
    reportError(tok,
3343
0
                inSwitch ? Severity::warning : Severity::style,
3344
0
                id,
3345
0
                msg,
3346
0
                CWE398,
3347
0
                Certainty::normal);
3348
0
}
3349
3350
3351
void CheckOther::checkEvaluationOrder()
3352
1.36k
{
3353
    // This checker is not written according to C++11 sequencing rules
3354
1.36k
    if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11)
3355
1.36k
        return;
3356
3357
0
    logChecker("CheckOther::checkEvaluationOrder"); // C/C++03
3358
3359
0
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3360
0
    for (const Scope * functionScope : symbolDatabase->functionScopes) {
3361
0
        for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
3362
0
            if (tok->tokType() != Token::eIncDecOp && !tok->isAssignmentOp())
3363
0
                continue;
3364
0
            if (!tok->astOperand1())
3365
0
                continue;
3366
0
            for (const Token *tok2 = tok;; tok2 = tok2->astParent()) {
3367
                // If ast parent is a sequence point then break
3368
0
                const Token * const parent = tok2->astParent();
3369
0
                if (!parent)
3370
0
                    break;
3371
0
                if (Token::Match(parent, "%oror%|&&|?|:|;"))
3372
0
                    break;
3373
0
                if (parent->str() == ",") {
3374
0
                    const Token *par = parent;
3375
0
                    while (Token::simpleMatch(par,","))
3376
0
                        par = par->astParent();
3377
                    // not function or in a while clause => break
3378
0
                    if (!(par && par->str() == "(" && par->astOperand2() && par->strAt(-1) != "while"))
3379
0
                        break;
3380
                    // control flow (if|while|etc) => break
3381
0
                    if (Token::simpleMatch(par->link(),") {"))
3382
0
                        break;
3383
                    // sequence point in function argument: dostuff((1,2),3) => break
3384
0
                    par = par->next();
3385
0
                    while (par && (par->previous() != parent))
3386
0
                        par = par->nextArgument();
3387
0
                    if (!par)
3388
0
                        break;
3389
0
                }
3390
0
                if (parent->str() == "(" && parent->astOperand2())
3391
0
                    break;
3392
3393
                // self assignment..
3394
0
                if (tok2 == tok &&
3395
0
                    tok->str() == "=" &&
3396
0
                    parent->str() == "=" &&
3397
0
                    isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) {
3398
0
                    if (mSettings->severity.isEnabled(Severity::warning) &&
3399
0
                        isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false))
3400
0
                        selfAssignmentError(parent, tok->astOperand1()->expressionString());
3401
0
                    break;
3402
0
                }
3403
3404
                // Is expression used?
3405
0
                bool foundError = false;
3406
0
                visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(),
3407
0
                              [&](const Token *tok3) {
3408
0
                    if (tok3->str() == "&" && !tok3->astOperand2())
3409
0
                        return ChildrenToVisit::none; // don't handle address-of for now
3410
0
                    if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof"))
3411
0
                        return ChildrenToVisit::none; // don't care about sizeof usage
3412
0
                    if (isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok3, mSettings->library, true, false))
3413
0
                        foundError = true;
3414
0
                    return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2;
3415
0
                });
3416
3417
0
                if (foundError) {
3418
0
                    unknownEvaluationOrder(parent);
3419
0
                    break;
3420
0
                }
3421
0
            }
3422
0
        }
3423
0
    }
3424
0
}
3425
3426
void CheckOther::unknownEvaluationOrder(const Token* tok)
3427
0
{
3428
0
    reportError(tok, Severity::error, "unknownEvaluationOrder",
3429
0
                "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, Certainty::normal);
3430
0
}
3431
3432
void CheckOther::checkAccessOfMovedVariable()
3433
1.36k
{
3434
1.36k
    if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11 || !mSettings->severity.isEnabled(Severity::warning))
3435
0
        return;
3436
1.36k
    logChecker("CheckOther::checkAccessOfMovedVariable"); // c++11,warning
3437
1.36k
    const bool reportInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
3438
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3439
1.73k
    for (const Scope * scope : symbolDatabase->functionScopes) {
3440
1.73k
        const Token * scopeStart = scope->bodyStart;
3441
1.73k
        if (scope->function) {
3442
1.73k
            const Token * memberInitializationStart = scope->function->constructorMemberInitialization();
3443
1.73k
            if (memberInitializationStart)
3444
0
                scopeStart = memberInitializationStart;
3445
1.73k
        }
3446
35.9k
        for (const Token* tok = scopeStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
3447
34.1k
            if (!tok->astParent())
3448
15.2k
                continue;
3449
18.9k
            const ValueFlow::Value * movedValue = tok->getMovedValue();
3450
18.9k
            if (!movedValue || movedValue->moveKind == ValueFlow::Value::MoveKind::NonMovedVariable)
3451
18.9k
                continue;
3452
0
            if (movedValue->isInconclusive() && !reportInconclusive)
3453
0
                continue;
3454
3455
0
            bool inconclusive = false;
3456
0
            bool accessOfMoved = false;
3457
0
            if (tok->strAt(1) == ".") {
3458
0
                if (tok->next()->originalName() == "->")
3459
0
                    accessOfMoved = true;
3460
0
                else
3461
0
                    inconclusive = true;
3462
0
            } else {
3463
0
                const ExprUsage usage = getExprUsage(tok, 0, mSettings, mTokenizer->isCPP());
3464
0
                if (usage == ExprUsage::Used)
3465
0
                    accessOfMoved = true;
3466
0
                if (usage == ExprUsage::PassedByReference)
3467
0
                    accessOfMoved = !isVariableChangedByFunctionCall(tok, 0, mSettings, &inconclusive);
3468
0
                else if (usage == ExprUsage::Inconclusive)
3469
0
                    inconclusive = true;
3470
0
            }
3471
0
            if (accessOfMoved || (inconclusive && reportInconclusive))
3472
0
                accessMovedError(tok, tok->str(), movedValue, inconclusive || movedValue->isInconclusive());
3473
0
        }
3474
1.73k
    }
3475
1.36k
}
3476
3477
void CheckOther::accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive)
3478
0
{
3479
0
    if (!tok) {
3480
0
        reportError(tok, Severity::warning, "accessMoved", "Access of moved variable 'v'.", CWE672, Certainty::normal);
3481
0
        reportError(tok, Severity::warning, "accessForwarded", "Access of forwarded variable 'v'.", CWE672, Certainty::normal);
3482
0
        return;
3483
0
    }
3484
3485
0
    const char * errorId = nullptr;
3486
0
    std::string kindString;
3487
0
    switch (value->moveKind) {
3488
0
    case ValueFlow::Value::MoveKind::MovedVariable:
3489
0
        errorId = "accessMoved";
3490
0
        kindString = "moved";
3491
0
        break;
3492
0
    case ValueFlow::Value::MoveKind::ForwardedVariable:
3493
0
        errorId = "accessForwarded";
3494
0
        kindString = "forwarded";
3495
0
        break;
3496
0
    default:
3497
0
        return;
3498
0
    }
3499
0
    const std::string errmsg("$symbol:" + varname + "\nAccess of " + kindString + " variable '$symbol'.");
3500
0
    const ErrorPath errorPath = getErrorPath(tok, value, errmsg);
3501
0
    reportError(errorPath, Severity::warning, errorId, errmsg, CWE672, inconclusive ? Certainty::inconclusive : Certainty::normal);
3502
0
}
3503
3504
3505
3506
void CheckOther::checkFuncArgNamesDifferent()
3507
1.36k
{
3508
1.36k
    const bool style = mSettings->severity.isEnabled(Severity::style);
3509
1.36k
    const bool inconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
3510
1.36k
    const bool warning = mSettings->severity.isEnabled(Severity::warning);
3511
3512
1.36k
    if (!(warning || (style && inconclusive)))
3513
0
        return;
3514
3515
1.36k
    logChecker("CheckOther::checkFuncArgNamesDifferent"); // style,warning,inconclusive
3516
3517
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3518
    // check every function
3519
1.73k
    for (const Scope *scope : symbolDatabase->functionScopes) {
3520
1.73k
        const Function * function = scope->function;
3521
        // only check functions with arguments
3522
1.73k
        if (!function || function->argCount() == 0)
3523
1.73k
            continue;
3524
3525
        // only check functions with separate declarations and definitions
3526
0
        if (function->argDef == function->arg)
3527
0
            continue;
3528
3529
        // get the function argument name tokens
3530
0
        std::vector<const Token *>  declarations(function->argCount());
3531
0
        std::vector<const Token *>  definitions(function->argCount());
3532
0
        const Token * decl = function->argDef->next();
3533
0
        for (int j = 0; j < function->argCount(); ++j) {
3534
0
            declarations[j] = nullptr;
3535
0
            definitions[j] = nullptr;
3536
            // get the definition
3537
0
            const Variable * variable = function->getArgumentVar(j);
3538
0
            if (variable) {
3539
0
                definitions[j] = variable->nameToken();
3540
0
            }
3541
            // get the declaration (search for first token with varId)
3542
0
            while (decl && !Token::Match(decl, ",|)|;")) {
3543
                // skip everything after the assignment because
3544
                // it could also have a varId or be the first
3545
                // token with a varId if there is no name token
3546
0
                if (decl->str() == "=") {
3547
0
                    decl = decl->nextArgument();
3548
0
                    break;
3549
0
                }
3550
                // skip over template
3551
0
                if (decl->link())
3552
0
                    decl = decl->link();
3553
0
                else if (decl->varId())
3554
0
                    declarations[j] = decl;
3555
0
                decl = decl->next();
3556
0
            }
3557
0
            if (Token::simpleMatch(decl, ","))
3558
0
                decl = decl->next();
3559
0
        }
3560
        // check for different argument order
3561
0
        if (warning) {
3562
0
            bool order_different = false;
3563
0
            for (int j = 0; j < function->argCount(); ++j) {
3564
0
                if (!declarations[j] || !definitions[j] || declarations[j]->str() == definitions[j]->str())
3565
0
                    continue;
3566
3567
0
                for (int k = 0; k < function->argCount(); ++k) {
3568
0
                    if (j != k && definitions[k] && declarations[j]->str() == definitions[k]->str()) {
3569
0
                        order_different = true;
3570
0
                        break;
3571
0
                    }
3572
0
                }
3573
0
            }
3574
0
            if (order_different) {
3575
0
                funcArgOrderDifferent(function->name(), function->argDef->next(), function->arg->next(), declarations, definitions);
3576
0
                continue;
3577
0
            }
3578
0
        }
3579
        // check for different argument names
3580
0
        if (style && inconclusive) {
3581
0
            for (int j = 0; j < function->argCount(); ++j) {
3582
0
                if (declarations[j] && definitions[j] && declarations[j]->str() != definitions[j]->str())
3583
0
                    funcArgNamesDifferent(function->name(), j, declarations[j], definitions[j]);
3584
0
            }
3585
0
        }
3586
0
    }
3587
1.36k
}
3588
3589
void CheckOther::funcArgNamesDifferent(const std::string & functionName, nonneg int index,
3590
                                       const Token* declaration, const Token* definition)
3591
0
{
3592
0
    std::list<const Token *> tokens = { declaration,definition };
3593
0
    reportError(tokens, Severity::style, "funcArgNamesDifferent",
3594
0
                "$symbol:" + functionName + "\n"
3595
0
                "Function '$symbol' argument " + std::to_string(index + 1) + " names different: declaration '" +
3596
0
                (declaration ? declaration->str() : std::string("A")) + "' definition '" +
3597
0
                (definition ? definition->str() : std::string("B")) + "'.", CWE628, Certainty::inconclusive);
3598
0
}
3599
3600
void CheckOther::funcArgOrderDifferent(const std::string & functionName,
3601
                                       const Token* declaration, const Token* definition,
3602
                                       const std::vector<const Token *> & declarations,
3603
                                       const std::vector<const Token *> & definitions)
3604
0
{
3605
0
    std::list<const Token *> tokens = {
3606
0
        !declarations.empty() ? declarations[0] ? declarations[0] : declaration : nullptr,
3607
0
        !definitions.empty() ? definitions[0] ? definitions[0] : definition : nullptr
3608
0
    };
3609
0
    std::string msg = "$symbol:" + functionName + "\nFunction '$symbol' argument order different: declaration '";
3610
0
    for (int i = 0; i < declarations.size(); ++i) {
3611
0
        if (i != 0)
3612
0
            msg += ", ";
3613
0
        if (declarations[i])
3614
0
            msg += declarations[i]->str();
3615
0
    }
3616
0
    msg += "' definition '";
3617
0
    for (int i = 0; i < definitions.size(); ++i) {
3618
0
        if (i != 0)
3619
0
            msg += ", ";
3620
0
        if (definitions[i])
3621
0
            msg += definitions[i]->str();
3622
0
    }
3623
0
    msg += "'";
3624
0
    reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, Certainty::normal);
3625
0
}
3626
3627
static const Token *findShadowed(const Scope *scope, const std::string &varname, int linenr)
3628
0
{
3629
0
    if (!scope)
3630
0
        return nullptr;
3631
0
    for (const Variable &var : scope->varlist) {
3632
0
        if (scope->isExecutable() && var.nameToken()->linenr() > linenr)
3633
0
            continue;
3634
0
        if (var.name() == varname)
3635
0
            return var.nameToken();
3636
0
    }
3637
0
    auto it = std::find_if(scope->functionList.cbegin(), scope->functionList.cend(), [&](const Function& f) {
3638
0
        return f.type == Function::Type::eFunction && f.name() == varname;
3639
0
    });
3640
0
    if (it != scope->functionList.end())
3641
0
        return it->tokenDef;
3642
3643
0
    if (scope->type == Scope::eLambda)
3644
0
        return nullptr;
3645
0
    const Token* shadowed = findShadowed(scope->nestedIn, varname, linenr);
3646
0
    if (!shadowed)
3647
0
        shadowed = findShadowed(scope->functionOf, varname, linenr);
3648
0
    return shadowed;
3649
0
}
3650
3651
void CheckOther::checkShadowVariables()
3652
1.36k
{
3653
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
3654
0
        return;
3655
1.36k
    logChecker("CheckOther::checkShadowVariables"); // style
3656
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3657
4.94k
    for (const Scope & scope : symbolDatabase->scopeList) {
3658
4.94k
        if (!scope.isExecutable() || scope.type == Scope::eLambda)
3659
1.36k
            continue;
3660
3.58k
        const Scope *functionScope = &scope;
3661
6.20k
        while (functionScope && functionScope->type != Scope::ScopeType::eFunction && functionScope->type != Scope::ScopeType::eLambda)
3662
2.62k
            functionScope = functionScope->nestedIn;
3663
3.58k
        for (const Variable &var : scope.varlist) {
3664
0
            if (var.nameToken() && var.nameToken()->isExpandedMacro()) // #8903
3665
0
                continue;
3666
3667
0
            if (functionScope && functionScope->type == Scope::ScopeType::eFunction && functionScope->function) {
3668
0
                const auto argList = functionScope->function->argumentList;
3669
0
                auto it = std::find_if(argList.cbegin(), argList.cend(), [&](const Variable& arg) {
3670
0
                    return arg.nameToken() && var.name() == arg.name();
3671
0
                });
3672
0
                if (it != argList.end()) {
3673
0
                    shadowError(var.nameToken(), it->nameToken(), "argument");
3674
0
                    continue;
3675
0
                }
3676
0
            }
3677
3678
0
            const Token *shadowed = findShadowed(scope.nestedIn, var.name(), var.nameToken()->linenr());
3679
0
            if (!shadowed)
3680
0
                shadowed = findShadowed(scope.functionOf, var.name(), var.nameToken()->linenr());
3681
0
            if (!shadowed)
3682
0
                continue;
3683
0
            if (scope.type == Scope::eFunction && scope.className == var.name())
3684
0
                continue;
3685
0
            if (functionScope->functionOf && functionScope->functionOf->isClassOrStructOrUnion() && functionScope->function && functionScope->function->isStatic() &&
3686
0
                shadowed->variable() && !shadowed->variable()->isLocal())
3687
0
                continue;
3688
0
            shadowError(var.nameToken(), shadowed, (shadowed->varId() != 0) ? "variable" : "function");
3689
0
        }
3690
3.58k
    }
3691
1.36k
}
3692
3693
void CheckOther::shadowError(const Token *var, const Token *shadowed, std::string type)
3694
0
{
3695
0
    ErrorPath errorPath;
3696
0
    errorPath.emplace_back(shadowed, "Shadowed declaration");
3697
0
    errorPath.emplace_back(var, "Shadow variable");
3698
0
    const std::string &varname = var ? var->str() : type;
3699
0
    const std::string Type = char(std::toupper(type[0])) + type.substr(1);
3700
0
    const std::string id = "shadow" + Type;
3701
0
    const std::string message = "$symbol:" + varname + "\nLocal variable \'$symbol\' shadows outer " + type;
3702
0
    reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal);
3703
0
}
3704
3705
static bool isVariableExpression(const Token* tok)
3706
0
{
3707
0
    if (tok->varId() != 0)
3708
0
        return true;
3709
0
    if (Token::simpleMatch(tok, "."))
3710
0
        return isVariableExpression(tok->astOperand1()) &&
3711
0
               isVariableExpression(tok->astOperand2());
3712
0
    if (Token::simpleMatch(tok, "["))
3713
0
        return isVariableExpression(tok->astOperand1());
3714
0
    return false;
3715
0
}
3716
3717
static bool isVariableExprHidden(const Token* tok)
3718
0
{
3719
0
    if (!tok)
3720
0
        return false;
3721
0
    if (!tok->astParent())
3722
0
        return false;
3723
0
    if (Token::simpleMatch(tok->astParent(), "*") && Token::simpleMatch(tok->astSibling(), "0"))
3724
0
        return true;
3725
0
    if (Token::simpleMatch(tok->astParent(), "&&") && Token::simpleMatch(tok->astSibling(), "false"))
3726
0
        return true;
3727
0
    if (Token::simpleMatch(tok->astParent(), "||") && Token::simpleMatch(tok->astSibling(), "true"))
3728
0
        return true;
3729
0
    return false;
3730
0
}
3731
3732
void CheckOther::checkKnownArgument()
3733
1.36k
{
3734
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
3735
0
        return;
3736
1.36k
    logChecker("CheckOther::checkKnownArgument"); // style
3737
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3738
1.73k
    for (const Scope *functionScope : symbolDatabase->functionScopes) {
3739
37.6k
        for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
3740
35.9k
            if (!tok->hasKnownIntValue())
3741
31.9k
                continue;
3742
3.95k
            if (Token::Match(tok, "++|--|%assign%"))
3743
354
                continue;
3744
3.59k
            if (!Token::Match(tok->astParent(), "(|{|,"))
3745
3.43k
                continue;
3746
164
            if (tok->astParent()->isCast() || (tok->isCast() && Token::Match(tok->astOperand2(), "++|--|%assign%")))
3747
0
                continue;
3748
164
            int argn = -1;
3749
164
            const Token* ftok = getTokenArgumentFunction(tok, argn);
3750
164
            if (!ftok)
3751
0
                continue;
3752
164
            if (ftok->isCast())
3753
0
                continue;
3754
164
            if (Token::Match(ftok, "if|while|switch|sizeof"))
3755
164
                continue;
3756
0
            if (tok == tok->astParent()->previous())
3757
0
                continue;
3758
0
            if (isConstVarExpression(tok))
3759
0
                continue;
3760
0
            if (Token::Match(tok->astOperand1(), "%name% ("))
3761
0
                continue;
3762
0
            const Token * tok2 = tok;
3763
0
            if (isCPPCast(tok2))
3764
0
                tok2 = tok2->astOperand2();
3765
0
            if (isVariableExpression(tok2))
3766
0
                continue;
3767
0
            if (tok->isComparisonOp() &&
3768
0
                isSameExpression(
3769
0
                    mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true))
3770
0
                continue;
3771
            // ensure that there is a integer variable in expression with unknown value
3772
0
            const Token* vartok = nullptr;
3773
0
            visitAstNodes(tok, [&](const Token* child) {
3774
0
                if (Token::Match(child, "%var%|.|[")) {
3775
0
                    if (child->hasKnownIntValue())
3776
0
                        return ChildrenToVisit::none;
3777
0
                    if (astIsIntegral(child, false) && !astIsPointer(child) && child->values().empty()) {
3778
0
                        vartok = child;
3779
0
                        return ChildrenToVisit::done;
3780
0
                    }
3781
0
                }
3782
0
                return ChildrenToVisit::op1_and_op2;
3783
0
            });
3784
0
            if (!vartok)
3785
0
                continue;
3786
0
            if (vartok->astSibling() &&
3787
0
                findAstNode(vartok->astSibling(), [](const Token* child) {
3788
0
                return Token::simpleMatch(child, "sizeof");
3789
0
            }))
3790
0
                continue;
3791
            // ensure that function name does not contain "assert"
3792
0
            std::string funcname = ftok->str();
3793
0
            strTolower(funcname);
3794
0
            if (funcname.find("assert") != std::string::npos)
3795
0
                continue;
3796
0
            knownArgumentError(tok, ftok, &tok->values().front(), vartok->expressionString(), isVariableExprHidden(vartok));
3797
0
        }
3798
1.73k
    }
3799
1.36k
}
3800
3801
void CheckOther::knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden)
3802
0
{
3803
0
    if (!tok) {
3804
0
        reportError(tok, Severity::style, "knownArgument", "Argument 'x-x' to function 'func' is always 0. It does not matter what value 'x' has.");
3805
0
        reportError(tok, Severity::style, "knownArgumentHiddenVariableExpression", "Argument 'x*0' to function 'func' is always 0. Constant literal calculation disable/hide variable expression 'x'.");
3806
0
        return;
3807
0
    }
3808
3809
0
    const MathLib::bigint intvalue = value->intvalue;
3810
0
    const std::string &expr = tok->expressionString();
3811
0
    const std::string &fun = ftok->str();
3812
3813
0
    std::string ftype = "function ";
3814
0
    if (ftok->type())
3815
0
        ftype = "constructor ";
3816
0
    else if (fun == "{")
3817
0
        ftype = "init list ";
3818
3819
0
    const char *id;
3820
0
    std::string errmsg = "Argument '" + expr + "' to " + ftype + fun + " is always " + std::to_string(intvalue) + ". ";
3821
0
    if (!isVariableExpressionHidden) {
3822
0
        id = "knownArgument";
3823
0
        errmsg += "It does not matter what value '" + varexpr + "' has.";
3824
0
    } else {
3825
0
        id = "knownArgumentHiddenVariableExpression";
3826
0
        errmsg += "Constant literal calculation disable/hide variable expression '" + varexpr + "'.";
3827
0
    }
3828
3829
0
    const ErrorPath errorPath = getErrorPath(tok, value, errmsg);
3830
0
    reportError(errorPath, Severity::style, id, errmsg, CWE570, Certainty::normal);
3831
0
}
3832
3833
void CheckOther::checkKnownPointerToBool()
3834
1.36k
{
3835
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
3836
0
        return;
3837
1.36k
    logChecker("CheckOther::checkKnownPointerToBool"); // style
3838
1.36k
    const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
3839
1.73k
    for (const Scope* functionScope : symbolDatabase->functionScopes) {
3840
37.6k
        for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
3841
35.9k
            if (!tok->hasKnownIntValue())
3842
31.9k
                continue;
3843
3.95k
            if (!astIsPointer(tok))
3844
3.95k
                continue;
3845
0
            if (Token::Match(tok->astParent(), "?|!|&&|%oror%|%comp%"))
3846
0
                continue;
3847
0
            if (tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|switch|sizeof ("))
3848
0
                continue;
3849
0
            if (tok->isExpandedMacro())
3850
0
                continue;
3851
0
            if (findParent(tok, [](const Token* parent) {
3852
0
                return parent->isExpandedMacro();
3853
0
            }))
3854
0
                continue;
3855
0
            if (!isUsedAsBool(tok, mSettings))
3856
0
                continue;
3857
0
            const ValueFlow::Value& value = tok->values().front();
3858
0
            knownPointerToBoolError(tok, &value);
3859
0
        }
3860
1.73k
    }
3861
1.36k
}
3862
3863
void CheckOther::knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value)
3864
0
{
3865
0
    if (!tok) {
3866
0
        reportError(tok, Severity::style, "knownPointerToBool", "Pointer expression 'p' converted to bool is always true.");
3867
0
        return;
3868
0
    }
3869
0
    std::string cond = bool_to_string(value->intvalue);
3870
0
    const std::string& expr = tok->expressionString();
3871
0
    std::string errmsg = "Pointer expression '" + expr + "' converted to bool is always " + cond + ".";
3872
0
    const ErrorPath errorPath = getErrorPath(tok, value, errmsg);
3873
0
    reportError(errorPath, Severity::style, "knownPointerToBool", errmsg, CWE570, Certainty::normal);
3874
0
}
3875
3876
void CheckOther::checkComparePointers()
3877
1.36k
{
3878
1.36k
    logChecker("CheckOther::checkComparePointers");
3879
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
3880
1.73k
    for (const Scope *functionScope : symbolDatabase->functionScopes) {
3881
37.6k
        for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
3882
35.9k
            if (!Token::Match(tok, "<|>|<=|>=|-"))
3883
34.1k
                continue;
3884
1.76k
            const Token *tok1 = tok->astOperand1();
3885
1.76k
            const Token *tok2 = tok->astOperand2();
3886
1.76k
            if (!astIsPointer(tok1) || !astIsPointer(tok2))
3887
1.76k
                continue;
3888
0
            ValueFlow::Value v1 = ValueFlow::getLifetimeObjValue(tok1);
3889
0
            ValueFlow::Value v2 = ValueFlow::getLifetimeObjValue(tok2);
3890
0
            if (!v1.isLocalLifetimeValue() || !v2.isLocalLifetimeValue())
3891
0
                continue;
3892
0
            const Variable *var1 = v1.tokvalue->variable();
3893
0
            const Variable *var2 = v2.tokvalue->variable();
3894
0
            if (!var1 || !var2)
3895
0
                continue;
3896
0
            if (v1.tokvalue->varId() == v2.tokvalue->varId())
3897
0
                continue;
3898
0
            if (var1->isReference() || var2->isReference())
3899
0
                continue;
3900
0
            if (var1->isRValueReference() || var2->isRValueReference())
3901
0
                continue;
3902
0
            if (const Token* parent2 = getParentLifetime(mTokenizer->isCPP(), v2.tokvalue, &mSettings->library))
3903
0
                if (var1 == parent2->variable())
3904
0
                    continue;
3905
0
            if (const Token* parent1 = getParentLifetime(mTokenizer->isCPP(), v1.tokvalue, &mSettings->library))
3906
0
                if (var2 == parent1->variable())
3907
0
                    continue;
3908
0
            comparePointersError(tok, &v1, &v2);
3909
0
        }
3910
1.73k
    }
3911
1.36k
}
3912
3913
void CheckOther::comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2)
3914
0
{
3915
0
    ErrorPath errorPath;
3916
0
    std::string verb = "Comparing";
3917
0
    if (Token::simpleMatch(tok, "-"))
3918
0
        verb = "Subtracting";
3919
0
    if (v1) {
3920
0
        errorPath.emplace_back(v1->tokvalue->variable()->nameToken(), "Variable declared here.");
3921
0
        errorPath.insert(errorPath.end(), v1->errorPath.cbegin(), v1->errorPath.cend());
3922
0
    }
3923
0
    if (v2) {
3924
0
        errorPath.emplace_back(v2->tokvalue->variable()->nameToken(), "Variable declared here.");
3925
0
        errorPath.insert(errorPath.end(), v2->errorPath.cbegin(), v2->errorPath.cend());
3926
0
    }
3927
0
    errorPath.emplace_back(tok, "");
3928
0
    reportError(
3929
0
        errorPath, Severity::error, "comparePointers", verb + " pointers that point to different objects", CWE570, Certainty::normal);
3930
0
}
3931
3932
void CheckOther::checkModuloOfOne()
3933
1.36k
{
3934
1.36k
    if (!mSettings->severity.isEnabled(Severity::style))
3935
0
        return;
3936
3937
1.36k
    logChecker("CheckOther::checkModuloOfOne"); // style
3938
3939
93.5k
    for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
3940
92.2k
        if (!tok->astOperand2() || !tok->astOperand1())
3941
78.0k
            continue;
3942
14.2k
        if (tok->str() != "%")
3943
13.9k
            continue;
3944
289
        if (!tok->valueType() || !tok->valueType()->isIntegral())
3945
127
            continue;
3946
3947
        // Value flow..
3948
162
        const ValueFlow::Value *value = tok->astOperand2()->getValue(1LL);
3949
162
        if (value && value->isKnown())
3950
0
            checkModuloOfOneError(tok);
3951
162
    }
3952
1.36k
}
3953
3954
void CheckOther::checkModuloOfOneError(const Token *tok)
3955
0
{
3956
0
    reportError(tok, Severity::style, "moduloofone", "Modulo of one is always equal to zero");
3957
0
}
3958
3959
//-----------------------------------------------------------------------------
3960
// Overlapping write (undefined behavior)
3961
//-----------------------------------------------------------------------------
3962
static bool getBufAndOffset(const Token *expr, const Token **buf, MathLib::bigint *offset)
3963
0
{
3964
0
    if (!expr)
3965
0
        return false;
3966
0
    const Token *bufToken, *offsetToken;
3967
0
    if (expr->isUnaryOp("&") && Token::simpleMatch(expr->astOperand1(), "[")) {
3968
0
        bufToken = expr->astOperand1()->astOperand1();
3969
0
        offsetToken = expr->astOperand1()->astOperand2();
3970
0
    } else if (Token::Match(expr, "+|-") && expr->isBinaryOp()) {
3971
0
        const bool pointer1 = (expr->astOperand1()->valueType() && expr->astOperand1()->valueType()->pointer > 0);
3972
0
        const bool pointer2 = (expr->astOperand2()->valueType() && expr->astOperand2()->valueType()->pointer > 0);
3973
0
        if (pointer1 && !pointer2) {
3974
0
            bufToken = expr->astOperand1();
3975
0
            offsetToken = expr->astOperand2();
3976
0
        } else if (!pointer1 && pointer2) {
3977
0
            bufToken = expr->astOperand2();
3978
0
            offsetToken = expr->astOperand1();
3979
0
        } else {
3980
0
            return false;
3981
0
        }
3982
0
    } else if (expr->valueType() && expr->valueType()->pointer > 0) {
3983
0
        *buf = expr;
3984
0
        *offset = 0;
3985
0
        return true;
3986
0
    } else {
3987
0
        return false;
3988
0
    }
3989
0
    if (!bufToken->valueType() || !bufToken->valueType()->pointer)
3990
0
        return false;
3991
0
    if (!offsetToken->hasKnownIntValue())
3992
0
        return false;
3993
0
    *buf = bufToken;
3994
0
    *offset = offsetToken->getKnownIntValue();
3995
0
    return true;
3996
0
}
3997
3998
void CheckOther::checkOverlappingWrite()
3999
1.36k
{
4000
1.36k
    logChecker("CheckOther::checkOverlappingWrite");
4001
1.36k
    const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
4002
1.73k
    for (const Scope *functionScope : symbolDatabase->functionScopes) {
4003
37.6k
        for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
4004
35.9k
            if (tok->isAssignmentOp()) {
4005
                // check if LHS is a union member..
4006
1.90k
                const Token * const lhs = tok->astOperand1();
4007
1.90k
                if (!Token::simpleMatch(lhs, ".") || !lhs->isBinaryOp())
4008
1.90k
                    continue;
4009
0
                const Variable * const lhsvar = lhs->astOperand1()->variable();
4010
0
                if (!lhsvar || !lhsvar->typeScope() || lhsvar->typeScope()->type != Scope::ScopeType::eUnion)
4011
0
                    continue;
4012
0
                const Token* const lhsmember = lhs->astOperand2();
4013
0
                if (!lhsmember)
4014
0
                    continue;
4015
4016
                // Is other union member used in RHS?
4017
0
                const Token *errorToken = nullptr;
4018
0
                visitAstNodes(tok->astOperand2(), [lhsvar, lhsmember, &errorToken](const Token *rhs) {
4019
0
                    if (!Token::simpleMatch(rhs, "."))
4020
0
                        return ChildrenToVisit::op1_and_op2;
4021
0
                    if (!rhs->isBinaryOp() || rhs->astOperand1()->variable() != lhsvar)
4022
0
                        return ChildrenToVisit::none;
4023
0
                    if (lhsmember->str() == rhs->astOperand2()->str())
4024
0
                        return ChildrenToVisit::none;
4025
0
                    const Variable* rhsmembervar = rhs->astOperand2()->variable();
4026
0
                    const Scope* varscope1 = lhsmember->variable() ? lhsmember->variable()->typeStartToken()->scope() : nullptr;
4027
0
                    const Scope* varscope2 = rhsmembervar ? rhsmembervar->typeStartToken()->scope() : nullptr;
4028
0
                    if (varscope1 && varscope1 == varscope2 && varscope1 != lhsvar->typeScope())
4029
                        // lhsmember and rhsmember are declared in same anonymous scope inside union
4030
0
                        return ChildrenToVisit::none;
4031
0
                    errorToken = rhs->astOperand2();
4032
0
                    return ChildrenToVisit::done;
4033
0
                });
4034
0
                if (errorToken)
4035
0
                    overlappingWriteUnion(tok);
4036
34.0k
            } else if (Token::Match(tok, "%name% (")) {
4037
1.46k
                const Library::NonOverlappingData *nonOverlappingData = mSettings->library.getNonOverlappingData(tok);
4038
1.46k
                if (!nonOverlappingData)
4039
1.46k
                    continue;
4040
0
                const std::vector<const Token *> args = getArguments(tok);
4041
0
                if (nonOverlappingData->ptr1Arg <= 0 || nonOverlappingData->ptr1Arg > args.size())
4042
0
                    continue;
4043
0
                if (nonOverlappingData->ptr2Arg <= 0 || nonOverlappingData->ptr2Arg > args.size())
4044
0
                    continue;
4045
4046
0
                const Token *ptr1 = args[nonOverlappingData->ptr1Arg - 1];
4047
0
                if (ptr1->hasKnownIntValue() && ptr1->getKnownIntValue() == 0)
4048
0
                    continue;
4049
4050
0
                const Token *ptr2 = args[nonOverlappingData->ptr2Arg - 1];
4051
0
                if (ptr2->hasKnownIntValue() && ptr2->getKnownIntValue() == 0)
4052
0
                    continue;
4053
4054
                // TODO: nonOverlappingData->strlenArg
4055
0
                if (nonOverlappingData->sizeArg <= 0 || nonOverlappingData->sizeArg > args.size()) {
4056
0
                    if (nonOverlappingData->sizeArg == -1) {
4057
0
                        ErrorPath errorPath;
4058
0
                        const bool macro = true;
4059
0
                        const bool pure = true;
4060
0
                        const bool follow = true;
4061
0
                        if (!isSameExpression(mTokenizer->isCPP(), macro, ptr1, ptr2, mSettings->library, pure, follow, &errorPath))
4062
0
                            continue;
4063
0
                        overlappingWriteFunction(tok);
4064
0
                    }
4065
0
                    continue;
4066
0
                }
4067
0
                if (!args[nonOverlappingData->sizeArg-1]->hasKnownIntValue())
4068
0
                    continue;
4069
0
                const MathLib::bigint sizeValue = args[nonOverlappingData->sizeArg-1]->getKnownIntValue();
4070
0
                const Token *buf1, *buf2;
4071
0
                MathLib::bigint offset1, offset2;
4072
0
                if (!getBufAndOffset(ptr1, &buf1, &offset1))
4073
0
                    continue;
4074
0
                if (!getBufAndOffset(ptr2, &buf2, &offset2))
4075
0
                    continue;
4076
4077
0
                if (offset1 < offset2 && offset1 + sizeValue <= offset2)
4078
0
                    continue;
4079
0
                if (offset2 < offset1 && offset2 + sizeValue <= offset1)
4080
0
                    continue;
4081
4082
0
                ErrorPath errorPath;
4083
0
                const bool macro = true;
4084
0
                const bool pure = true;
4085
0
                const bool follow = true;
4086
0
                if (!isSameExpression(mTokenizer->isCPP(), macro, buf1, buf2, mSettings->library, pure, follow, &errorPath))
4087
0
                    continue;
4088
0
                overlappingWriteFunction(tok);
4089
0
            }
4090
35.9k
        }
4091
1.73k
    }
4092
1.36k
}
4093
4094
void CheckOther::overlappingWriteUnion(const Token *tok)
4095
0
{
4096
0
    reportError(tok, Severity::error, "overlappingWriteUnion", "Overlapping read/write of union is undefined behavior");
4097
0
}
4098
4099
void CheckOther::overlappingWriteFunction(const Token *tok)
4100
0
{
4101
0
    const std::string &funcname = tok ? tok->str() : emptyString;
4102
0
    reportError(tok, Severity::error, "overlappingWriteFunction", "Overlapping read/write in " + funcname + "() is undefined behavior");
4103
0
}