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