Skip to content

Commit 869e4ba

Browse files
rikardfalkeborndanmar
authored andcommitted
Add check for return value of boolean function (cppcheck-opensource#1451)
* Add check for return value of boolean function The rule for converting an integer to a boolean is that 0 is mapped to false and everything else is mapped to true. There is nothing wrong with the following code (according to the standards): bool f() { return -1; } and neither gcc nor clang will warn about it. However, it's a bit confusing. This commit adds a check that warns when a value other than 0 or 1 is returned from a boolean function (similar to the existing check that functions with boolean arguments are only passed 0 or 1). Since the code is perfectly legal, set the severity to "Style". * Use early continue and remove some braces * Add testcase with multiple returns * Avoid null pointer dereference in case of return without operand * Skip lambdas Add TODO-test cases that shows FPs when the return type of lambdas are specified explicitly (this is a problem with findLambdaEndToken). * Enable testcases
1 parent 88008fe commit 869e4ba

3 files changed

Lines changed: 136 additions & 1 deletion

File tree

lib/checkbool.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "symboldatabase.h"
2828
#include "token.h"
2929
#include "tokenize.h"
30+
#include "valueflow.h"
3031

3132
#include <cstddef>
3233
#include <list>
@@ -454,3 +455,31 @@ void CheckBool::assignBoolToFloatError(const Token *tok)
454455
reportError(tok, Severity::style, "assignBoolToFloat",
455456
"Boolean value assigned to floating point variable.", CWE704, false);
456457
}
458+
459+
void CheckBool::returnValueOfFunctionReturningBool(void)
460+
{
461+
if (!mSettings->isEnabled(Settings::STYLE))
462+
return;
463+
464+
const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
465+
466+
for (const Scope * scope : symbolDatabase->functionScopes) {
467+
if (!(scope->function && Token::Match(scope->function->retDef, "bool|_Bool")))
468+
continue;
469+
470+
for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) {
471+
// Skip lambdas
472+
const Token* tok2 = findLambdaEndToken(tok);
473+
if (tok2)
474+
tok = tok2;
475+
else if (Token::simpleMatch(tok, "return") && tok->astOperand1() &&
476+
(tok->astOperand1()->getValueGE(2, mSettings) || tok->astOperand1()->getValueLE(-1, mSettings)))
477+
returnValueBoolError(tok);
478+
}
479+
}
480+
}
481+
482+
void CheckBool::returnValueBoolError(const Token *tok)
483+
{
484+
reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool");
485+
}

lib/checkbool.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class CPPCHECKLIB CheckBool : public Check {
5858
checkBool.checkComparisonOfBoolWithInt();
5959
checkBool.checkAssignBoolToFloat();
6060
checkBool.pointerArithBool();
61+
checkBool.returnValueOfFunctionReturningBool();
6162
}
6263

