/src/cppcheck/oss-fuzz/build/checkbool.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | #include "matchcompiler.h" |
2 | | #include <string> |
3 | | #include <cstring> |
4 | | #include "errorlogger.h" |
5 | | #include "token.h" |
6 | | // pattern: bool|_Bool |
7 | 1.51k | static inline bool match1(const Token* tok) { |
8 | 1.51k | if (!tok || !((tok->str() == MatchCompiler::makeConstString("bool")) || (tok->str() == MatchCompiler::makeConstString("_Bool")))) |
9 | 1.51k | return false; |
10 | 0 | return true; |
11 | 1.51k | } |
12 | | // pattern: if|while ( |
13 | 18 | static inline bool match2(const Token* tok) { |
14 | 18 | if (!tok || !(((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("if")) || ((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("while")))) |
15 | 4 | return false; |
16 | 14 | tok = tok->next(); |
17 | 14 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
18 | 0 | return false; |
19 | 14 | return true; |
20 | 14 | } |
21 | | // pattern: %name% ( |
22 | 0 | static inline bool match3(const Token* tok) { |
23 | 0 | if (!tok || !tok->isName()) |
24 | 0 | return false; |
25 | 0 | tok = tok->next(); |
26 | 0 | if (!tok || !((tok->tokType() == Token::eExtendedOp) && tok->str() == MatchCompiler::makeConstString("("))) |
27 | 0 | return false; |
28 | 0 | return true; |
29 | 0 | } |
30 | | // pattern: ! |
31 | 1.25k | static inline bool match4(const Token* tok) { |
32 | 1.25k | if (!tok || !((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("!"))) |
33 | 1.25k | return false; |
34 | 0 | return true; |
35 | 1.25k | } |
36 | | // pattern: ==|!= |
37 | 112 | static inline bool match5(const Token* tok) { |
38 | 112 | if (!tok || !(((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("==")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("!=")))) |
39 | 81 | return false; |
40 | 31 | return true; |
41 | 112 | } |
42 | | // pattern: >|==|!= |
43 | 2 | static inline bool match6(const Token* tok) { |
44 | 2 | if (!tok || !(((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString(">")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("==")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("!=")))) |
45 | 1 | return false; |
46 | 1 | return true; |
47 | 2 | } |
48 | | // pattern: <|==|!= |
49 | 1 | static inline bool match7(const Token* tok) { |
50 | 1 | if (!tok || !(((tok->tokType() == Token::eBracket || tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("<")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("==")) || ((tok->tokType() == Token::eComparisonOp) && tok->str() == MatchCompiler::makeConstString("!=")))) |
51 | 0 | return false; |
52 | 1 | return true; |
53 | 1 | } |
54 | | // pattern: ; |
55 | 0 | template<class T> static inline T * findmatch8(T * start_tok) { |
56 | 0 | for (; start_tok; start_tok = start_tok->next()) { |
57 | |
|
58 | 0 | T * tok = start_tok; |
59 | 0 | if (!tok || !(tok->str() == MatchCompiler::makeConstString(";"))) |
60 | 0 | continue; |
61 | 0 | return start_tok; |
62 | 0 | } |
63 | 0 | return nullptr; |
64 | 0 | } |
65 | | // pattern: &&|%oror% |
66 | 652 | static inline bool match9(const Token* tok) { |
67 | 652 | if (!tok || !(((tok->tokType() == Token::eLogicalOp) && tok->str() == MatchCompiler::makeConstString("&&")) || (tok->tokType() == Token::eLogicalOp && tok->str() == MatchCompiler::makeConstString("||")))) |
68 | 652 | return false; |
69 | 0 | return true; |
70 | 652 | } |
71 | | // pattern: return |
72 | 0 | static inline bool match10(const Token* tok) { |
73 | 0 | if (!tok || !((tok->tokType() == Token::eKeyword) && tok->str() == MatchCompiler::makeConstString("return"))) |
74 | 0 | return false; |
75 | 0 | return true; |
76 | 0 | } |
77 | | // pattern: &|%or% |
78 | 0 | static inline bool match11(const Token* tok) { |
79 | 0 | if (!tok || !(((tok->tokType() == Token::eBitOp) && tok->str() == MatchCompiler::makeConstString("&")) || (tok->tokType() == Token::eBitOp && tok->str() == MatchCompiler::makeConstString("|") ))) |
80 | 0 | return false; |
81 | 0 | return true; |
82 | 0 | } |
83 | | /* |
84 | | * Cppcheck - A tool for static C/C++ code analysis |
85 | | * Copyright (C) 2007-2024 Cppcheck team. |
86 | | * |
87 | | * This program is free software: you can redistribute it and/or modify |
88 | | * it under the terms of the GNU General Public License as published by |
89 | | * the Free Software Foundation, either version 3 of the License, or |
90 | | * (at your option) any later version. |
91 | | * |
92 | | * This program is distributed in the hope that it will be useful, |
93 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
94 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
95 | | * GNU General Public License for more details. |
96 | | * |
97 | | * You should have received a copy of the GNU General Public License |
98 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
99 | | */ |
100 | | |
101 | | |
102 | | //--------------------------------------------------------------------------- |
103 | | #include "checkbool.h" |
104 | | |
105 | | #include "astutils.h" |
106 | | #include "errortypes.h" |
107 | | #include "settings.h" |
108 | | #include "symboldatabase.h" |
109 | | #include "token.h" |
110 | | #include "tokenize.h" |
111 | | #include "vfvalue.h" |
112 | | |
113 | | #include <list> |
114 | | #include <vector> |
115 | | //--------------------------------------------------------------------------- |
116 | | |
117 | | // Register this check class (by creating a static instance of it) |
118 | | namespace { |
119 | | CheckBool instance; |
120 | | } |
121 | | |
122 | | static const CWE CWE398(398U); // Indicator of Poor Code Quality |
123 | | static const CWE CWE571(571U); // Expression is Always True |
124 | | static const CWE CWE587(587U); // Assignment of a Fixed Address to a Pointer |
125 | | static const CWE CWE704(704U); // Incorrect Type Conversion or Cast |
126 | | |
127 | | static bool isBool(const Variable* var) |
128 | 412 | { |
129 | 412 | return (var && match1(var->typeEndToken())); |
130 | 412 | } |
131 | | |
132 | | //--------------------------------------------------------------------------- |
133 | | void CheckBool::checkIncrementBoolean() |
134 | 764 | { |
135 | 764 | if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("incrementboolean")) |
136 | 0 | return; |
137 | | |
138 | 764 | logChecker("CheckBool::checkIncrementBoolean"); // style |
139 | | |
140 | 764 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
141 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
142 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
143 | 21.5k | if (astIsBool(tok) && tok->astParent() && tok->astParent()->str() == MatchCompiler::makeConstString("++")) { |
144 | 0 | incrementBooleanError(tok); |
145 | 0 | } |
146 | 21.5k | } |
147 | 1.10k | } |
148 | 764 | } |
149 | | |
150 | | void CheckBool::incrementBooleanError(const Token *tok) |
151 | 0 | { |
152 | 0 | reportError( |
153 | 0 | tok, |
154 | 0 | Severity::style, |
155 | 0 | "incrementboolean", |
156 | 0 | "Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n" |
157 | 0 | "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true. You should assign it the value 'true' instead.", |
158 | 0 | CWE398, Certainty::normal |
159 | 0 | ); |
160 | 0 | } |
161 | | |
162 | | static bool isConvertedToBool(const Token* tok) |
163 | 18 | { |
164 | 18 | if (!tok->astParent()) |
165 | 0 | return false; |
166 | 18 | return astIsBool(tok->astParent()) || match2(tok->astParent()->previous()); |
167 | 18 | } |
168 | | |
169 | | //--------------------------------------------------------------------------- |
170 | | // if (bool & bool) -> if (bool && bool) |
171 | | // if (bool | bool) -> if (bool || bool) |
172 | | //--------------------------------------------------------------------------- |
173 | | void CheckBool::checkBitwiseOnBoolean() |
174 | 764 | { |
175 | 764 | if (!mSettings->severity.isEnabled(Severity::style)) |
176 | 0 | return; |
177 | | |
178 | | // danmar: this is inconclusive because I don't like that there are |
179 | | // warnings for calculations. Example: set_flag(a & b); |
180 | 764 | if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) |
181 | 0 | return; |
182 | | |
183 | 764 | logChecker("CheckBool::checkBitwiseOnBoolean"); // style,inconclusive |
184 | | |
185 | 764 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
186 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
187 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
188 | 21.5k | if (tok->isBinaryOp()) { |
189 | 4.52k | bool isCompound{}; |
190 | 4.52k | if (tok->str() == MatchCompiler::makeConstString("&") || tok->str() == MatchCompiler::makeConstString("|")) |
191 | 200 | isCompound = false; |
192 | 4.32k | else if (tok->str() == MatchCompiler::makeConstString("&=") || tok->str() == MatchCompiler::makeConstString("|=")) |
193 | 0 | isCompound = true; |
194 | 4.32k | else |
195 | 4.32k | continue; |
196 | 200 | const bool isBoolOp1 = astIsBool(tok->astOperand1()); |
197 | 200 | const bool isBoolOp2 = astIsBool(tok->astOperand2()); |
198 | 200 | if (!tok->astOperand1()->valueType() || !tok->astOperand2()->valueType()) |
199 | 94 | continue; |
200 | 106 | if (!(isBoolOp1 || isBoolOp2)) |
201 | 56 | continue; |
202 | 50 | if (isCompound && (!isBoolOp1 || isBoolOp2)) |
203 | 0 | continue; |
204 | 50 | if (tok->str() == MatchCompiler::makeConstString("|") && !isConvertedToBool(tok) && !(isBoolOp1 && isBoolOp2)) |
205 | 4 | continue; |
206 | | // first operand will always be evaluated |
207 | 46 | if (!isConstExpression(tok->astOperand2(), mSettings->library)) |
208 | 11 | continue; |
209 | 35 | if (tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2()) |
210 | 0 | continue; |
211 | 35 | const std::string expression = (isBoolOp1 ? tok->astOperand1() : tok->astOperand2())->expressionString(); |
212 | 35 | bitwiseOnBooleanError(tok, expression, tok->str() == MatchCompiler::makeConstString("&") ? "&&" : "||", isCompound); |
213 | 35 | } |
214 | 21.5k | } |
215 | 1.10k | } |
216 | 764 | } |
217 | | |
218 | | void CheckBool::bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op, bool isCompound) |
219 | 35 | { |
220 | 35 | std::string msg = "Boolean expression '" + expression + "' is used in bitwise operation."; |
221 | 35 | if (!isCompound) |
222 | 35 | msg += " Did you mean '" + op + "'?"; |
223 | 35 | reportError(tok, |
224 | 35 | Severity::style, |
225 | 35 | "bitwiseOnBoolean", |
226 | 35 | msg, |
227 | 35 | CWE398, |
228 | 35 | Certainty::inconclusive); |
229 | 35 | } |
230 | | |
231 | | //--------------------------------------------------------------------------- |
232 | | // if (!x==3) <- Probably meant to be "x!=3" |
233 | | //--------------------------------------------------------------------------- |
234 | | |
235 | | void CheckBool::checkComparisonOfBoolWithInt() |
236 | 764 | { |
237 | 764 | if (!mSettings->severity.isEnabled(Severity::warning) || !mTokenizer->isCPP()) |
238 | 0 | return; |
239 | | |
240 | 764 | logChecker("CheckBool::checkComparisonOfBoolWithInt"); // warning,c++ |
241 | | |
242 | 764 | const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); |
243 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
244 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
245 | 21.5k | if (!tok->isComparisonOp() || !tok->isBinaryOp()) |
246 | 20.6k | continue; |
247 | 932 | const Token* const left = tok->astOperand1(); |
248 | 932 | const Token* const right = tok->astOperand2(); |
249 | 932 | if (left->isBoolean() && right->varId()) { // Comparing boolean constant with variable |
250 | 0 | if (tok->str() != MatchCompiler::makeConstString("==") && tok->str() != MatchCompiler::makeConstString("!=")) { |
251 | 0 | comparisonOfBoolWithInvalidComparator(right, left->str()); |
252 | 0 | } |
253 | 932 | } else if (left->varId() && right->isBoolean()) { // Comparing variable with boolean constant |
254 | 0 | if (tok->str() != MatchCompiler::makeConstString("==") && tok->str() != MatchCompiler::makeConstString("!=")) { |
255 | 0 | comparisonOfBoolWithInvalidComparator(right, left->str()); |
256 | 0 | } |
257 | 0 | } |
258 | 932 | } |
259 | 1.10k | } |
260 | 764 | } |
261 | | |
262 | | void CheckBool::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression) |
263 | 0 | { |
264 | 0 | reportError(tok, Severity::warning, "comparisonOfBoolWithInvalidComparator", |
265 | 0 | "Comparison of a boolean value using relational operator (<, >, <= or >=).\n" |
266 | 0 | "The result of the expression '" + expression + "' is of type 'bool'. " |
267 | 0 | "Comparing 'bool' value using relational (<, >, <= or >=)" |
268 | 0 | " operator could cause unexpected results."); |
269 | 0 | } |
270 | | |
271 | | //------------------------------------------------------------------------------- |
272 | | // Comparing functions which are returning value of type bool |
273 | | //------------------------------------------------------------------------------- |
274 | | |
275 | | static bool tokenIsFunctionReturningBool(const Token* tok) |
276 | 1.25k | { |
277 | 1.25k | const Function* func = tok ? tok->function() : nullptr; |
278 | 1.25k | if (func && match3(tok)) { |
279 | 0 | if (func->tokenDef && match1(func->tokenDef->previous())) { |
280 | 0 | return true; |
281 | 0 | } |
282 | 0 | } |
283 | 1.25k | return false; |
284 | 1.25k | } |
285 | | |
286 | | void CheckBool::checkComparisonOfFuncReturningBool() |
287 | 764 | { |
288 | 764 | if (!mSettings->severity.isEnabled(Severity::style)) |
289 | 0 | return; |
290 | | |
291 | 764 | if (!mTokenizer->isCPP()) |
292 | 0 | return; |
293 | | |
294 | 764 | logChecker("CheckBool::checkComparisonOfFuncReturningBool"); // style,c++ |
295 | | |
296 | 764 | const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); |
297 | 1.25k | auto getFunctionTok = [](const Token* tok) -> const Token* { |
298 | 1.25k | while (match4(tok) || (tok && tok->isCast() && !isCPPCast(tok))) |
299 | 0 | tok = tok->astOperand1(); |
300 | 1.25k | if (isCPPCast(tok)) |
301 | 0 | tok = tok->astOperand2(); |
302 | 1.25k | if (tok) |
303 | 1.25k | return tok->previous(); |
304 | 0 | return nullptr; |
305 | 1.25k | }; |
306 | | |
307 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
308 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
309 | 21.5k | if (!tok->isComparisonOp() || tok->str() == MatchCompiler::makeConstString("==") || tok->str() == MatchCompiler::makeConstString("!=")) |
310 | 20.9k | continue; |
311 | | |
312 | 627 | const Token* firstToken = getFunctionTok(tok->astOperand1()); |
313 | 627 | const Token* secondToken = getFunctionTok(tok->astOperand2()); |
314 | 627 | if (!firstToken || !secondToken) |
315 | 0 | continue; |
316 | | |
317 | 627 | const bool firstIsFunctionReturningBool = tokenIsFunctionReturningBool(firstToken); |
318 | 627 | const bool secondIsFunctionReturningBool = tokenIsFunctionReturningBool(secondToken); |
319 | 627 | if (firstIsFunctionReturningBool && secondIsFunctionReturningBool) { |
320 | 0 | comparisonOfTwoFuncsReturningBoolError(firstToken->next(), firstToken->str(), secondToken->str()); |
321 | 627 | } else if (firstIsFunctionReturningBool) { |
322 | 0 | comparisonOfFuncReturningBoolError(firstToken->next(), firstToken->str()); |
323 | 627 | } else if (secondIsFunctionReturningBool) { |
324 | 0 | comparisonOfFuncReturningBoolError(secondToken->previous(), secondToken->str()); |
325 | 0 | } |
326 | 627 | } |
327 | 1.10k | } |
328 | 764 | } |
329 | | |
330 | | void CheckBool::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression) |
331 | 0 | { |
332 | 0 | reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError", |
333 | 0 | "Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" |
334 | 0 | "The return type of function '" + expression + "' is 'bool' " |
335 | 0 | "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" |
336 | 0 | " operator could cause unexpected results.", CWE398, Certainty::normal); |
337 | 0 | } |
338 | | |
339 | | void CheckBool::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2) |
340 | 0 | { |
341 | 0 | reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError", |
342 | 0 | "Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" |
343 | 0 | "The return type of function '" + expression1 + "' and function '" + expression2 + "' is 'bool' " |
344 | 0 | "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" |
345 | 0 | " operator could cause unexpected results.", CWE398, Certainty::normal); |
346 | 0 | } |
347 | | |
348 | | //------------------------------------------------------------------------------- |
349 | | // Comparison of bool with bool |
350 | | //------------------------------------------------------------------------------- |
351 | | |
352 | | void CheckBool::checkComparisonOfBoolWithBool() |
353 | 764 | { |
354 | 764 | if (!mSettings->severity.isEnabled(Severity::style)) |
355 | 0 | return; |
356 | | |
357 | 764 | if (!mTokenizer->isCPP()) |
358 | 0 | return; |
359 | | |
360 | 764 | logChecker("CheckBool::checkComparisonOfBoolWithBool"); // style,c++ |
361 | | |
362 | 764 | const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); |
363 | | |
364 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
365 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
366 | 21.5k | if (!tok->isComparisonOp() || tok->str() == MatchCompiler::makeConstString("==") || tok->str() == MatchCompiler::makeConstString("!=")) |
367 | 20.9k | continue; |
368 | 627 | bool firstTokenBool = false; |
369 | | |
370 | 627 | const Token *firstToken = tok->previous(); |
371 | 627 | if (firstToken->varId()) { |
372 | 412 | if (isBool(firstToken->variable())) { |
373 | 0 | firstTokenBool = true; |
374 | 0 | } |
375 | 412 | } |
376 | 627 | if (!firstTokenBool) |
377 | 627 | continue; |
378 | | |
379 | 0 | bool secondTokenBool = false; |
380 | 0 | const Token *secondToken = tok->next(); |
381 | 0 | if (secondToken->varId()) { |
382 | 0 | if (isBool(secondToken->variable())) { |
383 | 0 | secondTokenBool = true; |
384 | 0 | } |
385 | 0 | } |
386 | 0 | if (secondTokenBool) { |
387 | 0 | comparisonOfBoolWithBoolError(firstToken->next(), secondToken->str()); |
388 | 0 | } |
389 | 0 | } |
390 | 1.10k | } |
391 | 764 | } |
392 | | |
393 | | void CheckBool::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression) |
394 | 0 | { |
395 | 0 | reportError(tok, Severity::style, "comparisonOfBoolWithBoolError", |
396 | 0 | "Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n" |
397 | 0 | "The variable '" + expression + "' is of type 'bool' " |
398 | 0 | "and comparing 'bool' value using relational (<, >, <= or >=)" |
399 | 0 | " operator could cause unexpected results.", CWE398, Certainty::normal); |
400 | 0 | } |
401 | | |
402 | | //----------------------------------------------------------------------------- |
403 | | void CheckBool::checkAssignBoolToPointer() |
404 | 764 | { |
405 | 764 | logChecker("CheckBool::checkAssignBoolToPointer"); |
406 | 764 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
407 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
408 | 23.7k | for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
409 | 22.6k | if (tok->str() == MatchCompiler::makeConstString("=") && astIsPointer(tok->astOperand1()) && astIsBool(tok->astOperand2())) { |
410 | 0 | assignBoolToPointerError(tok); |
411 | 0 | } |
412 | 22.6k | } |
413 | 1.10k | } |
414 | 764 | } |
415 | | |
416 | | void CheckBool::assignBoolToPointerError(const Token *tok) |
417 | 0 | { |
418 | 0 | reportError(tok, Severity::error, "assignBoolToPointer", |
419 | 0 | "Boolean value assigned to pointer.", CWE587, Certainty::normal); |
420 | 0 | } |
421 | | |
422 | | //----------------------------------------------------------------------------- |
423 | | //----------------------------------------------------------------------------- |
424 | | void CheckBool::checkComparisonOfBoolExpressionWithInt() |
425 | 764 | { |
426 | 764 | if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("compareBoolExpressionWithInt")) |
427 | 0 | return; |
428 | | |
429 | 764 | logChecker("CheckBool::checkComparisonOfBoolExpressionWithInt"); // warning |
430 | | |
431 | 764 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
432 | | |
433 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
434 | 22.6k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
435 | 21.5k | if (!tok->isComparisonOp()) |
436 | 20.6k | continue; |
437 | | |
438 | 932 | const Token* numTok = nullptr; |
439 | 932 | const Token* boolExpr = nullptr; |
440 | 932 | bool numInRhs; |
441 | 932 | if (astIsBool(tok->astOperand1())) { |
442 | 119 | boolExpr = tok->astOperand1(); |
443 | 119 | numTok = tok->astOperand2(); |
444 | 119 | numInRhs = true; |
445 | 813 | } else if (astIsBool(tok->astOperand2())) { |
446 | 18 | boolExpr = tok->astOperand2(); |
447 | 18 | numTok = tok->astOperand1(); |
448 | 18 | numInRhs = false; |
449 | 795 | } else { |
450 | 795 | continue; |
451 | 795 | } |
452 | | |
453 | 137 | if (!numTok || !boolExpr) |
454 | 0 | continue; |
455 | | |
456 | 137 | if (boolExpr->isOp() && numTok->isName() && match5(tok)) |
457 | | // there is weird code such as: ((a<b)==c) |
458 | | // but it is probably written this way by design. |
459 | 31 | continue; |
460 | | |
461 | 106 | if (astIsBool(numTok)) |
462 | 1 | continue; |
463 | | |
464 | 105 | const ValueFlow::Value *minval = numTok->getValueLE(0, *mSettings); |
465 | 105 | if (minval && minval->intvalue == 0 && |
466 | 105 | (numInRhs ? match6(tok) |
467 | 2 | : match7(tok))) |
468 | 1 | minval = nullptr; |
469 | | |
470 | 105 | const ValueFlow::Value *maxval = numTok->getValueGE(1, *mSettings); |
471 | 105 | if (maxval && maxval->intvalue == 1 && |
472 | 105 | (numInRhs ? match7(tok) |
473 | 1 | : match6(tok))) |
474 | 1 | maxval = nullptr; |
475 | | |
476 | 105 | if (minval || maxval) { |
477 | 1 | const bool not0or1 = (minval && minval->intvalue < 0) || (maxval && maxval->intvalue > 1); |
478 | 1 | comparisonOfBoolExpressionWithIntError(tok, not0or1); |
479 | 1 | } |
480 | 105 | } |
481 | 1.10k | } |
482 | 764 | } |
483 | | |
484 | | void CheckBool::comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1) |
485 | 1 | { |
486 | 1 | if (not0or1) |
487 | 0 | reportError(tok, Severity::warning, "compareBoolExpressionWithInt", |
488 | 0 | "Comparison of a boolean expression with an integer other than 0 or 1.", CWE398, Certainty::normal); |
489 | 1 | else |
490 | 1 | reportError(tok, Severity::warning, "compareBoolExpressionWithInt", |
491 | 1 | "Comparison of a boolean expression with an integer.", CWE398, Certainty::normal); |
492 | 1 | } |
493 | | |
494 | | |
495 | | void CheckBool::pointerArithBool() |
496 | 764 | { |
497 | 764 | logChecker("CheckBool::pointerArithBool"); |
498 | | |
499 | 764 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
500 | | |
501 | 2.79k | for (const Scope &scope : symbolDatabase->scopeList) { |
502 | 2.79k | if (scope.type != Scope::eIf && !scope.isLoopScope()) |
503 | 2.14k | continue; |
504 | 652 | const Token* tok = scope.classDef->next()->astOperand2(); |
505 | 652 | if (scope.type == Scope::eFor) { |
506 | 0 | tok = findmatch8(scope.classDef->tokAt(2)) ; |
507 | 0 | if (tok) |
508 | 0 | tok = tok->astOperand2(); |
509 | 0 | if (tok) |
510 | 0 | tok = tok->astOperand1(); |
511 | 652 | } else if (scope.type == Scope::eDo) |
512 | 0 | tok = (scope.bodyEnd->tokAt(2)) ? scope.bodyEnd->tokAt(2)->astOperand2() : nullptr; |
513 | | |
514 | 652 | pointerArithBoolCond(tok); |
515 | 652 | } |
516 | 764 | } |
517 | | |
518 | | void CheckBool::pointerArithBoolCond(const Token *tok) |
519 | 652 | { |
520 | 652 | if (!tok) |
521 | 0 | return; |
522 | 652 | if (match9(tok)) { |
523 | 0 | pointerArithBoolCond(tok->astOperand1()); |
524 | 0 | pointerArithBoolCond(tok->astOperand2()); |
525 | 0 | return; |
526 | 0 | } |
527 | 652 | if (tok->str() != MatchCompiler::makeConstString("+") && tok->str() != MatchCompiler::makeConstString("-")) |
528 | 652 | return; |
529 | | |
530 | 0 | if (tok->isBinaryOp() && |
531 | 0 | tok->astOperand1()->isName() && |
532 | 0 | tok->astOperand1()->variable() && |
533 | 0 | tok->astOperand1()->variable()->isPointer() && |
534 | 0 | tok->astOperand2()->isNumber()) |
535 | 0 | pointerArithBoolError(tok); |
536 | 0 | } |
537 | | |
538 | | void CheckBool::pointerArithBoolError(const Token *tok) |
539 | 0 | { |
540 | 0 | reportError(tok, |
541 | 0 | Severity::error, |
542 | 0 | "pointerArithBool", |
543 | 0 | "Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n" |
544 | 0 | "Converting pointer arithmetic result to bool. The boolean result is always true unless there is pointer arithmetic overflow, and overflow is undefined behaviour. Probably a dereference is forgotten.", CWE571, Certainty::normal); |
545 | 0 | } |
546 | | |
547 | | void CheckBool::checkAssignBoolToFloat() |
548 | 764 | { |
549 | 764 | if (!mTokenizer->isCPP()) |
550 | 0 | return; |
551 | 764 | if (!mSettings->severity.isEnabled(Severity::style)) |
552 | 0 | return; |
553 | 764 | logChecker("CheckBool::checkAssignBoolToFloat"); // style,c++ |
554 | 764 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
555 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
556 | 23.7k | for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
557 | 22.6k | if (tok->str() == MatchCompiler::makeConstString("=") && astIsFloat(tok->astOperand1(), false) && astIsBool(tok->astOperand2())) { |
558 | 0 | assignBoolToFloatError(tok); |
559 | 0 | } |
560 | 22.6k | } |
561 | 1.10k | } |
562 | 764 | } |
563 | | |
564 | | void CheckBool::assignBoolToFloatError(const Token *tok) |
565 | 0 | { |
566 | 0 | reportError(tok, Severity::style, "assignBoolToFloat", |
567 | 0 | "Boolean value assigned to floating point variable.", CWE704, Certainty::normal); |
568 | 0 | } |
569 | | |
570 | | void CheckBool::returnValueOfFunctionReturningBool() |
571 | 764 | { |
572 | 764 | if (!mSettings->severity.isEnabled(Severity::style)) |
573 | 0 | return; |
574 | | |
575 | 764 | logChecker("CheckBool::returnValueOfFunctionReturningBool"); // style |
576 | | |
577 | 764 | const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); |
578 | | |
579 | 1.10k | for (const Scope * scope : symbolDatabase->functionScopes) { |
580 | 1.10k | if (!(scope->function && match1(scope->function->retDef))) |
581 | 1.10k | continue; |
582 | | |
583 | 0 | for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { |
584 | | // Skip lambdas |
585 | 0 | const Token* tok2 = findLambdaEndToken(tok); |
586 | 0 | if (tok2) |
587 | 0 | tok = tok2; |
588 | 0 | else if (tok->scope() && tok->scope()->isClassOrStruct()) |
589 | 0 | tok = tok->scope()->bodyEnd; |
590 | 0 | else if (match10(tok) && tok->astOperand1() && |
591 | 0 | (tok->astOperand1()->getValueGE(2, *mSettings) || tok->astOperand1()->getValueLE(-1, *mSettings)) && |
592 | 0 | !(tok->astOperand1()->astOperand1() && match11(tok->astOperand1()))) |
593 | 0 | returnValueBoolError(tok); |
594 | 0 | } |
595 | 0 | } |
596 | 764 | } |
597 | | |
598 | | void CheckBool::returnValueBoolError(const Token *tok) |
599 | 0 | { |
600 | 0 | reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool"); |
601 | 0 | } |
602 | | |
603 | | void CheckBool::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) |
604 | 764 | { |
605 | 764 | CheckBool checkBool(&tokenizer, &tokenizer.getSettings(), errorLogger); |
606 | | |
607 | | // Checks |
608 | 764 | checkBool.checkComparisonOfBoolExpressionWithInt(); |
609 | 764 | checkBool.checkComparisonOfBoolWithInt(); |
610 | 764 | checkBool.checkAssignBoolToFloat(); |
611 | 764 | checkBool.pointerArithBool(); |
612 | 764 | checkBool.returnValueOfFunctionReturningBool(); |
613 | 764 | checkBool.checkComparisonOfFuncReturningBool(); |
614 | 764 | checkBool.checkComparisonOfBoolWithBool(); |
615 | 764 | checkBool.checkIncrementBoolean(); |
616 | 764 | checkBool.checkAssignBoolToPointer(); |
617 | 764 | checkBool.checkBitwiseOnBoolean(); |
618 | 764 | } |
619 | | |
620 | | void CheckBool::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const |
621 | 0 | { |
622 | 0 | CheckBool c(nullptr, settings, errorLogger); |
623 | 0 | c.assignBoolToPointerError(nullptr); |
624 | 0 | c.assignBoolToFloatError(nullptr); |
625 | 0 | c.comparisonOfFuncReturningBoolError(nullptr, "func_name"); |
626 | 0 | c.comparisonOfTwoFuncsReturningBoolError(nullptr, "func_name1", "func_name2"); |
627 | 0 | c.comparisonOfBoolWithBoolError(nullptr, "var_name"); |
628 | 0 | c.incrementBooleanError(nullptr); |
629 | 0 | c.bitwiseOnBooleanError(nullptr, "expression", "&&"); |
630 | 0 | c.comparisonOfBoolExpressionWithIntError(nullptr, true); |
631 | 0 | c.pointerArithBoolError(nullptr); |
632 | 0 | c.comparisonOfBoolWithInvalidComparator(nullptr, "expression"); |
633 | 0 | c.returnValueBoolError(nullptr); |
634 | 0 | } |