Fixed #2617 (improve check: comparing boolean with '<')

This commit is contained in:
Mohit Mate 2012-09-26 18:18:36 +02:00 committed by Daniel Marjamäki
parent 8010bcfbe8
commit 9e297c95f2
5 changed files with 299 additions and 0 deletions

View File

@ -2239,6 +2239,126 @@ void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& va
"unusedScopedObject", "Instance of '" + varname + "' object is destroyed immediately."); "unusedScopedObject", "Instance of '" + varname + "' object is destroyed immediately.");
} }
//-------------------------------------------------------------------------------
// Comparing functions which are returning value of type bool
//-------------------------------------------------------------------------------
void CheckOther::checkComparisonOfFuncReturningBool()
{
if (!_settings->isEnabled("style"))
return;
if (!_tokenizer->isCPP())
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (tok->type() == Token::eComparisonOp && tok->str() != "==" && tok->str() != "!=") {
const Token *first_token;
bool first_token_func_of_type_bool = false;
bool second_token_func_of_type_bool = false;
if (Token::simpleMatch(tok->previous(), ")")) {
first_token = tok->previous()->link()->previous();
} else {
first_token = tok->previous();
}
std::string const first_token_name = first_token->str();
if (first_token->isName()&& isFunction(first_token->str(), _tokenizer->tokens())) {
const Token *fToken = _tokenizer->getFunctionTokenByName(first_token_name.c_str());
if (fToken &&fToken->previous() && fToken->previous()->str() == "bool") {
first_token_func_of_type_bool = true;
}
}
Token *second_token = tok->next();
while (second_token->str()=="!") {
second_token = second_token->next();
}
std::string const second_token_name = second_token->str();
if (second_token->isName()&& isFunction(second_token->str(), _tokenizer->tokens())) {
const Token *fToken = _tokenizer->getFunctionTokenByName(second_token_name.c_str());
if (fToken &&fToken->previous() && fToken->previous()->str() == "bool") {
second_token_func_of_type_bool = true;
}
}
if ((first_token_func_of_type_bool == true) && (second_token_func_of_type_bool == true)) {
comparisonOfTwoFuncsReturningBoolError(first_token->next(), first_token->str(), second_token->str());
}
if ((first_token_func_of_type_bool == true) && (second_token_func_of_type_bool == false)) {
comparisonOfFuncReturningBoolError(first_token->next(), first_token->str());
}
if ((first_token_func_of_type_bool == false) && (second_token_func_of_type_bool == true)) {
comparisonOfFuncReturningBoolError(second_token->previous(), second_token->str());
}
}
}
}
void CheckOther::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression)
{
reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError",
"Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n"
"The return type of function '" + expression + "' is 'bool' "
"and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)"
" operator could cause unexpected results.");
}
void CheckOther::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2)
{
reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError",
"Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n"
"The return type of function '" + expression1 + "' and function '" + expression2 + "' is 'bool' "
"and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)"
" operator could cause unexpected results.");
}
//-------------------------------------------------------------------------------
// Comparison of bool with bool
//-------------------------------------------------------------------------------
void CheckOther::checkComparisonOfBoolWithBool()
{
if (!_settings->isEnabled("style"))
return;
if (!_tokenizer->isCPP())
return;
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (tok->type() == Token::eComparisonOp && tok->str() != "==" && tok->str() != "!=") {
bool first_token_bool = false;
bool second_token_bool = false;
const Token *first_token = tok->previous();
if (first_token->varId()) {
if (isBool(symbolDatabase->getVariableFromVarId(first_token->varId()))) {
first_token_bool = true;
}
}
const Token *second_token = tok->next();
if (second_token->varId()) {
if (isBool(symbolDatabase->getVariableFromVarId(second_token->varId()))) {
second_token_bool = true;
}
}
if ((first_token_bool == true) && (second_token_bool == true)) {
comparisonOfBoolWithBoolError(first_token->next(), first_token->str());
}
}
}
}
void CheckOther::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression)
{
reportError(tok, Severity::style, "comparisonOfBoolWithBoolError",
"Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n"
"The variable '" + expression + "' is of type 'bool' "
"and comparing 'bool' value using relational (<, >, <= or >=)"
" operator could cause unexpected results.");
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CheckOther::checkIncorrectStringCompare() void CheckOther::checkIncorrectStringCompare()

View File

@ -97,6 +97,8 @@ public:
checkOther.checkIncorrectLogicOperator(); checkOther.checkIncorrectLogicOperator();
checkOther.checkMisusedScopedObject(); checkOther.checkMisusedScopedObject();
checkOther.checkComparisonOfFuncReturningBool();
checkOther.checkComparisonOfBoolWithBool();
checkOther.checkMemsetZeroBytes(); checkOther.checkMemsetZeroBytes();
checkOther.checkIncorrectStringCompare(); checkOther.checkIncorrectStringCompare();
checkOther.checkIncrementBoolean(); checkOther.checkIncrementBoolean();
@ -195,6 +197,12 @@ public:
/** @brief %Check for objects that are destroyed immediately */ /** @brief %Check for objects that are destroyed immediately */
void checkMisusedScopedObject(); void checkMisusedScopedObject();
/** @brief %Check for comparison of function returning bool*/
void checkComparisonOfFuncReturningBool();
/** @brief %Check for comparison of variable of type bool*/
void checkComparisonOfBoolWithBool();
/** @brief %Check for filling zero bytes with memset() */ /** @brief %Check for filling zero bytes with memset() */
void checkMemsetZeroBytes(); void checkMemsetZeroBytes();
@ -302,6 +310,9 @@ private:
void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always); void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always);
void redundantConditionError(const Token *tok, const std::string &text); void redundantConditionError(const Token *tok, const std::string &text);
void misusedScopeObjectError(const Token *tok, const std::string &varname); void misusedScopeObjectError(const Token *tok, const std::string &varname);
void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression);
void comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2);
void comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression);
void memsetZeroBytesError(const Token *tok, const std::string &varname); void memsetZeroBytesError(const Token *tok, const std::string &varname);
void sizeofForArrayParameterError(const Token *tok); void sizeofForArrayParameterError(const Token *tok);
void sizeofForPointerError(const Token *tok, const std::string &varname); void sizeofForPointerError(const Token *tok, const std::string &varname);
@ -343,6 +354,9 @@ private:
c.zerodivError(0); c.zerodivError(0);
c.mathfunctionCallError(0); c.mathfunctionCallError(0);
c.misusedScopeObjectError(NULL, "varname"); c.misusedScopeObjectError(NULL, "varname");
c.comparisonOfFuncReturningBoolError(0, "func_name");
c.comparisonOfTwoFuncsReturningBoolError(0, "func_name1", "func_name2");
c.comparisonOfBoolWithBoolError(0, "var_name");
c.sizeofForArrayParameterError(0); c.sizeofForArrayParameterError(0);
c.sizeofForPointerError(0, "varname"); c.sizeofForPointerError(0, "varname");
c.sizeofForNumericParameterError(0); c.sizeofForNumericParameterError(0);
@ -451,6 +465,8 @@ private:
"* using increment on boolean\n" "* using increment on boolean\n"
"* comparison of a boolean with a non-zero integer\n" "* comparison of a boolean with a non-zero integer\n"
"* comparison of a boolean expression with an integer other than 0 or 1\n" "* comparison of a boolean expression with an integer other than 0 or 1\n"
"* comparison of a function returning boolean value using relational operator\n"
"* comparison of a boolean value with boolean value using relational operator\n"
"* suspicious condition (assignment+comparison)\n" "* suspicious condition (assignment+comparison)\n"
"* suspicious condition (runtime comparison of string literals)\n" "* suspicious condition (runtime comparison of string literals)\n"
"* suspicious condition (string literals as boolean)\n" "* suspicious condition (string literals as boolean)\n"

