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