Cppcheck
checkcondition.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 // Check for condition mismatches
21 //---------------------------------------------------------------------------
22 
23 #include "checkcondition.h"
24 
25 #include "astutils.h"
26 #include "library.h"
27 #include "platform.h"
28 #include "settings.h"
29 #include "symboldatabase.h"
30 #include "token.h"
31 #include "tokenize.h"
32 #include "utils.h"
33 #include "vfvalue.h"
34 
35 #include "checkother.h" // comparisonNonZeroExpressionLessThanZero and testIfNonZeroExpressionIsPositive
36 
37 #include <algorithm>
38 #include <limits>
39 #include <list>
40 #include <set>
41 #include <sstream>
42 #include <utility>
43 #include <vector>
44 
45 // CWE ids used
46 static const CWE uncheckedErrorConditionCWE(391U);
47 static const CWE CWE398(398U); // Indicator of Poor Code Quality
48 static const CWE CWE570(570U); // Expression is Always False
49 static const CWE CWE571(571U); // Expression is Always True
50 
51 //---------------------------------------------------------------------------
52 
53 // Register this check class (by creating a static instance of it)
54 namespace {
55  CheckCondition instance;
56 }
57 
58 bool CheckCondition::diag(const Token* tok, bool insert)
59 {
60  if (!tok)
61  return false;
62  const Token* parent = tok->astParent();
63  bool hasParent = false;
64  while (Token::Match(parent, "!|&&|%oror%")) {
65  if (mCondDiags.count(parent) != 0) {
66  hasParent = true;
67  break;
68  }
69  parent = parent->astParent();
70  }
71  if (mCondDiags.count(tok) == 0 && !hasParent) {
72  if (insert)
73  mCondDiags.insert(tok);
74  return false;
75  }
76  return true;
77 }
78 
79 bool CheckCondition::isAliased(const std::set<int> &vars) const
80 {
81  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
82  if (Token::Match(tok, "= & %var% ;") && vars.find(tok->tokAt(2)->varId()) != vars.end())
83  return true;
84  }
85  return false;
86 }
87 
89 {
91  return;
92 
93  logChecker("CheckCondition::assignIf"); // style
94 
95  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
96  if (tok->str() != "=")
97  continue;
98 
99  if (Token::Match(tok->tokAt(-2), "[;{}] %var% =")) {
100  const Variable *var = tok->previous()->variable();
101  if (var == nullptr)
102  continue;
103 
104  char bitop = '\0';
105  MathLib::bigint num = 0;
106 
107  if (Token::Match(tok->next(), "%num% [&|]")) {
108  bitop = tok->strAt(2).at(0);
109  num = MathLib::toBigNumber(tok->next()->str());
110  } else {
111  const Token *endToken = Token::findsimplematch(tok, ";");
112 
113  // Casting address
114  if (endToken && Token::Match(endToken->tokAt(-4), "* ) & %any% ;"))
115  endToken = nullptr;
116 
117  if (endToken && Token::Match(endToken->tokAt(-2), "[&|] %num% ;")) {
118  bitop = endToken->strAt(-2).at(0);
119  num = MathLib::toBigNumber(endToken->previous()->str());
120  }
121  }
122 
123  if (bitop == '\0')
124  continue;
125 
126  if (num < 0 && bitop == '|')
127  continue;
128 
129  assignIfParseScope(tok, tok->tokAt(4), var->declarationId(), var->isLocal(), bitop, num);
130  }
131  }
132 }
133 
134 static bool isParameterChanged(const Token *partok)
135 {
136  bool addressOf = Token::Match(partok, "[(,] &");
137  int argumentNumber = 0;
138  const Token *ftok;
139  for (ftok = partok; ftok && ftok->str() != "("; ftok = ftok->previous()) {
140  if (ftok->str() == ")")
141  ftok = ftok->link();
142  else if (argumentNumber == 0U && ftok->str() == "&")
143  addressOf = true;
144  else if (ftok->str() == ",")
145  argumentNumber++;
146  }
147  ftok = ftok ? ftok->previous() : nullptr;
148  if (!(ftok && ftok->function()))
149  return true;
150  const Variable *par = ftok->function()->getArgumentVar(argumentNumber);
151  if (!par)
152  return true;
153  if (par->isConst())
154  return false;
155  if (addressOf || par->isReference() || par->isPointer())
156  return true;
157  return false;
158 }
159 
160 /** parse scopes recursively */
161 bool CheckCondition::assignIfParseScope(const Token * const assignTok,
162  const Token * const startTok,
163  const nonneg int varid,
164  const bool islocal,
165  const char bitop,
166  const MathLib::bigint num)
167 {
168  bool ret = false;
169 
170  for (const Token *tok2 = startTok; tok2; tok2 = tok2->next()) {
171  if ((bitop == '&') && Token::Match(tok2->tokAt(2), "%varid% %cop% %num% ;", varid) && tok2->strAt(3) == std::string(1U, bitop)) {
172  const MathLib::bigint num2 = MathLib::toBigNumber(tok2->strAt(4));
173  if (0 == (num & num2))
174  mismatchingBitAndError(assignTok, num, tok2, num2);
175  }
176  if (Token::Match(tok2, "%varid% =", varid)) {
177  return true;
178  }
179  if (bitop == '&' && Token::Match(tok2, "%varid% &= %num% ;", varid)) {
180  const MathLib::bigint num2 = MathLib::toBigNumber(tok2->strAt(2));
181  if (0 == (num & num2))
182  mismatchingBitAndError(assignTok, num, tok2, num2);
183  }
184  if (Token::Match(tok2, "++|-- %varid%", varid) || Token::Match(tok2, "%varid% ++|--", varid))
185  return true;
186  if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid) && isParameterChanged(tok2))
187  return true;
188  if (tok2->str() == "}")
189  return false;
190  if (Token::Match(tok2, "break|continue|return"))
191  ret = true;
192  if (ret && tok2->str() == ";")
193  return false;
194  if (!islocal && Token::Match(tok2, "%name% (") && !Token::simpleMatch(tok2->next()->link(), ") {"))
195  return true;
196  if (Token::Match(tok2, "if|while (")) {
197  if (!islocal && tok2->str() == "while")
198  continue;
199  if (tok2->str() == "while") {
200  // is variable changed in loop?
201  const Token *bodyStart = tok2->linkAt(1)->next();
202  const Token *bodyEnd = bodyStart ? bodyStart->link() : nullptr;
203  if (!bodyEnd || bodyEnd->str() != "}" || isVariableChanged(bodyStart, bodyEnd, varid, !islocal, *mSettings))
204  continue;
205  }
206 
207  // parse condition
208  const Token * const end = tok2->next()->link();
209  for (; tok2 != end; tok2 = tok2->next()) {
210  if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid)) {
211  return true;
212  }
213  if (Token::Match(tok2,"&&|%oror%|( %varid% ==|!= %num% &&|%oror%|)", varid)) {
214  const Token *vartok = tok2->next();
215  const MathLib::bigint num2 = MathLib::toBigNumber(vartok->strAt(2));
216  if ((num & num2) != ((bitop=='&') ? num2 : num)) {
217  const std::string& op(vartok->strAt(1));
218  const bool alwaysTrue = op == "!=";
219  const std::string condition(vartok->str() + op + vartok->strAt(2));
220  assignIfError(assignTok, tok2, condition, alwaysTrue);
221  }
222  }
223  if (Token::Match(tok2, "%varid% %op%", varid) && tok2->next()->isAssignmentOp()) {
224  return true;
225  }
226  }
227 
228  const bool ret1 = assignIfParseScope(assignTok, end->tokAt(2), varid, islocal, bitop, num);
229  bool ret2 = false;
230  if (Token::simpleMatch(end->next()->link(), "} else {"))
231  ret2 = assignIfParseScope(assignTok, end->next()->link()->tokAt(3), varid, islocal, bitop, num);
232  if (ret1 || ret2)
233  return true;
234  }
235  }
236  return false;
237 }
238 
239 void CheckCondition::assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result)
240 {
241  if (tok2 && diag(tok2->tokAt(2)))
242  return;
243  std::list<const Token *> locations = { tok1, tok2 };
244  reportError(locations,
246  "assignIfError",
247  "Mismatching assignment and comparison, comparison '" + condition + "' is always " + std::string(bool_to_string(result)) + ".", CWE398, Certainty::normal);
248 }
249 
250 
251 void CheckCondition::mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2)
252 {
253  std::list<const Token *> locations = { tok1, tok2 };
254 
255  std::ostringstream msg;
256  msg << "Mismatching bitmasks. Result is always 0 ("
257  << "X = Y & 0x" << std::hex << num1 << "; Z = X & 0x" << std::hex << num2 << "; => Z=0).";
258 
259  reportError(locations,
261  "mismatchingBitAnd",
262  msg.str(), CWE398, Certainty::normal);
263 }
264 
265 
266 static void getnumchildren(const Token *tok, std::list<MathLib::bigint> &numchildren)
267 {
268  if (tok->astOperand1() && tok->astOperand1()->isNumber())
269  numchildren.push_back(MathLib::toBigNumber(tok->astOperand1()->str()));
270  else if (tok->astOperand1() && tok->str() == tok->astOperand1()->str())
271  getnumchildren(tok->astOperand1(), numchildren);
272  if (tok->astOperand2() && tok->astOperand2()->isNumber())
273  numchildren.push_back(MathLib::toBigNumber(tok->astOperand2()->str()));
274  else if (tok->astOperand2() && tok->str() == tok->astOperand2()->str())
275  getnumchildren(tok->astOperand2(), numchildren);
276 }
277 
278 /* Return whether tok is in the body for a function returning a boolean. */
279 static bool inBooleanFunction(const Token *tok)
280 {
281  const Scope *scope = tok ? tok->scope() : nullptr;
282  while (scope && scope->isLocal())
283  scope = scope->nestedIn;
284  if (scope && scope->type == Scope::eFunction) {
285  const Function *func = scope->function;
286  if (func) {
287  const Token *ret = func->retDef;
288  while (Token::Match(ret, "static|const"))
289  ret = ret->next();
290  return Token::Match(ret, "bool|_Bool");
291  }
292  }
293  return false;
294 }
295 
296 static bool isOperandExpanded(const Token *tok)
297 {
298  if (tok->isExpandedMacro() || tok->isEnumerator())
299  return true;
300  if (tok->astOperand1() && isOperandExpanded(tok->astOperand1()))
301  return true;
302  if (tok->astOperand2() && isOperandExpanded(tok->astOperand2()))
303  return true;
304  return false;
305 }
306 
308 {
309  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("badBitmaskCheck"))
310  return;
311 
312  logChecker("CheckCondition::checkBadBitmaskCheck"); // style
313 
314  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
315  if (tok->str() == "|" && tok->astOperand1() && tok->astOperand2() && tok->astParent()) {
316  const Token* parent = tok->astParent();
317  const bool isBoolean = Token::Match(parent, "&&|%oror%") ||
318  (parent->str() == "?" && parent->astOperand1() == tok) ||
319  (parent->str() == "=" && parent->astOperand2() == tok && parent->astOperand1() && parent->astOperand1()->variable() && Token::Match(parent->astOperand1()->variable()->typeStartToken(), "bool|_Bool")) ||
320  (parent->str() == "(" && Token::Match(parent->astOperand1(), "if|while")) ||
321  (parent->str() == "return" && parent->astOperand1() == tok && inBooleanFunction(tok));
322 
323  const bool isTrue = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue != 0) ||
324  (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue != 0);
325 
326  if (isBoolean && isTrue)
328 
329  // If there are #ifdef in the expression don't warn about redundant | to avoid FP
330  const auto& startStop = tok->findExpressionStartEndTokens();
331  if (mTokenizer->hasIfdef(startStop.first, startStop.second))
332  continue;
333 
334  const bool isZero1 = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue == 0);
335  const bool isZero2 = (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue == 0);
336  if (!isZero1 && !isZero2)
337  continue;
338 
339  if (!tok->isExpandedMacro() &&
340  !(isZero1 && isOperandExpanded(tok->astOperand1())) &&
341  !(isZero2 && isOperandExpanded(tok->astOperand2())))
342  badBitmaskCheckError(tok, /*isNoOp*/ true);
343  }
344  }
345 }
346 
347 void CheckCondition::badBitmaskCheckError(const Token *tok, bool isNoOp)
348 {
349  if (isNoOp)
350  reportError(tok, Severity::style, "badBitmaskCheck", "Operator '|' with one operand equal to zero is redundant.", CWE571, Certainty::normal);
351  else
352  reportError(tok, Severity::warning, "badBitmaskCheck", "Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?", CWE571, Certainty::normal);
353 }
354 
356 {
357  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("comparisonError"))
358  return;
359 
360  logChecker("CheckCondition::comparison"); // style
361 
362  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
363  if (!tok->isComparisonOp())
364  continue;
365 
366  const Token *expr1 = tok->astOperand1();
367  const Token *expr2 = tok->astOperand2();
368  if (!expr1 || !expr2)
369  continue;
370  if (expr1->isNumber())
371  std::swap(expr1,expr2);
372  if (!expr2->isNumber())
373  continue;
374  const MathLib::bigint num2 = MathLib::toBigNumber(expr2->str());
375  if (num2 < 0)
376  continue;
377  if (!Token::Match(expr1,"[&|]"))
378  continue;
379  std::list<MathLib::bigint> numbers;
380  getnumchildren(expr1, numbers);
381  for (const MathLib::bigint num1 : numbers) {
382  if (num1 < 0)
383  continue;
384  if (Token::Match(tok, "==|!=")) {
385  if ((expr1->str() == "&" && (num1 & num2) != num2) ||
386  (expr1->str() == "|" && (num1 | num2) != num2)) {
387  const std::string& op(tok->str());
388  comparisonError(expr1, expr1->str(), num1, op, num2, op != "==");
389  }
390  } else if (expr1->str() == "&") {
391  const bool or_equal = Token::Match(tok, ">=|<=");
392  const std::string& op(tok->str());
393  if ((Token::Match(tok, ">=|<")) && (num1 < num2)) {
394  comparisonError(expr1, expr1->str(), num1, op, num2, !or_equal);
395  } else if ((Token::Match(tok, "<=|>")) && (num1 <= num2)) {
396  comparisonError(expr1, expr1->str(), num1, op, num2, or_equal);
397  }
398  } else if (expr1->str() == "|") {
399  if ((expr1->astOperand1()->valueType()) &&
400  (expr1->astOperand1()->valueType()->sign == ValueType::Sign::UNSIGNED)) {
401  const bool or_equal = Token::Match(tok, ">=|<=");
402  const std::string& op(tok->str());
403  if ((Token::Match(tok, ">=|<")) && (num1 >= num2)) {
404  //"(a | 0x07) >= 7U" is always true for unsigned a
405  //"(a | 0x07) < 7U" is always false for unsigned a
406  comparisonError(expr1, expr1->str(), num1, op, num2, or_equal);
407  } else if ((Token::Match(tok, "<=|>")) && (num1 > num2)) {
408  //"(a | 0x08) <= 7U" is always false for unsigned a
409  //"(a | 0x07) > 6U" is always true for unsigned a
410  comparisonError(expr1, expr1->str(), num1, op, num2, !or_equal);
411  }
412  }
413  }
414  }
415  }
416 }
417 
418 void CheckCondition::comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result)
419 {
420  std::ostringstream expression;
421  expression << std::hex << "(X " << bitop << " 0x" << value1 << ") " << op << " 0x" << value2;
422 
423  const std::string errmsg("Expression '" + expression.str() + "' is always " + bool_to_string(result) + ".\n"
424  "The expression '" + expression.str() + "' is always " + bool_to_string(result) +
425  ". Check carefully constants and operators used, these errors might be hard to "
426  "spot sometimes. In case of complex expression it might help to split it to "
427  "separate expressions.");
428 
429  reportError(tok, Severity::style, "comparisonError", errmsg, CWE398, Certainty::normal);
430 }
431 
432 bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const
433 {
434  if (!cond1 || !cond2)
435  return false;
436 
437  // same expressions
438  if (isSameExpression(true, cond1, cond2, *mSettings, pure, false))
439  return true;
440 
441  // bitwise overlap for example 'x&7' and 'x==1'
442  if (cond1->str() == "&" && cond1->astOperand1() && cond2->astOperand2()) {
443  const Token *expr1 = cond1->astOperand1();
444  const Token *num1 = cond1->astOperand2();
445  if (!num1) // unary operator&
446  return false;
447  if (!num1->isNumber())
448  std::swap(expr1,num1);
449  if (!num1->isNumber() || MathLib::isNegative(num1->str()))
450  return false;
451 
452  if (!Token::Match(cond2, "&|==") || !cond2->astOperand1() || !cond2->astOperand2())
453  return false;
454  const Token *expr2 = cond2->astOperand1();
455  const Token *num2 = cond2->astOperand2();
456  if (!num2->isNumber())
457  std::swap(expr2,num2);
458  if (!num2->isNumber() || MathLib::isNegative(num2->str()))
459  return false;
460 
461  if (!isSameExpression(true, expr1, expr2, *mSettings, pure, false))
462  return false;
463 
464  const MathLib::bigint value1 = MathLib::toBigNumber(num1->str());
465  const MathLib::bigint value2 = MathLib::toBigNumber(num2->str());
466  if (cond2->str() == "&")
467  return ((value1 & value2) == value2);
468  return ((value1 & value2) > 0);
469  }
470  return false;
471 }
472 
474 {
475  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("duplicateCondition"))
476  return;
477 
478  logChecker("CheckCondition::duplicateCondition"); // style
479 
480  const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase();
481 
482  for (const Scope &scope : symbolDatabase->scopeList) {
483  if (scope.type != Scope::eIf)
484  continue;
485 
486  const Token* tok2 = scope.classDef->next();
487  if (!tok2)
488  continue;
489  const Token* cond1 = tok2->astOperand2();
490  if (!cond1)
491  continue;
492  if (cond1->hasKnownIntValue())
493  continue;
494 
495  tok2 = tok2->link();
496  if (!Token::simpleMatch(tok2, ") {"))
497  continue;
498  tok2 = tok2->linkAt(1);
499  if (!Token::simpleMatch(tok2, "} if ("))
500  continue;
501  const Token *cond2 = tok2->tokAt(2)->astOperand2();
502  if (!cond2)
503  continue;
504 
505  ErrorPath errorPath;
506  if (!findExpressionChanged(cond1, scope.classDef->next(), cond2, *mSettings) &&
507  isSameExpression(true, cond1, cond2, *mSettings, true, true, &errorPath))
508  duplicateConditionError(cond1, cond2, std::move(errorPath));
509  }
510 }
511 
512 void CheckCondition::duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath)
513 {
514  if (diag(tok1) & diag(tok2))
515  return;
516  errorPath.emplace_back(tok1, "First condition");
517  errorPath.emplace_back(tok2, "Second condition");
518 
519  std::string msg = "The if condition is the same as the previous if condition";
520 
521  reportError(errorPath, Severity::style, "duplicateCondition", msg, CWE398, Certainty::normal);
522 }
523 
525 {
527  return;
528 
529  logChecker("CheckCondition::multiCondition"); // style
530 
531  const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
532 
533  for (const Scope &scope : symbolDatabase->scopeList) {
534  if (scope.type != Scope::eIf)
535  continue;
536 
537  const Token * const cond1 = scope.classDef->next()->astOperand2();
538  if (!cond1)
539  continue;
540 
541  const Token * tok2 = scope.classDef->next();
542 
543  // Check each 'else if'
544  for (;;) {
545  tok2 = tok2->link();
546  if (!Token::simpleMatch(tok2, ") {"))
547  break;
548  tok2 = tok2->linkAt(1);
549  if (!Token::simpleMatch(tok2, "} else { if ("))
550  break;
551  tok2 = tok2->tokAt(4);
552 
553  if (tok2->astOperand2()) {
554  ErrorPath errorPath;
555  if (isOverlappingCond(cond1, tok2->astOperand2(), true) &&
556  !findExpressionChanged(cond1, cond1, tok2->astOperand2(), *mSettings))
558  else if (isOppositeCond(
559  true, cond1, tok2->astOperand2(), *mSettings, true, true, &errorPath) &&
560  !findExpressionChanged(cond1, cond1, tok2->astOperand2(), *mSettings))
561  oppositeElseIfConditionError(cond1, tok2->astOperand2(), std::move(errorPath));
562  }
563  }
564  }
565 }
566 
568 {
569  if (diag(tok))
570  return;
571  std::ostringstream errmsg;
572  errmsg << "Expression is always false because 'else if' condition matches previous condition at line "
573  << line1 << ".";
574 
575  reportError(tok, Severity::style, "multiCondition", errmsg.str(), CWE398, Certainty::normal);
576 }
577 
578 void CheckCondition::oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath)
579 {
580  if (diag(ifCond) & diag(elseIfCond))
581  return;
582  std::ostringstream errmsg;
583  errmsg << "Expression is always true because 'else if' condition is opposite to previous condition at line "
584  << ifCond->linenr() << ".";
585 
586  errorPath.emplace_back(ifCond, "first condition");
587  errorPath.emplace_back(elseIfCond, "else if condition is opposite to first condition");
588 
589  reportError(errorPath, Severity::style, "multiCondition", errmsg.str(), CWE398, Certainty::normal);
590 }
591 
592 //---------------------------------------------------------------------------
593 // - Opposite inner conditions => always false
594 // - (TODO) Same/Overlapping inner condition => always true
595 // - same condition after early exit => always false
596 //---------------------------------------------------------------------------
597 
598 static bool isNonConstFunctionCall(const Token *ftok, const Library &library)
599 {
600  if (library.isFunctionConst(ftok))
601  return false;
602  const Token *obj = ftok->next()->astOperand1();
603  while (obj && obj->str() == ".")
604  obj = obj->astOperand1();
605  if (!obj)
606  return true;
607  if (obj->variable() && obj->variable()->isConst())
608  return false;
609  if (ftok->function() && ftok->function()->isConst())
610  return false;
611  return true;
612 }
613 
615 {
617  return;
618 
619  logChecker("CheckCondition::multiCondition2"); // warning
620 
621  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
622 
623  for (const Scope &scope : symbolDatabase->scopeList) {
624  const Token *condTok = nullptr;
625  if (scope.type == Scope::eIf || scope.type == Scope::eWhile)
626  condTok = scope.classDef->next()->astOperand2();
627  else if (scope.type == Scope::eFor) {
628  condTok = scope.classDef->next()->astOperand2();
629  if (!condTok || condTok->str() != ";")
630  continue;
631  condTok = condTok->astOperand2();
632  if (!condTok || condTok->str() != ";")
633  continue;
634  condTok = condTok->astOperand1();
635  }
636  if (!condTok)
637  continue;
638  const Token * const cond1 = condTok;
639 
640  if (!Token::simpleMatch(scope.classDef->linkAt(1), ") {"))
641  continue;
642 
643  bool functionCall = false;
644  bool nonConstFunctionCall = false;
645  bool nonlocal = false; // nonlocal variable used in condition
646  std::set<int> vars; // variables used in condition
647  visitAstNodes(condTok,
648  [&](const Token *cond) {
649  if (Token::Match(cond, "%name% (")) {
650  functionCall = true;
651  nonConstFunctionCall = isNonConstFunctionCall(cond, mSettings->library);
652  if (nonConstFunctionCall)
653  return ChildrenToVisit::done;
654  }
655 
656  if (cond->varId()) {
657  vars.insert(cond->varId());
658  const Variable *var = cond->variable();
659  if (!nonlocal && var) {
660  if (!(var->isLocal() || var->isArgument()))
661  nonlocal = true;
662  else if ((var->isPointer() || var->isReference()) && !Token::Match(cond->astParent(), "%oror%|&&|!"))
663  // TODO: if var is pointer check what it points at
664  nonlocal = true;
665  }
666  } else if (!nonlocal && cond->isName()) {
667  // varid is 0. this is possibly a nonlocal variable..
668  nonlocal = Token::Match(cond->astParent(), "%cop%|(|[") || Token::Match(cond, "%name% .") || (cond->isCpp() && cond->str() == "this");
669  } else {
671  }
672  return ChildrenToVisit::none;
673  });
674 
675  if (nonConstFunctionCall)
676  continue;
677 
678  std::vector<const Variable*> varsInCond;
679  visitAstNodes(condTok,
680  [&varsInCond](const Token *cond) {
681  if (cond->variable()) {
682  const Variable *var = cond->variable();
683  if (std::find(varsInCond.cbegin(), varsInCond.cend(), var) == varsInCond.cend())
684  varsInCond.push_back(var);
685  }
687  });
688 
689  // parse until second condition is reached..
690  enum MULTICONDITIONTYPE { INNER, AFTER };
691  const Token *tok;
692 
693  // Parse inner condition first and then early return condition
694  std::vector<MULTICONDITIONTYPE> types = {MULTICONDITIONTYPE::INNER};
695  if (Token::Match(scope.bodyStart, "{ return|throw|continue|break"))
696  types.push_back(MULTICONDITIONTYPE::AFTER);
697  for (const MULTICONDITIONTYPE type:types) {
698  if (type == MULTICONDITIONTYPE::AFTER) {
699  tok = scope.bodyEnd->next();
700  } else {
701  tok = scope.bodyStart;
702  }
703  const Token * const endToken = tok->scope()->bodyEnd;
704 
705  for (; tok && tok != endToken; tok = tok->next()) {
706  if (isExpressionChangedAt(cond1, tok, 0, false, *mSettings))
707  break;
708  if (Token::Match(tok, "if|return")) {
709  const Token * condStartToken = tok->str() == "if" ? tok->next() : tok;
710  const Token * condEndToken = tok->str() == "if" ? condStartToken->link() : Token::findsimplematch(condStartToken, ";");
711  // Does condition modify tracked variables?
712  if (findExpressionChanged(cond1, condStartToken, condEndToken, *mSettings))
713  break;
714 
715  // Condition..
716  const Token *cond2 = tok->str() == "if" ? condStartToken->astOperand2() : condStartToken->astOperand1();
717  const bool isReturnVar = (tok->str() == "return" && !Token::Match(cond2, "%cop%"));
718 
719  ErrorPath errorPath;
720 
721  if (type == MULTICONDITIONTYPE::INNER) {
722  visitAstNodes(cond1, [&](const Token* firstCondition) {
723  if (!firstCondition)
724  return ChildrenToVisit::none;
725  if (firstCondition->str() == "&&") {
726  if (!isOppositeCond(false, firstCondition, cond2, *mSettings, true, true))
727  return ChildrenToVisit::op1_and_op2;
728  }
729  if (!firstCondition->hasKnownIntValue()) {
730  if (!isReturnVar && isOppositeCond(false, firstCondition, cond2, *mSettings, true, true, &errorPath)) {
731  if (!isAliased(vars))
732  oppositeInnerConditionError(firstCondition, cond2, errorPath);
733  } else if (!isReturnVar && isSameExpression(true, firstCondition, cond2, *mSettings, true, true, &errorPath)) {
734  identicalInnerConditionError(firstCondition, cond2, errorPath);
735  }
736  }
737  return ChildrenToVisit::none;
738  });
739  } else {
740  visitAstNodes(cond2, [&](const Token *secondCondition) {
741  if (secondCondition->str() == "||" || secondCondition->str() == "&&")
743 
744  if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) &&
745  isSameExpression(true, cond1, secondCondition, *mSettings, true, true, &errorPath)) {
746  if (!isAliased(vars) && !mTokenizer->hasIfdef(cond1, secondCondition)) {
747  identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath);
748  return ChildrenToVisit::done;
749  }
750  }
751  return ChildrenToVisit::none;
752  });
753  }
754  }
755  if (Token::Match(tok, "%name% (") &&
756  isVariablesChanged(tok, tok->linkAt(1), 0, varsInCond, *mSettings)) {
757  break;
758  }
759  if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables
760  break;
761  if (Token::Match(tok, "case|break|continue|return|throw") && tok->scope() == endToken->scope())
762  break;
763  if (Token::Match(tok, "[;{}] %name% :"))
764  break;
765  // bailout if loop is seen.
766  // TODO: handle loops better.
767  if (Token::Match(tok, "for|while|do")) {
768  const Token *tok1 = tok->next();
769  const Token *tok2;
770  if (Token::simpleMatch(tok, "do {")) {
771  if (!Token::simpleMatch(tok->linkAt(1), "} while ("))
772  break;
773  tok2 = tok->linkAt(1)->linkAt(2);
774  } else if (Token::Match(tok, "if|while (")) {
775  tok2 = tok->linkAt(1);
776  if (Token::simpleMatch(tok2, ") {"))
777  tok2 = tok2->linkAt(1);
778  if (!tok2)
779  break;
780  } else {
781  // Incomplete code
782  break;
783  }
784  const bool changed = std::any_of(vars.cbegin(), vars.cend(), [&](int varid) {
785  return isVariableChanged(tok1, tok2, varid, nonlocal, *mSettings);
786  });
787  if (changed)
788  break;
789  }
790  if ((tok->varId() && vars.find(tok->varId()) != vars.end()) ||
791  (!tok->varId() && nonlocal) ||
792  (functionCall && tok->variable() && !tok->variable()->isLocal())) {
793  if (Token::Match(tok, "%name% %assign%|++|--"))
794  break;
795  if (Token::Match(tok->astParent(), "*|.|[")) {
796  const Token *parent = tok;
797  while (Token::Match(parent->astParent(), ".|[") || (parent->astParent() && parent->astParent()->isUnaryOp("*")))
798  parent = parent->astParent();
799  if (Token::Match(parent->astParent(), "%assign%|++|--"))
800  break;
801  }
802  if (tok->isCpp() && Token::Match(tok, "%name% <<") && (!tok->valueType() || !tok->valueType()->isIntegral()))
803  break;
804  if (isLikelyStreamRead(tok->next()) || isLikelyStreamRead(tok->previous()))
805  break;
806  if (Token::Match(tok, "%name% [")) {
807  const Token *tok2 = tok->linkAt(1);
808  while (Token::simpleMatch(tok2, "] ["))
809  tok2 = tok2->linkAt(1);
810  if (Token::Match(tok2, "] %assign%|++|--"))
811  break;
812  }
813  if (Token::Match(tok->previous(), "++|--|& %name%"))
814  break;
815  if (tok->variable() &&
816  !tok->variable()->isConst() &&
817  Token::Match(tok, "%name% . %name% (")) {
818  const Function* function = tok->tokAt(2)->function();
819  if (!function || !function->isConst())
820  break;
821  }
822  if (Token::Match(tok->previous(), "[(,] *|& %name% [,)]") && isParameterChanged(tok))
823  break;
824  }
825  }
826  }
827  }
828 }
829 
830 static std::string innerSmtString(const Token * tok)
831 {
832  if (!tok)
833  return "if";
834  if (!tok->astTop())
835  return "if";
836  const Token * top = tok->astTop();
837  if (top->str() == "(" && top->astOperand1())
838  return top->astOperand1()->str();
839  return top->str();
840 }
841 
842 void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath)
843 {
844  if (diag(tok1) & diag(tok2))
845  return;
846  const std::string s1(tok1 ? tok1->expressionString() : "x");
847  const std::string s2(tok2 ? tok2->expressionString() : "!x");
848  const std::string innerSmt = innerSmtString(tok2);
849  errorPath.emplace_back(tok1, "outer condition: " + s1);
850  errorPath.emplace_back(tok2, "opposite inner condition: " + s2);
851 
852  const std::string msg("Opposite inner '" + innerSmt + "' condition leads to a dead code block.\n"
853  "Opposite inner '" + innerSmt + "' condition leads to a dead code block (outer condition is '" + s1 + "' and inner condition is '" + s2 + "').");
854  reportError(errorPath, Severity::warning, "oppositeInnerCondition", msg, CWE398, Certainty::normal);
855 }
856 
857 void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath)
858 {
859  if (diag(tok1) & diag(tok2))
860  return;
861  const std::string s1(tok1 ? tok1->expressionString() : "x");
862  const std::string s2(tok2 ? tok2->expressionString() : "x");
863  const std::string innerSmt = innerSmtString(tok2);
864  errorPath.emplace_back(tok1, "outer condition: " + s1);
865  errorPath.emplace_back(tok2, "identical inner condition: " + s2);
866 
867  const std::string msg("Identical inner '" + innerSmt + "' condition is always true.\n"
868  "Identical inner '" + innerSmt + "' condition is always true (outer condition is '" + s1 + "' and inner condition is '" + s2 + "').");
869  reportError(errorPath, Severity::warning, "identicalInnerCondition", msg, CWE398, Certainty::normal);
870 }
871 
873 {
874  if (diag(cond1) & diag(cond2))
875  return;
876 
877  const bool isReturnValue = cond2 && Token::simpleMatch(cond2->astParent(), "return");
878 
879  const std::string cond(cond1 ? cond1->expressionString() : "x");
880  const std::string value = (cond2 && cond2->valueType() && cond2->valueType()->type == ValueType::Type::BOOL) ? "false" : "0";
881 
882  errorPath.emplace_back(cond1, "If condition '" + cond + "' is true, the function will return/exit");
883  errorPath.emplace_back(cond2, (isReturnValue ? "Returning identical expression '" : "Testing identical condition '") + cond + "'");
884 
885  reportError(errorPath,
887  "identicalConditionAfterEarlyExit",
888  isReturnValue
889  ? ("Identical condition and return expression '" + cond + "', return value is always " + value)
890  : ("Identical condition '" + cond + "', second condition is always false"),
891  CWE398,
893 }
894 
895 //---------------------------------------------------------------------------
896 // if ((x != 1) || (x != 3)) // expression always true
897 // if ((x == 1) && (x == 3)) // expression always false
898 // if ((x < 1) && (x > 3)) // expression always false
899 // if ((x > 3) || (x < 10)) // expression always true
900 // if ((x > 5) && (x != 1)) // second comparison always true
901 //
902 // Check for suspect logic for an expression consisting of 2 comparison
903 // expressions with a shared variable and constants and a logical operator
904 // between them.
905 //
906 // Suggest a different logical operator when the logical operator between
907 // the comparisons is probably wrong.
908 //
909 // Inform that second comparison is always true when first comparison is true.
910 //---------------------------------------------------------------------------
911 
912 static std::string invertOperatorForOperandSwap(std::string s)
913 {
914  if (s[0] == '<')
915  s[0] = '>';
916  else if (s[0] == '>')
917  s[0] = '<';
918  return s;
919 }
920 
921 template<typename T>
922 static int sign(const T v) {
923  return static_cast<int>(v > 0) - static_cast<int>(v < 0);
924 }
925 
926 // returns 1 (-1) if the first (second) condition is sufficient, 0 if indeterminate
927 template<typename T>
928 static int sufficientCondition(std::string op1, const bool not1, const T value1, std::string op2, const bool not2, const T value2, const bool isAnd) {
929  auto transformOp = [](std::string& op, const bool invert) {
930  if (invert) {
931  if (op == "==")
932  op = "!=";
933  else if (op == "!=")
934  op = "==";
935  else if (op == "<")
936  op = ">=";
937  else if (op == ">")
938  op = "<=";
939  else if (op == "<=")
940  op = ">";
941  else if (op == ">=")
942  op = "<";
943  }
944  };
945  transformOp(op1, not1);
946  transformOp(op2, not2);
947  int res = 0;
948  bool equal = false;
949  if (op1 == op2) {
950  equal = true;
951  if (op1 == ">" || op1 == ">=")
952  res = sign(value1 - value2);
953  else if (op1 == "<" || op1 == "<=")
954  res = -sign(value1 - value2);
955  } else { // not equal
956  if (op1 == "!=")
957  res = 1;
958  else if (op2 == "!=")
959  res = -1;
960  else if (op1 == "==")
961  res = -1;
962  else if (op2 == "==")
963  res = 1;
964  else if (op1 == ">" && op2 == ">=")
965  res = sign(value1 - (value2 - 1));
966  else if (op1 == ">=" && op2 == ">")
967  res = sign((value1 - 1) - value2);
968  else if (op1 == "<" && op2 == "<=")
969  res = -sign(value1 - (value2 + 1));
970  else if (op1 == "<=" && op2 == "<")
971  res = -sign((value1 + 1) - value2);
972  }
973  return res * (isAnd == equal ? 1 : -1);
974 }
975 
976 template<typename T>
977 static bool checkIntRelation(const std::string &op, const T value1, const T value2)
978 {
979  return (op == "==" && value1 == value2) ||
980  (op == "!=" && value1 != value2) ||
981  (op == ">" && value1 > value2) ||
982  (op == ">=" && value1 >= value2) ||
983  (op == "<" && value1 < value2) ||
984  (op == "<=" && value1 <= value2);
985 }
986 
987 static bool checkFloatRelation(const std::string &op, const double value1, const double value2)
988 {
989  return (op == ">" && value1 > value2) ||
990  (op == ">=" && value1 >= value2) ||
991  (op == "<" && value1 < value2) ||
992  (op == "<=" && value1 <= value2);
993 }
994 
995 template<class T>
996 static T getvalue3(const T value1, const T value2)
997 {
998  const T min = std::min(value1, value2);
999  if (min== std::numeric_limits<T>::max())
1000  return min;
1001  return min + 1; // see #5895
1002 }
1003 
1004 template<>
1005 double getvalue3(const double value1, const double value2)
1006 {
1007  return (value1 + value2) / 2.0;
1008 }
1009 
1010 
1011 template<class T>
1012 static inline T getvalue(const int test, const T value1, const T value2)
1013 {
1014  // test:
1015  // 1 => return value that is less than both value1 and value2
1016  // 2 => return value1
1017  // 3 => return value that is between value1 and value2
1018  // 4 => return value2
1019  // 5 => return value that is larger than both value1 and value2
1020  switch (test) {
1021  case 1:
1022  return std::numeric_limits<T>::lowest();
1023  case 2:
1024  return value1;
1025  case 3:
1026  return getvalue3<T>(value1, value2);
1027  case 4:
1028  return value2;
1029  case 5:
1030  return std::numeric_limits<T>::max();
1031  }
1032  return 0;
1033 }
1034 
1035 static bool parseComparison(const Token *comp, bool &not1, std::string &op, std::string &value, const Token *&expr, bool &inconclusive)
1036 {
1037  not1 = false;
1038  while (comp && comp->str() == "!") {
1039  not1 = !(not1);
1040  comp = comp->astOperand1();
1041  }
1042 
1043  if (!comp)
1044  return false;
1045 
1046  const Token* op1 = comp->astOperand1();
1047  const Token* op2 = comp->astOperand2();
1048  if (!comp->isComparisonOp() || !op1 || !op2) {
1049  op = "!=";
1050  value = "0";
1051  expr = comp;
1052  } else if (op1->isLiteral()) {
1053  if (op1->isExpandedMacro())
1054  return false;
1055  op = invertOperatorForOperandSwap(comp->str());
1056  if (op1->enumerator() && op1->enumerator()->value_known)
1057  value = std::to_string(op1->enumerator()->value);
1058  else
1059  value = op1->str();
1060  expr = op2;
1061  } else if (comp->astOperand2()->isLiteral()) {
1062  if (op2->isExpandedMacro())
1063  return false;
1064  op = comp->str();
1065  if (op2->enumerator() && op2->enumerator()->value_known)
1066  value = std::to_string(op2->enumerator()->value);
1067  else
1068  value = op2->str();
1069  expr = op1;
1070  } else {
1071  op = "!=";
1072  value = "0";
1073  expr = comp;
1074  }
1075 
1076  inconclusive = inconclusive || ((value)[0] == '\'' && !(op == "!=" || op == "=="));
1077 
1078  // Only float and int values are currently handled
1079  return MathLib::isInt(value) || MathLib::isFloat(value) || (value[0] == '\'');
1080 }
1081 
1082 static std::string conditionString(bool not1, const Token *expr1, const std::string &op, const std::string &value1)
1083 {
1084  if (expr1->astParent()->isComparisonOp())
1085  return std::string(not1 ? "!(" : "") + expr1->expressionString() +
1086  " " +
1087  op +
1088  " " +
1089  value1 +
1090  (not1 ? ")" : "");
1091 
1092  return std::string(not1 ? "!" : "") + expr1->expressionString();
1093 }
1094 
1095 static std::string conditionString(const Token * tok)
1096 {
1097  if (!tok)
1098  return "";
1099  if (tok->isComparisonOp()) {
1100  bool inconclusive = false;
1101  bool not_;
1102  std::string op, value;
1103  const Token *expr;
1104  if (parseComparison(tok, not_, op, value, expr, inconclusive) && expr->isName()) {
1105  return conditionString(not_, expr, op, value);
1106  }
1107  }
1108  if (Token::Match(tok, "%cop%|&&|%oror%")) {
1109  if (tok->astOperand2())
1110  return conditionString(tok->astOperand1()) + " " + tok->str() + " " + conditionString(tok->astOperand2());
1111  return tok->str() + "(" + conditionString(tok->astOperand1()) + ")";
1112 
1113  }
1114  return tok->expressionString();
1115 }
1116 
1117 static bool isIfConstexpr(const Token* tok) {
1118  const Token* const top = tok->astTop();
1119  return top && Token::simpleMatch(top->astOperand1(), "if") && top->astOperand1()->isConstexpr();
1120 }
1121 
1123 {
1124  const bool printStyle = mSettings->severity.isEnabled(Severity::style);
1125  const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
1126  if (!printWarning && !printStyle && !mSettings->isPremiumEnabled("incorrectLogicOperator"))
1127  return;
1128  const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
1129 
1130  logChecker("CheckCondition::checkIncorrectLogicOperator"); // style,warning
1131 
1132  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1133  for (const Scope * scope : symbolDatabase->functionScopes) {
1134 
1135  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1136  if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2())
1137  continue;
1138 
1139 
1140  // 'A && (!A || B)' is equivalent to 'A && B'
1141  // 'A || (!A && B)' is equivalent to 'A || B'
1142  // 'A && (A || B)' is equivalent to 'A'
1143  // 'A || (A && B)' is equivalent to 'A'
1144  if (printStyle &&
1145  ((tok->str() == "||" && tok->astOperand2()->str() == "&&") ||
1146  (tok->str() == "&&" && tok->astOperand2()->str() == "||"))) {
1147  const Token* tok2 = tok->astOperand2()->astOperand1();
1148  if (isOppositeCond(true, tok->astOperand1(), tok2, *mSettings, true, false)) {
1149  std::string expr1(tok->astOperand1()->expressionString());
1150  std::string expr2(tok->astOperand2()->astOperand1()->expressionString());
1151  std::string expr3(tok->astOperand2()->astOperand2()->expressionString());
1152  // make copy for later because the original string might get overwritten
1153  const std::string expr1VerboseMsg = expr1;
1154  const std::string expr2VerboseMsg = expr2;
1155  const std::string expr3VerboseMsg = expr3;
1156 
1157  if (expr1.length() + expr2.length() + expr3.length() > 50U) {
1158  if (expr1[0] == '!' && expr2[0] != '!') {
1159  expr1 = "!A";
1160  expr2 = "A";
1161  } else {
1162  expr1 = "A";
1163  expr2 = "!A";
1164  }
1165 
1166  expr3 = "B";
1167  }
1168 
1169  const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")";
1170  const std::string cond2 = expr1 + " " + tok->str() + " " + expr3;
1171 
1172  const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg;
1173  const std::string cond2VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr3VerboseMsg;
1174  // for the --verbose message, transform the actual condition and print it
1175  const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n"
1176  "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'.";
1177  redundantConditionError(tok, msg, false);
1178  continue;
1179  }
1180  if (isSameExpression(false, tok->astOperand1(), tok2, *mSettings, true, true)) {
1181  std::string expr1(tok->astOperand1()->expressionString());
1182  std::string expr2(tok->astOperand2()->astOperand1()->expressionString());
1183  std::string expr3(tok->astOperand2()->astOperand2()->expressionString());
1184  // make copy for later because the original string might get overwritten
1185  const std::string expr1VerboseMsg = expr1;
1186  const std::string expr2VerboseMsg = expr2;
1187  const std::string expr3VerboseMsg = expr3;
1188 
1189  if (expr1.length() + expr2.length() + expr3.length() > 50U) {
1190  expr1 = "A";
1191  expr2 = "A";
1192  expr3 = "B";
1193  }
1194 
1195  const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")";
1196  const std::string cond2 = std::move(expr1);
1197 
1198  const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg;
1199  const std::string& cond2VerboseMsg = expr1VerboseMsg;
1200  // for the --verbose message, transform the actual condition and print it
1201  const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n"
1202  "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'.";
1203  redundantConditionError(tok, msg, false);
1204  continue;
1205  }
1206  }
1207 
1208  // Comparison #1 (LHS)
1209  const Token *comp1 = tok->astOperand1();
1210  if (comp1->str() == tok->str())
1211  comp1 = comp1->astOperand2();
1212 
1213  // Comparison #2 (RHS)
1214  const Token *comp2 = tok->astOperand2();
1215 
1216  bool inconclusive = false;
1217  bool parseable = true;
1218 
1219  // Parse LHS
1220  bool not1;
1221  std::string op1, value1;
1222  const Token *expr1 = nullptr;
1223  parseable &= (parseComparison(comp1, not1, op1, value1, expr1, inconclusive));
1224 
1225  // Parse RHS
1226  bool not2;
1227  std::string op2, value2;
1228  const Token *expr2 = nullptr;
1229  parseable &= (parseComparison(comp2, not2, op2, value2, expr2, inconclusive));
1230 
1231  if (inconclusive && !printInconclusive)
1232  continue;
1233 
1234  const bool isUnknown = (expr1 && expr1->valueType() && expr1->valueType()->type == ValueType::UNKNOWN_TYPE) ||
1235  (expr2 && expr2->valueType() && expr2->valueType()->type == ValueType::UNKNOWN_TYPE);
1236  if (isUnknown)
1237  continue;
1238 
1239  const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2);
1240 
1241  ErrorPath errorPath;
1242 
1243  // Opposite comparisons around || or && => always true or always false
1244  const bool isLogicalOr(tok->str() == "||");
1245  if (!isfloat && isOppositeCond(isLogicalOr, tok->astOperand1(), tok->astOperand2(), *mSettings, true, true, &errorPath)) {
1246  if (!isIfConstexpr(tok)) {
1247  const bool alwaysTrue(isLogicalOr);
1248  incorrectLogicOperatorError(tok, conditionString(tok), alwaysTrue, inconclusive, errorPath);
1249  }
1250  continue;
1251  }
1252 
1253  if (!parseable)
1254  continue;
1255 
1256  if (isSameExpression(true, comp1, comp2, *mSettings, true, true))
1257  continue; // same expressions => only report that there are same expressions
1258  if (!isSameExpression(true, expr1, expr2, *mSettings, true, true))
1259  continue;
1260 
1261 
1262  // don't check floating point equality comparisons. that is bad
1263  // and deserves different warnings.
1264  if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!="))
1265  continue;
1266 
1267 
1268  const double d1 = (isfloat) ? MathLib::toDoubleNumber(value1) : 0;
1269  const double d2 = (isfloat) ? MathLib::toDoubleNumber(value2) : 0;
1270  const MathLib::bigint i1 = (isfloat) ? 0 : MathLib::toBigNumber(value1);
1271  const MathLib::bigint i2 = (isfloat) ? 0 : MathLib::toBigNumber(value2);
1272  const bool useUnsignedInt = (std::numeric_limits<MathLib::bigint>::max()==i1) || (std::numeric_limits<MathLib::bigint>::max()==i2);
1273  const MathLib::biguint u1 = (useUnsignedInt) ? MathLib::toBigNumber(value1) : 0;
1274  const MathLib::biguint u2 = (useUnsignedInt) ? MathLib::toBigNumber(value2) : 0;
1275  // evaluate if expression is always true/false
1276  bool alwaysTrue = true, alwaysFalse = true;
1277  bool firstTrue = true, secondTrue = true;
1278  const bool isAnd = tok->str() == "&&";
1279  for (int test = 1; test <= 5; ++test) {
1280  // test:
1281  // 1 => testvalue is less than both value1 and value2
1282  // 2 => testvalue is value1
1283  // 3 => testvalue is between value1 and value2
1284  // 4 => testvalue value2
1285  // 5 => testvalue is larger than both value1 and value2
1286  bool result1, result2;
1287  if (isfloat) {
1288  const auto testvalue = getvalue<double>(test, d1, d2);
1289  result1 = checkFloatRelation(op1, testvalue, d1);
1290  result2 = checkFloatRelation(op2, testvalue, d2);
1291  } else if (useUnsignedInt) {
1292  const auto testvalue = getvalue<MathLib::biguint>(test, u1, u2);
1293  result1 = checkIntRelation(op1, testvalue, u1);
1294  result2 = checkIntRelation(op2, testvalue, u2);
1295  } else {
1296  const auto testvalue = getvalue<MathLib::bigint>(test, i1, i2);
1297  result1 = checkIntRelation(op1, testvalue, i1);
1298  result2 = checkIntRelation(op2, testvalue, i2);
1299  }
1300  if (not1)
1301  result1 = !result1;
1302  if (not2)
1303  result2 = !result2;
1304  if (isAnd) {
1305  alwaysTrue &= (result1 && result2);
1306  alwaysFalse &= !(result1 && result2);
1307  } else {
1308  alwaysTrue &= (result1 || result2);
1309  alwaysFalse &= !(result1 || result2);
1310  }
1311  firstTrue &= !(!result1 && result2);
1312  secondTrue &= !(result1 && !result2);
1313  }
1314 
1315  const std::string cond1str = conditionString(not1, expr1, op1, value1);
1316  const std::string cond2str = conditionString(not2, expr2, op2, value2);
1317  if (printWarning && (alwaysTrue || alwaysFalse)) {
1318  const std::string text = cond1str + " " + tok->str() + " " + cond2str;
1319  incorrectLogicOperatorError(tok, text, alwaysTrue, inconclusive, std::move(errorPath));
1320  } else if (printStyle && (firstTrue || secondTrue)) {
1321  // cppcheck-suppress accessMoved - TODO: FP - see #12174
1322  const int which = isfloat ? sufficientCondition(std::move(op1), not1, d1, std::move(op2), not2, d2, isAnd) : sufficientCondition(std::move(op1), not1, i1, std::move(op2), not2, i2, isAnd);
1323  std::string text;
1324  if (which != 0) {
1325  text = "The condition '" + (which == 1 ? cond2str : cond1str) + "' is redundant since '" + (which == 1 ? cond1str : cond2str) + "' is sufficient.";
1326  } else
1327  text = "If '" + (secondTrue ? cond1str : cond2str) + "', the comparison '" + (secondTrue ? cond2str : cond1str) + "' is always true.";
1329  }
1330  }
1331  }
1332 }
1333 
1334 void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors)
1335 {
1336  if (diag(tok))
1337  return;
1338  errors.emplace_back(tok, "");
1339  if (always)
1340  reportError(errors, Severity::warning, "incorrectLogicOperator",
1341  "Logical disjunction always evaluates to true: " + condition + ".\n"
1342  "Logical disjunction always evaluates to true: " + condition + ". "
1343  "Are these conditions necessary? Did you intend to use && instead? Are the numbers correct? Are you comparing the correct variables?", CWE571, inconclusive ? Certainty::inconclusive : Certainty::normal);
1344  else
1345  reportError(errors, Severity::warning, "incorrectLogicOperator",
1346  "Logical conjunction always evaluates to false: " + condition + ".\n"
1347  "Logical conjunction always evaluates to false: " + condition + ". "
1348  "Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables?", CWE570, inconclusive ? Certainty::inconclusive : Certainty::normal);
1349 }
1350 
1351 void CheckCondition::redundantConditionError(const Token *tok, const std::string &text, bool inconclusive)
1352 {
1353  if (diag(tok))
1354  return;
1355  reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
1356 }
1357 
1358 //-----------------------------------------------------------------------------
1359 // Detect "(var % val1) > val2" where val2 is >= val1.
1360 //-----------------------------------------------------------------------------
1362 {
1364  return;
1365 
1366  logChecker("CheckCondition::checkModuloAlwaysTrueFalse"); // warning
1367 
1368  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1369  for (const Scope * scope : symbolDatabase->functionScopes) {
1370  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1371  if (!tok->isComparisonOp())
1372  continue;
1373  const Token *num, *modulo;
1374  if (Token::simpleMatch(tok->astOperand1(), "%") && Token::Match(tok->astOperand2(), "%num%")) {
1375  modulo = tok->astOperand1();
1376  num = tok->astOperand2();
1377  } else if (Token::Match(tok->astOperand1(), "%num%") && Token::simpleMatch(tok->astOperand2(), "%")) {
1378  num = tok->astOperand1();
1379  modulo = tok->astOperand2();
1380  } else {
1381  continue;
1382  }
1383 
1384  if (Token::Match(modulo->astOperand2(), "%num%") &&
1385  MathLib::isLessEqual(modulo->astOperand2()->str(), num->str()))
1386  moduloAlwaysTrueFalseError(tok, modulo->astOperand2()->str());
1387  }
1388  }
1389 }
1390 
1391 void CheckCondition::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal)
1392 {
1393  if (diag(tok))
1394  return;
1395  reportError(tok, Severity::warning, "moduloAlwaysTrueFalse",
1396  "Comparison of modulo result is predetermined, because it is always less than " + maxVal + ".", CWE398, Certainty::normal);
1397 }
1398 
1399 static int countPar(const Token *tok1, const Token *tok2)
1400 {
1401  int par = 0;
1402  for (const Token *tok = tok1; tok && tok != tok2; tok = tok->next()) {
1403  if (tok->str() == "(")
1404  ++par;
1405  else if (tok->str() == ")")
1406  --par;
1407  else if (tok->str() == ";")
1408  return -1;
1409  }
1410  return par;
1411 }
1412 
1413 //---------------------------------------------------------------------------
1414 // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))'
1415 // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))'
1416 //---------------------------------------------------------------------------
1418 {
1419  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("clarifyCondition"))
1420  return;
1421 
1422  logChecker("CheckCondition::clarifyCondition"); // style
1423 
1424  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1425  for (const Scope * scope : symbolDatabase->functionScopes) {
1426  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1427  if (Token::Match(tok, "( %name% [=&|^]")) {
1428  for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) {
1429  if (tok2->str() == "(" || tok2->str() == "[")
1430  tok2 = tok2->link();
1431  else if (tok2->isComparisonOp()) {
1432  // This might be a template
1433  if (!tok2->isC() && tok2->link())
1434  break;
1435  if (Token::simpleMatch(tok2->astParent(), "?"))
1436  break;
1437  clarifyConditionError(tok, tok->strAt(2) == "=", false);
1438  break;
1439  } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".")
1440  break;
1441  }
1442  } else if (tok->tokType() == Token::eBitOp && !tok->isUnaryOp("&")) {
1443  if (tok->astOperand2() && tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2())
1444  continue;
1445 
1446  // using boolean result in bitwise operation ! x [&|^]
1447  const ValueType* vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr;
1448  const ValueType* vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr;
1449  if (vt1 && vt1->type == ValueType::BOOL && !Token::Match(tok->astOperand1(), "%name%|(|[|::|.") && countPar(tok->astOperand1(), tok) == 0)
1450  clarifyConditionError(tok, false, true);
1451  else if (vt2 && vt2->type == ValueType::BOOL && !Token::Match(tok->astOperand2(), "%name%|(|[|::|.") && countPar(tok, tok->astOperand2()) == 0)
1452  clarifyConditionError(tok, false, true);
1453  }
1454  }
1455  }
1456 }
1457 
1458 void CheckCondition::clarifyConditionError(const Token *tok, bool assign, bool boolop)
1459 {
1460  std::string errmsg;
1461 
1462  if (assign)
1463  errmsg = "Suspicious condition (assignment + comparison); Clarify expression with parentheses.";
1464 
1465  else if (boolop)
1466  errmsg = "Boolean result is used in bitwise operation. Clarify expression with parentheses.\n"
1467  "Suspicious expression. Boolean result is used in bitwise operation. The operator '!' "
1468  "and the comparison operators have higher precedence than bitwise operators. "
1469  "It is recommended that the expression is clarified with parentheses.";
1470  else
1471  errmsg = "Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n"
1472  "Suspicious condition. Comparison operators have higher precedence than bitwise operators. "
1473  "Please clarify the condition with parentheses.";
1474 
1475  reportError(tok,
1477  "clarifyCondition",
1478  errmsg, CWE398, Certainty::normal);
1479 }
1480 
1482 {
1484  !mSettings->isPremiumEnabled("alwaysTrue") &&
1485  !mSettings->isPremiumEnabled("alwaysFalse"))
1486  return;
1487 
1488  logChecker("CheckCondition::alwaysTrueFalse"); // style
1489 
1490  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1491  for (const Scope * scope : symbolDatabase->functionScopes) {
1492  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1493  // don't write false positives when templates are used or inside of asserts or non-evaluated contexts
1494  if (tok->link() && (Token::simpleMatch(tok, "<") ||
1495  Token::Match(tok->previous(), "static_assert|assert|ASSERT|sizeof|decltype ("))) {
1496  tok = tok->link();
1497  continue;
1498  }
1499  if (!tok->hasKnownIntValue())
1500  continue;
1501  if (Token::Match(tok->previous(), "%name% (") && tok->previous()->function()) {
1502  const Function* f = tok->previous()->function();
1503  if (f->functionScope && Token::Match(f->functionScope->bodyStart, "{ return true|false ;"))
1504  continue;
1505  }
1506  const Token* condition = nullptr;
1507  {
1508  // is this a condition..
1509  const Token *parent = tok->astParent();
1510  while (Token::Match(parent, "%oror%|&&"))
1511  parent = parent->astParent();
1512  if (!parent)
1513  continue;
1514  if (parent->str() == "?" && precedes(tok, parent))
1515  condition = parent;
1516  else if (Token::Match(parent->previous(), "if|while ("))
1517  condition = parent->previous();
1518  else if (Token::simpleMatch(parent, "return"))
1519  condition = parent;
1520  else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() &&
1521  Token::simpleMatch(parent->astParent()->astParent()->previous(), "for ("))
1522  condition = parent->astParent()->astParent()->previous();
1523  else if (Token::Match(tok, "%comp%"))
1524  condition = tok;
1525  else
1526  continue;
1527  }
1528  // Skip already diagnosed values
1529  if (diag(tok, false))
1530  continue;
1531  if (condition->isConstexpr())
1532  continue;
1533  if (!isUsedAsBool(tok, *mSettings))
1534  continue;
1535  if (Token::simpleMatch(condition, "return") && Token::Match(tok, "%assign%"))
1536  continue;
1537  if (Token::simpleMatch(tok->astParent(), "return") && Token::Match(tok, ".|%var%"))
1538  continue;
1539  if (Token::Match(tok, "%num%|%bool%|%char%"))
1540  continue;
1541  if (Token::Match(tok, "! %num%|%bool%|%char%"))
1542  continue;
1543  if (Token::Match(tok, "%oror%|&&")) {
1544  bool bail = false;
1545  for (const Token* op : { tok->astOperand1(), tok->astOperand2() }) {
1546  if (op->hasKnownIntValue() && (!op->isLiteral() || op->isBoolean())) {
1547  bail = true;
1548  break;
1549  }
1550  }
1551  if (bail)
1552  continue;
1553  }
1554  if (Token::simpleMatch(tok, ":"))
1555  continue;
1556  if (Token::Match(tok->astOperand1(), "%name% (") && Token::simpleMatch(tok->astParent(), "return"))
1557  continue;
1558  if (tok->isComparisonOp() && isWithoutSideEffects(tok->astOperand1()) &&
1559  isSameExpression(true,
1560  tok->astOperand1(),
1561  tok->astOperand2(),
1562  *mSettings,
1563  true,
1564  true))
1565  continue;
1566  if (isConstVarExpression(tok, [](const Token* tok) {
1567  return Token::Match(tok, "[|(|&|+|-|*|/|%|^|>>|<<") && !Token::simpleMatch(tok, "( )");
1568  }))
1569  continue;
1570 
1571  // there are specific warnings about nonzero expressions (pointer/unsigned)
1572  // do not write alwaysTrueFalse for these comparisons.
1573  {
1574  const ValueFlow::Value *zeroValue = nullptr;
1575  const Token *nonZeroExpr = nullptr;
1576  if (CheckOther::comparisonNonZeroExpressionLessThanZero(tok, zeroValue, nonZeroExpr, /*suppress*/ true) ||
1577  CheckOther::testIfNonZeroExpressionIsPositive(tok, zeroValue, nonZeroExpr))
1578  continue;
1579  }
1580 
1581  // Don't warn when there are expanded macros..
1582  bool isExpandedMacro = false;
1583  visitAstNodes(tok, [&](const Token * tok2) {
1584  if (!tok2)
1585  return ChildrenToVisit::none;
1586  if (tok2->isExpandedMacro()) {
1587  isExpandedMacro = true;
1588  return ChildrenToVisit::done;
1589  }
1591  });
1592  if (isExpandedMacro)
1593  continue;
1594  for (const Token *parent = tok; parent; parent = parent->astParent()) {
1595  if (parent->isExpandedMacro()) {
1596  isExpandedMacro = true;
1597  break;
1598  }
1599  }
1600  if (isExpandedMacro)
1601  continue;
1602 
1603  // don't warn when condition checks sizeof result
1604  bool hasSizeof = false;
1605  visitAstNodes(tok, [&](const Token * tok2) {
1606  if (!tok2)
1607  return ChildrenToVisit::none;
1608  if (tok2->isNumber())
1609  return ChildrenToVisit::none;
1610  if (Token::simpleMatch(tok2->previous(), "sizeof (")) {
1611  hasSizeof = true;
1612  return ChildrenToVisit::none;
1613  }
1614  if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) {
1615  return ChildrenToVisit::op1_and_op2;
1616  }
1617  return ChildrenToVisit::none;
1618  });
1619  if (hasSizeof)
1620  continue;
1621 
1622  alwaysTrueFalseError(tok, condition, &tok->values().front());
1623  }
1624  }
1625 }
1626 
1627 void CheckCondition::alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value)
1628 {
1629  const bool alwaysTrue = value && (value->intvalue != 0 || value->isImpossible());
1630  const std::string expr = tok ? tok->expressionString() : std::string("x");
1631  const std::string conditionStr = (Token::simpleMatch(condition, "return") ? "Return value" : "Condition");
1632  const std::string errmsg = conditionStr + " '" + expr + "' is always " + bool_to_string(alwaysTrue);
1633  const ErrorPath errorPath = getErrorPath(tok, value, errmsg);
1634  reportError(errorPath,
1636  "knownConditionTrueFalse",
1637  errmsg,
1638  (alwaysTrue ? CWE571 : CWE570), Certainty::normal);
1639 }
1640 
1642 {
1643  // Interesting blogs:
1644  // https://www.airs.com/blog/archives/120
1645  // https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html
1646  // https://research.checkpoint.com/2020/optout-compiler-undefined-behavior-optimizations/
1647 
1648  // x + c < x -> false
1649  // x + c <= x -> false
1650  // x + c > x -> true
1651  // x + c >= x -> true
1652 
1653  // x + y < x -> y < 0
1654 
1655 
1657  return;
1658 
1659  logChecker("CheckCondition::checkInvalidTestForOverflow"); // warning
1660 
1661  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
1662  if (!Token::Match(tok, "<|<=|>=|>") || !tok->isBinaryOp())
1663  continue;
1664 
1665  const Token *lhsTokens[2] = {tok->astOperand1(), tok->astOperand2()};
1666  for (const Token *lhs: lhsTokens) {
1667  std::string cmp = tok->str();
1668  if (lhs == tok->astOperand2())
1669  cmp[0] = (cmp[0] == '<') ? '>' : '<';
1670 
1671  if (!Token::Match(lhs, "[+-]") || !lhs->isBinaryOp())
1672  continue;
1673 
1674  const bool isSignedInteger = lhs->valueType() && lhs->valueType()->isIntegral() && lhs->valueType()->sign == ValueType::Sign::SIGNED;
1675  const bool isPointer = lhs->valueType() && lhs->valueType()->pointer > 0;
1676  if (!isSignedInteger && !isPointer)
1677  continue;
1678 
1679  const Token *exprTokens[2] = {lhs->astOperand1(), lhs->astOperand2()};
1680  for (const Token *expr: exprTokens) {
1681  if (lhs->str() == "-" && expr == lhs->astOperand2())
1682  continue; // TODO?
1683 
1684  if (expr->hasKnownIntValue())
1685  continue;
1686 
1687  if (!isSameExpression(true,
1688  expr,
1689  lhs->astSibling(),
1690  *mSettings,
1691  true,
1692  false))
1693  continue;
1694 
1695  const Token * const other = expr->astSibling();
1696 
1697  // x [+-] c cmp x
1698  if ((other->isNumber() && other->getKnownIntValue() > 0) ||
1699  (!other->isNumber() && other->valueType() && other->valueType()->isIntegral() && other->valueType()->sign == ValueType::Sign::UNSIGNED)) {
1700  bool result;
1701  if (lhs->str() == "+")
1702  result = (cmp == ">" || cmp == ">=");
1703  else
1704  result = (cmp == "<" || cmp == "<=");
1705  invalidTestForOverflow(tok, lhs->valueType(), bool_to_string(result));
1706  continue;
1707  }
1708 
1709  // x + y cmp x
1710  if (lhs->str() == "+" && other->varId() > 0) {
1711  const std::string result = other->str() + cmp + "0";
1712  invalidTestForOverflow(tok, lhs->valueType(), result);
1713  continue;
1714  }
1715 
1716  // x - y cmp x
1717  if (lhs->str() == "-" && other->varId() > 0) {
1718  std::string cmp2 = cmp;
1719  cmp2[0] = (cmp[0] == '<') ? '>' : '<';
1720  const std::string result = other->str() + cmp2 + "0";
1721  invalidTestForOverflow(tok, lhs->valueType(), result);
1722  continue;
1723  }
1724  }
1725  }
1726  }
1727 }
1728 
1729 void CheckCondition::invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace)
1730 {
1731  const std::string expr = (tok ? tok->expressionString() : std::string("x + c < x"));
1732  const std::string overflow = (valueType && valueType->pointer) ? "pointer overflow" : "signed integer overflow";
1733 
1734  std::string errmsg =
1735  "Invalid test for overflow '" + expr + "'; " + overflow + " is undefined behavior.";
1736  if (replace == "false" || replace == "true")
1737  errmsg += " Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " + replace + ".";
1738  else
1739  errmsg += " Some mainstream compilers removes handling of overflows when optimising the code and change the code to '" + replace + "'.";
1740  reportError(tok, Severity::warning, "invalidTestForOverflow", errmsg, uncheckedErrorConditionCWE, Certainty::normal);
1741 }
1742 
1743 
1745 {
1747  return;
1748 
1749  logChecker("CheckCondition::checkPointerAdditionResultNotNull"); // warning
1750 
1751  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1752  for (const Scope * scope : symbolDatabase->functionScopes) {
1753 
1754  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1755  if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2())
1756  continue;
1757 
1758  // Macros might have pointless safety checks
1759  if (tok->isExpandedMacro())
1760  continue;
1761 
1762  const Token *calcToken, *exprToken;
1763  if (tok->astOperand1()->str() == "+") {
1764  calcToken = tok->astOperand1();
1765  exprToken = tok->astOperand2();
1766  } else if (tok->astOperand2()->str() == "+") {
1767  calcToken = tok->astOperand2();
1768  exprToken = tok->astOperand1();
1769  } else
1770  continue;
1771 
1772  // pointer comparison against NULL (ptr+12==0)
1773  if (calcToken->hasKnownIntValue())
1774  continue;
1775  if (!calcToken->valueType() || calcToken->valueType()->pointer==0)
1776  continue;
1777  if (!exprToken->hasKnownIntValue() || !exprToken->getValue(0))
1778  continue;
1779 
1780  pointerAdditionResultNotNullError(tok, calcToken);
1781  }
1782  }
1783 }
1784 
1786 {
1787  const std::string s = calc ? calc->expressionString() : "ptr+1";
1788  reportError(tok, Severity::warning, "pointerAdditionResultNotNull", "Comparison is wrong. Result of '" + s + "' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour.");
1789 }
1790 
1792 {
1793  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("duplicateConditionalAssign"))
1794  return;
1795 
1796  logChecker("CheckCondition::checkDuplicateConditionalAssign"); // style
1797 
1798  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1799  for (const Scope *scope : symbolDatabase->functionScopes) {
1800  for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1801  if (!Token::simpleMatch(tok, "if ("))
1802  continue;
1803  if (!Token::simpleMatch(tok->next()->link(), ") {"))
1804  continue;
1805  const Token *blockTok = tok->next()->link()->next();
1806  const Token *condTok = tok->next()->astOperand2();
1807  const bool isBoolVar = Token::Match(condTok, "!| %var%");
1808  if (!isBoolVar && !Token::Match(condTok, "==|!="))
1809  continue;
1810  if ((isBoolVar || condTok->str() == "!=") && Token::simpleMatch(blockTok->link(), "} else {"))
1811  continue;
1812  if (!blockTok->next())
1813  continue;
1814  const Token *assignTok = blockTok->next()->astTop();
1815  if (!Token::simpleMatch(assignTok, "="))
1816  continue;
1817  if (nextAfterAstRightmostLeaf(assignTok) != blockTok->link()->previous())
1818  continue;
1819  bool isRedundant = false;
1820  if (isBoolVar) {
1821  const bool isNegation = condTok->str() == "!";
1822  const Token* const varTok = isNegation ? condTok->next() : condTok;
1823  const ValueType* vt = varTok->variable() ? varTok->variable()->valueType() : nullptr;
1824  if (!(vt && vt->type == ValueType::Type::BOOL && !vt->pointer))
1825  continue;
1826 
1827  if (!(assignTok->astOperand1() && assignTok->astOperand1()->varId() == varTok->varId()))
1828  continue;
1829  if (!(assignTok->astOperand2() && assignTok->astOperand2()->hasKnownIntValue()))
1830  continue;
1831  const MathLib::bigint val = assignTok->astOperand2()->getKnownIntValue();
1832  if (val < 0 || val > 1)
1833  continue;
1834  isRedundant = (isNegation && val == 0) || (!isNegation && val == 1);
1835  } else { // comparison
1836  if (!isSameExpression(
1837  true, condTok->astOperand1(), assignTok->astOperand1(), *mSettings, true, true))
1838  continue;
1839  if (!isSameExpression(
1840  true, condTok->astOperand2(), assignTok->astOperand2(), *mSettings, true, true))
1841  continue;
1842  }
1843  duplicateConditionalAssignError(condTok, assignTok, isRedundant);
1844  }
1845  }
1846 }
1847 
1848 void CheckCondition::duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant)
1849 {
1850  ErrorPath errors;
1851  std::string msg = "Duplicate expression for the condition and assignment.";
1852  if (condTok && assignTok) {
1853  if (condTok->str() == "==") {
1854  msg = "Assignment '" + assignTok->expressionString() + "' is redundant with condition '" + condTok->expressionString() + "'.";
1855  errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "'");
1856  errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "' is redundant");
1857  } else {
1858  msg = "The statement 'if (" + condTok->expressionString() + ") " + assignTok->expressionString();
1859  msg += isRedundant ? "' is redundant." : "' is logically equivalent to '" + assignTok->expressionString() + "'.";
1860  errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "'");
1861  errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "' is redundant");
1862  }
1863  }
1864 
1865  reportError(
1866  errors, Severity::style, "duplicateConditionalAssign", msg, CWE398, Certainty::normal);
1867 }
1868 
1869 
1871 {
1872  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("assignmentInCondition"))
1873  return;
1874 
1875  logChecker("CheckCondition::checkAssignmentInCondition"); // style
1876 
1877  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1878  for (const Scope * scope : symbolDatabase->functionScopes) {
1879  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1880  if (tok->str() != "=")
1881  continue;
1882  if (!tok->astParent())
1883  continue;
1884 
1885  // Is this assignment of container/iterator?
1886  if (!tok->valueType())
1887  continue;
1888  if (tok->valueType()->pointer > 0)
1889  continue;
1890  if (tok->valueType()->type != ValueType::Type::CONTAINER && tok->valueType()->type != ValueType::Type::ITERATOR)
1891  continue;
1892 
1893  // warn if this is a conditional expression..
1894  if (Token::Match(tok->astParent()->previous(), "if|while ("))
1895  assignmentInCondition(tok);
1896  else if (Token::Match(tok->astParent(), "%oror%|&&"))
1897  assignmentInCondition(tok);
1898  else if (Token::simpleMatch(tok->astParent(), "?") && tok == tok->astParent()->astOperand1())
1899  assignmentInCondition(tok);
1900  }
1901  }
1902 }
1903 
1905 {
1906  std::string expr = eq ? eq->expressionString() : "x=y";
1907 
1908  reportError(
1909  eq,
1911  "assignmentInCondition",
1912  "Suspicious assignment in condition. Condition '" + expr + "' is always true.",
1913  CWE571,
1915 }
1916 
1918 {
1919  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("compareValueOutOfTypeRange"))
1920  return;
1921 
1922  if (mSettings->platform.type == Platform::Type::Native ||
1923  mSettings->platform.type == Platform::Type::Unspecified)
1924  return;
1925 
1926  logChecker("CheckCondition::checkCompareValueOutOfTypeRange"); // style,platform
1927 
1928  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1929  for (const Scope * scope : symbolDatabase->functionScopes) {
1930  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1931  if (!tok->isComparisonOp() || !tok->isBinaryOp())
1932  continue;
1933 
1934  for (int i = 0; i < 2; ++i) {
1935  const Token * const valueTok = (i == 0) ? tok->astOperand1() : tok->astOperand2();
1936  const Token * const typeTok = valueTok->astSibling();
1937  if (!valueTok->hasKnownIntValue() || !typeTok->valueType() || typeTok->valueType()->pointer)
1938  continue;
1939  if (valueTok->getKnownIntValue() < 0 && valueTok->valueType() && valueTok->valueType()->sign != ValueType::Sign::SIGNED)
1940  continue;
1941  if (valueTok->valueType() && valueTok->valueType()->isTypeEqual(typeTok->valueType()))
1942  continue;
1943  int bits = 0;
1944  switch (typeTok->valueType()->type) {
1945  case ValueType::Type::BOOL:
1946  bits = 1;
1947  break;
1948  case ValueType::Type::CHAR:
1949  bits = mSettings->platform.char_bit;
1950  break;
1951  case ValueType::Type::SHORT:
1952  bits = mSettings->platform.short_bit;
1953  break;
1954  case ValueType::Type::INT:
1955  bits = mSettings->platform.int_bit;
1956  break;
1957  case ValueType::Type::LONG:
1958  bits = mSettings->platform.long_bit;
1959  break;
1960  case ValueType::Type::LONGLONG:
1962  break;
1963  default:
1964  break;
1965  }
1966  if (bits == 0 || bits >= 64)
1967  continue;
1968 
1969  const auto typeMinValue = (typeTok->valueType()->sign == ValueType::Sign::UNSIGNED) ? 0 : (-(1LL << (bits-1)));
1970  const auto unsignedTypeMaxValue = (1LL << bits) - 1LL;
1971  long long typeMaxValue;
1972  if (typeTok->valueType()->sign != ValueType::Sign::SIGNED)
1973  typeMaxValue = unsignedTypeMaxValue;
1974  else if (bits >= mSettings->platform.int_bit && (!valueTok->valueType() || valueTok->valueType()->sign != ValueType::Sign::SIGNED))
1975  typeMaxValue = unsignedTypeMaxValue;
1976  else
1977  typeMaxValue = unsignedTypeMaxValue / 2;
1978 
1979  bool result{};
1980  const auto kiv = valueTok->getKnownIntValue();
1981  if (tok->str() == "==")
1982  result = false;
1983  else if (tok->str() == "!=")
1984  result = true;
1985  else if (tok->str()[0] == '>' && i == 0)
1986  // num > var
1987  result = (kiv > 0);
1988  else if (tok->str()[0] == '>' && i == 1)
1989  // var > num
1990  result = (kiv < 0);
1991  else if (tok->str()[0] == '<' && i == 0)
1992  // num < var
1993  result = (kiv < 0);
1994  else if (tok->str()[0] == '<' && i == 1)
1995  // var < num
1996  result = (kiv > 0);
1997 
1998  bool error = false;
1999  if (kiv < typeMinValue || kiv > typeMaxValue) {
2000  error = true;
2001  } else {
2002  switch (i) {
2003  case 0: // num cmp var
2004  if (kiv == typeMinValue) {
2005  if (tok->str() == "<=") {
2006  result = true;
2007  error = true;
2008  } else if (tok->str() == ">")
2009  error = true;
2010  }
2011  else if (kiv == typeMaxValue && (tok->str() == ">=" || tok->str() == "<")) {
2012  error = true;
2013  }
2014  break;
2015  case 1: // var cmp num
2016  if (kiv == typeMinValue) {
2017  if (tok->str() == ">=") {
2018  result = true;
2019  error = true;
2020  } else if (tok->str() == "<")
2021  error = true;
2022  }
2023  else if (kiv == typeMaxValue && (tok->str() == "<=" || tok->str() == ">")) {
2024  error = true;
2025  }
2026  break;
2027  }
2028  }
2029  if (error)
2030  compareValueOutOfTypeRangeError(valueTok, typeTok->valueType()->str(), kiv, result);
2031  }
2032  }
2033  }
2034 }
2035 
2036 void CheckCondition::compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result)
2037 {
2038  reportError(
2039  comparison,
2041  "compareValueOutOfTypeRangeError",
2042  "Comparing expression of type '" + type + "' against value " + std::to_string(value) + ". Condition is always " + bool_to_string(result) + ".",
2043  CWE398,
2045 }
static bool isExpressionChangedAt(const F &getExprTok, const Token *tok, int indirect, const nonneg int exprid, bool globalvar, const Settings &settings, int depth)
Definition: astutils.cpp:2803
bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Settings &settings, bool pure, bool followVar, ErrorPath *errors)
Definition: astutils.cpp:1549
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:987
bool isUsedAsBool(const Token *const tok, const Settings &settings)
Is token used as boolean, that is to say cast to a bool, or used as a condition in a if/while/for.
Definition: astutils.cpp:1482
bool isLikelyStreamRead(const Token *op)
do we see a likely write of rhs through overloaded operator s >> x; a & x;
Definition: astutils.cpp:3216
const Token * findExpressionChanged(const Token *expr, const Token *start, const Token *end, const Settings &settings, int depth)
Definition: astutils.cpp:3023
bool isOppositeCond(bool isNot, const Token *const cond1, const Token *const cond2, const Settings &settings, bool pure, bool followVar, ErrorPath *errors)
Are two conditions opposite.
Definition: astutils.cpp:1780
bool isConstVarExpression(const Token *tok, const std::function< bool(const Token *)> &skipPredicate)
Definition: astutils.cpp:3247
bool isWithoutSideEffects(const Token *tok, bool checkArrayAccess, bool checkReference)
Definition: astutils.cpp:2066
bool astIsFloat(const Token *tok, bool unknown)
Is expression of floating point type?
Definition: astutils.cpp:207
const Token * nextAfterAstRightmostLeaf(const Token *tok)
Definition: astutils.cpp:548
bool isVariablesChanged(const Token *start, const Token *end, int indirect, const std::vector< const Variable * > &vars, const Settings &settings)
Definition: astutils.cpp:2891
bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, int depth)
Definition: astutils.cpp:2541
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
Definition: astutils.h:54
static const CWE CWE571(571U)
static const CWE CWE398(398U)
static bool parseComparison(const Token *comp, bool &not1, std::string &op, std::string &value, const Token *&expr, bool &inconclusive)
static bool checkFloatRelation(const std::string &op, const double value1, const double value2)
static T getvalue(const int test, const T value1, const T value2)
static bool checkIntRelation(const std::string &op, const T value1, const T value2)
static std::string innerSmtString(const Token *tok)
static const CWE uncheckedErrorConditionCWE(391U)
static const CWE CWE570(570U)
static bool isNonConstFunctionCall(const Token *ftok, const Library &library)
static std::string conditionString(bool not1, const Token *expr1, const std::string &op, const std::string &value1)
static T getvalue3(const T value1, const T value2)
static bool inBooleanFunction(const Token *tok)
static std::string invertOperatorForOperandSwap(std::string s)
static int countPar(const Token *tok1, const Token *tok2)
static bool isOperandExpanded(const Token *tok)
static bool isParameterChanged(const Token *partok)
static bool isIfConstexpr(const Token *tok)
static int sufficientCondition(std::string op1, const bool not1, const T value1, std::string op2, const bool not2, const T value2, const bool isAnd)
static void getnumchildren(const Token *tok, std::list< MathLib::bigint > &numchildren)
static int sign(const T v)
Check for condition mismatches.
void oppositeInnerConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath)
void checkInvalidTestForOverflow()
Check for invalid test for overflow 'x+100 < x'
void checkIncorrectLogicOperator()
Check for testing for mutual exclusion over ||
void comparison()
mismatching lhs and rhs in comparison
std::set< const Token * > mCondDiags
void multiCondition()
match 'if' and 'else if' conditions
void assignIf()
mismatching assignment / comparison
bool assignIfParseScope(const Token *const assignTok, const Token *const startTok, const nonneg int varid, const bool islocal, const char bitop, const MathLib::bigint num)
parse scopes recursively
void invalidTestForOverflow(const Token *tok, const ValueType *valueType, const std::string &replace)
void checkPointerAdditionResultNotNull()
Check if pointer addition result is NULL '(ptr + 1) == NULL'.
void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result)
void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive)
void oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath)
void alwaysTrueFalseError(const Token *tok, const Token *condition, const ValueFlow::Value *value)
void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath)
void assignmentInCondition(const Token *eq)
void multiCondition2()
multiconditions #2
void badBitmaskCheckError(const Token *tok, bool isNoOp=false)
void identicalInnerConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath)
void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath)
bool isOverlappingCond(const Token *const cond1, const Token *const cond2, bool pure) const
void moduloAlwaysTrueFalseError(const Token *tok, const std::string &maxVal)
void checkModuloAlwaysTrueFalse()
Check for suspicious usage of modulo (e.g.
void clarifyConditionError(const Token *tok, bool assign, bool boolop)
bool isAliased(const std::set< int > &vars) const
void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2)
void clarifyCondition()
Suspicious condition (assignment+comparison)
void duplicateConditionalAssignError(const Token *condTok, const Token *assignTok, bool isRedundant=false)
void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors)
void compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result)
void checkDuplicateConditionalAssign()
void alwaysTrueFalse()
Condition is always true/false.
void overlappingElseIfConditionError(const Token *tok, nonneg int line1)
void comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result)
void pointerAdditionResultNotNullError(const Token *tok, const Token *calc)
bool diag(const Token *tok, bool insert=true)
void checkCompareValueOutOfTypeRange()
void checkAssignmentInCondition()
Assignment in condition.
void checkBadBitmaskCheck()
check bitmask using | instead of &
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?
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 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
const Scope * functionScope
scope of function body
const Token * retDef
function return type token
Library definitions handling.
Definition: library.h:52
bool isFunctionConst(const std::string &functionName, bool pure) const
Definition: library.cpp:1518
static bool isLessEqual(const std::string &first, const std::string &second)
Definition: mathlib.cpp:1239
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 isFloat(const std::string &str)
Definition: mathlib.cpp:535
static bool isNegative(const std::string &str)
Definition: mathlib.cpp:636
static bool isInt(const std::string &str)
Definition: mathlib.cpp:1007
static double toDoubleNumber(const std::string &str)
for conversion of numeric literals
Definition: mathlib.cpp:487
unsigned long long biguint
Definition: mathlib.h:69
nonneg int short_bit
bits in char
Definition: platform.h:86
nonneg int int_bit
bits in short
Definition: platform.h:87
nonneg int long_bit
bits in int
Definition: platform.h:88
nonneg int char_bit
Definition: platform.h:85
nonneg int long_long_bit
bits in long
Definition: platform.h:89
Type type
platform type
Definition: platform.h:118
ScopeType type
Function * function
function info for this function
const Scope * nestedIn
const Token * classDef
class/struct/union/namespace token
bool isLocal() const
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
Library library
Library.
Definition: settings.h:237
Platform platform
Definition: settings.h:255
bool isPremiumEnabled(const char id[]) const
Is checker id enabled by premiumArgs.
Definition: settings.cpp:557
SimpleEnableGroup< Certainty > certainty
Definition: settings.h:359
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
bool isEnabled(T flag) const
Definition: settings.h:66
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
const ValueFlow::Value * getValue(const MathLib::bigint val) const
Definition: token.cpp:2596
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:722
bool isConstexpr() const
Definition: token.h:594
bool isEnumerator() const
Definition: token.h:374
bool isName() const
Definition: token.h:361
bool hasKnownIntValue() const
Definition: token.cpp:2553
MathLib::bigint getKnownIntValue() const
Definition: token.h:1218
bool isExpandedMacro() const
Definition: token.h:455
bool isArithmeticalOp() const
Definition: token.h:395
bool isCpp() const
Definition: token.cpp:2752
const ValueType * valueType() const
Definition: token.h:331
const std::string & strAt(int index) const
Definition: token.cpp:457
void astOperand1(Token *tok)
Definition: token.cpp:1490
bool isNumber() const
Definition: token.h:371
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1127
nonneg int varId() const
Definition: token.h:870
std::string expressionString() const
Definition: token.cpp:1681
static const Token * findsimplematch(const Token *const startTok, const char(&pattern)[count])
Definition: token.h:763
const Token * tokAt(int index) const
Definition: token.cpp:427
void astOperand2(Token *tok)
Definition: token.cpp:1502
void scope(const Scope *s)
Associate this token with given scope.
Definition: token.h:1042
void link(Token *linkToToken)
Create link to given token.
Definition: token.h:1015
const Token * linkAt(int index) const
Definition: token.cpp:447
@ eBitOp
Definition: token.h:163
Token * previous()
Definition: token.h:862
nonneg int linenr() const
Definition: token.h:816
Token * astSibling()
Definition: token.h:1396
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
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
void astParent(Token *tok)
Definition: token.cpp:1471
const Token * tokens() const
Definition: tokenize.h:592
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
bool hasIfdef(const Token *start, const Token *end) const
bool isImpossible() const
Definition: vfvalue.h:365
long long intvalue
int value (or sometimes bool value?)
Definition: vfvalue.h:268
Value type.
enum ValueType::Type type
bool isTypeEqual(const ValueType *that) const
Check if type is the same ignoring const and references.
bool isIntegral() const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
enum ValueType::Sign sign
std::string str() const
Information about a member variable.
bool isArgument() const
Is variable a function argument.
bool isReference() const
Is reference variable.
bool isLocal() const
Is variable local.
bool isConst() const
Is variable const.
nonneg int declarationId() const
Get declaration ID (varId used for variable in its declaration).
bool isPointer() const
Is pointer variable.
#define nonneg
Definition: config.h:138
static void replace(std::string &source, const std::unordered_map< std::string, std::string > &substitutionMap)
std::list< ErrorPathItem > ErrorPath
Definition: errortypes.h:130
@ warning
Warning.
@ style
Style warning.
@ error
Programming error.
static bool isTrue(const ValueFlow::Value &v)
static const char * bool_to_string(bool b)
Definition: utils.h:345