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