"Fossies" - the Fresh Open Source Software Archive

Member "cppcheck-1.89/lib/checkbool.cpp" (1 Sep 2019, 20163 Bytes) of package /windows/misc/cppcheck-1.89.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "checkbool.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.87_vs_1.88.

    1 /*
    2  * Cppcheck - A tool for static C/C++ code analysis
    3  * Copyright (C) 2007-2019 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 //---------------------------------------------------------------------------
   21 #include "checkbool.h"
   22 
   23 #include "astutils.h"
   24 #include "errorlogger.h"
   25 #include "mathlib.h"
   26 #include "settings.h"
   27 #include "symboldatabase.h"
   28 #include "token.h"
   29 #include "tokenize.h"
   30 #include "valueflow.h"
   31 
   32 #include <cstddef>
   33 #include <list>
   34 //---------------------------------------------------------------------------
   35 
   36 // Register this check class (by creating a static instance of it)
   37 namespace {
   38     CheckBool instance;
   39 }
   40 
   41 static const CWE CWE398(398U);  // Indicator of Poor Code Quality
   42 static const CWE CWE571(571U);  // Expression is Always True
   43 static const CWE CWE587(587U);  // Assignment of a Fixed Address to a Pointer
   44 static const CWE CWE704(704U);  // Incorrect Type Conversion or Cast
   45 
   46 static bool isBool(const Variable* var)
   47 {
   48     return (var && Token::Match(var->typeEndToken(), "bool|_Bool"));
   49 }
   50 
   51 //---------------------------------------------------------------------------
   52 void CheckBool::checkIncrementBoolean()
   53 {
   54     if (!mSettings->isEnabled(Settings::STYLE))
   55         return;
   56 
   57     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
   58     for (const Scope * scope : symbolDatabase->functionScopes) {
   59         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
   60             if (Token::Match(tok, "%var% ++")) {
   61                 const Variable *var = tok->variable();
   62                 if (isBool(var))
   63                     incrementBooleanError(tok);
   64             }
   65         }
   66     }
   67 }
   68 
   69 void CheckBool::incrementBooleanError(const Token *tok)
   70 {
   71     reportError(
   72         tok,
   73         Severity::style,
   74         "incrementboolean",
   75         "Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n"
   76         "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.",
   77         CWE398, false
   78     );
   79 }
   80 
   81 //---------------------------------------------------------------------------
   82 // if (bool & bool) -> if (bool && bool)
   83 // if (bool | bool) -> if (bool || bool)
   84 //---------------------------------------------------------------------------
   85 void CheckBool::checkBitwiseOnBoolean()
   86 {
   87     if (!mSettings->isEnabled(Settings::STYLE))
   88         return;
   89 
   90     // danmar: this is inconclusive because I don't like that there are
   91     //         warnings for calculations. Example: set_flag(a & b);
   92     if (!mSettings->inconclusive)
   93         return;
   94 
   95     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
   96     for (const Scope * scope : symbolDatabase->functionScopes) {
   97         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
   98             if (Token::Match(tok, "(|.|return|&&|%oror%|throw|, %var% [&|]")) {
   99                 const Variable *var = tok->next()->variable();
  100                 if (isBool(var)) {
  101                     bitwiseOnBooleanError(tok->next(), var->name(), tok->strAt(2) == "&" ? "&&" : "||");
  102                     tok = tok->tokAt(2);
  103                 }
  104             } else if (Token::Match(tok, "[&|] %var% )|.|return|&&|%oror%|throw|,") && (!tok->previous() || !tok->previous()->isExtendedOp() || tok->strAt(-1) == ")" || tok->strAt(-1) == "]")) {
  105                 const Variable *var = tok->next()->variable();
  106                 if (isBool(var)) {
  107                     bitwiseOnBooleanError(tok->next(), var->name(), tok->str() == "&" ? "&&" : "||");
  108                     tok = tok->tokAt(2);
  109                 }
  110             }
  111         }
  112     }
  113 }
  114 
  115 void CheckBool::bitwiseOnBooleanError(const Token *tok, const std::string &varname, const std::string &op)
  116 {
  117     reportError(tok, Severity::style, "bitwiseOnBoolean",
  118                 "Boolean variable '" + varname + "' is used in bitwise operation. Did you mean '" + op + "'?",
  119                 CWE398,
  120                 true);
  121 }
  122 
  123 //---------------------------------------------------------------------------
  124 //    if (!x==3) <- Probably meant to be "x!=3"
  125 //---------------------------------------------------------------------------
  126 
  127 void CheckBool::checkComparisonOfBoolWithInt()
  128 {
  129     if (!mSettings->isEnabled(Settings::WARNING) || !mTokenizer->isCPP())
  130         return;
  131 
  132     const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
  133     for (const Scope * scope : symbolDatabase->functionScopes) {
  134         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  135             if (!tok->isComparisonOp() || !tok->isBinaryOp())
  136                 continue;
  137             const Token* const left = tok->astOperand1();
  138             const Token* const right = tok->astOperand2();
  139             if (left->isBoolean() && right->varId()) { // Comparing boolean constant with variable
  140                 if (tok->str() != "==" && tok->str() != "!=") {
  141                     comparisonOfBoolWithInvalidComparator(right, left->str());
  142                 }
  143             } else if (left->varId() && right->isBoolean()) { // Comparing variable with boolean constant
  144                 if (tok->str() != "==" && tok->str() != "!=") {
  145                     comparisonOfBoolWithInvalidComparator(right, left->str());
  146                 }
  147             }
  148         }
  149     }
  150 }
  151 
  152 void CheckBool::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression)
  153 {
  154     reportError(tok, Severity::warning, "comparisonOfBoolWithInvalidComparator",
  155                 "Comparison of a boolean value using relational operator (<, >, <= or >=).\n"
  156                 "The result of the expression '" + expression + "' is of type 'bool'. "
  157                 "Comparing 'bool' value using relational (<, >, <= or >=)"
  158                 " operator could cause unexpected results.");
  159 }
  160 
  161 //-------------------------------------------------------------------------------
  162 // Comparing functions which are returning value of type bool
  163 //-------------------------------------------------------------------------------
  164 
  165 static bool tokenIsFunctionReturningBool(const Token* tok)
  166 {
  167     const Function* func = tok->function();
  168     if (func && Token::Match(tok, "%name% (")) {
  169         if (func->tokenDef && Token::Match(func->tokenDef->previous(), "bool|_Bool")) {
  170             return true;
  171         }
  172     }
  173     return false;
  174 }
  175 
  176 void CheckBool::checkComparisonOfFuncReturningBool()
  177 {
  178     if (!mSettings->isEnabled(Settings::STYLE))
  179         return;
  180 
  181     if (!mTokenizer->isCPP())
  182         return;
  183 
  184     const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
  185 
  186     for (const Scope * scope : symbolDatabase->functionScopes) {
  187         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  188             if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=")
  189                 continue;
  190             const Token *firstToken = tok->previous();
  191             if (tok->strAt(-1) == ")") {
  192                 firstToken = firstToken->link()->previous();
  193             }
  194             const Token *secondToken = tok->next();
  195             while (secondToken->str() == "!") {
  196                 secondToken = secondToken->next();
  197             }
  198             const bool firstIsFunctionReturningBool = tokenIsFunctionReturningBool(firstToken);
  199             const bool secondIsFunctionReturningBool = tokenIsFunctionReturningBool(secondToken);
  200             if (firstIsFunctionReturningBool && secondIsFunctionReturningBool) {
  201                 comparisonOfTwoFuncsReturningBoolError(firstToken->next(), firstToken->str(), secondToken->str());
  202             } else if (firstIsFunctionReturningBool) {
  203                 comparisonOfFuncReturningBoolError(firstToken->next(), firstToken->str());
  204             } else if (secondIsFunctionReturningBool) {
  205                 comparisonOfFuncReturningBoolError(secondToken->previous(), secondToken->str());
  206             }
  207         }
  208     }
  209 }
  210 
  211 void CheckBool::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression)
  212 {
  213     reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError",
  214                 "Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n"
  215                 "The return type of function '" + expression + "' is 'bool' "
  216                 "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)"
  217                 " operator could cause unexpected results.", CWE398, false);
  218 }
  219 
  220 void CheckBool::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2)
  221 {
  222     reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError",
  223                 "Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n"
  224                 "The return type of function '" + expression1 + "' and function '" + expression2 + "' is 'bool' "
  225                 "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)"
  226                 " operator could cause unexpected results.", CWE398, false);
  227 }
  228 
  229 //-------------------------------------------------------------------------------
  230 // Comparison of bool with bool
  231 //-------------------------------------------------------------------------------
  232 
  233 void CheckBool::checkComparisonOfBoolWithBool()
  234 {
  235     // FIXME: This checking is "experimental" because of the false positives
  236     //        when self checking lib/tokenize.cpp (#2617)
  237     if (!mSettings->experimental)
  238         return;
  239 
  240     if (!mSettings->isEnabled(Settings::STYLE))
  241         return;
  242 
  243     if (!mTokenizer->isCPP())
  244         return;
  245 
  246     const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
  247 
  248     for (const Scope * scope : symbolDatabase->functionScopes) {
  249         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  250             if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=")
  251                 continue;
  252             bool firstTokenBool = false;
  253 
  254             const Token *firstToken = tok->previous();
  255             if (firstToken->varId()) {
  256                 if (isBool(firstToken->variable())) {
  257                     firstTokenBool = true;
  258                 }
  259             }
  260             if (!firstTokenBool)
  261                 continue;
  262 
  263             bool secondTokenBool = false;
  264             const Token *secondToken = tok->next();
  265             if (secondToken->varId()) {
  266                 if (isBool(secondToken->variable())) {
  267                     secondTokenBool = true;
  268                 }
  269             }
  270             if (secondTokenBool) {
  271                 comparisonOfBoolWithBoolError(firstToken->next(), secondToken->str());
  272             }
  273         }
  274     }
  275 }
  276 
  277 void CheckBool::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression)
  278 {
  279     reportError(tok, Severity::style, "comparisonOfBoolWithBoolError",
  280                 "Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n"
  281                 "The variable '" + expression + "' is of type 'bool' "
  282                 "and comparing 'bool' value using relational (<, >, <= or >=)"
  283                 " operator could cause unexpected results.", CWE398, false);
  284 }
  285 
  286 //-----------------------------------------------------------------------------
  287 void CheckBool::checkAssignBoolToPointer()
  288 {
  289     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  290     for (const Scope * scope : symbolDatabase->functionScopes) {
  291         for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
  292             if (tok->str() != "=")
  293                 continue;
  294             const ValueType *lhsType = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr;
  295             if (!lhsType || lhsType->pointer == 0)
  296                 continue;
  297             const ValueType *rhsType = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr;
  298             if (!rhsType || rhsType->pointer > 0 || rhsType->type != ValueType::Type::BOOL)
  299                 continue;
  300 
  301             assignBoolToPointerError(tok);
  302         }
  303     }
  304 }
  305 
  306 void CheckBool::assignBoolToPointerError(const Token *tok)
  307 {
  308     reportError(tok, Severity::error, "assignBoolToPointer",
  309                 "Boolean value assigned to pointer.", CWE587, false);
  310 }
  311 
  312 //-----------------------------------------------------------------------------
  313 //-----------------------------------------------------------------------------
  314 void CheckBool::checkComparisonOfBoolExpressionWithInt()
  315 {
  316     if (!mSettings->isEnabled(Settings::WARNING))
  317         return;
  318 
  319     const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
  320 
  321     for (const Scope * scope : symbolDatabase->functionScopes) {
  322         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  323             if (!tok->isComparisonOp())
  324                 continue;
  325 
  326             const Token* numTok = nullptr;
  327             const Token* boolExpr = nullptr;
  328             bool numInRhs;
  329             if (astIsBool(tok->astOperand1())) {
  330                 boolExpr = tok->astOperand1();
  331                 numTok = tok->astOperand2();
  332                 numInRhs = true;
  333             } else if (astIsBool(tok->astOperand2())) {
  334                 boolExpr = tok->astOperand2();
  335                 numTok = tok->astOperand1();
  336                 numInRhs = false;
  337             } else {
  338                 continue;
  339             }
  340 
  341             if (!numTok || !boolExpr)
  342                 continue;
  343 
  344             if (boolExpr->isOp() && numTok->isName() && Token::Match(tok, "==|!="))
  345                 // there is weird code such as:  ((a<b)==c)
  346                 // but it is probably written this way by design.
  347                 continue;
  348 
  349             if (astIsBool(numTok))
  350                 continue;
  351 
  352             if (numTok->isNumber()) {
  353                 const MathLib::bigint num = MathLib::toLongNumber(numTok->str());
  354                 if (num==0 &&
  355                     (numInRhs ? Token::Match(tok, ">|==|!=")
  356                      : Token::Match(tok, "<|==|!=")))
  357                     continue;
  358                 if (num==1 &&
  359                     (numInRhs ? Token::Match(tok, "<|==|!=")
  360                      : Token::Match(tok, ">|==|!=")))
  361                     continue;
  362                 comparisonOfBoolExpressionWithIntError(tok, true);
  363             } else if (astIsIntegral(numTok, false) && mTokenizer->isCPP())
  364                 comparisonOfBoolExpressionWithIntError(tok, false);
  365         }
  366     }
  367 }
  368 
  369 void CheckBool::comparisonOfBoolExpressionWithIntError(const Token *tok, bool n0o1)
  370 {
  371     if (n0o1)
  372         reportError(tok, Severity::warning, "compareBoolExpressionWithInt",
  373                     "Comparison of a boolean expression with an integer other than 0 or 1.", CWE398, false);
  374     else
  375         reportError(tok, Severity::warning, "compareBoolExpressionWithInt",
  376                     "Comparison of a boolean expression with an integer.", CWE398, false);
  377 }
  378 
  379 
  380 void CheckBool::pointerArithBool()
  381 {
  382     const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
  383 
  384     for (const Scope &scope : symbolDatabase->scopeList) {
  385         if (scope.type != Scope::eIf && scope.type != Scope::eWhile && scope.type != Scope::eDo && scope.type != Scope::eFor)
  386             continue;
  387         const Token* tok = scope.classDef->next()->astOperand2();
  388         if (scope.type == Scope::eFor) {
  389             tok = Token::findsimplematch(scope.classDef->tokAt(2), ";");
  390             if (tok)
  391                 tok = tok->astOperand2();
  392             if (tok)
  393                 tok = tok->astOperand1();
  394         } else if (scope.type == Scope::eDo)
  395             tok = (scope.bodyEnd->tokAt(2)) ? scope.bodyEnd->tokAt(2)->astOperand2() : nullptr;
  396 
  397         pointerArithBoolCond(tok);
  398     }
  399 }
  400 
  401 void CheckBool::pointerArithBoolCond(const Token *tok)
  402 {
  403     if (!tok)
  404         return;
  405     if (Token::Match(tok, "&&|%oror%")) {
  406         pointerArithBoolCond(tok->astOperand1());
  407         pointerArithBoolCond(tok->astOperand2());
  408         return;
  409     }
  410     if (tok->str() != "+" && tok->str() != "-")
  411         return;
  412 
  413     if (tok->isBinaryOp() &&
  414         tok->astOperand1()->isName() &&
  415         tok->astOperand1()->variable() &&
  416         tok->astOperand1()->variable()->isPointer() &&
  417         tok->astOperand2()->isNumber())
  418         pointerArithBoolError(tok);
  419 }
  420 
  421 void CheckBool::pointerArithBoolError(const Token *tok)
  422 {
  423     reportError(tok,
  424                 Severity::error,
  425                 "pointerArithBool",
  426                 "Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n"
  427                 "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, false);
  428 }
  429 
  430 void CheckBool::checkAssignBoolToFloat()
  431 {
  432     if (!mTokenizer->isCPP())
  433         return;
  434     if (!mSettings->isEnabled(Settings::STYLE))
  435         return;
  436     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  437     for (const Scope * scope : symbolDatabase->functionScopes) {
  438         for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
  439             if (tok->str() == "=" && astIsBool(tok->astOperand2())) {
  440                 const Token *lhs = tok->astOperand1();
  441                 while (lhs && (lhs->str() == "." || lhs->str() == "::"))
  442                     lhs = lhs->astOperand2();
  443                 if (!lhs || !lhs->variable())
  444                     continue;
  445                 const Variable* var = lhs->variable();
  446                 if (var && var->isFloatingType() && !var->isArrayOrPointer())
  447                     assignBoolToFloatError(tok->next());
  448             }
  449         }
  450     }
  451 }
  452 
  453 void CheckBool::assignBoolToFloatError(const Token *tok)
  454 {
  455     reportError(tok, Severity::style, "assignBoolToFloat",
  456                 "Boolean value assigned to floating point variable.", CWE704, false);
  457 }
  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 (tok->scope() && tok->scope()->isClassOrStruct())
  476                 tok = tok->scope()->bodyEnd;
  477             else if (Token::simpleMatch(tok, "return") && tok->astOperand1() &&
  478                      (tok->astOperand1()->getValueGE(2, mSettings) || tok->astOperand1()->getValueLE(-1, mSettings)) &&
  479                      !(tok->astOperand1()->astOperand1() && Token::Match(tok->astOperand1(), "&|%or%")))
  480                 returnValueBoolError(tok);
  481         }
  482     }
  483 }
  484 
  485 void CheckBool::returnValueBoolError(const Token *tok)
  486 {
  487     reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool");
  488 }