6364
/** @brief Run checks against the simplified token list */
@@ -100,6 +101,9 @@ class CPPCHECKLIB CheckBool : public Check {
100101
void pointerArithBool();
101102
void pointerArithBoolCond(const Token *tok);
102103

104+
/** @brief %Check if a function returning bool returns an integer other than 0 or 1 */
105+
void returnValueOfFunctionReturningBool();
106+
103107
private:
104108
// Error messages..
105109
void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression);
@@ -112,6 +116,7 @@ class CPPCHECKLIB CheckBool : public Check {
112116
void bitwiseOnBooleanError(const Token *tok, const std::string &varname, const std::string &op);
113117
void comparisonOfBoolExpressionWithIntError(const Token *tok, bool n0o1);
114118
void pointerArithBoolError(const Token *tok);
119+
void returnValueBoolError(const Token *tok);
115120

116121
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override {
117122
CheckBool c(nullptr, settings, errorLogger);
@@ -126,6 +131,7 @@ class CPPCHECKLIB CheckBool : public Check {
126131
c.comparisonOfBoolExpressionWithIntError(nullptr, true);
127132
c.pointerArithBoolError(nullptr);
128133
c.comparisonOfBoolWithInvalidComparator(nullptr, "expression");
134+
c.returnValueBoolError(nullptr);
129135
}
130136

131137
static std::string myName() {
@@ -140,7 +146,8 @@ class CPPCHECKLIB CheckBool : public Check {
140146
"- comparison of a boolean value with boolean value using relational operator\n"
141147
"- using bool in bitwise expression\n"
142148
"- pointer addition in condition (either dereference is forgot or pointer overflow is required to make the condition false)\n"
143-
"- Assigning bool value to pointer or float\n";
149+
"- Assigning bool value to pointer or float\n"
150+
"- Returning an integer other than 0 or 1 from a function with boolean return value\n";
144151
}
145152
};
146153
/// @}

test/testbool.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class TestBool : public TestFixture {
6464

6565
// Converting pointer addition result to bool
6666
TEST_CASE(pointerArithBool1);
67+
68+
TEST_CASE(returnNonBool);
6769
}
6870

6971
void check(const char code[], bool experimental = false, const char filename[] = "test.cpp") {
@@ -1008,6 +1010,103 @@ class TestBool : public TestFixture {
10081010
"}");
10091011
ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str());
10101012
}
1013+
1014+
void returnNonBool() {
1015+
check("bool f(void) {\n"
1016+
" return 0;\n"
1017+
"}");
1018+
ASSERT_EQUALS("", errout.str());
1019+
1020+
check("bool f(void) {\n"
1021+
" return 1;\n"
1022+
"}");
1023+
ASSERT_EQUALS("", errout.str());
1024+
1025+
check("bool f(void) {\n"
1026+
" return 2;\n"
1027+
"}");
1028+
ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1029+
1030+
check("bool f(void) {\n"
1031+
" return -1;\n"
1032+
"}");
1033+
ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1034+
1035+
check("bool f(void) {\n"
1036+
" return 1 + 1;\n"
1037+
"}");
1038+
ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1039+
1040+
check("bool f(void) {\n"
1041+
" int x = 0;\n"
1042+
" return x;\n"
1043+
"}");
1044+
ASSERT_EQUALS("", errout.str());
1045+
1046+
check("bool f(void) {\n"
1047+
" int x = 10;\n"
1048+
" return x;\n"
1049+
"}");
1050+
ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1051+
1052+
check("bool f(void) {\n"
1053+
" return 2 < 1;\n"
1054+
"}");
1055+
ASSERT_EQUALS("", errout.str());
1056+
1057+
check("bool f(void) {\n"
1058+
" int ret = 0;\n"
1059+
" if (a)\n"
1060+
" ret = 1;\n"
1061+
" return ret;\n"
1062+
"}");
1063+
ASSERT_EQUALS("", errout.str());
1064+
1065+
check("bool f(void) {\n"
1066+
" int ret = 0;\n"
1067+
" if (a)\n"
1068+
" ret = 3;\n"
1069+
" return ret;\n"
1070+
"}");
1071+
ASSERT_EQUALS("[test.cpp:5]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1072+
1073+
check("bool f(void) {\n"
1074+
" if (a)\n"
1075+
" return 3;\n"
1076+
" return 4;\n"
1077+
"}");
1078+
ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n"
1079+
"[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1080+
1081+
check("bool f(void) {\n"
1082+
" return;\n"
1083+
"}");
1084+
ASSERT_EQUALS("", errout.str());
1085+
1086+
check("bool f(void) {\n"
1087+
"auto x = [](void) { return -1; };\n"
1088+
"return false;\n"
1089+
"}\n");
1090+
ASSERT_EQUALS("", errout.str());
1091+
1092+
check("bool f(void) {\n"
1093+
"auto x = [](void) { return -1; };\n"
1094+
"return 2;\n"
1095+
"}\n");
1096+
ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1097+
1098+
check("bool f(void) {\n"
1099+
"auto x = [](void) -> int { return -1; };\n"
1100+
"return false;\n"
1101+
"}\n");
1102+
ASSERT_EQUALS("", errout.str());
1103+
1104+
check("bool f(void) {\n"
1105+
"auto x = [](void) -> int { return -1; };\n"
1106+
"return 2;\n"
1107+
"}\n");
1108+
ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str());
1109+
}
10111110
};
10121111

10131112
REGISTER_TEST(TestBool)

0 commit comments

Comments
 (0)