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