"Fossies" - the Fresh Open Source Software Archive

Member "cppcheck-1.89/lib/checkinternal.cpp" (1 Sep 2019, 17670 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 "checkinternal.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 #ifdef CHECK_INTERNAL
   20 
   21 #include "checkinternal.h"
   22 
   23 #include "astutils.h"
   24 #include "symboldatabase.h"
   25 #include "utils.h"
   26 
   27 #include <set>
   28 #include <cstring>
   29 
   30 // Register this check class (by creating a static instance of it).
   31 // Disabled in release builds
   32 namespace {
   33     CheckInternal instance;
   34 }
   35 
   36 void CheckInternal::checkTokenMatchPatterns()
   37 {
   38     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
   39     for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) {
   40         const Scope * scope = symbolDatabase->functionScopes[i];
   41         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
   42             if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch ("))
   43                 continue;
   44 
   45             const std::string& funcname = tok->strAt(2);
   46 
   47             // Get pattern string
   48             const Token *patternTok = tok->tokAt(4)->nextArgument();
   49             if (!patternTok || patternTok->tokType() != Token::eString)
   50                 continue;
   51 
   52             const std::string pattern = patternTok->strValue();
   53             if (pattern.empty()) {
   54                 simplePatternError(tok, pattern, funcname);
   55                 continue;
   56             }
   57 
   58             if (pattern.find("||") != std::string::npos || pattern.find(" | ") != std::string::npos || pattern[0] == '|' || (pattern[pattern.length() - 1] == '|' && pattern[pattern.length() - 2] == ' '))
   59                 orInComplexPattern(tok, pattern, funcname);
   60 
   61             // Check for signs of complex patterns
   62             if (pattern.find_first_of("[|") != std::string::npos)
   63                 continue;
   64             else if (pattern.find("!!") != std::string::npos)
   65                 continue;
   66 
   67             bool complex = false;
   68             size_t index = pattern.find('%');
   69             while (index != std::string::npos) {
   70                 if (pattern.length() <= index + 2) {
   71                     complex = true;
   72                     break;
   73                 }
   74                 if (pattern[index + 1] == 'o' && pattern[index + 2] == 'r') // %or% or %oror%
   75                     index = pattern.find('%', index + 1);
   76                 else {
   77                     complex = true;
   78                     break;
   79                 }
   80                 index = pattern.find('%', index + 1);
   81             }
   82             if (!complex)
   83                 simplePatternError(tok, pattern, funcname);
   84         }
   85     }
   86 }
   87 
   88 void CheckInternal::checkRedundantTokCheck()
   89 {
   90     for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
   91         if (Token::Match(tok, "&& Token :: simpleMatch|Match|findsimplematch|findmatch (")) {
   92             // in code like
   93             // if (tok->previous() && Token::match(tok->previous(), "bla")) {}
   94             // the first tok->previous() check is redundant
   95             const Token *astOp1 = tok->astOperand1();
   96             const Token *astOp2 = getArguments(tok->tokAt(3))[0];
   97             if (Token::simpleMatch(astOp1, "&&")) {
   98                 astOp1 = astOp1->astOperand2();
   99             }
  100             if (astOp1->expressionString() == astOp2->expressionString()) {
  101                 checkRedundantTokCheckError(astOp2);
  102             }
  103             // if (!tok || !Token::match(tok, "foo"))
  104         } else if (Token::Match(tok, "%oror% ! Token :: simpleMatch|Match|findsimplematch|findmatch (")) {
  105             const Token *negTok = tok->next()->astParent()->astOperand1();
  106             if (Token::simpleMatch(negTok, "||")) {
  107                 negTok = negTok->astOperand2();
  108             }
  109             // the first tok condition is negated
  110             if (Token::simpleMatch(negTok, "!")) {
  111                 const Token *astOp1 = negTok->astOperand1();
  112                 const Token *astOp2 = getArguments(tok->tokAt(4))[0];
  113 
  114                 if (astOp1->expressionString() == astOp2->expressionString()) {
  115                     checkRedundantTokCheckError(astOp2);
  116                 }
  117             }
  118         }
  119     }
  120 }
  121 
  122 
  123 void CheckInternal::checkRedundantTokCheckError(const Token* tok)
  124 {
  125     reportError(tok, Severity::style, "redundantTokCheck",
  126                 "Unnecessary check of \"" + (tok? tok->expressionString(): emptyString) + "\", match-function already checks if it is null.");
  127 }
  128 
  129 void CheckInternal::checkTokenSimpleMatchPatterns()
  130 {
  131     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  132     for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) {
  133         const Scope * scope = symbolDatabase->functionScopes[i];
  134         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  135             if (!Token::simpleMatch(tok, "Token :: simpleMatch (") && !Token::simpleMatch(tok, "Token :: findsimplematch ("))
  136                 continue;
  137 
  138             const std::string& funcname = tok->strAt(2);
  139 
  140             // Get pattern string
  141             const Token *patternTok = tok->tokAt(4)->nextArgument();
  142             if (!patternTok || patternTok->tokType() != Token::eString)
  143                 continue;
  144 
  145             const std::string pattern = patternTok->strValue();
  146             if (pattern.empty()) {
  147                 complexPatternError(tok, pattern, funcname);
  148                 continue;
  149             }
  150 
  151             // Check for [xyz] usage - but exclude standalone square brackets
  152             unsigned int char_count = 0;
  153             for (std::string::size_type pos = 0; pos < pattern.size(); ++pos) {
  154                 char c = pattern[pos];
  155 
  156                 if (c == ' ') {
  157                     char_count = 0;
  158                 } else if (c == ']') {
  159                     if (char_count > 0) {
  160                         complexPatternError(tok, pattern, funcname);
  161                         continue;
  162                     }
  163                 } else {
  164                     ++char_count;
  165                 }
  166             }
  167 
  168             // Check | usage: Count characters before the symbol
  169             char_count = 0;
  170             for (std::string::size_type pos = 0; pos < pattern.size(); ++pos) {
  171                 const char c = pattern[pos];
  172 
  173                 if (c == ' ') {
  174                     char_count = 0;
  175                 } else if (c == '|') {
  176                     if (char_count > 0) {
  177                         complexPatternError(tok, pattern, funcname);
  178                         continue;
  179                     }
  180                 } else {
  181                     ++char_count;
  182                 }
  183             }
  184 
  185             // Check for real errors
  186             if (pattern.length() > 1) {
  187                 for (size_t j = 0; j < pattern.length() - 1; j++) {
  188                     if (pattern[j] == '%' && pattern[j + 1] != ' ')
  189                         complexPatternError(tok, pattern, funcname);
  190                     else if (pattern[j] == '!' && pattern[j + 1] == '!')
  191                         complexPatternError(tok, pattern, funcname);
  192                 }
  193             }
  194         }
  195     }
  196 }
  197 
  198 namespace {
  199     const std::set<std::string> knownPatterns = {
  200         "%any%"
  201         , "%assign%"
  202         , "%bool%"
  203         , "%char%"
  204         , "%comp%"
  205         , "%num%"
  206         , "%op%"
  207         , "%cop%"
  208         , "%or%"
  209         , "%oror%"
  210         , "%str%"
  211         , "%type%"
  212         , "%name%"
  213         , "%var%"
  214         , "%varid%"
  215     };
  216 }
  217 
  218 void CheckInternal::checkMissingPercentCharacter()
  219 {
  220     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  221     for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) {
  222         const Scope * scope = symbolDatabase->functionScopes[i];
  223         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  224             if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch ("))
  225                 continue;
  226 
  227             const std::string& funcname = tok->strAt(2);
  228 
  229             // Get pattern string
  230             const Token *patternTok = tok->tokAt(4)->nextArgument();
  231             if (!patternTok || patternTok->tokType() != Token::eString)
  232                 continue;
  233 
  234             const std::string pattern = patternTok->strValue();
  235 
  236             std::set<std::string>::const_iterator knownPattern, knownPatternsEnd = knownPatterns.end();
  237             for (knownPattern = knownPatterns.begin(); knownPattern != knownPatternsEnd; ++knownPattern) {
  238                 const std::string brokenPattern = (*knownPattern).substr(0, (*knownPattern).size() - 1);
  239 
  240                 std::string::size_type pos = 0;
  241                 while ((pos = pattern.find(brokenPattern, pos)) != std::string::npos) {
  242                     // Check if it's the full pattern
  243                     if (pattern.find(*knownPattern, pos) != pos) {
  244                         // Known whitelist of substrings
  245                         if ((brokenPattern == "%var" && pattern.find("%varid%", pos) == pos) ||
  246                             (brokenPattern == "%or" && pattern.find("%oror%", pos) == pos)) {
  247                             ++pos;
  248                             continue;
  249                         }
  250 
  251                         missingPercentCharacterError(tok, pattern, funcname);
  252                     }
  253 
  254                     ++pos;
  255                 }
  256             }
  257         }
  258     }
  259 }
  260 
  261 void CheckInternal::checkUnknownPattern()
  262 {
  263     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  264     for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) {
  265         const Scope * scope = symbolDatabase->functionScopes[i];
  266         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  267             if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch ("))
  268                 continue;
  269 
  270             // Get pattern string
  271             const Token *patternTok = tok->tokAt(4)->nextArgument();
  272             if (!patternTok || patternTok->tokType() != Token::eString)
  273                 continue;
  274 
  275             const std::string pattern = patternTok->strValue();
  276             bool inBrackets = false;
  277 
  278             for (std::string::size_type j = 0; j < pattern.length() - 1; j++) {
  279                 if (pattern[j] == '[' && (j == 0 || pattern[j - 1] == ' '))
  280                     inBrackets = true;
  281                 else if (pattern[j] == ']')
  282                     inBrackets = false;
  283                 else if (pattern[j] == '%' && pattern[j + 1] != ' ' && pattern[j + 1] != '|' && !inBrackets) {
  284                     const std::string::size_type end = pattern.find('%', j + 1);
  285                     if (end != std::string::npos) {
  286                         const std::string s = pattern.substr(j, end - j + 1);
  287                         if (knownPatterns.find(s) == knownPatterns.end())
  288                             unknownPatternError(tok, s);
  289                     }
  290                 }
  291             }
  292         }
  293     }
  294 }
  295 
  296 void CheckInternal::checkRedundantNextPrevious()
  297 {
  298     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  299     for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) {
  300         const Scope * scope = symbolDatabase->functionScopes[i];
  301         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  302             if (tok->str() != ".")
  303                 continue;
  304             tok = tok->next();
  305 
  306             if (Token::Match(tok, "previous ( ) . next|tokAt|strAt|linkAt (") || Token::Match(tok, "next ( ) . previous|tokAt|strAt|linkAt (") ||
  307                 (Token::simpleMatch(tok, "tokAt (") && Token::Match(tok->linkAt(1), ") . previous|next|tokAt|strAt|linkAt|str|link ("))) {
  308                 const std::string& func1 = tok->str();
  309                 const std::string& func2 = tok->linkAt(1)->strAt(2);
  310 
  311                 if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->linkAt(1)->strAt(4) != ")")
  312                     continue;
  313 
  314                 redundantNextPreviousError(tok, func1, func2);
  315             } else if (Token::Match(tok, "next|previous ( ) . next|previous ( ) . next|previous|linkAt|strAt|link|str (")) {
  316                 const std::string& func1 = tok->str();
  317                 const std::string& func2 = tok->strAt(8);
  318 
  319                 if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->strAt(10) != ")")
  320                     continue;
  321 
  322                 redundantNextPreviousError(tok, func1, func2);
  323             }
  324         }
  325     }
  326 }
  327 
  328 void CheckInternal::checkExtraWhitespace()
  329 {
  330     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  331     for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) {
  332         const Scope * scope = symbolDatabase->functionScopes[i];
  333         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  334             if (!Token::Match(tok, "Token :: simpleMatch|findsimplematch|Match|findmatch ("))
  335                 continue;
  336 
  337             const std::string& funcname = tok->strAt(2);
  338 
  339             // Get pattern string
  340             const Token *patternTok = tok->tokAt(4)->nextArgument();
  341             if (!patternTok || patternTok->tokType() != Token::eString)
  342                 continue;
  343 
  344             const std::string pattern = patternTok->strValue();
  345             if (!pattern.empty() && (pattern[0] == ' ' || *pattern.rbegin() == ' '))
  346                 extraWhitespaceError(tok, pattern, funcname);
  347 
  348             // two whitespaces or more
  349             if (pattern.find("  ") != std::string::npos)
  350                 extraWhitespaceError(tok, pattern, funcname);
  351         }
  352     }
  353 }
  354 
  355 void CheckInternal::checkStlUsage()
  356 {
  357     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  358     for (const Scope *scope : symbolDatabase->functionScopes) {
  359         for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
  360             if (Token::simpleMatch(tok, ". emplace ("))
  361                 reportError(tok, Severity::error, "internalStlUsage", "The 'emplace' function shall be avoided for now. It is not available e.g. in Slackware 14.0. 'emplace_back' is fine.");
  362             //if (Token::simpleMatch(tok, ". back ( )") && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->container && Token::simpleMatch(tok->astOperand1()->valueType()->container, "std :: string"))
  363             //  reportError(tok, Severity::error, "internalStlUsage", "The 'std::string::back()' function shall be avoided for now.");
  364         }
  365     }
  366 }
  367 
  368 void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
  369 {
  370     reportError(tok, Severity::error, "multiComparePatternError",
  371                 "Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%name%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\""
  372                );
  373 }
  374 
  375 void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
  376 {
  377     reportError(tok, Severity::warning, "simplePatternError",
  378                 "Found simple pattern inside Token::" + funcname + "() call: \"" + pattern + "\""
  379                );
  380 }
  381 
  382 void CheckInternal::complexPatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
  383 {
  384     reportError(tok, Severity::error, "complexPatternError",
  385                 "Found complex pattern inside Token::" + funcname + "() call: \"" + pattern + "\""
  386                );
  387 }
  388 
  389 void CheckInternal::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname)
  390 {
  391     reportError(tok, Severity::error, "missingPercentCharacter",
  392                 "Missing percent end character in Token::" + funcname + "() pattern: \"" + pattern + "\""
  393                );
  394 }
  395 
  396 void CheckInternal::unknownPatternError(const Token* tok, const std::string& pattern)
  397 {
  398     reportError(tok, Severity::error, "unknownPattern",
  399                 "Unknown pattern used: \"" + pattern + "\"");
  400 }
  401 
  402 void CheckInternal::redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2)
  403 {
  404     reportError(tok, Severity::style, "redundantNextPrevious",
  405                 "Call to 'Token::" + func1 + "()' followed by 'Token::" + func2 + "()' can be simplified.");
  406 }
  407 
  408 void CheckInternal::orInComplexPattern(const Token* tok, const std::string& pattern, const std::string &funcname)
  409 {
  410     reportError(tok, Severity::error, "orInComplexPattern",
  411                 "Token::" + funcname + "() pattern \"" + pattern + "\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".");
  412 }
  413 
  414 void CheckInternal::extraWhitespaceError(const Token* tok, const std::string& pattern, const std::string &funcname)
  415 {
  416     reportError(tok, Severity::warning, "extraWhitespaceError",
  417                 "Found extra whitespace inside Token::" + funcname + "() call: \"" + pattern + "\""
  418                );
  419 }
  420 
  421 #endif // #ifdef CHECK_INTERNAL