From 81ca6e9f98a29c8af956ff50ea078e66e35a7e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 16 Jan 2010 08:47:46 +0100 Subject: [PATCH] Revert "minor tweak in Borland C++ project file" This reverts commit ad06ee4f71698fcd67d2d42d27c8117ff1ecc234. --- cppcheck.cbproj | 2 +- lib/checkclass.cpp | 2813 ++++++++++++++++++++++---------------------- lib/checkclass.h | 334 +++--- 3 files changed, 1576 insertions(+), 1573 deletions(-) mode change 100755 => 100644 lib/checkclass.cpp mode change 100755 => 100644 lib/checkclass.h diff --git a/cppcheck.cbproj b/cppcheck.cbproj index 41d48580c..1b9fe84d8 100644 --- a/cppcheck.cbproj +++ b/cppcheck.cbproj @@ -113,7 +113,7 @@ CPlusPlusBuilder.Personality CppConsoleApplication -FalseFalse1000FalseFalseFalseFalseFalse105312521.0.0.01.0.0.0FalseFalseFalseTrueFalseFalseTrueTrue3$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;C:\cppcheck;src;cli;lib$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;C:\cppcheck;src$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;C:\cppcheck2$(BDS)\lib\debug;$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;C:\cppcheck;src;cli;lib$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;C:\cppcheck;src;cli;lib2_DEBUG;NO_STRICT;_HAS_ITERATOR_DEBUGGING=1NO_STRICT3.\bcb_debug.12501bcb_Debug +FalseFalse1000FalseFalseFalseFalseFalse105312521.0.0.01.0.0.0applicom.cFalseC:\linux-2.6.25.1\drivers\charFalseFalseTrueFalseFalseTrueTrue3$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;C:\cppcheck;src;cli;lib$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;C:\cppcheck;src$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;C:\cppcheck2$(BDS)\lib\debug;$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;C:\cppcheck;src;cli;lib$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;C:\cppcheck;src;cli;lib2_DEBUG;NO_STRICT;_HAS_ITERATOR_DEBUGGING=1NO_STRICT3.\bcb_debug.12501bcb_Debug diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp old mode 100755 new mode 100644 index 673b4c36d..c5d5490a7 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -1,1405 +1,1408 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki and Cppcheck team. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -//--------------------------------------------------------------------------- -#include "checkclass.h" - -#include "tokenize.h" -#include "token.h" -#include "errorlogger.h" - -#include - -#include -#include -#include -#include - -//--------------------------------------------------------------------------- - -// Register CheckClass.. -namespace -{ -CheckClass instance; -} - - -//--------------------------------------------------------------------------- - -CheckClass::Var *CheckClass::getVarList(const Token *tok1, bool withClasses, bool isStruct) -{ - // Get variable list.. - Var *varlist = NULL; - unsigned int indentlevel = 0; - bool priv = !isStruct; - for (const Token *tok = tok1; tok; tok = tok->next()) - { - if (!tok->next()) - break; - - if (tok->str() == "{") - ++indentlevel; - else if (tok->str() == "}") - { - if (indentlevel <= 1) - break; - --indentlevel; - } - - if (indentlevel != 1) - continue; - - if (tok->str() == "__published:") - { - priv = false; - for (; tok; tok = tok->next()) - { - if (tok->str() == "{") - ++indentlevel; - else if (tok->str() == "}") - { - if (indentlevel <= 1) - break; - --indentlevel; - } - if (indentlevel == 1 && Token::Match(tok->next(), "private:|protected:|public:")) - break; - } - if (tok) - continue; - else - break; - } - - // "private:" "public:" "protected:" etc - const bool b((*tok->strAt(0) != ':') && strchr(tok->strAt(0), ':') != 0); - - if (b) - priv = bool(tok->str() == "private:"); - - // Search for start of statement.. - if (! Token::Match(tok, "[;{}]") && ! b) - continue; - - // This is the start of a statement - const Token *next = tok->next(); - const char *varname = 0; - - // If next token contains a ":".. it is not part of a variable declaration - if (next->str().find(":") != std::string::npos) - continue; - - // Variable declarations that start with "static" shall be ignored.. - if (next->str() == "static") - continue; - - // Type definitions shall be ignored.. - if (next->str() == "typedef") - continue; - - // Is it a variable declaration? - if (Token::Match(next, "%type% %var% ;")) - { - if (withClasses) - varname = next->strAt(1); - else if (next->isStandardType()) - varname = next->strAt(1); - else if (Token::findmatch(_tokenizer->tokens(), ("enum " + next->str()).c_str())) - varname = next->strAt(1); - } - - // Pointer? - else if (Token::Match(next, "%type% * %var% ;")) - { - varname = next->strAt(2); - } - - // Pointer? - else if (Token::Match(next, "%type% %type% * %var% ;")) - { - varname = next->strAt(3); - } - - // Array? - else if (Token::Match(next, "%type% %var% [") && next->next()->str() != "operator") - { - if (Token::findmatch(_tokenizer->tokens(), ("class " + next->str()).c_str())) - continue; - varname = next->strAt(1); - } - - // Pointer array? - else if (Token::Match(next, "%type% * %var% [")) - { - varname = next->strAt(2); - } - - // std::string.. - else if (withClasses && Token::Match(next, "std :: string %var% ;")) - { - varname = next->strAt(3); - } - - // Container.. - else if (withClasses && Token::Match(next, "std :: %type% <")) - { - while (next && next->str() != ">") - next = next->next(); - if (Token::Match(next, "> %var% ;")) - varname = next->strAt(1); - } - - // If the varname was set in one of the two if-block above, create a entry for this variable.. - if (varname && strcmp(varname, "operator")) - { - Var *var = new Var(varname, false, priv, varlist); - varlist = var; - } - } - - return varlist; -} -//--------------------------------------------------------------------------- - -void CheckClass::initVar(Var *varlist, const char varname[]) -{ - for (Var *var = varlist; var; var = var->next) - { - if (strcmp(var->name, varname) == 0) - { - var->init = true; - break; - } - } -} -//--------------------------------------------------------------------------- - -void CheckClass::initializeVarList(const Token *tok1, const Token *ftok, Var *varlist, const char classname[], std::list &callstack, bool isStruct) -{ - bool Assign = false; - unsigned int indentlevel = 0; - - for (; ftok; ftok = ftok->next()) - { - if (!ftok->next()) - break; - - // Class constructor.. initializing variables like this - // clKalle::clKalle() : var(value) { } - if (indentlevel == 0) - { - if (Assign && Token::Match(ftok, "%var% (")) - { - initVar(varlist, ftok->strAt(0)); - } - - Assign |= (ftok->str() == ":"); - } - - - if (ftok->str() == "{") - { - ++indentlevel; - Assign = false; - } - - else if (ftok->str() == "}") - { - if (indentlevel <= 1) - break; - --indentlevel; - } - - if (indentlevel < 1) - continue; - - // Variable getting value from stream? - if (Token::Match(ftok, ">> %var%")) - { - initVar(varlist, ftok->strAt(1)); - } - - // Before a new statement there is "[{};)=]" or "else" - if (! Token::Match(ftok, "[{};)=]") && ftok->str() != "else") - continue; - - // Using the operator= function to initialize all variables.. - if (Token::simpleMatch(ftok->next(), "* this = ")) - { - for (Var *var = varlist; var; var = var->next) - var->init = true; - break; - } - - if (!Token::Match(ftok->next(), "%var%") && - !Token::Match(ftok->next(), "this . %var%") && - !Token::Match(ftok->next(), "* %var% =") && - !Token::Match(ftok->next(), "( * this ) . %var%")) - continue; - - // Goto the first token in this statement.. - ftok = ftok->next(); - - // Skip "( * this )" - if (Token::simpleMatch(ftok, "( * this ) .")) - { - ftok = ftok->tokAt(5); - } - - // Skip "this->" - if (Token::simpleMatch(ftok, "this .")) - ftok = ftok->tokAt(2); - - // Skip "classname :: " - if (Token::Match(ftok, "%var% ::")) - ftok = ftok->tokAt(2); - - // Clearing all variables.. - if (Token::simpleMatch(ftok, "memset ( this ,")) - { - for (Var *var = varlist; var; var = var->next) - var->init = true; - break; - } - - // Calling member function? - else if (Token::Match(ftok, "%var% (")) - { - // No recursive calls! - if (std::find(callstack.begin(), callstack.end(), ftok->str()) == callstack.end()) - { - callstack.push_back(ftok->str()); - int i = 0; - const Token *ftok2 = Tokenizer::findClassFunction(tok1, classname, ftok->strAt(0), i, isStruct); - if (ftok2) - { - initializeVarList(tok1, ftok2, varlist, classname, callstack, isStruct); - } - else // there is a called member function, but it is not defined where we can find it, so we assume it initializes everything - { - for (Var *var = varlist; var; var = var->next) - var->init = true; - break; - - // we don't report this, as somewhere along the line we hope that the class and member function - // are checked together. It is possible that this will not be the case (where there are enough - // nested functions defined in different files), but that isn't really likely. - } - } - } - - // Assignment of member variable? - else if (Token::Match(ftok, "%var% =")) - { - initVar(varlist, ftok->strAt(0)); - } - - // Assignment of array item of member variable? - else if (Token::Match(ftok, "%var% [ %any% ] =")) - { - initVar(varlist, ftok->strAt(0)); - } - - // Assignment of array item of member variable? - else if (Token::Match(ftok, "%var% [ %any% ] [ %any% ] =")) - { - initVar(varlist, ftok->strAt(0)); - } - - // Assignment of array item of member variable? - else if (Token::Match(ftok, "* %var% =")) - { - initVar(varlist, ftok->strAt(1)); - } - - // The functions 'clear' and 'Clear' are supposed to initialize variable. - if (Token::Match(ftok, "%var% . clear|Clear (")) - { - initVar(varlist, ftok->strAt(0)); - } - } -} - - - - - - -//--------------------------------------------------------------------------- -// ClassCheck: Check that all class constructors are ok. -//--------------------------------------------------------------------------- - -void CheckClass::constructors() -{ - const char pattern_class[] = "class|struct %var% [{:]"; - - // Locate class - const Token *tok1 = Token::findmatch(_tokenizer->tokens(), pattern_class); - while (tok1) - { - const char *className; - className = tok1->strAt(1); - const Token *classNameToken = tok1->tokAt(1); - bool isStruct = tok1->str() == "struct"; - - /** @todo handling of private constructors should be improved */ - bool hasPrivateConstructor = false; - { - int indentlevel = 0; - bool isPrivate = !isStruct; - for (const Token *tok = tok1; tok; tok = tok->next()) - { - // Indentation - if (tok->str() == "{") - ++indentlevel; - - else if (tok->str() == "}") - { - --indentlevel; - if (indentlevel <= 0) - break; - } - - // Parse class contents (indentlevel == 1).. - if (indentlevel == 1) - { - // What section are we in.. private/non-private - if (tok->str() == "private:") - isPrivate = true; - else if (tok->str() == "protected:" || tok->str() == "public:") - isPrivate = false; - - // Is there a private constructor? - else if (isPrivate && Token::simpleMatch(tok, (classNameToken->str() + " (").c_str())) - { - hasPrivateConstructor = true; - break; - } - } - } - } - - if (hasPrivateConstructor && !_settings->_showAll) - { - /** @todo Handle private constructors. Right now to avoid - * false positives we just bail out */ - tok1 = Token::findmatch(tok1->next(), pattern_class); - continue; - } - - // Are there a class constructor? - std::string tempPattern = "%any% " + classNameToken->str() + " ("; - const Token *constructor_token = Token::findmatch(tok1, tempPattern.c_str()); - while (constructor_token && constructor_token->str() == "~") - constructor_token = Token::findmatch(constructor_token->next(), tempPattern.c_str()); - - // There are no constructor. - if (! constructor_token) - { - // If "--style" has been given, give a warning - if (_settings->_checkCodingStyle) - { - // Get class variables... - Var *varlist = getVarList(tok1, false, isStruct); - - // If there is a private variable, there should be a constructor.. - for (const Var *var = varlist; var; var = var->next) - { - if (var->priv) - { - noConstructorError(tok1, classNameToken->str(), isStruct); - break; - } - } - - // Delete the varlist.. - while (varlist) - { - Var *nextvar = varlist->next; - delete varlist; - varlist = nextvar; - } - } - - tok1 = Token::findmatch(tok1->next(), pattern_class); - continue; - } - - // Check constructors - checkConstructors(tok1, className, hasPrivateConstructor, isStruct); - - // Check assignment operators - checkConstructors(tok1, "operator =", hasPrivateConstructor, isStruct); - - tok1 = Token::findmatch(tok1->next(), pattern_class); - } -} - -void CheckClass::checkConstructors(const Token *tok1, const char funcname[], bool hasPrivateConstructor, bool isStruct) -{ - const char * const className = tok1->strAt(1); - - // Check that all member variables are initialized.. - bool withClasses = bool(_settings->_showAll && std::string(funcname) == "operator ="); - Var *varlist = getVarList(tok1, withClasses, isStruct); - - int indentlevel = 0; - const Token *constructor_token = Tokenizer::findClassFunction(tok1, className, funcname, indentlevel, isStruct); - std::list callstack; - initializeVarList(tok1, constructor_token, varlist, className, callstack, isStruct); - while (constructor_token) - { - // Check if any variables are uninitialized - for (Var *var = varlist; var; var = var->next) - { - if (var->init) - continue; - - // Is it a static member variable? - std::ostringstream pattern; - pattern << className << " :: " << var->name << " ="; - if (Token::findmatch(_tokenizer->tokens(), pattern.str().c_str())) - continue; - - // It's non-static and it's not initialized => error - if (Token::simpleMatch(constructor_token, "operator = (") || - Token::simpleMatch(constructor_token->tokAt(2), "operator = (")) - { - const Token *operStart = 0; - if (Token::simpleMatch(constructor_token, "operator = (")) - operStart = constructor_token->tokAt(2); - else - operStart = constructor_token->tokAt(4); - - bool classNameUsed = false; - for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next()) - { - if (operTok->str() == className) - { - classNameUsed = true; - break; - } - } - - if (classNameUsed) - operatorEqVarError(constructor_token, className, var->name); - } - else - uninitVarError(constructor_token, className, var->name, hasPrivateConstructor); - } - - for (Var *var = varlist; var; var = var->next) - var->init = false; - - constructor_token = Tokenizer::findClassFunction(constructor_token->next(), className, funcname, indentlevel, isStruct); - callstack.clear(); - initializeVarList(tok1, constructor_token, varlist, className, callstack, isStruct); - } - - // Delete the varlist.. - while (varlist) - { - Var *nextvar = varlist->next; - delete varlist; - varlist = nextvar; - } -} - - -//--------------------------------------------------------------------------- -// ClassCheck: Unused private functions -//--------------------------------------------------------------------------- - -void CheckClass::privateFunctions() -{ - // Locate some class - for (const Token *tok1 = Token::findmatch(_tokenizer->tokens(), "class|struct %var% {"); tok1; tok1 = Token::findmatch(tok1->next(), "class|struct %var% {")) - { - /** @todo check that the whole class implementation is seen */ - // until the todo above is fixed we only check classes that are - // declared in the source file - if (tok1->fileIndex() != 0) - continue; - - const std::string &classname = tok1->next()->str(); - - // Get private functions.. - std::list FuncList; - FuncList.clear(); - bool isStruct = tok1->str() == "struct"; - bool priv = !isStruct; - unsigned int indent_level = 0; - for (const Token *tok = tok1; tok; tok = tok->next()) - { - if (Token::Match(tok, "friend %var%")) - { - /** @todo Handle friend classes */ - FuncList.clear(); - break; - } - - if (tok->str() == "{") - ++indent_level; - else if (tok->str() == "}") - { - if (indent_level <= 1) - break; - --indent_level; - } - - else if (indent_level != 1) - continue; - else if (tok->str() == "private:") - priv = true; - else if (tok->str() == "public:") - priv = false; - else if (tok->str() == "protected:") - priv = false; - else if (priv) - { - if (Token::Match(tok, "typedef %type% (")) - tok = tok->tokAt(2)->link(); - - else if (Token::Match(tok, "[:,] %var% (")) - tok = tok->tokAt(2)->link(); - - else if (Token::Match(tok, "%var% (") && - !Token::Match(tok, classname.c_str())) - { - FuncList.push_back(tok); - } - } - - /** @todo embedded class have access to private functions */ - if (tok->str() == "class") - { - FuncList.clear(); - break; - } - } - - // Check that all private functions are used.. - bool HasFuncImpl = false; - bool inclass = false; - indent_level = 0; - for (const Token *ftok = _tokenizer->tokens(); ftok; ftok = ftok->next()) - { - if (ftok->str() == "{") - ++indent_level; - else if (ftok->str() == "}") - { - if (indent_level > 0) - --indent_level; - if (indent_level == 0) - inclass = false; - } - - if (Token::Match(ftok, ("class " + classname + " :|{").c_str())) - { - indent_level = 0; - inclass = true; - } - - // Check member class functions to see what functions are used.. - if ((inclass && indent_level == 1 && Token::Match(ftok, ") const| {")) || - (Token::Match(ftok, (classname + " :: ~| %var% (").c_str()))) - { - while (ftok && ftok->str() != ")") - ftok = ftok->next(); - if (!ftok) - break; - if (Token::Match(ftok, ") : %var% (")) - { - while (!Token::Match(ftok->next(), "[{};]")) - ftok = ftok->next(); - } - if (!Token::Match(ftok, ") const| {")) - continue; - - if (ftok->fileIndex() == 0) - HasFuncImpl = true; - - // Parse function.. - int indentlevel2 = 0; - for (const Token *tok2 = ftok; tok2; tok2 = tok2->next()) - { - if (tok2->str() == "{") - ++indentlevel2; - else if (tok2->str() == "}") - { - --indentlevel2; - if (indentlevel2 < 1) - break; - } - else if (Token::Match(tok2, "%var% (")) - { - // Remove function from FuncList - std::list::iterator it = FuncList.begin(); - while (it != FuncList.end()) - { - if (tok2->str() == (*it)->str()) - FuncList.erase(it++); - else - it++; - } - } - } - } - } - - while (HasFuncImpl && !FuncList.empty()) - { - // Final check; check if the function pointer is used somewhere.. - const std::string _pattern("return|(|)|,|= " + FuncList.front()->str()); - if (!Token::findmatch(_tokenizer->tokens(), _pattern.c_str())) - { - unusedPrivateFunctionError(FuncList.front(), classname, FuncList.front()->str()); - } - FuncList.pop_front(); - } - } -} - -//--------------------------------------------------------------------------- -// ClassCheck: Check that memset is not used on classes -//--------------------------------------------------------------------------- - -void CheckClass::noMemset() -{ - // Locate all 'memset' tokens.. - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (!Token::Match(tok, "memset|memcpy|memmove")) - continue; - - const char *type = NULL; - if (Token::Match(tok, "memset ( %var% , %num% , sizeof ( %type% ) )")) - type = tok->strAt(8); - else if (Token::Match(tok, "memset ( & %var% , %num% , sizeof ( %type% ) )")) - type = tok->strAt(9); - else if (Token::Match(tok, "memset ( %var% , %num% , sizeof ( struct %type% ) )")) - type = tok->strAt(9); - else if (Token::Match(tok, "memset ( & %var% , %num% , sizeof ( struct %type% ) )")) - type = tok->strAt(10); - else if (Token::Match(tok, "%type% ( %var% , %var% , sizeof ( %type% ) )")) - type = tok->strAt(8); - - // No type defined => The tokens didn't match - if (!(type && type[0])) - continue; - - // Warn if type is a class.. - const std::string pattern1(std::string("class ") + type); - if (Token::findmatch(_tokenizer->tokens(), pattern1.c_str())) - { - memsetClassError(tok, tok->str()); - continue; - } - - // Warn if type is a struct that contains any std::* - const std::string pattern2(std::string("struct ") + type + " {"); - for (const Token *tstruct = Token::findmatch(_tokenizer->tokens(), pattern2.c_str()); tstruct; tstruct = tstruct->next()) - { - if (tstruct->str() == "}") - break; - - if (Token::Match(tstruct, "std :: %type% %var% ;")) - { - memsetStructError(tok, tok->str(), tstruct->strAt(2)); - break; - } - } - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// ClassCheck: "void operator=(" -//--------------------------------------------------------------------------- - -void CheckClass::operatorEq() -{ - const Token *tok2 = _tokenizer->tokens(); - - for (const Token *tok = Token::findmatch(tok2, "void operator = ("); tok;) - { - const Token *tok1 = tok; - while (tok1 && !Token::Match(tok1, "class|struct %var%")) - { - if (tok1->str() == "public:") - { - operatorEqReturnError(tok); - break; - } - if (tok1->str() == "private:" || tok1->str() == "protected:") - break; - tok1 = tok1->previous(); - } - - if (tok1 && Token::Match(tok1, "struct %var%")) - operatorEqReturnError(tok); - - tok2 = tok->next(); - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// ClassCheck: "C& operator=(const C&) { ... return *this; }" -// operator= should return a reference to *this -//--------------------------------------------------------------------------- - -// match two lists of tokens -static bool nameMatch(const Token * tok1, const Token * tok2, int length) -{ - for (int i = 0; i < length; i++) - { - if (tok1->tokAt(i) == 0 || tok2->tokAt(i) == 0) - return false; - - if (tok1->tokAt(i)->str() != tok2->tokAt(i)->str()) - return false; - } - - return true; -} - -// create a class name from a list of tokens -static void nameStr(const Token * name, int length, std::string & str) -{ - for (int i = 0; i < length; i++) - { - if (i != 0) - str += " "; - - str += name->tokAt(i)->str(); - } -} - -void CheckClass::operatorEqRetRefThis() -{ - const Token *tok2 = _tokenizer->tokens(); - - for (const Token *tok = Token::findmatch(tok2, "operator = ("); tok;) - { - const Token *tok1 = tok; - - if (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) - { - // make sure this is an assignment operator - int nameLength = 1; - - tok1 = tok1->tokAt(-2); - - // check backwards for proper function signature - while (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) - { - tok1 = tok1->tokAt(-2); - nameLength += 2; - } - - const Token *name = tok1; - std::string nameString; - - nameStr(name, nameLength, nameString); - - if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") - { - // check class name - if (tok1->tokAt(-(1 + nameLength)) && nameMatch(name, tok1->tokAt(-(1 + nameLength)), nameLength)) - { - // may take overloaded argument types so no need to check them - tok1 = tok->tokAt(2)->link(); - - if (tok1 && tok1->next() && tok1->next()->str() == "{") - { - const Token *last = tok1->next()->link()->tokAt(-2); - for (tok1 = tok1->tokAt(2); tok1 != last; tok1 = tok1->next()) - { - // check for return of reference to this - if (tok1->str() == "return") - { - if (!(Token::Match(tok1->tokAt(1), "* this ;") || - Token::Match(tok1->tokAt(1), "operator = ("))) - operatorEqRetRefThisError(tok); - } - } - } - } - } - } - else - { - // make sure this is an assignment operator - tok1 = tok; - - // check backwards for proper function signature - if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") - { - const Token *name = 0; - bool isPublic = false; - while (tok1 && !Token::Match(tok1, "class|struct %var%")) - { - if (!isPublic) - { - if (tok1->str() == "public:") - isPublic = true; - } - tok1 = tok1->previous(); - } - - if (tok1 && Token::Match(tok1, "struct %var%")) - { - isPublic = true; - name = tok1->tokAt(1); - } - else if (tok1 && Token::Match(tok1, "class %var%")) - { - name = tok1->tokAt(1); - } - - if (tok->tokAt(-2) && tok->tokAt(-2)->str() == name->str()) - { - // may take overloaded argument types so no need to check them - tok1 = tok->tokAt(2)->link(); - - if (tok1 && tok1->next() && tok1->next()->str() == "{") - { - const Token *last = tok1->next()->link()->tokAt(-2); - for (tok1 = tok1->tokAt(2); tok1 != last; tok1 = tok1->next()) - { - // check for return of reference to this - if (tok1->str() == "return") - { - if (!(Token::Match(tok1->tokAt(1), "* this ;") || - Token::Match(tok1->tokAt(1), "operator = ("))) - operatorEqRetRefThisError(tok); - } - } - } - } - } - } - - tok2 = tok->next(); - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }" -// operator= should check for assignment to self -// -// For simple classes, an assignment to self check is only a potential optimization. -// -// For classes that allocate dynamic memory, assignment to self can be a real error -// if it is deallocated and allocated again without being checked for. -// -// This check is not valid for classes with multiple inheritance because a -// class can have multiple addresses so there is no trivial way to check for -// assignment to self. -//--------------------------------------------------------------------------- - -static bool hasDeallocation(const Token * first, const Token * last) -{ - // This function is called when no simple check was found for assignment - // to self. We are currently looking for a specific sequence of: - // deallocate member ; ... member = allocate - // This check is far from ideal because it can cause false negatives. - // Unfortunately, this is necessary to prevent false positives. - // This check needs to do careful analysis someday to get this - // correct with a high degree of certainty. - for (const Token * tok = first; tok && (tok != last); tok = tok->next()) - { - // check for deallocating memory - if (Token::Match(tok, "{|;|, free ( %type%")) - { - const Token * var = tok->tokAt(3); - - // we should probably check that var is a pointer in this class - - const Token * tok1 = tok->tokAt(4); - - while (tok1 && (tok1 != last)) - { - if (Token::Match(tok1, "%type% =")) - { - if (tok1->str() == var->str()) - return true; - } - - tok1 = tok1->next(); - } - } - else if (Token::Match(tok, "{|;|, delete [ ] %type%")) - { - const Token * var = tok->tokAt(4); - - // we should probably check that var is a pointer in this class - - const Token * tok1 = tok->tokAt(5); - - while (tok1 && (tok1 != last)) - { - if (Token::Match(tok1, "%type% = new [")) - { - if (tok1->str() == var->str()) - return true; - } - - tok1 = tok1->next(); - } - } - else if (Token::Match(tok, "{|;|, delete %type%")) - { - const Token * var = tok->tokAt(2); - - // we should probably check that var is a pointer in this class - - const Token * tok1 = tok->tokAt(3); - - while (tok1 && (tok1 != last)) - { - if (Token::Match(tok1, "%type% = new")) - { - if (tok1->str() == var->str()) - return true; - } - - tok1 = tok1->next(); - } - } - } - - return false; -} - -static bool hasAssignSelf(const Token * first, const Token * last, const Token * rhs) -{ - for (const Token * tok = first; tok && tok != last; tok = tok->next()) - { - if (Token::Match(tok, "if (")) - { - const Token * tok1 = tok->tokAt(2); - const Token * tok2 = tok->tokAt(1)->link(); - - if (tok1 && tok2) - { - for (; tok1 && tok1 != tok2; tok1 = tok1->next()) - { - if (Token::Match(tok1, "this ==|!= & %var%")) - { - if (tok1->tokAt(3)->str() == rhs->str()) - return true; - } - else if (Token::Match(tok1, "& %var% ==|!= this")) - { - if (tok1->tokAt(1)->str() == rhs->str()) - return true; - } - } - } - } - } - - return false; -} - -static bool hasMultipleInheritanceInline(const Token * tok) -{ - while (tok && tok->str() != "{") - { - if (tok->str() == ",") - return true; - - tok = tok->next(); - } - - return false; -} - -static bool hasMultipleInheritanceGlobal(const Token * start, const std::string & name) -{ - const Token *tok = start; - std::string pattern; - std::string className = name; - - // check for nested classes - while (className.find("::") != std::string::npos) - { - std::string tempName; - - // there is probably a better way to do this - while (className[0] != ' ') - { - tempName += className[0]; - className.erase(0, 1); - } - - className.erase(0, 4); - - pattern = "class|struct " + tempName; - - tok = Token::findmatch(tok, pattern.c_str()); - } - - pattern = "class|struct " + className; - - tok = Token::findmatch(tok, pattern.c_str()); - - return hasMultipleInheritanceInline(tok); -} - -void CheckClass::operatorEqToSelf() -{ - const Token *tok2 = _tokenizer->tokens(); - - for (const Token *tok = Token::findmatch(tok2, "operator = ("); tok;) - { - const Token *tok1 = tok; - - // make sure this is an assignment operator - if (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) - { - int nameLength = 1; - - tok1 = tok1->tokAt(-2); - - // check backwards for proper function signature - while (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) - { - tok1 = tok1->tokAt(-2); - nameLength += 2; - } - - const Token *name = tok1; - std::string nameString; - - nameStr(name, nameLength, nameString); - - if (!hasMultipleInheritanceGlobal(_tokenizer->tokens(), nameString)) - { - if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") - { - // check returned class name - if (tok1->tokAt(-(1 + nameLength)) && nameMatch(name, tok1->tokAt(-(1 + nameLength)), nameLength)) - { - // check forward for proper function signature - std::string pattern = "const " + nameString + " & %type% )"; - if (Token::Match(tok->tokAt(3), pattern.c_str())) - { - const Token * rhs = tok->tokAt(5 + nameLength); - - if (nameMatch(name, tok->tokAt(4), nameLength)) - { - tok1 = tok->tokAt(2)->link(); - - if (tok1 && tok1->tokAt(1) && tok1->tokAt(1)->str() == "{" && tok1->tokAt(1)->link()) - { - const Token *first = tok1->tokAt(1); - const Token *last = first->link(); - - if (!hasAssignSelf(first, last, rhs)) - { - if (hasDeallocation(first, last)) - operatorEqToSelfError(tok); - } - } - } - } - } - } - } - } - else - { - tok1 = tok; - - // check backwards for proper function signature - if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") - { - const Token *name = 0; - while (tok1 && !Token::Match(tok1, "class|struct %var%")) - tok1 = tok1->previous(); - - if (tok1 && Token::Match(tok1, "struct %var%")) - name = tok1->tokAt(1); - else if (tok1 && Token::Match(tok1, "class %var%")) - name = tok1->tokAt(1); - - if (!hasMultipleInheritanceInline(tok1)) - { - if (tok->tokAt(-2) && tok->tokAt(-2)->str() == name->str()) - { - // check forward for proper function signature - if (Token::Match(tok->tokAt(3), "const %type% & %type% )")) - { - const Token * rhs = tok->tokAt(6); - - if (tok->tokAt(4)->str() == name->str()) - { - tok1 = tok->tokAt(2)->link(); - - if (tok1 && tok1->tokAt(1) && tok1->tokAt(1)->str() == "{" && tok1->tokAt(1)->link()) - { - const Token *first = tok1->tokAt(1); - const Token *last = first->link(); - - if (!hasAssignSelf(first, last, rhs)) - { - if (hasDeallocation(first, last)) - operatorEqToSelfError(tok); - } - } - } - } - } - } - } - } - - tok2 = tok->next(); - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// A destructor in a base class should be virtual -//--------------------------------------------------------------------------- - -void CheckClass::virtualDestructor() -{ - const char pattern_classdecl[] = "class %var% : %var%"; - - const Token *derived = _tokenizer->tokens(); - while ((derived = Token::findmatch(derived, pattern_classdecl)) != NULL) - { - // Check that the derived class has a non empty destructor.. - { - std::ostringstream destructorPattern; - destructorPattern << "~ " << derived->strAt(1) << " ( ) {"; - const Token *derived_destructor = Token::findmatch(_tokenizer->tokens(), destructorPattern.str().c_str()); - - // No destructor.. - if (! derived_destructor) - { - derived = derived->next(); - continue; - } - - // Empty destructor.. - if (Token::Match(derived_destructor, "~ %var% ( ) { }")) - { - derived = derived->next(); - continue; - } - } - - const Token *derivedClass = derived->tokAt(1); - - // Iterate through each base class... - derived = derived->tokAt(3); - while (Token::Match(derived, "%var%")) - { - bool isPublic(derived->str() == "public"); - - // What kind of inheritance is it.. public|protected|private - if (Token::Match(derived, "public|protected|private")) - derived = derived->next(); - - // Name of base class.. - const char *baseName[2]; - baseName[0] = derived->strAt(0); - baseName[1] = 0; - - // Update derived so it's ready for the next loop. - do - { - if (derived->str() == "{") - break; - - if (derived->str() == ",") - { - derived = derived->next(); - break; - } - - derived = derived->next(); - } - while (derived); - - // If not public inheritance, skip checking of this base class.. - if (! isPublic) - continue; - - // Find the destructor declaration for the base class. - const Token *base = Token::findmatch(_tokenizer->tokens(), (std::string("%any% ~ ") + baseName[0] + " (").c_str()); - while (base && base->str() == "::") - base = Token::findmatch(base->next(), (std::string("%any% ~ ") + baseName[0] + " (").c_str()); - - const Token *reverseTok = base; - while (Token::Match(base, "%var%") && base->str() != "virtual") - base = base->previous(); - - // Check that there is a destructor.. - if (! base) - { - // Is the class declaration available? - base = Token::findmatch(_tokenizer->tokens(), (std::string("class ") + baseName[0] + " {").c_str()); - if (base) - { - virtualDestructorError(base, baseName[0], derivedClass->str()); - } - - continue; - } - - // There is a destructor. Check that it's virtual.. - else if (base->str() == "virtual") - continue; - - // TODO: This is just a temporary fix, better solution is needed. - // Skip situations where base class has base classes of its own, because - // some of the base classes might have virtual destructor. - // Proper solution is to check all of the base classes. If base class is not - // found or if one of the base classes has virtual destructor, error should not - // be printed. See TODO test case "virtualDestructorInherited" - if (!Token::findmatch(_tokenizer->tokens(), (std::string("class ") + baseName[0] + " {").c_str())) - continue; - - // Make sure that the destructor is public (protected or private - // would not compile if inheritance is used in a way that would - // cause the bug we are trying to find here.) - int indent = 0; - while (reverseTok) - { - if (reverseTok->str() == "public:") - { - virtualDestructorError(base, baseName[0], derivedClass->str()); - break; - } - else if (reverseTok->str() == "protected:" || - reverseTok->str() == "private:") - { - // No bug, protected/private destructor is allowed - break; - } - else if (reverseTok->str() == "{") - { - indent++; - if (indent >= 1) - { - // We have found the start of the class without any sign - // of "public :" so we can assume that the destructor is not - // public and there is no bug in the code we are checking. - break; - } - } - else if (reverseTok->str() == "}") - indent--; - - reverseTok = reverseTok->previous(); - } - } - } -} -//--------------------------------------------------------------------------- - -void CheckClass::thisSubtractionError(const Token *tok) -{ - reportError(tok, Severity::possibleStyle, "thisSubtraction", "Suspicious pointer subtraction"); -} - -void CheckClass::thisSubtraction() -{ - const Token *tok = _tokenizer->tokens(); - for (;;) - { - tok = Token::findmatch(tok, "this - %var%"); - if (!tok) - break; - - if (!Token::simpleMatch(tok->previous(), "*")) - thisSubtractionError(tok); - - tok = tok->next(); - } -} - -void CheckClass::noConstructorError(const Token *tok, const std::string &classname, bool isStruct) -{ - reportError(tok, Severity::style, "noConstructor", "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + "' has no constructor. Member variables not initialized."); -} - -void CheckClass::uninitVarError(const Token *tok, const std::string &classname, const std::string &varname, bool hasPrivateConstructor) -{ - reportError(tok, hasPrivateConstructor ? Severity::possibleStyle : Severity::style, "uninitVar", "Member variable not initialized in the constructor '" + classname + "::" + varname + "'"); -} - -void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname) -{ - reportError(tok, Severity::possibleStyle, "operatorEqVarError", "Member variable '" + classname + "::" + varname + "' is not assigned a value in '" + classname + "::operator=" + "'"); -} - -void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) -{ - reportError(tok, Severity::style, "unusedPrivateFunction", "Unused private function '" + classname + "::" + funcname + "'"); -} - -void CheckClass::memsetClassError(const Token *tok, const std::string &memfunc) -{ - reportError(tok, Severity::error, "memsetClass", "Using '" + memfunc + "' on class"); -} - -void CheckClass::memsetStructError(const Token *tok, const std::string &memfunc, const std::string &classname) -{ - reportError(tok, Severity::error, "memsetStruct", "Using '" + memfunc + "' on struct that contains a 'std::" + classname + "'"); -} - -void CheckClass::operatorEqReturnError(const Token *tok) -{ - reportError(tok, Severity::style, "operatorEq", "'operator=' should return something"); -} - -void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived) -{ - reportError(tok, Severity::error, "virtualDestructor", "Class " + Base + " which is inherited by class " + Derived + " does not have a virtual destructor"); -} - -void CheckClass::operatorEqRetRefThisError(const Token *tok) -{ - reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to self"); -} - -void CheckClass::operatorEqToSelfError(const Token *tok) -{ - reportError(tok, Severity::possibleStyle, "operatorEqToSelf", "'operator=' should check for assignment to self"); -} - +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#include "checkclass.h" + +#include "tokenize.h" +#include "token.h" +#include "errorlogger.h" + +#include + +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register CheckClass.. +namespace +{ +CheckClass instance; +} + + +//--------------------------------------------------------------------------- + +CheckClass::Var *CheckClass::getVarList(const Token *tok1, bool withClasses, bool isStruct) +{ + // Get variable list.. + Var *varlist = NULL; + unsigned int indentlevel = 0; + bool priv = !isStruct; + for (const Token *tok = tok1; tok; tok = tok->next()) + { + if (!tok->next()) + break; + + if (tok->str() == "{") + ++indentlevel; + else if (tok->str() == "}") + { + if (indentlevel <= 1) + break; + --indentlevel; + } + + if (indentlevel != 1) + continue; + + if (tok->str() == "__published:") + { + priv = false; + for (; tok; tok = tok->next()) + { + if (tok->str() == "{") + ++indentlevel; + else if (tok->str() == "}") + { + if (indentlevel <= 1) + break; + --indentlevel; + } + if (indentlevel == 1 && Token::Match(tok->next(), "private:|protected:|public:")) + break; + } + if (tok) + continue; + else + break; + } + + // "private:" "public:" "protected:" etc + const bool b((*tok->strAt(0) != ':') && strchr(tok->strAt(0), ':') != 0); + + if (b) + priv = bool(tok->str() == "private:"); + + // Search for start of statement.. + if (! Token::Match(tok, "[;{}]") && ! b) + continue; + + // This is the start of a statement + const Token *next = tok->next(); + const char *varname = 0; + + // If next token contains a ":".. it is not part of a variable declaration + if (next->str().find(":") != std::string::npos) + continue; + + // Variable declarations that start with "static" shall be ignored.. + if (next->str() == "static") + continue; + + // Type definitions shall be ignored.. + if (next->str() == "typedef") + continue; + + // Is it a variable declaration? + if (Token::Match(next, "%type% %var% ;")) + { + if (withClasses) + varname = next->strAt(1); + else if (next->isStandardType()) + varname = next->strAt(1); + else if (Token::findmatch(_tokenizer->tokens(), ("enum " + next->str()).c_str())) + varname = next->strAt(1); + } + + // Pointer? + else if (Token::Match(next, "%type% * %var% ;")) + { + varname = next->strAt(2); + } + + // Pointer? + else if (Token::Match(next, "%type% %type% * %var% ;")) + { + varname = next->strAt(3); + } + + // Array? + else if (Token::Match(next, "%type% %var% [") && next->next()->str() != "operator") + { + if (Token::findmatch(_tokenizer->tokens(), ("class " + next->str()).c_str())) + continue; + varname = next->strAt(1); + } + + // Pointer array? + else if (Token::Match(next, "%type% * %var% [")) + { + varname = next->strAt(2); + } + + // std::string.. + else if (withClasses && Token::Match(next, "std :: string %var% ;")) + { + varname = next->strAt(3); + } + + // Container.. + else if (withClasses && Token::Match(next, "std :: %type% <")) + { + while (next && next->str() != ">") + next = next->next(); + if (Token::Match(next, "> %var% ;")) + varname = next->strAt(1); + } + + // If the varname was set in one of the two if-block above, create a entry for this variable.. + if (varname && strcmp(varname, "operator")) + { + Var *var = new Var(varname, false, priv, varlist); + varlist = var; + } + } + + return varlist; +} +//--------------------------------------------------------------------------- + +void CheckClass::initVar(Var *varlist, const char varname[]) +{ + for (Var *var = varlist; var; var = var->next) + { + if (strcmp(var->name, varname) == 0) + { + var->init = true; + break; + } + } +} +//--------------------------------------------------------------------------- + +void CheckClass::initializeVarList(const Token *tok1, const Token *ftok, Var *varlist, const char classname[], std::list &callstack, bool isStruct) +{ + bool Assign = false; + unsigned int indentlevel = 0; + + for (; ftok; ftok = ftok->next()) + { + if (!ftok->next()) + break; + + // Class constructor.. initializing variables like this + // clKalle::clKalle() : var(value) { } + if (indentlevel == 0) + { + if (Assign && Token::Match(ftok, "%var% (")) + { + initVar(varlist, ftok->strAt(0)); + } + + Assign |= (ftok->str() == ":"); + } + + + if (ftok->str() == "{") + { + ++indentlevel; + Assign = false; + } + + else if (ftok->str() == "}") + { + if (indentlevel <= 1) + break; + --indentlevel; + } + + if (indentlevel < 1) + continue; + + // Variable getting value from stream? + if (Token::Match(ftok, ">> %var%")) + { + initVar(varlist, ftok->strAt(1)); + } + + // Before a new statement there is "[{};)=]" or "else" + if (! Token::Match(ftok, "[{};)=]") && ftok->str() != "else") + continue; + + // Using the operator= function to initialize all variables.. + if (Token::simpleMatch(ftok->next(), "* this = ")) + { + for (Var *var = varlist; var; var = var->next) + var->init = true; + break; + } + + if (!Token::Match(ftok->next(), "%var%") && + !Token::Match(ftok->next(), "this . %var%") && + !Token::Match(ftok->next(), "* %var% =") && + !Token::Match(ftok->next(), "( * this ) . %var%")) + continue; + + // Goto the first token in this statement.. + ftok = ftok->next(); + + // Skip "( * this )" + if (Token::simpleMatch(ftok, "( * this ) .")) + { + ftok = ftok->tokAt(5); + } + + // Skip "this->" + if (Token::simpleMatch(ftok, "this .")) + ftok = ftok->tokAt(2); + + // Skip "classname :: " + if (Token::Match(ftok, "%var% ::")) + ftok = ftok->tokAt(2); + + // Clearing all variables.. + if (Token::simpleMatch(ftok, "memset ( this ,")) + { + for (Var *var = varlist; var; var = var->next) + var->init = true; + break; + } + + // Calling member function? + else if (Token::Match(ftok, "%var% (")) + { + // No recursive calls! + if (std::find(callstack.begin(), callstack.end(), ftok->str()) == callstack.end()) + { + callstack.push_back(ftok->str()); + int i = 0; + const Token *ftok2 = Tokenizer::findClassFunction(tok1, classname, ftok->strAt(0), i, isStruct); + if (ftok2) + { + initializeVarList(tok1, ftok2, varlist, classname, callstack, isStruct); + } + else // there is a called member function, but it is not defined where we can find it, so we assume it initializes everything + { + for (Var *var = varlist; var; var = var->next) + var->init = true; + break; + + // we don't report this, as somewhere along the line we hope that the class and member function + // are checked together. It is possible that this will not be the case (where there are enough + // nested functions defined in different files), but that isn't really likely. + } + } + } + + // Assignment of member variable? + else if (Token::Match(ftok, "%var% =")) + { + initVar(varlist, ftok->strAt(0)); + } + + // Assignment of array item of member variable? + else if (Token::Match(ftok, "%var% [ %any% ] =")) + { + initVar(varlist, ftok->strAt(0)); + } + + // Assignment of array item of member variable? + else if (Token::Match(ftok, "%var% [ %any% ] [ %any% ] =")) + { + initVar(varlist, ftok->strAt(0)); + } + + // Assignment of array item of member variable? + else if (Token::Match(ftok, "* %var% =")) + { + initVar(varlist, ftok->strAt(1)); + } + + // The functions 'clear' and 'Clear' are supposed to initialize variable. + if (Token::Match(ftok, "%var% . clear|Clear (")) + { + initVar(varlist, ftok->strAt(0)); + } + } +} + + + + + + +//--------------------------------------------------------------------------- +// ClassCheck: Check that all class constructors are ok. +//--------------------------------------------------------------------------- + +void CheckClass::constructors() +{ + const char pattern_class[] = "class|struct %var% [{:]"; + + // Locate class + const Token *tok1 = Token::findmatch(_tokenizer->tokens(), pattern_class); + while (tok1) + { + const char *className; + className = tok1->strAt(1); + const Token *classNameToken = tok1->tokAt(1); + bool isStruct = tok1->str() == "struct"; + + /** @todo handling of private constructors should be improved */ + bool hasPrivateConstructor = false; + { + int indentlevel = 0; + bool isPrivate = !isStruct; + for (const Token *tok = tok1; tok; tok = tok->next()) + { + // Indentation + if (tok->str() == "{") + ++indentlevel; + + else if (tok->str() == "}") + { + --indentlevel; + if (indentlevel <= 0) + break; + } + + // Parse class contents (indentlevel == 1).. + if (indentlevel == 1) + { + // What section are we in.. private/non-private + if (tok->str() == "private:") + isPrivate = true; + else if (tok->str() == "protected:" || tok->str() == "public:") + isPrivate = false; + + // Is there a private constructor? + else if (isPrivate && Token::simpleMatch(tok, (classNameToken->str() + " (").c_str())) + { + hasPrivateConstructor = true; + break; + } + } + } + } + + if (hasPrivateConstructor && !_settings->_showAll) + { + /** @todo Handle private constructors. Right now to avoid + * false positives we just bail out */ + tok1 = Token::findmatch(tok1->next(), pattern_class); + continue; + } + + // Are there a class constructor? + std::string tempPattern = "%any% " + classNameToken->str() + " ("; + const Token *constructor_token = Token::findmatch(tok1, tempPattern.c_str()); + while (constructor_token && constructor_token->str() == "~") + constructor_token = Token::findmatch(constructor_token->next(), tempPattern.c_str()); + + // There are no constructor. + if (! constructor_token) + { + // If "--style" has been given, give a warning + if (_settings->_checkCodingStyle) + { + // Get class variables... + Var *varlist = getVarList(tok1, false, isStruct); + + // If there is a private variable, there should be a constructor.. + for (const Var *var = varlist; var; var = var->next) + { + if (var->priv) + { + noConstructorError(tok1, classNameToken->str(), isStruct); + break; + } + } + + // Delete the varlist.. + while (varlist) + { + Var *nextvar = varlist->next; + delete varlist; + varlist = nextvar; + } + } + + tok1 = Token::findmatch(tok1->next(), pattern_class); + continue; + } + + // Check constructors + checkConstructors(tok1, className, hasPrivateConstructor, isStruct); + + // Check assignment operators + checkConstructors(tok1, "operator =", hasPrivateConstructor, isStruct); + + tok1 = Token::findmatch(tok1->next(), pattern_class); + } +} + +void CheckClass::checkConstructors(const Token *tok1, const char funcname[], bool hasPrivateConstructor, bool isStruct) +{ + const char * const className = tok1->strAt(1); + + // Check that all member variables are initialized.. + bool withClasses = bool(_settings->_showAll && std::string(funcname) == "operator ="); + Var *varlist = getVarList(tok1, withClasses, isStruct); + + int indentlevel = 0; + const Token *constructor_token = Tokenizer::findClassFunction(tok1, className, funcname, indentlevel, isStruct); + std::list callstack; + initializeVarList(tok1, constructor_token, varlist, className, callstack, isStruct); + while (constructor_token) + { + // Check if any variables are uninitialized + for (Var *var = varlist; var; var = var->next) + { + if (var->init) + continue; + + // Is it a static member variable? + std::ostringstream pattern; + pattern << className << " :: " << var->name << " ="; + if (Token::findmatch(_tokenizer->tokens(), pattern.str().c_str())) + continue; + + // It's non-static and it's not initialized => error + if (Token::simpleMatch(constructor_token, "operator = (") || + Token::simpleMatch(constructor_token->tokAt(2), "operator = (")) + { + const Token *operStart = 0; + if (Token::simpleMatch(constructor_token, "operator = (")) + operStart = constructor_token->tokAt(2); + else + operStart = constructor_token->tokAt(4); + + bool classNameUsed = false; + for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next()) + { + if (operTok->str() == className) + { + classNameUsed = true; + break; + } + } + + if (classNameUsed) + operatorEqVarError(constructor_token, className, var->name); + } + else + uninitVarError(constructor_token, className, var->name, hasPrivateConstructor); + } + + for (Var *var = varlist; var; var = var->next) + var->init = false; + + constructor_token = Tokenizer::findClassFunction(constructor_token->next(), className, funcname, indentlevel, isStruct); + callstack.clear(); + initializeVarList(tok1, constructor_token, varlist, className, callstack, isStruct); + } + + // Delete the varlist.. + while (varlist) + { + Var *nextvar = varlist->next; + delete varlist; + varlist = nextvar; + } +} + + +//--------------------------------------------------------------------------- +// ClassCheck: Unused private functions +//--------------------------------------------------------------------------- + +void CheckClass::privateFunctions() +{ + // Locate some class + for (const Token *tok1 = Token::findmatch(_tokenizer->tokens(), "class|struct %var% {"); tok1; tok1 = Token::findmatch(tok1->next(), "class|struct %var% {")) + { + /** @todo check that the whole class implementation is seen */ + // until the todo above is fixed we only check classes that are + // declared in the source file + if (tok1->fileIndex() != 0) + continue; + + const std::string &classname = tok1->next()->str(); + + // Get private functions.. + std::list FuncList; + FuncList.clear(); + bool isStruct = tok1->str() == "struct"; + bool priv = !isStruct; + unsigned int indent_level = 0; + for (const Token *tok = tok1; tok; tok = tok->next()) + { + if (Token::Match(tok, "friend %var%")) + { + /** @todo Handle friend classes */ + FuncList.clear(); + break; + } + + if (tok->str() == "{") + ++indent_level; + else if (tok->str() == "}") + { + if (indent_level <= 1) + break; + --indent_level; + } + + else if (indent_level != 1) + continue; + else if (tok->str() == "private:") + priv = true; + else if (tok->str() == "public:") + priv = false; + else if (tok->str() == "protected:") + priv = false; + else if (priv) + { + if (Token::Match(tok, "typedef %type% (")) + tok = tok->tokAt(2)->link(); + + else if (Token::Match(tok, "[:,] %var% (")) + tok = tok->tokAt(2)->link(); + + else if (Token::Match(tok, "%var% (") && + !Token::Match(tok, classname.c_str())) + { + FuncList.push_back(tok); + } + } + + /** @todo embedded class have access to private functions */ + if (tok->str() == "class") + { + FuncList.clear(); + break; + } + } + + // Check that all private functions are used.. + bool HasFuncImpl = false; + bool inclass = false; + indent_level = 0; + for (const Token *ftok = _tokenizer->tokens(); ftok; ftok = ftok->next()) + { + if (ftok->str() == "{") + ++indent_level; + else if (ftok->str() == "}") + { + if (indent_level > 0) + --indent_level; + if (indent_level == 0) + inclass = false; + } + + if (Token::Match(ftok, ("class " + classname + " :|{").c_str())) + { + indent_level = 0; + inclass = true; + } + + // Check member class functions to see what functions are used.. + if ((inclass && indent_level == 1 && Token::Match(ftok, ") const| {")) || + (Token::Match(ftok, (classname + " :: ~| %var% (").c_str()))) + { + while (ftok && ftok->str() != ")") + ftok = ftok->next(); + if (!ftok) + break; + if (Token::Match(ftok, ") : %var% (")) + { + while (!Token::Match(ftok->next(), "[{};]")) + ftok = ftok->next(); + } + if (!Token::Match(ftok, ") const| {")) + continue; + + if (ftok->fileIndex() == 0) + HasFuncImpl = true; + + // Parse function.. + int indentlevel2 = 0; + for (const Token *tok2 = ftok; tok2; tok2 = tok2->next()) + { + if (tok2->str() == "{") + ++indentlevel2; + else if (tok2->str() == "}") + { + --indentlevel2; + if (indentlevel2 < 1) + break; + } + else if (Token::Match(tok2, "%var% (")) + { + // Remove function from FuncList + std::list::iterator it = FuncList.begin(); + while (it != FuncList.end()) + { + if (tok2->str() == (*it)->str()) + FuncList.erase(it++); + else + it++; + } + } + } + } + } + + while (HasFuncImpl && !FuncList.empty()) + { + // Final check; check if the function pointer is used somewhere.. + const std::string _pattern("return|(|)|,|= " + FuncList.front()->str()); + if (!Token::findmatch(_tokenizer->tokens(), _pattern.c_str())) + { + unusedPrivateFunctionError(FuncList.front(), classname, FuncList.front()->str()); + } + FuncList.pop_front(); + } + } +} + +//--------------------------------------------------------------------------- +// ClassCheck: Check that memset is not used on classes +//--------------------------------------------------------------------------- + +void CheckClass::noMemset() +{ + // Locate all 'memset' tokens.. + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (!Token::Match(tok, "memset|memcpy|memmove")) + continue; + + const char *type = NULL; + if (Token::Match(tok, "memset ( %var% , %num% , sizeof ( %type% ) )")) + type = tok->strAt(8); + else if (Token::Match(tok, "memset ( & %var% , %num% , sizeof ( %type% ) )")) + type = tok->strAt(9); + else if (Token::Match(tok, "memset ( %var% , %num% , sizeof ( struct %type% ) )")) + type = tok->strAt(9); + else if (Token::Match(tok, "memset ( & %var% , %num% , sizeof ( struct %type% ) )")) + type = tok->strAt(10); + else if (Token::Match(tok, "%type% ( %var% , %var% , sizeof ( %type% ) )")) + type = tok->strAt(8); + + // No type defined => The tokens didn't match + if (!(type && type[0])) + continue; + + // Warn if type is a class.. + const std::string pattern1(std::string("class ") + type); + if (Token::findmatch(_tokenizer->tokens(), pattern1.c_str())) + { + memsetClassError(tok, tok->str()); + continue; + } + + // Warn if type is a struct that contains any std::* + const std::string pattern2(std::string("struct ") + type + " {"); + for (const Token *tstruct = Token::findmatch(_tokenizer->tokens(), pattern2.c_str()); tstruct; tstruct = tstruct->next()) + { + if (tstruct->str() == "}") + break; + + if (Token::Match(tstruct, "std :: %type% %var% ;")) + { + memsetStructError(tok, tok->str(), tstruct->strAt(2)); + break; + } + } + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// ClassCheck: "void operator=(" +//--------------------------------------------------------------------------- + +void CheckClass::operatorEq() +{ + const Token *tok2 = _tokenizer->tokens(); + const Token *tok; + + while ((tok = Token::findmatch(tok2, "void operator = ("))) + { + const Token *tok1 = tok; + while (tok1 && !Token::Match(tok1, "class|struct %var%")) + { + if (tok1->str() == "public:") + { + operatorEqReturnError(tok); + break; + } + if (tok1->str() == "private:" || tok1->str() == "protected:") + break; + tok1 = tok1->previous(); + } + + if (tok1 && Token::Match(tok1, "struct %var%")) + operatorEqReturnError(tok); + + tok2 = tok->next(); + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// ClassCheck: "C& operator=(const C&) { ... return *this; }" +// operator= should return a reference to *this +//--------------------------------------------------------------------------- + +// match two lists of tokens +static bool nameMatch(const Token * tok1, const Token * tok2, int length) +{ + for (int i = 0; i < length; i++) + { + if (tok1->tokAt(i) == 0 || tok2->tokAt(i) == 0) + return false; + + if (tok1->tokAt(i)->str() != tok2->tokAt(i)->str()) + return false; + } + + return true; +} + +// create a class name from a list of tokens +static void nameStr(const Token * name, int length, std::string & str) +{ + for (int i = 0; i < length; i++) + { + if (i != 0) + str += " "; + + str += name->tokAt(i)->str(); + } +} + +void CheckClass::operatorEqRetRefThis() +{ + const Token *tok2 = _tokenizer->tokens(); + const Token *tok; + + while ((tok = Token::findmatch(tok2, "operator = ("))) + { + const Token *tok1 = tok; + + if (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) + { + // make sure this is an assignment operator + int nameLength = 1; + + tok1 = tok1->tokAt(-2); + + // check backwards for proper function signature + while (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) + { + tok1 = tok1->tokAt(-2); + nameLength += 2; + } + + const Token *name = tok1; + std::string nameString; + + nameStr(name, nameLength, nameString); + + if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") + { + // check class name + if (tok1->tokAt(-(1 + nameLength)) && nameMatch(name, tok1->tokAt(-(1 + nameLength)), nameLength)) + { + // may take overloaded argument types so no need to check them + tok1 = tok->tokAt(2)->link(); + + if (tok1 && tok1->next() && tok1->next()->str() == "{") + { + const Token *last = tok1->next()->link()->tokAt(-2); + for (tok1 = tok1->tokAt(2); tok1 != last; tok1 = tok1->next()) + { + // check for return of reference to this + if (tok1->str() == "return") + { + if (!(Token::Match(tok1->tokAt(1), "* this ;") || + Token::Match(tok1->tokAt(1), "operator = ("))) + operatorEqRetRefThisError(tok); + } + } + } + } + } + } + else + { + // make sure this is an assignment operator + tok1 = tok; + + // check backwards for proper function signature + if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") + { + const Token *name = 0; + bool isPublic = false; + while (tok1 && !Token::Match(tok1, "class|struct %var%")) + { + if (!isPublic) + { + if (tok1->str() == "public:") + isPublic = true; + } + tok1 = tok1->previous(); + } + + if (tok1 && Token::Match(tok1, "struct %var%")) + { + isPublic = true; + name = tok1->tokAt(1); + } + else if (tok1 && Token::Match(tok1, "class %var%")) + { + name = tok1->tokAt(1); + } + + if (tok->tokAt(-2) && tok->tokAt(-2)->str() == name->str()) + { + // may take overloaded argument types so no need to check them + tok1 = tok->tokAt(2)->link(); + + if (tok1 && tok1->next() && tok1->next()->str() == "{") + { + const Token *last = tok1->next()->link()->tokAt(-2); + for (tok1 = tok1->tokAt(2); tok1 != last; tok1 = tok1->next()) + { + // check for return of reference to this + if (tok1->str() == "return") + { + if (!(Token::Match(tok1->tokAt(1), "* this ;") || + Token::Match(tok1->tokAt(1), "operator = ("))) + operatorEqRetRefThisError(tok); + } + } + } + } + } + } + + tok2 = tok->next(); + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }" +// operator= should check for assignment to self +// +// For simple classes, an assignment to self check is only a potential optimization. +// +// For classes that allocate dynamic memory, assignment to self can be a real error +// if it is deallocated and allocated again without being checked for. +// +// This check is not valid for classes with multiple inheritance because a +// class can have multiple addresses so there is no trivial way to check for +// assignment to self. +//--------------------------------------------------------------------------- + +static bool hasDeallocation(const Token * first, const Token * last) +{ + // This function is called when no simple check was found for assignment + // to self. We are currently looking for a specific sequence of: + // deallocate member ; ... member = allocate + // This check is far from ideal because it can cause false negatives. + // Unfortunately, this is necessary to prevent false positives. + // This check needs to do careful analysis someday to get this + // correct with a high degree of certainty. + for (const Token * tok = first; tok && (tok != last); tok = tok->next()) + { + // check for deallocating memory + if (Token::Match(tok, "{|;|, free ( %type%")) + { + const Token * var = tok->tokAt(3); + + // we should probably check that var is a pointer in this class + + const Token * tok1 = tok->tokAt(4); + + while (tok1 && (tok1 != last)) + { + if (Token::Match(tok1, "%type% =")) + { + if (tok1->str() == var->str()) + return true; + } + + tok1 = tok1->next(); + } + } + else if (Token::Match(tok, "{|;|, delete [ ] %type%")) + { + const Token * var = tok->tokAt(4); + + // we should probably check that var is a pointer in this class + + const Token * tok1 = tok->tokAt(5); + + while (tok1 && (tok1 != last)) + { + if (Token::Match(tok1, "%type% = new [")) + { + if (tok1->str() == var->str()) + return true; + } + + tok1 = tok1->next(); + } + } + else if (Token::Match(tok, "{|;|, delete %type%")) + { + const Token * var = tok->tokAt(2); + + // we should probably check that var is a pointer in this class + + const Token * tok1 = tok->tokAt(3); + + while (tok1 && (tok1 != last)) + { + if (Token::Match(tok1, "%type% = new")) + { + if (tok1->str() == var->str()) + return true; + } + + tok1 = tok1->next(); + } + } + } + + return false; +} + +static bool hasAssignSelf(const Token * first, const Token * last, const Token * rhs) +{ + for (const Token * tok = first; tok && tok != last; tok = tok->next()) + { + if (Token::Match(tok, "if (")) + { + const Token * tok1 = tok->tokAt(2); + const Token * tok2 = tok->tokAt(1)->link(); + + if (tok1 && tok2) + { + for (; tok1 && tok1 != tok2; tok1 = tok1->next()) + { + if (Token::Match(tok1, "this ==|!= & %var%")) + { + if (tok1->tokAt(3)->str() == rhs->str()) + return true; + } + else if (Token::Match(tok1, "& %var% ==|!= this")) + { + if (tok1->tokAt(1)->str() == rhs->str()) + return true; + } + } + } + } + } + + return false; +} + +static bool hasMultipleInheritanceInline(const Token * tok) +{ + while (tok && tok->str() != "{") + { + if (tok->str() == ",") + return true; + + tok = tok->next(); + } + + return false; +} + +static bool hasMultipleInheritanceGlobal(const Token * start, const std::string & name) +{ + const Token *tok = start; + std::string pattern; + std::string className = name; + + // check for nested classes + while (className.find("::") != std::string::npos) + { + std::string tempName; + + // there is probably a better way to do this + while (className[0] != ' ') + { + tempName += className[0]; + className.erase(0, 1); + } + + className.erase(0, 4); + + pattern = "class|struct " + tempName; + + tok = Token::findmatch(tok, pattern.c_str()); + } + + pattern = "class|struct " + className; + + tok = Token::findmatch(tok, pattern.c_str()); + + return hasMultipleInheritanceInline(tok); +} + +void CheckClass::operatorEqToSelf() +{ + const Token *tok2 = _tokenizer->tokens(); + const Token *tok; + + while ((tok = Token::findmatch(tok2, "operator = ("))) + { + const Token *tok1 = tok; + + // make sure this is an assignment operator + if (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) + { + int nameLength = 1; + + tok1 = tok1->tokAt(-2); + + // check backwards for proper function signature + while (tok1->tokAt(-2) && Token::Match(tok1->tokAt(-2), " %type% ::")) + { + tok1 = tok1->tokAt(-2); + nameLength += 2; + } + + const Token *name = tok1; + std::string nameString; + + nameStr(name, nameLength, nameString); + + if (!hasMultipleInheritanceGlobal(_tokenizer->tokens(), nameString)) + { + if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") + { + // check returned class name + if (tok1->tokAt(-(1 + nameLength)) && nameMatch(name, tok1->tokAt(-(1 + nameLength)), nameLength)) + { + // check forward for proper function signature + std::string pattern = "const " + nameString + " & %type% )"; + if (Token::Match(tok->tokAt(3), pattern.c_str())) + { + const Token * rhs = tok->tokAt(5 + nameLength); + + if (nameMatch(name, tok->tokAt(4), nameLength)) + { + tok1 = tok->tokAt(2)->link(); + + if (tok1 && tok1->tokAt(1) && tok1->tokAt(1)->str() == "{" && tok1->tokAt(1)->link()) + { + const Token *first = tok1->tokAt(1); + const Token *last = first->link(); + + if (!hasAssignSelf(first, last, rhs)) + { + if (hasDeallocation(first, last)) + operatorEqToSelfError(tok); + } + } + } + } + } + } + } + } + else + { + tok1 = tok; + + // check backwards for proper function signature + if (tok1->tokAt(-1) && tok1->tokAt(-1)->str() == "&") + { + const Token *name = 0; + while (tok1 && !Token::Match(tok1, "class|struct %var%")) + tok1 = tok1->previous(); + + if (tok1 && Token::Match(tok1, "struct %var%")) + name = tok1->tokAt(1); + else if (tok1 && Token::Match(tok1, "class %var%")) + name = tok1->tokAt(1); + + if (!hasMultipleInheritanceInline(tok1)) + { + if (tok->tokAt(-2) && tok->tokAt(-2)->str() == name->str()) + { + // check forward for proper function signature + if (Token::Match(tok->tokAt(3), "const %type% & %type% )")) + { + const Token * rhs = tok->tokAt(6); + + if (tok->tokAt(4)->str() == name->str()) + { + tok1 = tok->tokAt(2)->link(); + + if (tok1 && tok1->tokAt(1) && tok1->tokAt(1)->str() == "{" && tok1->tokAt(1)->link()) + { + const Token *first = tok1->tokAt(1); + const Token *last = first->link(); + + if (!hasAssignSelf(first, last, rhs)) + { + if (hasDeallocation(first, last)) + operatorEqToSelfError(tok); + } + } + } + } + } + } + } + } + + tok2 = tok->next(); + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// A destructor in a base class should be virtual +//--------------------------------------------------------------------------- + +void CheckClass::virtualDestructor() +{ + const char pattern_classdecl[] = "class %var% : %var%"; + + const Token *derived = _tokenizer->tokens(); + while ((derived = Token::findmatch(derived, pattern_classdecl)) != NULL) + { + // Check that the derived class has a non empty destructor.. + { + std::ostringstream destructorPattern; + destructorPattern << "~ " << derived->strAt(1) << " ( ) {"; + const Token *derived_destructor = Token::findmatch(_tokenizer->tokens(), destructorPattern.str().c_str()); + + // No destructor.. + if (! derived_destructor) + { + derived = derived->next(); + continue; + } + + // Empty destructor.. + if (Token::Match(derived_destructor, "~ %var% ( ) { }")) + { + derived = derived->next(); + continue; + } + } + + const Token *derivedClass = derived->tokAt(1); + + // Iterate through each base class... + derived = derived->tokAt(3); + while (Token::Match(derived, "%var%")) + { + bool isPublic(derived->str() == "public"); + + // What kind of inheritance is it.. public|protected|private + if (Token::Match(derived, "public|protected|private")) + derived = derived->next(); + + // Name of base class.. + const char *baseName[2]; + baseName[0] = derived->strAt(0); + baseName[1] = 0; + + // Update derived so it's ready for the next loop. + do + { + if (derived->str() == "{") + break; + + if (derived->str() == ",") + { + derived = derived->next(); + break; + } + + derived = derived->next(); + } + while (derived); + + // If not public inheritance, skip checking of this base class.. + if (! isPublic) + continue; + + // Find the destructor declaration for the base class. + const Token *base = Token::findmatch(_tokenizer->tokens(), (std::string("%any% ~ ") + baseName[0] + " (").c_str()); + while (base && base->str() == "::") + base = Token::findmatch(base->next(), (std::string("%any% ~ ") + baseName[0] + " (").c_str()); + + const Token *reverseTok = base; + while (Token::Match(base, "%var%") && base->str() != "virtual") + base = base->previous(); + + // Check that there is a destructor.. + if (! base) + { + // Is the class declaration available? + base = Token::findmatch(_tokenizer->tokens(), (std::string("class ") + baseName[0] + " {").c_str()); + if (base) + { + virtualDestructorError(base, baseName[0], derivedClass->str()); + } + + continue; + } + + // There is a destructor. Check that it's virtual.. + else if (base->str() == "virtual") + continue; + + // TODO: This is just a temporary fix, better solution is needed. + // Skip situations where base class has base classes of its own, because + // some of the base classes might have virtual destructor. + // Proper solution is to check all of the base classes. If base class is not + // found or if one of the base classes has virtual destructor, error should not + // be printed. See TODO test case "virtualDestructorInherited" + if (!Token::findmatch(_tokenizer->tokens(), (std::string("class ") + baseName[0] + " {").c_str())) + continue; + + // Make sure that the destructor is public (protected or private + // would not compile if inheritance is used in a way that would + // cause the bug we are trying to find here.) + int indent = 0; + while (reverseTok) + { + if (reverseTok->str() == "public:") + { + virtualDestructorError(base, baseName[0], derivedClass->str()); + break; + } + else if (reverseTok->str() == "protected:" || + reverseTok->str() == "private:") + { + // No bug, protected/private destructor is allowed + break; + } + else if (reverseTok->str() == "{") + { + indent++; + if (indent >= 1) + { + // We have found the start of the class without any sign + // of "public :" so we can assume that the destructor is not + // public and there is no bug in the code we are checking. + break; + } + } + else if (reverseTok->str() == "}") + indent--; + + reverseTok = reverseTok->previous(); + } + } + } +} +//--------------------------------------------------------------------------- + +void CheckClass::thisSubtractionError(const Token *tok) +{ + reportError(tok, Severity::possibleStyle, "thisSubtraction", "Suspicious pointer subtraction"); +} + +void CheckClass::thisSubtraction() +{ + const Token *tok = _tokenizer->tokens(); + for (;;) + { + tok = Token::findmatch(tok, "this - %var%"); + if (!tok) + break; + + if (!Token::simpleMatch(tok->previous(), "*")) + thisSubtractionError(tok); + + tok = tok->next(); + } +} + +void CheckClass::noConstructorError(const Token *tok, const std::string &classname, bool isStruct) +{ + reportError(tok, Severity::style, "noConstructor", "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + "' has no constructor. Member variables not initialized."); +} + +void CheckClass::uninitVarError(const Token *tok, const std::string &classname, const std::string &varname, bool hasPrivateConstructor) +{ + reportError(tok, hasPrivateConstructor ? Severity::possibleStyle : Severity::style, "uninitVar", "Member variable not initialized in the constructor '" + classname + "::" + varname + "'"); +} + +void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname) +{ + reportError(tok, Severity::possibleStyle, "operatorEqVarError", "Member variable '" + classname + "::" + varname + "' is not assigned a value in '" + classname + "::operator=" + "'"); +} + +void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) +{ + reportError(tok, Severity::style, "unusedPrivateFunction", "Unused private function '" + classname + "::" + funcname + "'"); +} + +void CheckClass::memsetClassError(const Token *tok, const std::string &memfunc) +{ + reportError(tok, Severity::error, "memsetClass", "Using '" + memfunc + "' on class"); +} + +void CheckClass::memsetStructError(const Token *tok, const std::string &memfunc, const std::string &classname) +{ + reportError(tok, Severity::error, "memsetStruct", "Using '" + memfunc + "' on struct that contains a 'std::" + classname + "'"); +} + +void CheckClass::operatorEqReturnError(const Token *tok) +{ + reportError(tok, Severity::style, "operatorEq", "'operator=' should return something"); +} + +void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived) +{ + reportError(tok, Severity::error, "virtualDestructor", "Class " + Base + " which is inherited by class " + Derived + " does not have a virtual destructor"); +} + +void CheckClass::operatorEqRetRefThisError(const Token *tok) +{ + reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to self"); +} + +void CheckClass::operatorEqToSelfError(const Token *tok) +{ + reportError(tok, Severity::possibleStyle, "operatorEqToSelf", "'operator=' should check for assignment to self"); +} + diff --git a/lib/checkclass.h b/lib/checkclass.h old mode 100755 new mode 100644 index 5c18ca316..49e9e2aaa --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -1,167 +1,167 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki and Cppcheck team. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -//--------------------------------------------------------------------------- -#ifndef CheckClassH -#define CheckClassH -//--------------------------------------------------------------------------- - -#include "check.h" -#include "settings.h" - -class Token; - -/// @addtogroup Checks -/// @{ - - -class CheckClass : public Check -{ -public: - /** This constructor is used when registering the CheckClass */ - CheckClass() : Check() - { } - - /** This constructor is used when running checks.. */ - CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(tokenizer, settings, errorLogger) - { } - - void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - { - CheckClass checkClass(tokenizer, settings, errorLogger); - checkClass.noMemset(); - } - - void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - { - CheckClass checkClass(tokenizer, settings, errorLogger); - - if (settings->_checkCodingStyle) - { - checkClass.constructors(); - checkClass.operatorEq(); - checkClass.privateFunctions(); - checkClass.operatorEqRetRefThis(); - if (settings->_showAll) - { - checkClass.thisSubtraction(); - checkClass.operatorEqToSelf(); - } - } - checkClass.virtualDestructor(); - } - - - // Check that all class constructors are ok. - void constructors(); - - // Check that all private functions are called. - void privateFunctions(); - - // Check that the memsets are valid. - // The 'memset' function can do dangerous things if used wrong. - // Important: The checking doesn't work on simplified tokens list. - void noMemset(); - - // 'operator=' should return something.. - void operatorEq(); // Warning upon "void operator=(.." - - // 'operator=' should return reference to *this - void operatorEqRetRefThis(); // Warning upon no "return *this;" - - // 'operator=' should check for assignment to self - void operatorEqToSelf(); // Warning upon no check for assignment to self - - // The destructor in a base class should be virtual - void virtualDestructor(); - - void thisSubtraction(); -private: - class Var - { - public: - Var(const char *name = 0, bool init = false, bool priv = false, Var *next = 0) - { - this->name = name; - this->init = init; - this->priv = priv; - this->next = next; - } - - const char *name; - bool init; - bool priv; - Var *next; - }; - - void initializeVarList(const Token *tok1, const Token *ftok, Var *varlist, const char classname[], std::list &callstack, bool isStruct); - void initVar(Var *varlist, const char varname[]); - Var *getVarList(const Token *tok1, bool withClasses, bool isStruct); - - // Check constructors for a specified class - void checkConstructors(const Token *tok1, const char funcname[], bool hasPrivateConstructor, bool isStruct); - - // Reporting errors.. - void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); - void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname, bool hasPrivateConstructor); - void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname); - void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname); - void memsetClassError(const Token *tok, const std::string &memfunc); - void memsetStructError(const Token *tok, const std::string &memfunc, const std::string &classname); - void operatorEqReturnError(const Token *tok); - void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived); - void thisSubtractionError(const Token *tok); - void operatorEqRetRefThisError(const Token *tok); - void operatorEqToSelfError(const Token *tok); - - void getErrorMessages() - { - noConstructorError(0, "classname", false); - uninitVarError(0, "classname", "varname", false); - operatorEqVarError(0, "classname", ""); - unusedPrivateFunctionError(0, "classname", "funcname"); - memsetClassError(0, "memfunc"); - memsetStructError(0, "memfunc", "classname"); - operatorEqReturnError(0); - virtualDestructorError(0, "Base", "Derived"); - thisSubtractionError(0); - operatorEqRetRefThisError(0); - operatorEqToSelfError(0); - } - - std::string name() const - { - return "Class"; - } - - std::string classInfo() const - { - return "Check the code for each class.\n" - " * Missing constructors\n" - " * Are all variables initialized by the constructors?\n" - " * [[CheckMemset|Warn if memset, memcpy etc are used on a class]]\n" - " * If it's a base class, check that the destructor is virtual\n" - " * The operator= should return a constant reference to itself\n" - " * Are there unused private functions\n"; - } -}; -/// @} -//--------------------------------------------------------------------------- -#endif - +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef CheckClassH +#define CheckClassH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "settings.h" + +class Token; + +/// @addtogroup Checks +/// @{ + + +class CheckClass : public Check +{ +public: + /** This constructor is used when registering the CheckClass */ + CheckClass() : Check() + { } + + /** This constructor is used when running checks.. */ + CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(tokenizer, settings, errorLogger) + { } + + void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + { + CheckClass checkClass(tokenizer, settings, errorLogger); + checkClass.noMemset(); + } + + void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + { + CheckClass checkClass(tokenizer, settings, errorLogger); + + if (settings->_checkCodingStyle) + { + checkClass.constructors(); + checkClass.operatorEq(); + checkClass.privateFunctions(); + checkClass.operatorEqRetRefThis(); + if (settings->_showAll) + { + checkClass.thisSubtraction(); + checkClass.operatorEqToSelf(); + } + } + checkClass.virtualDestructor(); + } + + + // Check that all class constructors are ok. + void constructors(); + + // Check that all private functions are called. + void privateFunctions(); + + // Check that the memsets are valid. + // The 'memset' function can do dangerous things if used wrong. + // Important: The checking doesn't work on simplified tokens list. + void noMemset(); + + // 'operator=' should return something.. + void operatorEq(); // Warning upon "void operator=(.." + + // 'operator=' should return reference to *this + void operatorEqRetRefThis(); // Warning upon no "return *this;" + + // 'operator=' should check for assignment to self + void operatorEqToSelf(); // Warning upon no check for assignment to self + + // The destructor in a base class should be virtual + void virtualDestructor(); + + void thisSubtraction(); +private: + class Var + { + public: + Var(const char *name = 0, bool init = false, bool priv = false, Var *next = 0) + { + this->name = name; + this->init = init; + this->priv = priv; + this->next = next; + } + + const char *name; + bool init; + bool priv; + Var *next; + }; + + void initializeVarList(const Token *tok1, const Token *ftok, Var *varlist, const char classname[], std::list &callstack, bool isStruct); + void initVar(Var *varlist, const char varname[]); + Var *getVarList(const Token *tok1, bool withClasses, bool isStruct); + + // Check constructors for a specified class + void checkConstructors(const Token *tok1, const char funcname[], bool hasPrivateConstructor, bool isStruct); + + // Reporting errors.. + void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); + void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname, bool hasPrivateConstructor); + void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname); + void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname); + void memsetClassError(const Token *tok, const std::string &memfunc); + void memsetStructError(const Token *tok, const std::string &memfunc, const std::string &classname); + void operatorEqReturnError(const Token *tok); + void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived); + void thisSubtractionError(const Token *tok); + void operatorEqRetRefThisError(const Token *tok); + void operatorEqToSelfError(const Token *tok); + + void getErrorMessages() + { + noConstructorError(0, "classname", false); + uninitVarError(0, "classname", "varname", false); + operatorEqVarError(0, "classname", ""); + unusedPrivateFunctionError(0, "classname", "funcname"); + memsetClassError(0, "memfunc"); + memsetStructError(0, "memfunc", "classname"); + operatorEqReturnError(0); + virtualDestructorError(0, "Base", "Derived"); + thisSubtractionError(0); + operatorEqRetRefThisError(0); + operatorEqToSelfError(0); + } + + std::string name() const + { + return "Class"; + } + + std::string classInfo() const + { + return "Check the code for each class.\n" + " * Missing constructors\n" + " * Are all variables initialized by the constructors?\n" + " * [[CheckMemset|Warn if memset, memcpy etc are used on a class]]\n" + " * If it's a base class, check that the destructor is virtual\n" + " * The operator= should return a constant reference to itself\n" + " * Are there unused private functions\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif +