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  !mSettings->isPremiumEnabled("identicalConditionAfterEarlyExit") &&
618  !mSettings->isPremiumEnabled("identicalInnerCondition"))
619  return;
620 
621  logChecker("CheckCondition::multiCondition2"); // warning
622 
623  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
624 
625  for (const Scope &scope : symbolDatabase->scopeList) {
626  const Token *condTok = nullptr;
627  if (scope.type == Scope::eIf || scope.type == Scope::eWhile)
628  condTok = scope.classDef->next()->astOperand2();
629  else if (scope.type == Scope::eFor) {
630  condTok = scope.classDef->next()->astOperand2();
631  if (!condTok || condTok->str() != ";")
632  continue;
633  condTok = condTok->astOperand2();
634  if (!condTok || condTok->str() != ";")
635  continue;
636  condTok = condTok->astOperand1();
637  }
638  if (!condTok)
639  continue;
640  const Token * const cond1 = condTok;
641 
642  if (!Token::simpleMatch(scope.classDef->linkAt(1), ") {"))
643  continue;
644 
645  bool functionCall = false;
646  bool nonConstFunctionCall = false;
647  bool nonlocal = false; // nonlocal variable used in condition
648  std::set<int> vars; // variables used in condition
649  visitAstNodes(condTok,
650  [&](const Token *cond) {
651  if (Token::Match(cond, "%name% (")) {
652  functionCall = true;
653  nonConstFunctionCall = isNonConstFunctionCall(cond, mSettings->library);
654  if (nonConstFunctionCall)
655  return ChildrenToVisit::done;
656  }
657 
658  if (cond->varId()) {
659  vars.insert(cond->varId());
660  const Variable *var = cond->variable();
661  if (!nonlocal && var) {
662  if (!(var->isLocal() || var->isArgument()))
663  nonlocal = true;
664  else if ((var->isPointer() || var->isReference()) && !Token::Match(cond->astParent(), "%oror%|&&|!"))
665  // TODO: if var is pointer check what it points at
666  nonlocal = true;
667  }
668  } else if (!nonlocal && cond->isName()) {
669  // varid is 0. this is possibly a nonlocal variable..
670  nonlocal = Token::Match(cond->astParent(), "%cop%|(|[") || Token::Match(cond, "%name% .") || (cond->isCpp() && cond->str() == "this");
671  } else {
673  }
674  return ChildrenToVisit::none;
675  });
676 
677  if (nonConstFunctionCall)
678  continue;
679 
680  std::vector<const Variable*> varsInCond;
681  visitAstNodes(condTok,
682  [&varsInCond](const Token *cond) {
683  if (cond->variable()) {
684  const Variable *var = cond->variable();
685  if (std::find(varsInCond.cbegin(), varsInCond.cend(), var) == varsInCond.cend())
686  varsInCond.push_back(var);
687  }
689  });
690 
691  // parse until second condition is reached..
692  enum MULTICONDITIONTYPE { INNER, AFTER };
693  const Token *tok;
694 
695  // Parse inner condition first and then early return condition
696  std::vector<MULTICONDITIONTYPE> types = {MULTICONDITIONTYPE::INNER};
697  if (Token::Match(scope.bodyStart, "{ return|throw|continue|break"))
698  types.push_back(MULTICONDITIONTYPE::AFTER);
699  for (const MULTICONDITIONTYPE type:types) {
700  if (type == MULTICONDITIONTYPE::AFTER) {
701  tok = scope.bodyEnd->next();
702  } else {
703  tok = scope.bodyStart;
704  }
705  const Token * const endToken = tok->scope()->bodyEnd;
706 
707  for (; tok && tok != endToken; tok = tok->next()) {
708  if (isExpressionChangedAt(cond1, tok, 0, false, *mSettings))
709  break;
710  if (Token::Match(tok, "if|return")) {
711  const Token * condStartToken = tok->str() == "if" ? tok->next() : tok;
712  const Token * condEndToken = tok->str() == "if" ? condStartToken->link() : Token::findsimplematch(condStartToken, ";");
713  // Does condition modify tracked variables?
714  if (findExpressionChanged(cond1, condStartToken, condEndToken, *mSettings))
715  break;
716 
717  // Condition..
718  const Token *cond2 = tok->str() == "if" ? condStartToken->astOperand2() : condStartToken->astOperand1();
719  const bool isReturnVar = (tok->str() == "return" && (!Token::Match(cond2, "%cop%") || (cond2 && cond2->isUnaryOp("!"))));
720 
721  ErrorPath errorPath;
722 
723  if (type == MULTICONDITIONTYPE::INNER) {
724  visitAstNodes(cond1, [&](const Token* firstCondition) {
725  if (!firstCondition)
726  return ChildrenToVisit::none;
727  if (firstCondition->str() == "&&") {
728  if (!isOppositeCond(false, firstCondition, cond2, *mSettings, true, true))
729  return ChildrenToVisit::op1_and_op2;
730  }
731  if (!firstCondition->hasKnownIntValue()) {
732  if (!isReturnVar && isOppositeCond(false, firstCondition, cond2, *mSettings, true, true, &errorPath)) {
733  if (!isAliased(vars))
734  oppositeInnerConditionError(firstCondition, cond2, errorPath);
735  } else if (!isReturnVar && isSameExpression(true, firstCondition, cond2, *mSettings, true, true, &errorPath)) {
736  identicalInnerConditionError(firstCondition, cond2, errorPath);
737  }
738  }
739  return ChildrenToVisit::none;
740  });
741  } else {
742  visitAstNodes(cond2, [&](const Token *secondCondition) {
743  if (secondCondition->str() == "||" || secondCondition->str() == "&&")
745 
746  if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) &&
747  isSameExpression(true, cond1, secondCondition, *mSettings, true, true, &errorPath)) {
748  if (!isAliased(vars) && !mTokenizer->hasIfdef(cond1, secondCondition)) {
749  identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath);
750  return ChildrenToVisit::done;
751  }
752  }
753  return ChildrenToVisit::none;
754  });
755  }
756  }
757  if (Token::Match(tok, "%name% (") &&
758  isVariablesChanged(tok, tok->linkAt(1), 0, varsInCond, *mSettings)) {
759  break;
760  }
761  if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables
762  break;
763  if (Token::Match(tok, "case|break|continue|return|throw") && tok->scope() == endToken->scope())
764  break;
765  if (Token::Match(tok, "[;{}] %name% :"))
766  break;
767  // bailout if loop is seen.
768  // TODO: handle loops better.
769  if (Token::Match(tok, "for|while|do")) {
770  const Token *tok1 = tok->next();
771  const Token *tok2;
772  if (Token::simpleMatch(tok, "do {")) {
773  if (!Token::simpleMatch(tok->linkAt(1), "} while ("))
774  break;
775  tok2 = tok->linkAt(1)->linkAt(2);
776  } else if (Token::Match(tok, "if|while (")) {
777  tok2 = tok->linkAt(1);
778  if (Token::simpleMatch(tok2, ") {"))
779  tok2 = tok2->linkAt(1);
780  if (!tok2)
781  break;
782  } else {
783  // Incomplete code
784  break;
785  }
786  const bool changed = std::any_of(vars.cbegin(), vars.cend(), [&](int varid) {
787  return isVariableChanged(tok1, tok2, varid, nonlocal, *mSettings);
788  });
789  if (changed)
790  break;
791  }
792  if ((tok->varId() && vars.find(tok->varId()) != vars.end()) ||
793  (!tok->varId() && nonlocal) ||
794  (functionCall && tok->variable() && !tok->variable()->isLocal())) {
795  if (Token::Match(tok, "%name% %assign%|++|--"))
796  break;
797  if (Token::Match(tok->astParent(), "*|.|[")) {
798  const Token *parent = tok;
799  while (Token::Match(parent->astParent(), ".|[") || (parent->astParent() && parent->astParent()->isUnaryOp("*")))
800  parent = parent->astParent();
801  if (Token::Match(parent->astParent(), "%assign%|++|--"))
802  break;
803  }
804  if (tok->isCpp() && Token::Match(tok, "%name% <<") && (!tok->valueType() || !tok->valueType()->isIntegral()))
805  break;
806  if (isLikelyStreamRead(tok->next()) || isLikelyStreamRead(tok->previous()))
807  break;
808  if (Token::Match(tok, "%name% [")) {
809  const Token *tok2 = tok->linkAt(1);
810  while (Token::simpleMatch(tok2, "] ["))
811  tok2 = tok2->linkAt(1);
812  if (Token::Match(tok2, "] %assign%|++|--"))
813  break;
814  }
815  if (Token::Match(tok->previous(), "++|--|& %name%"))
816  break;
817  if (tok->variable() &&
818  !tok->variable()->isConst() &&
819  Token::Match(tok, "%name% . %name% (")) {
820  const Function* function = tok->tokAt(2)->function();
821  if (!function || !function->isConst())
822  break;
823  }
824  if (Token::Match(tok->previous(), "[(,] *|& %name% [,)]") && isParameterChanged(tok))
825  break;
826  }
827  }
828  }
829  }
830 }
831 
832 static std::string innerSmtString(const Token * tok)
833 {
834  if (!tok)
835  return "if";
836  if (!tok->astTop())
837  return "if";
838  const Token * top = tok->astTop();
839  if (top->str() == "(" && top->astOperand1())
840  return top->astOperand1()->str();
841  return top->str();
842 }
843 
844 void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath)
845 {
846  if (diag(tok1) & diag(tok2))
847  return;
848  const std::string s1(tok1 ? tok1->expressionString() : "x");
849  const std::string s2(tok2 ? tok2->expressionString() : "!x");
850  const std::string innerSmt = innerSmtString(tok2);
851  errorPath.emplace_back(tok1, "outer condition: " + s1);
852  errorPath.emplace_back(tok2, "opposite inner condition: " + s2);
853 
854  const std::string msg("Opposite inner '" + innerSmt + "' condition leads to a dead code block.\n"
855  "Opposite inner '" + innerSmt + "' condition leads to a dead code block (outer condition is '" + s1 + "' and inner condition is '" + s2 + "').");
856  reportError(errorPath, Severity::warning, "oppositeInnerCondition", msg, CWE398, Certainty::normal);
857 }
858 
859 void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath)
860 {
861  if (diag(tok1) & diag(tok2))
862  return;
863  const std::string s1(tok1 ? tok1->expressionString() : "x");
864  const std::string s2(tok2 ? tok2->expressionString() : "x");
865  const std::string innerSmt = innerSmtString(tok2);
866  errorPath.emplace_back(tok1, "outer condition: " + s1);
867  errorPath.emplace_back(tok2, "identical inner condition: " + s2);
868 
869  const std::string msg("Identical inner '" + innerSmt + "' condition is always true.\n"
870  "Identical inner '" + innerSmt + "' condition is always true (outer condition is '" + s1 + "' and inner condition is '" + s2 + "').");
871  reportError(errorPath, Severity::warning, "identicalInnerCondition", msg, CWE398, Certainty::normal);
872 }
873 
875 {
876  if (diag(cond1) & diag(cond2))
877  return;
878 
879  const bool isReturnValue = cond2 && Token::simpleMatch(cond2->astParent(), "return");
880 
881  const std::string cond(cond1 ? cond1->expressionString() : "x");
882  const std::string value = (cond2 && cond2->valueType() && cond2->valueType()->type == ValueType::Type::BOOL) ? "false" : "0";
883 
884  errorPath.emplace_back(cond1, "If condition '" + cond + "' is true, the function will return/exit");
885  errorPath.emplace_back(cond2, (isReturnValue ? "Returning identical expression '" : "Testing identical condition '") + cond + "'");
886 
887  reportError(errorPath,
889  "identicalConditionAfterEarlyExit",
890  isReturnValue
891  ? ("Identical condition and return expression '" + cond + "', return value is always " + value)
892  : ("Identical condition '" + cond + "', second condition is always false"),
893  CWE398,
895 }
896 
897 //---------------------------------------------------------------------------
898 // if ((x != 1) || (x != 3)) // expression always true
899 // if ((x == 1) && (x == 3)) // expression always false
900 // if ((x < 1) && (x > 3)) // expression always false
901 // if ((x > 3) || (x < 10)) // expression always true
902 // if ((x > 5) && (x != 1)) // second comparison always true
903 //
904 // Check for suspect logic for an expression consisting of 2 comparison
905 // expressions with a shared variable and constants and a logical operator
906 // between them.
907 //
908 // Suggest a different logical operator when the logical operator between
909 // the comparisons is probably wrong.
910 //
911 // Inform that second comparison is always true when first comparison is true.
912 //---------------------------------------------------------------------------
913 
914 static std::string invertOperatorForOperandSwap(std::string s)
915 {
916  if (s[0] == '<')
917  s[0] = '>';
918  else if (s[0] == '>')
919  s[0] = '<';
920  return s;
921 }
922 
923 template<typename T>
924 static int sign(const T v) {
925  return static_cast<int>(v > 0) - static_cast<int>(v < 0);
926 }
927 
928 // returns 1 (-1) if the first (second) condition is sufficient, 0 if indeterminate
929 template<typename T>
930 static int sufficientCondition(std::string op1, const bool not1, const T value1, std::string op2, const bool not2, const T value2, const bool isAnd) {
931  auto transformOp = [](std::string& op, const bool invert) {
932  if (invert) {
933  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  else if (op == ">=")
944  op = "<";
945  }
946  };
947  transformOp(op1, not1);
948  transformOp(op2, not2);
949  int res = 0;
950  bool equal = false;
951  if (op1 == op2) {
952  equal = true;
953  if (op1 == ">" || op1 == ">=")
954  res = sign(value1 - value2);
955  else if (op1 == "<" || op1 == "<=")
956  res = -sign(value1 - value2);
957  } else { // not equal
958  if (op1 == "!=")
959  res = 1;
960  else if (op2 == "!=")
961  res = -1;
962  else if (op1 == "==")
963  res = -1;
964  else if (op2 == "==")
965  res = 1;
966  else if (op1 == ">" && op2 == ">=")
967  res = sign(value1 - (value2 - 1));
968  else if (op1 == ">=" && op2 == ">")
969  res = sign((value1 - 1) - value2);
970  else if (op1 == "<" && op2 == "<=")
971  res = -sign(value1 - (value2 + 1));
972  else if (op1 == "<=" && op2 == "<")
973  res = -sign((value1 + 1) - value2);
974  }
975  return res * (isAnd == equal ? 1 : -1);
976 }
977 
978 template<typename T>
979 static bool checkIntRelation(const std::string &op, const T value1, const T value2)
980 {
981  return (op == "==" && value1 == value2) ||
982  (op == "!=" && value1 != value2) ||
983  (op == ">" && value1 > value2) ||
984  (op == ">=" && value1 >= value2) ||
985  (op == "<" && value1 < value2) ||
986  (op == "<=" && value1 <= value2);
987 }
988 
989 static bool checkFloatRelation(const std::string &op, const double value1, const double value2)
990 {
991  return (op == ">" && value1 > value2) ||
992  (op == ">=" && value1 >= value2) ||
993  (op == "<" && value1 < value2) ||
994  (op == "<=" && value1 <= value2);
995 }
996 
997 template<class T>
998 static T getvalue3(const T value1, const T value2)
999 {
1000  const T min = std::min(value1, value2);
1001  if (min== std::numeric_limits<T>::max())
1002  return min;
1003  return min + 1; // see #5895
1004 }
1005 
1006 template<>
1007 double getvalue3(const double value1, const double value2)
1008 {
1009  return (value1 + value2) / 2.0;
1010 }
1011 
1012 
1013 template<class T>
1014 static inline T getvalue(const int test, const T value1, const T value2)
1015 {
1016  // test:
1017  // 1 => return value that is less than both value1 and value2
1018  // 2 => return value1
1019  // 3 => return value that is between value1 and value2
1020  // 4 => return value2
1021  // 5 => return value that is larger than both value1 and value2
1022  switch (test) {
1023  case 1:
1024  return std::numeric_limits<T>::lowest();
1025  case 2:
1026  return value1;
1027  case 3:
1028  return getvalue3<T>(value1, value2);
1029  case 4:
1030  return value2;
1031  case 5:
1032  return std::numeric_limits<T>::max();
1033  }
1034  return 0;
1035 }
1036 
1037 static bool parseComparison(const Token *comp, bool &not1, std::string &op, std::string &value, const Token *&expr, bool &inconclusive)
1038 {
1039  not1 = false;
1040  while (comp && comp->str() == "!") {
1041  not1 = !(not1);
1042  comp = comp->astOperand1();
1043  }
1044 
1045  if (!comp)
1046  return false;
1047 
1048  const Token* op1 = comp->astOperand1();
1049  const Token* op2 = comp->astOperand2();
1050  if (!comp->isComparisonOp() || !op1 || !op2) {
1051  op = "!=";
1052  value = "0";
1053  expr = comp;
1054  } else if (op1->isLiteral()) {
1055  if (op1->isExpandedMacro())
1056  return false;
1057  op = invertOperatorForOperandSwap(comp->str());
1058  if (op1->enumerator() && op1->enumerator()->value_known)
1059  value = std::to_string(op1->enumerator()->value);
1060  else
1061  value = op1->str();
1062  expr = op2;
1063  } else if (comp->astOperand2()->isLiteral()) {
1064  if (op2->isExpandedMacro())
1065  return false;
1066  op = comp->str();
1067  if (op2->enumerator() && op2->enumerator()->value_known)
1068  value = std::to_string(op2->enumerator()->value);
1069  else
1070  value = op2->str();
1071  expr = op1;
1072  } else {
1073  op = "!=";
1074  value = "0";
1075  expr = comp;
1076  }
1077 
1078  inconclusive = inconclusive || ((value)[0] == '\'' && !(op == "!=" || op == "=="));
1079 
1080  // Only float and int values are currently handled
1081  return MathLib::isInt(value) || MathLib::isFloat(value) || (value[0] == '\'');
1082 }
1083 
1084 static std::string conditionString(bool not1, const Token *expr1, const std::string &op, const std::string &value1)
1085 {
1086  if (expr1->astParent()->isComparisonOp())
1087  return std::string(not1 ? "!(" : "") + expr1->expressionString() +
1088  " " +
1089  op +
1090  " " +
1091  value1 +
1092  (not1 ? ")" : "");
1093 
1094  return std::string(not1 ? "!" : "") + expr1->expressionString();
1095 }
1096 
1097 static std::string conditionString(const Token * tok)
1098 {
1099  if (!tok)
1100  return "";
1101  if (tok->isComparisonOp()) {
1102  bool inconclusive = false;
1103  bool not_;
1104  std::string op, value;
1105  const Token *expr;
1106  if (parseComparison(tok, not_, op, value, expr, inconclusive) && expr->isName()) {
1107  return conditionString(not_, expr, op, value);
1108  }
1109  }
1110  if (Token::Match(tok, "%cop%|&&|%oror%")) {
1111  if (tok->astOperand2())
1112  return conditionString(tok->astOperand1()) + " " + tok->str() + " " + conditionString(tok->astOperand2());
1113  return tok->str() + "(" + conditionString(tok->astOperand1()) + ")";
1114 
1115  }
1116  return tok->expressionString();
1117 }
1118 
1119 static bool isIfConstexpr(const Token* tok) {
1120  const Token* const top = tok->astTop();
1121  return top && Token::simpleMatch(top->astOperand1(), "if") && top->astOperand1()->isConstexpr();
1122 }
1123 
1125 {
1126  const bool printStyle = mSettings->severity.isEnabled(Severity::style);
1127  const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
1128  if (!printWarning && !printStyle && !mSettings->isPremiumEnabled("incorrectLogicOperator"))
1129  return;
1130  const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
1131 
1132  logChecker("CheckCondition::checkIncorrectLogicOperator"); // style,warning
1133 
1134  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1135  for (const Scope * scope : symbolDatabase->functionScopes) {
1136 
1137  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1138  if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2())
1139  continue;
1140 
1141 
1142  // 'A && (!A || B)' is equivalent to 'A && B'
1143  // 'A || (!A && B)' is equivalent to 'A || B'
1144  // 'A && (A || B)' is equivalent to 'A'
1145  // 'A || (A && B)' is equivalent to 'A'
1146  if (printStyle &&
1147  ((tok->str() == "||" && tok->astOperand2()->str() == "&&") ||
1148  (tok->str() == "&&" && tok->astOperand2()->str() == "||"))) {
1149  const Token* tok2 = tok->astOperand2()->astOperand1();
1150  if (isOppositeCond(true, tok->astOperand1(), tok2, *mSettings, true, false)) {
1151  std::string expr1(tok->astOperand1()->expressionString());
1152  std::string expr2(tok->astOperand2()->astOperand1()->expressionString());
1153  std::string expr3(tok->astOperand2()->astOperand2()->expressionString());
1154  // make copy for later because the original string might get overwritten
1155  const std::string expr1VerboseMsg = expr1;
1156  const std::string expr2VerboseMsg = expr2;
1157  const std::string expr3VerboseMsg = expr3;
1158 
1159  if (expr1.length() + expr2.length() + expr3.length() > 50U) {
1160  if (expr1[0] == '!' && expr2[0] != '!') {
1161  expr1 = "!A";
1162  expr2 = "A";
1163  } else {
1164  expr1 = "A";
1165  expr2 = "!A";
1166  }
1167 
1168  expr3 = "B";
1169  }
1170 
1171  const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")";
1172  const std::string cond2 = expr1 + " " + tok->str() + " " + expr3;
1173 
1174  const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg;
1175  const std::string cond2VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr3VerboseMsg;
1176  // for the --verbose message, transform the actual condition and print it
1177  const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n"
1178  "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'.";
1179  redundantConditionError(tok, msg, false);
1180  continue;
1181  }
1182  if (isSameExpression(false, tok->astOperand1(), tok2, *mSettings, true, true)) {
1183  std::string expr1(tok->astOperand1()->expressionString());
1184  std::string expr2(tok->astOperand2()->astOperand1()->expressionString());
1185  std::string expr3(tok->astOperand2()->astOperand2()->expressionString());
1186  // make copy for later because the original string might get overwritten
1187  const std::string expr1VerboseMsg = expr1;
1188  const std::string expr2VerboseMsg = expr2;
1189  const std::string expr3VerboseMsg = expr3;
1190 
1191  if (expr1.length() + expr2.length() + expr3.length() > 50U) {
1192  expr1 = "A";
1193  expr2 = "A";
1194  expr3 = "B";
1195  }
1196 
1197  const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")";
1198  const std::string cond2 = std::move(expr1);
1199 
1200  const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg;
1201  const std::string& cond2VerboseMsg = expr1VerboseMsg;
1202  // for the --verbose message, transform the actual condition and print it
1203  const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n"
1204  "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'.";
1205  redundantConditionError(tok, msg, false);
1206  continue;
1207  }
1208  }
1209 
1210  // Comparison #1 (LHS)
1211  const Token *comp1 = tok->astOperand1();
1212  if (comp1->str() == tok->str())
1213  comp1 = comp1->astOperand2();
1214 
1215  // Comparison #2 (RHS)
1216  const Token *comp2 = tok->astOperand2();
1217 
1218  bool inconclusive = false;
1219  bool parseable = true;
1220 
1221  // Parse LHS
1222  bool not1;
1223  std::string op1, value1;
1224  const Token *expr1 = nullptr;
1225  parseable &= (parseComparison(comp1, not1, op1, value1, expr1, inconclusive));
1226 
1227  // Parse RHS
1228  bool not2;
1229  std::string op2, value2;
1230  const Token *expr2 = nullptr;
1231  parseable &= (parseComparison(comp2, not2, op2, value2, expr2, inconclusive));
1232 
1233  if (inconclusive && !printInconclusive)
1234  continue;
1235 
1236  const bool isUnknown = (expr1 && expr1->valueType() && expr1->valueType()->type == ValueType::UNKNOWN_TYPE) ||
1237  (expr2 && expr2->valueType() && expr2->valueType()->type == ValueType::UNKNOWN_TYPE);
1238  if (isUnknown)
1239  continue;
1240 
1241  const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2);
1242 
1243  ErrorPath errorPath;
1244 
1245  // Opposite comparisons around || or && => always true or always false
1246  const bool isLogicalOr(tok->str() == "||");
1247  if (!isfloat && isOppositeCond(isLogicalOr, tok->astOperand1(), tok->astOperand2(), *mSettings, true, true, &errorPath)) {
1248  if (!isIfConstexpr(tok)) {
1249  const bool alwaysTrue(isLogicalOr);
1250  incorrectLogicOperatorError(tok, conditionString(tok), alwaysTrue, inconclusive, errorPath);
1251  }
1252  continue;
1253  }
1254 
1255  if (!parseable)
1256  continue;
1257 
1258  if (isSameExpression(true, comp1, comp2, *mSettings, true, true))
1259  continue; // same expressions => only report that there are same expressions
1260  if (!isSameExpression(true, expr1, expr2, *mSettings, true, true))
1261  continue;
1262 
1263 
1264  // don't check floating point equality comparisons. that is bad
1265  // and deserves different warnings.
1266  if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!="))
1267  continue;
1268 
1269 
1270  const double d1 = (isfloat) ? MathLib::toDoubleNumber(value1) : 0;
1271  const double d2 = (isfloat) ? MathLib::toDoubleNumber(value2) : 0;
1272  const MathLib::bigint i1 = (isfloat) ? 0 : MathLib::toBigNumber(value1);
1273  const MathLib::bigint i2 = (isfloat) ? 0 : MathLib::toBigNumber(value2);
1274  const bool useUnsignedInt = (std::numeric_limits<MathLib::bigint>::max()==i1) || (std::numeric_limits<MathLib::bigint>::max()==i2);
1275  const MathLib::biguint u1 = (useUnsignedInt) ? MathLib::toBigNumber(value1) : 0;
1276  const MathLib::biguint u2 = (useUnsignedInt) ? MathLib::toBigNumber(value2) : 0;
1277  // evaluate if expression is always true/false
1278  bool alwaysTrue = true, alwaysFalse = true;
1279  bool firstTrue = true, secondTrue = true;
1280  const bool isAnd = tok->str() == "&&";
1281  for (int test = 1; test <= 5; ++test) {
1282  // test:
1283  // 1 => testvalue is less than both value1 and value2
1284  // 2 => testvalue is value1
1285  // 3 => testvalue is between value1 and value2
1286  // 4 => testvalue value2
1287  // 5 => testvalue is larger than both value1 and value2
1288  bool result1, result2;
1289  if (isfloat) {
1290  const auto testvalue = getvalue<double>(test, d1, d2);
1291  result1 = checkFloatRelation(op1, testvalue, d1);
1292  result2 = checkFloatRelation(op2, testvalue, d2);
1293  } else if (useUnsignedInt) {
1294  const auto testvalue = getvalue<MathLib::biguint>(test, u1, u2);
1295  result1 = checkIntRelation(op1, testvalue, u1);
1296  result2 = checkIntRelation(op2, testvalue, u2);
1297  } else {
1298  const auto testvalue = getvalue<MathLib::bigint>(test, i1, i2);
1299  result1 = checkIntRelation(op1, testvalue, i1);
1300  result2 = checkIntRelation(op2, testvalue, i2);
1301  }
1302  if (not1)
1303  result1 = !result1;
1304  if (not2)
1305  result2 = !result2;
1306  if (isAnd) {
1307  alwaysTrue &= (result1 && result2);
1308  alwaysFalse &= !(result1 && result2);
1309  } else {
1310  alwaysTrue &= (result1 || result2);
1311  alwaysFalse &= !(result1 || result2);
1312  }
1313  firstTrue &= !(!result1 && result2);
1314  secondTrue &= !(result1 && !result2);
1315  }
1316 
1317  const std::string cond1str = conditionString(not1, expr1, op1, value1);
1318  const std::string cond2str = conditionString(not2, expr2, op2, value2);
1319  if (printWarning && (alwaysTrue || alwaysFalse)) {
1320  const std::string text = cond1str + " " + tok->str() + " " + cond2str;
1321  incorrectLogicOperatorError(tok, text, alwaysTrue, inconclusive, std::move(errorPath));
1322  } else if (printStyle && (firstTrue || secondTrue)) {
1323  // cppcheck-suppress accessMoved - TODO: FP - see #12174
1324  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);
1325  std::string text;
1326  if (which != 0) {
1327  text = "The condition '" + (which == 1 ? cond2str : cond1str) + "' is redundant since '" + (which == 1 ? cond1str : cond2str) + "' is sufficient.";
1328  } else
1329  text = "If '" + (secondTrue ? cond1str : cond2str) + "', the comparison '" + (secondTrue ? cond2str : cond1str) + "' is always true.";
1331  }
1332  }
1333  }
1334 }
1335 
1336 void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors)
1337 {
1338  if (diag(tok))
1339  return;
1340  errors.emplace_back(tok, "");
1341  if (always)
1342  reportError(errors, Severity::warning, "incorrectLogicOperator",
1343  "Logical disjunction always evaluates to true: " + condition + ".\n"
1344  "Logical disjunction always evaluates to true: " + condition + ". "
1345  "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);
1346  else
1347  reportError(errors, Severity::warning, "incorrectLogicOperator",
1348  "Logical conjunction always evaluates to false: " + condition + ".\n"
1349  "Logical conjunction always evaluates to false: " + condition + ". "
1350  "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);
1351 }
1352 
1353 void CheckCondition::redundantConditionError(const Token *tok, const std::string &text, bool inconclusive)
1354 {
1355  if (diag(tok))
1356  return;
1357  reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
1358 }
1359 
1360 //-----------------------------------------------------------------------------
1361 // Detect "(var % val1) > val2" where val2 is >= val1.
1362 //-----------------------------------------------------------------------------
1364 {
1366  return;
1367 
1368  logChecker("CheckCondition::checkModuloAlwaysTrueFalse"); // warning
1369 
1370  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1371  for (const Scope * scope : symbolDatabase->functionScopes) {
1372  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1373  if (!tok->isComparisonOp())
1374  continue;
1375  const Token *num, *modulo;
1376  if (Token::simpleMatch(tok->astOperand1(), "%") && Token::Match(tok->astOperand2(), "%num%")) {
1377  modulo = tok->astOperand1();
1378  num = tok->astOperand2();
1379  } else if (Token::Match(tok->astOperand1(), "%num%") && Token::simpleMatch(tok->astOperand2(), "%")) {
1380  num = tok->astOperand1();
1381  modulo = tok->astOperand2();
1382  } else {
1383  continue;
1384  }
1385 
1386  if (Token::Match(modulo->astOperand2(), "%num%") &&
1387  MathLib::isLessEqual(modulo->astOperand2()->str(), num->str()))
1388  moduloAlwaysTrueFalseError(tok, modulo->astOperand2()->str());
1389  }
1390  }
1391 }
1392 
1393 void CheckCondition::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal)
1394 {
1395  if (diag(tok))
1396  return;
1397  reportError(tok, Severity::warning, "moduloAlwaysTrueFalse",
1398  "Comparison of modulo result is predetermined, because it is always less than " + maxVal + ".", CWE398, Certainty::normal);
1399 }
1400 
1401 static int countPar(const Token *tok1, const Token *tok2)
1402 {
1403  int par = 0;
1404  for (const Token *tok = tok1; tok && tok != tok2; tok = tok->next()) {
1405  if (tok->str() == "(")
1406  ++par;
1407  else if (tok->str() == ")")
1408  --par;
1409  else if (tok->str() == ";")
1410  return -1;
1411  }
1412  return par;
1413 }
1414 
1415 //---------------------------------------------------------------------------
1416 // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))'
1417 // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))'
1418 //---------------------------------------------------------------------------
1420 {
1421  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("clarifyCondition"))
1422  return;
1423 
1424  logChecker("CheckCondition::clarifyCondition"); // style
1425 
1426  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1427  for (const Scope * scope : symbolDatabase->functionScopes) {
1428  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1429  if (Token::Match(tok, "( %name% [=&|^]")) {
1430  for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) {
1431  if (tok2->str() == "(" || tok2->str() == "[")
1432  tok2 = tok2->link();
1433  else if (tok2->isComparisonOp()) {
1434  // This might be a template
1435  if (!tok2->isC() && tok2->link())
1436  break;
1437  if (Token::simpleMatch(tok2->astParent(), "?"))
1438  break;
1439  clarifyConditionError(tok, tok->strAt(2) == "=", false);
1440  break;
1441  } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".")
1442  break;
1443  }
1444  } else if (tok->tokType() == Token::eBitOp && !tok->isUnaryOp("&")) {
1445  if (tok->astOperand2() && tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2())
1446  continue;
1447 
1448  // using boolean result in bitwise operation ! x [&|^]
1449  const ValueType* vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr;
1450  const ValueType* vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr;
1451  if (vt1 && vt1->type == ValueType::BOOL && !Token::Match(tok->astOperand1(), "%name%|(|[|::|.") && countPar(tok->astOperand1(), tok) == 0)
1452  clarifyConditionError(tok, false, true);
1453  else if (vt2 && vt2->type == ValueType::BOOL && !Token::Match(tok->astOperand2(), "%name%|(|[|::|.") && countPar(tok, tok->astOperand2()) == 0)
1454  clarifyConditionError(tok, false, true);
1455  }
1456  }
1457  }
1458 }
1459 
1460 void CheckCondition::clarifyConditionError(const Token *tok, bool assign, bool boolop)
1461 {
1462  std::string errmsg;
1463 
1464  if (assign)
1465  errmsg = "Suspicious condition (assignment + comparison); Clarify expression with parentheses.";
1466 
1467  else if (boolop)
1468  errmsg = "Boolean result is used in bitwise operation. Clarify expression with parentheses.\n"
1469  "Suspicious expression. Boolean result is used in bitwise operation. The operator '!' "
1470  "and the comparison operators have higher precedence than bitwise operators. "
1471  "It is recommended that the expression is clarified with parentheses.";
1472  else
1473  errmsg = "Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n"
1474  "Suspicious condition. Comparison operators have higher precedence than bitwise operators. "
1475  "Please clarify the condition with parentheses.";
1476 
1477  reportError(tok,
1479  "clarifyCondition",
1480  errmsg, CWE398, Certainty::normal);
1481 }
1482 
1484 {
1486  !mSettings->isPremiumEnabled("alwaysTrue") &&
1487  !mSettings->isPremiumEnabled("alwaysFalse") &&
1488  !mSettings->isPremiumEnabled("knownConditionTrueFalse"))
1489  return;
1490 
1491  logChecker("CheckCondition::alwaysTrueFalse"); // style
1492 
1493  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1494  for (const Scope * scope : symbolDatabase->functionScopes) {
1495  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
1496  // don't write false positives when templates are used or inside of asserts or non-evaluated contexts
1497  if (tok->link() && (Token::simpleMatch(tok, "<") ||
1498  Token::Match(tok->previous(), "static_assert|assert|ASSERT|sizeof|decltype ("))) {
1499  tok = tok->link();
1500  continue;
1501  }
1502  if (!tok->hasKnownIntValue())
1503  continue;
1504  if (Token::Match(tok->previous(), "%name% (") && tok->previous()->function()) {
1505  const Function* f = tok->previous()->function();
1506  if (f->functionScope && Token::Match(f->functionScope->bodyStart, "{ return true|false ;"))
1507  continue;
1508  }
1509  const Token* condition = nullptr;
1510  {
1511  // is this a condition..
1512  const Token *parent = tok->astParent();
1513  while (Token::Match(parent, "%oror%|&&"))
1514  parent = parent->astParent();
1515  if (!parent)
1516  continue;
1517  if (parent->str() == "?" && precedes(tok, parent))
1518  condition = parent;
1519  else if (Token::Match(parent->previous(), "if|while ("))
1520  condition = parent->previous();
1521  else if (Token::simpleMatch(parent, "return"))
1522  condition = parent;
1523  else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() &&
1524  Token::simpleMatch(parent->astParent()->astParent()->previous(), "for ("))
1525  condition = parent->astParent()->astParent()->previous();
1526  else if (Token::Match(tok, "%comp%"))
1527  condition = tok;
1528  else
1529  continue;
1530  }
1531  // Skip already diagnosed values
1532  if (diag(tok, false))
1533  continue;
1534  if (condition->isConstexpr())
1535  continue;
1536  if (!isUsedAsBool(tok, *mSettings))
1537  continue;
1538  if (Token::simpleMatch(condition, "return") && Token::Match(tok, "%assign%"))
1539  continue;
1540  if (Token::simpleMatch(tok->astParent(), "return") && Token::Match(tok, ".|%var%"))
1541  continue;
1542  if (Token::Match(tok, "%num%|%bool%|%char%"))
1543  continue;
1544  if (Token::Match(tok, "! %num%|%bool%|%char%"))
1545  continue;
1546  if (Token::Match(tok, "%oror%|&&")) {
1547  bool bail = false;
1548  for (const Token* op : { tok->astOperand1(), tok->astOperand2() }) {
1549  if (op->hasKnownIntValue() && (!op->isLiteral() || op->isBoolean())) {
1550  bail = true;
1551  break;
1552  }
1553  }
1554  if (bail)
1555  continue;
1556  }
1557  if (Token::simpleMatch(tok, ":"))
1558  continue;
1559  if (Token::Match(tok->astOperand1(), "%name% (") && Token::simpleMatch(tok->astParent(), "return"))
1560  continue;
1561  if (tok->isComparisonOp() && isWithoutSideEffects(tok->astOperand1()) &&
1562  isSameExpression(true,
1563  tok->astOperand1(),
1564  tok->astOperand2(),
1565  *mSettings,
1566  true,
1567  true))
1568  continue;
1569  if (isConstVarExpression(tok, [](const Token* tok) {
1570  return Token::Match(tok, "[|(|&|+|-|*|/|%|^|>>|<<") && !Token::simpleMatch(tok, "( )");
1571  }))
1572  continue;
1573 
1574  // there are specific warnings about nonzero expressions (pointer/unsigned)
1575  // do not write alwaysTrueFalse for these comparisons.
1576  {
1577  const ValueFlow::Value *zeroValue = nullptr;
1578  const Token *nonZeroExpr = nullptr;
1579  if (CheckOther::comparisonNonZeroExpressionLessThanZero(tok, zeroValue, nonZeroExpr, /*suppress*/ true) ||
1580  CheckOther::testIfNonZeroExpressionIsPositive(tok, zeroValue, nonZeroExpr))
1581  continue;
1582  }
1583 
1584  // Don't warn when there are expanded macros..
1585  bool isExpandedMacro = false;
1586  visitAstNodes(tok, [&](const Token * tok2) {
1587  if (!tok2)
1588  return ChildrenToVisit::none;
1589  if (tok2->isExpandedMacro()) {
1590  isExpandedMacro = true;
1591  return ChildrenToVisit::done;
1592  }
1594  });
1595  if (isExpandedMacro)
1596  continue;
1597  for (const Token *parent = tok; parent; parent = parent->astParent()) {
1598  if (parent->isExpandedMacro()) {
1599  isExpandedMacro = true;
1600  break;
1601  }
1602  }
1603  if (isExpandedMacro)
1604  continue;
1605 
1606  // don't warn when condition checks sizeof result
1607  bool hasSizeof = false;
1608  visitAstNodes(tok, [&](const Token * tok2) {
1609  if (!tok2)
1610  return ChildrenToVisit::none;
1611  if (tok2->isNumber())
1612  return ChildrenToVisit::none;
1613  if (Token::simpleMatch(tok2->previous(), "sizeof (")) {
1614  hasSizeof = true;
1615  return ChildrenToVisit::none;
1616  }
1617  if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) {
1618  return ChildrenToVisit::op1_and_op2;
1619  }
1620  return ChildrenToVisit::none;
1621  });
1622  if (hasSizeof)
1623  continue;
1624 
1625  alwaysTrueFalseError(tok, condition, &tok->values().front());
1626  }
1627  }
1628 }
1629 
1630 void CheckCondition::alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value)
1631 {
1632  const bool alwaysTrue = value && (value->intvalue != 0 || value->isImpossible());
1633  const std::string expr = tok ? tok->expressionString() : std::string("x");
1634  const std::string conditionStr = (Token::simpleMatch(condition, "return") ? "Return value" : "Condition");
1635  const std::string errmsg = conditionStr + " '" + expr + "' is always " + bool_to_string(alwaysTrue);
1636  const ErrorPath errorPath = getErrorPath(tok, value, errmsg);
1637  reportError(errorPath,
1639  "knownConditionTrueFalse",
1640  errmsg,
1641  (alwaysTrue ? CWE571 : CWE570), Certainty::normal);
1642 }
1643 
1645 {
1646  // Interesting blogs:
1647  // https://www.airs.com/blog/archives/120
1648  // https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html
1649  // https://research.checkpoint.com/2020/optout-compiler-undefined-behavior-optimizations/
1650 
1651  // x + c < x -> false
1652  // x + c <= x -> false
1653  // x + c > x -> true
1654  // x + c >= x -> true
1655 
1656  // x + y < x -> y < 0
1657 
1658 
1660  return;
1661 
1662  logChecker("CheckCondition::checkInvalidTestForOverflow"); // warning
1663 
1664  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
1665  if (!Token::Match(tok, "<|<=|>=|>") || !tok->isBinaryOp())
1666  continue;
1667 
1668  const Token *lhsTokens[2] = {tok->astOperand1(), tok->astOperand2()};
1669  for (const Token *lhs: lhsTokens) {
1670  std::string cmp = tok->str();
1671  if (lhs == tok->astOperand2())
1672  cmp[0] = (cmp[0] == '<') ? '>' : '<';
1673 
1674  if (!Token::Match(lhs, "[+-]") || !lhs->isBinaryOp())
1675  continue;
1676 
1677  const bool isSignedInteger = lhs->valueType() && lhs->valueType()->isIntegral() && lhs->valueType()->sign == ValueType::Sign::SIGNED;
1678  const bool isPointer = lhs->valueType() && lhs->valueType()->pointer > 0;
1679  if (!isSignedInteger && !isPointer)
1680  continue;
1681 
1682  const Token *exprTokens[2] = {lhs->astOperand1(), lhs->astOperand2()};
1683  for (const Token *expr: exprTokens) {
1684  if (lhs->str() == "-" && expr == lhs->astOperand2())
1685  continue; // TODO?
1686 
1687  if (expr->hasKnownIntValue())
1688  continue;
1689 
1690  if (!isSameExpression(true,
1691  expr,
1692  lhs->astSibling(),
1693  *mSettings,
1694  true,
1695  false))
1696  continue;
1697 
1698  const Token * const other = expr->astSibling();
1699 
1700  // x [+-] c cmp x
1701  if ((other->isNumber() && other->getKnownIntValue() > 0) ||
1702  (!other->isNumber() && other->valueType() && other->valueType()->isIntegral() && other->valueType()->sign == ValueType::Sign::UNSIGNED)) {
1703  bool result;
1704  if (lhs->str() == "+")
1705  result = (cmp == ">" || cmp == ">=");
1706  else
1707  result = (cmp == "<" || cmp == "<=");
1708  invalidTestForOverflow(tok, lhs->valueType(), bool_to_string(result));
1709  continue;
1710  }
1711 
1712  // x + y cmp x
1713  if (lhs->str() == "+" && other->varId() > 0) {
1714  const std::string result = other->str() + cmp + "0";
1715  invalidTestForOverflow(tok, lhs->valueType(), result);
1716  continue;
1717  }
1718 
1719  // x - y cmp x
1720  if (lhs->str() == "-" && other->varId() > 0) {
1721  std::string cmp2 = cmp;
1722  cmp2[0] = (cmp[0] == '<') ? '>' : '<';
1723  const std::string result = other->str() + cmp2 + "0";
1724  invalidTestForOverflow(tok, lhs->valueType(), result);
1725  continue;
1726  }
1727  }
1728  }
1729  }
1730 }
1731 
1732 void CheckCondition::invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace)
1733 {
1734  const std::string expr = (tok ? tok->expressionString() : std::string("x + c < x"));
1735  const std::string overflow = (valueType && valueType->pointer) ? "pointer overflow" : "signed integer overflow";
1736 
1737  std::string errmsg =
1738  "Invalid test for overflow '" + expr + "'; " + overflow + " is undefined behavior.";
1739  if (replace == "false" || replace == "true")
1740  errmsg += " Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " + replace + ".";
1741  else
1742  errmsg += " Some mainstream compilers removes handling of overflows when optimising the code and change the code to '" + replace + "'.";
1743  reportError(tok, Severity::warning, "invalidTestForOverflow", errmsg, uncheckedErrorConditionCWE, Certainty::normal);
1744 }
1745 
1746 
1748 {
1750  return;
1751 
1752  logChecker("CheckCondition::checkPointerAdditionResultNotNull"); // warning
1753 
1754  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1755  for (const Scope * scope : symbolDatabase->functionScopes) {
1756 
1757  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1758  if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2())
1759  continue;
1760 
1761  // Macros might have pointless safety checks
1762  if (tok->isExpandedMacro())
1763  continue;
1764 
1765  const Token *calcToken, *exprToken;
1766  if (tok->astOperand1()->str() == "+") {
1767  calcToken = tok->astOperand1();
1768  exprToken = tok->astOperand2();
1769  } else if (tok->astOperand2()->str() == "+") {
1770  calcToken = tok->astOperand2();
1771  exprToken = tok->astOperand1();
1772  } else
1773  continue;
1774 
1775  // pointer comparison against NULL (ptr+12==0)
1776  if (calcToken->hasKnownIntValue())
1777  continue;
1778  if (!calcToken->valueType() || calcToken->valueType()->pointer==0)
1779  continue;
1780  if (!exprToken->hasKnownIntValue() || !exprToken->getValue(0))
1781  continue;
1782 
1783  pointerAdditionResultNotNullError(tok, calcToken);
1784  }
1785  }
1786 }
1787 
1789 {
1790  const std::string s = calc ? calc->expressionString() : "ptr+1";
1791  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.");
1792 }
1793 
1795 {
1796  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("duplicateConditionalAssign"))
1797  return;
1798 
1799  logChecker("CheckCondition::checkDuplicateConditionalAssign"); // style
1800 
1801  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1802  for (const Scope *scope : symbolDatabase->functionScopes) {
1803  for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1804  if (!Token::simpleMatch(tok, "if ("))
1805  continue;
1806  if (!Token::simpleMatch(tok->next()->link(), ") {"))
1807  continue;
1808  const Token *blockTok = tok->next()->link()->next();
1809  const Token *condTok = tok->next()->astOperand2();
1810  const bool isBoolVar = Token::Match(condTok, "!| %var%");
1811  if (!isBoolVar && !Token::Match(condTok, "==|!="))
1812  continue;
1813  if ((isBoolVar || condTok->str() == "!=") && Token::simpleMatch(blockTok->link(), "} else {"))
1814  continue;
1815  if (!blockTok->next())
1816  continue;
1817  const Token *assignTok = blockTok->next()->astTop();
1818  if (!Token::simpleMatch(assignTok, "="))
1819  continue;
1820  if (nextAfterAstRightmostLeaf(assignTok) != blockTok->link()->previous())
1821  continue;
1822  bool isRedundant = false;
1823  if (isBoolVar) {
1824  const bool isNegation = condTok->str() == "!";
1825  const Token* const varTok = isNegation ? condTok->next() : condTok;
1826  const ValueType* vt = varTok->variable() ? varTok->variable()->valueType() : nullptr;
1827  if (!(vt && vt->type == ValueType::Type::BOOL && !vt->pointer))
1828  continue;
1829 
1830  if (!(assignTok->astOperand1() && assignTok->astOperand1()->varId() == varTok->varId()))
1831  continue;
1832  if (!(assignTok->astOperand2() && assignTok->astOperand2()->hasKnownIntValue()))
1833  continue;
1834  const MathLib::bigint val = assignTok->astOperand2()->getKnownIntValue();
1835  if (val < 0 || val > 1)
1836  continue;
1837  isRedundant = (isNegation && val == 0) || (!isNegation && val == 1);
1838  } else { // comparison
1839  if (!isSameExpression(
1840  true, condTok->astOperand1(), assignTok->astOperand1(), *mSettings, true, true))
1841  continue;
1842  if (!isSameExpression(
1843  true, condTok->astOperand2(), assignTok->astOperand2(), *mSettings, true, true))
1844  continue;
1845  }
1846  duplicateConditionalAssignError(condTok, assignTok, isRedundant);
1847  }
1848  }
1849 }
1850 
1851 void CheckCondition::duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant)
1852 {
1853  ErrorPath errors;
1854  std::string msg = "Duplicate expression for the condition and assignment.";
1855  if (condTok && assignTok) {
1856  if (condTok->str() == "==") {
1857  msg = "Assignment '" + assignTok->expressionString() + "' is redundant with condition '" + condTok->expressionString() + "'.";
1858  errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "'");
1859  errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "' is redundant");
1860  } else {
1861  msg = "The statement 'if (" + condTok->expressionString() + ") " + assignTok->expressionString();
1862  msg += isRedundant ? "' is redundant." : "' is logically equivalent to '" + assignTok->expressionString() + "'.";
1863  errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "'");
1864  errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "' is redundant");
1865  }
1866  }
1867 
1868  reportError(
1869  errors, Severity::style, "duplicateConditionalAssign", msg, CWE398, Certainty::normal);
1870 }
1871 
1872 
1874 {
1875  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("assignmentInCondition"))
1876  return;
1877 
1878  logChecker("CheckCondition::checkAssignmentInCondition"); // style
1879 
1880  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1881  for (const Scope * scope : symbolDatabase->functionScopes) {
1882  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1883  if (tok->str() != "=")
1884  continue;
1885  if (!tok->astParent())
1886  continue;
1887 
1888  // Is this assignment of container/iterator?
1889  if (!tok->valueType())
1890  continue;
1891  if (tok->valueType()->pointer > 0)
1892  continue;
1893  if (tok->valueType()->type != ValueType::Type::CONTAINER && tok->valueType()->type != ValueType::Type::ITERATOR)
1894  continue;
1895 
1896  // warn if this is a conditional expression..
1897  if (Token::Match(tok->astParent()->previous(), "if|while ("))
1898  assignmentInCondition(tok);
1899  else if (Token::Match(tok->astParent(), "%oror%|&&"))
1900  assignmentInCondition(tok);
1901  else if (Token::simpleMatch(tok->astParent(), "?") && tok == tok->astParent()->astOperand1())
1902  assignmentInCondition(tok);
1903  }
1904  }
1905 }
1906 
1908 {
1909  std::string expr = eq ? eq->expressionString() : "x=y";
1910 
1911  reportError(
1912  eq,
1914  "assignmentInCondition",
1915  "Suspicious assignment in condition. Condition '" + expr + "' is always true.",
1916  CWE571,
1918 }
1919 
1921 {
1922  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("compareValueOutOfTypeRange"))
1923  return;
1924 
1925  if (mSettings->platform.type == Platform::Type::Native ||
1926  mSettings->platform.type == Platform::Type::Unspecified)
1927  return;
1928 
1929  logChecker("CheckCondition::checkCompareValueOutOfTypeRange"); // style,platform
1930 
1931  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1932  for (const Scope * scope : symbolDatabase->functionScopes) {
1933  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1934  if (!tok->isComparisonOp() || !tok->isBinaryOp())
1935  continue;
1936 
1937  for (int i = 0; i < 2; ++i) {
1938  const Token * const valueTok = (i == 0) ? tok->astOperand1() : tok->astOperand2();
1939  const Token * const typeTok = valueTok->astSibling();
1940  if (!valueTok->hasKnownIntValue() || !typeTok->valueType() || typeTok->valueType()->pointer)
1941  continue;
1942  if (valueTok->getKnownIntValue() < 0 && valueTok->valueType() && valueTok->valueType()->sign != ValueType::Sign::SIGNED)
1943  continue;
1944  if (valueTok->valueType() && valueTok->valueType()->isTypeEqual(typeTok->valueType()))
1945  continue;
1946  int bits = 0;
1947  switch (typeTok->valueType()->type) {
1948  case ValueType::Type::BOOL:
1949  bits = 1;
1950  break;
1951  case ValueType::Type::CHAR:
1952  bits = mSettings->platform.char_bit;
1953  break;
1954  case ValueType::Type::SHORT:
1955  bits = mSettings->platform.short_bit;
1956  break;
1957  case ValueType::Type::INT:
1958  bits = mSettings->platform.int_bit;
1959  break;
1960  case ValueType::Type::LONG:
1961  bits = mSettings->platform.long_bit;
1962  break;
1963  case ValueType::Type::LONGLONG:
1965  break;
1966  default:
1967  break;
1968  }
1969  if (bits == 0 || bits >= 64)
1970  continue;
1971 
1972  const auto typeMinValue = (typeTok->valueType()->sign == ValueType::Sign::UNSIGNED) ? 0 : (-(1LL << (bits-1)));
1973  const auto unsignedTypeMaxValue = (1LL << bits) - 1LL;
1974  long long typeMaxValue;
1975  if (typeTok->valueType()->sign != ValueType::Sign::SIGNED)
1976  typeMaxValue = unsignedTypeMaxValue;
1977  else if (bits >= mSettings->platform.int_bit && (!valueTok->valueType() || valueTok->valueType()->sign != ValueType::Sign::SIGNED))
1978  typeMaxValue = unsignedTypeMaxValue;
1979  else
1980  typeMaxValue = unsignedTypeMaxValue / 2;
1981 
1982  bool result{};
1983  const auto kiv = valueTok->getKnownIntValue();
1984  if (tok->str() == "==")
1985  result = false;
1986  else if (tok->str() == "!=")
1987  result = true;
1988  else if (tok->str()[0] == '>' && i == 0)
1989  // num > var
1990  result = (kiv > 0);
1991  else if (tok->str()[0] == '>' && i == 1)
1992  // var > num
1993  result = (kiv < 0);
1994  else if (tok->str()[0] == '<' && i == 0)
1995  // num < var
1996  result = (kiv < 0);
1997  else if (tok->str()[0] == '<' && i == 1)
1998  // var < num
1999  result = (kiv > 0);
2000 
2001  bool error = false;
2002  if (kiv < typeMinValue || kiv > typeMaxValue) {
2003  error = true;
2004  } else {
2005  switch (i) {
2006  case 0: // num cmp var
2007  if (kiv == typeMinValue) {
2008  if (tok->str() == "<=") {
2009  result = true;
2010  error = true;
2011  } else if (tok->str() == ">")
2012  error = true;
2013  }
2014  else if (kiv == typeMaxValue && (tok->str() == ">=" || tok->str() == "<")) {
2015  error = true;
2016  }
2017  break;
2018  case 1: // var cmp num
2019  if (kiv == typeMinValue) {
2020  if (tok->str() == ">=") {
2021  result = true;
2022  error = true;
2023  } else if (tok->str() == "<")
2024  error = true;
2025  }
2026  else if (kiv == typeMaxValue && (tok->str() == "<=" || tok->str() == ">")) {
2027  error = true;
2028  }
2029  break;
2030  }
2031  }
2032  if (error)
2033  compareValueOutOfTypeRangeError(valueTok, typeTok->valueType()->str(), kiv, result);
2034  }
2035  }
2036  }
2037 }
2038 
2039 void CheckCondition::compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result)
2040 {
2041  reportError(
2042  comparison,
2044  "compareValueOutOfTypeRangeError",
2045  "Comparing expression of type '" + type + "' against value " + std::to_string(value) + ". Condition is always " + bool_to_string(result) + ".",
2046  CWE398,
2048 }
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:2808
bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Settings &settings, bool pure, bool followVar, ErrorPath *errors)
Definition: astutils.cpp:1556
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:994
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 isLikelyStreamRead(const Token *op)
do we see a likely write of rhs through overloaded operator s >> x; a & x;
Definition: astutils.cpp:3221
const Token * findExpressionChanged(const Token *expr, const Token *start, const Token *end, const Settings &settings, int depth)
Definition: astutils.cpp:3028
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:1785
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
bool isVariablesChanged(const Token *start, const Token *end, int indirect, const std::vector< const Variable * > &vars, const Settings &settings)
Definition: astutils.cpp:2896
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
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:1534
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:608
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:2562
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 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:2519
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:2718
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
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1093
nonneg int varId() const
Definition: token.h:870
std::string expressionString() const
Definition: token.cpp:1647
bool isUnaryOp(const std::string &s) const
Definition: token.h:413
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
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
@ 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:1437
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