View File

@ -7553,6 +7553,19 @@ bool Tokenizer::IsScopeNoReturn(const Token *endScopeToken, bool *unknown)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const Token *Tokenizer::getFunctionTokenByName(const char funcname[]) const
{
std::list<Scope>::const_iterator scope;
for (scope = _symbolDatabase->scopeList.begin(); scope != _symbolDatabase->scopeList.end(); ++scope) {
if (scope->type == Scope::eFunction) {
if (scope->classDef->str() == funcname)
return scope->classDef;
}
}
return NULL;
}
bool Tokenizer::isFunctionParameterPassedByValue(const Token *fpar) const bool Tokenizer::isFunctionParameterPassedByValue(const Token *fpar) const
{ {
// TODO: If symbol database is available, use it. // TODO: If symbol database is available, use it.

View File

@ -142,6 +142,14 @@ public:
*/ */
unsigned int sizeOfType(const Token *type) const; unsigned int sizeOfType(const Token *type) const;
/**
* Get function token by function name
* @todo better handling of overloaded functions
* @todo only scan parent scopes
* @param funcname function name
*/
const Token *getFunctionTokenByName(const char funcname[]) const;
/** /**
* Try to determine if function parameter is passed by value by looking * Try to determine if function parameter is passed by value by looking
* at the function declaration. * at the function declaration.

View File

@ -143,6 +143,14 @@ private:
TEST_CASE(comparisonOfBoolWithInt4); TEST_CASE(comparisonOfBoolWithInt4);
TEST_CASE(comparisonOfBoolWithInt5); TEST_CASE(comparisonOfBoolWithInt5);
TEST_CASE(checkComparisonOfFuncReturningBool1);
TEST_CASE(checkComparisonOfFuncReturningBool2);
TEST_CASE(checkComparisonOfFuncReturningBool3);
TEST_CASE(checkComparisonOfFuncReturningBool4);
TEST_CASE(checkComparisonOfFuncReturningBool5);
TEST_CASE(checkComparisonOfBoolWithBool);
TEST_CASE(duplicateIf); TEST_CASE(duplicateIf);
TEST_CASE(duplicateIf1); // ticket 3689 TEST_CASE(duplicateIf1); // ticket 3689
TEST_CASE(duplicateBranch); TEST_CASE(duplicateBranch);
@ -3963,6 +3971,140 @@ private:
} }
void checkComparisonOfFuncReturningBool1() {
check("void f(){\n"
" int temp = 4;\n"
" if(compare1() > compare2()){\n"
" printf(\"foo\");\n"
" }\n"
"}\n"
"bool compare1(int temp){\n"
" if(temp==4){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n"
"bool compare2(int temp){\n"
" if(temp==4){\n"
" return false;\n"
" }\n"
" else\n"
" return true;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str());
}
void checkComparisonOfFuncReturningBool2() {
check("void f(){\n"
" int temp = 4;\n"
" bool a = true;\n"
" if(compare(temp) > a){\n"
" printf(\"foo\");\n"
" }\n"
"}\n"
"bool compare(int temp){\n"
" if(temp==4){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str());
}
void checkComparisonOfFuncReturningBool3() {
check("void f(){\n"
" int temp = 4;\n"
" if(compare(temp) > temp){\n"
" printf(\"foo\");\n"
" }\n"
"}\n"
"bool compare(int temp){\n"
" if(temp==4){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str());
}
void checkComparisonOfFuncReturningBool4() {
check("void f(){\n"
" int temp = 4;\n"
" bool b = compare2(6);\n"
" if(compare1(temp)> b){\n"
" printf(\"foo\");\n"
" }\n"
"}\n"
"bool compare1(int temp){\n"
" if(temp==4){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n"
"bool compare2(int temp){\n"
" if(temp == 5){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str());
}
void checkComparisonOfFuncReturningBool5() {
check("void f(){\n"
" int temp = 4;\n"
" if(compare1() > !compare2()){\n"
" printf(\"foo\");\n"
" }\n"
"}\n"
"bool compare1(int temp){\n"
" if(temp==4){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n"
"bool compare2(int temp){\n"
" if(temp==4){\n"
" return false;\n"
" }\n"
" else\n"
" return true;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str());
}
void checkComparisonOfBoolWithBool() {
check("void f(){\n"
" int temp = 4;\n"
" bool b = compare2(6);\n"
" bool a = compare1(4);\n"
" if(b > a){\n"
" printf(\"foo\");\n"
" }\n"
"}\n"
"bool compare1(int temp){\n"
" if(temp==4){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n"
"bool compare2(int temp){\n"
" if(temp == 5){\n"
" return true;\n"
" }\n"
" else\n"
" return false;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout.str());
}
void sizeofForNumericParameter() { void sizeofForNumericParameter() {
check("void f() {\n" check("void f() {\n"
" std::cout << sizeof(10) << std::endl;\n" " std::cout << sizeof(10) << std::endl;\n"