Refactoring: Global/Static variables, FunctionList, GlobalFunctions and UsedGlobalFunctions are now private members of Tokenizer class.

Class GlobalFunction is now private subclass of Tokenizer. 
Global functions CheckGlobalFunctionUsage, FillFunctionList and GetFunctionTokenByName are now member functions of Tokenizer.
This commit is contained in:
Reijo Tomperi 2008-11-15 22:41:56 +00:00
parent 2cbfa6b60b
commit 08b25dc0d4
11 changed files with 257 additions and 232 deletions

View File

@ -257,7 +257,7 @@ void CheckBufferOverrunClass::CheckBufferOverrun_CheckScope( const TOKEN *tok, c
continue;
// Find function..
const TOKEN *ftok = GetFunctionTokenByName( tok->str );
const TOKEN *ftok = _tokenizer->GetFunctionTokenByName( tok->str );
if ( ! ftok )
continue;

View File

@ -220,7 +220,7 @@ const char * CheckMemoryLeakClass::call_func( const TOKEN *tok, const char types
++par;
if ( Match(tok, "[,()] %var1% [,()]", varnames) )
{
const TOKEN *ftok = GetFunctionTokenByName(funcname);
const TOKEN *ftok = _tokenizer->GetFunctionTokenByName(funcname);
const char *parname = GetParameterName( ftok, par );
if ( ! parname )
return "use";

View File

@ -32,30 +32,9 @@
#include <ctype.h>
#endif
//---------------------------------------------------------------------------
extern bool CheckCodingStyle;
bool OnlyReportUniqueErrors;
std::ostringstream errout;
static std::list<const TOKEN *> FunctionList;
class GlobalFunction
{
private:
unsigned int _FileId;
std::string _FuncName;
public:
GlobalFunction( const unsigned int FileId, const char FuncName[] )
{
_FileId = FileId;
_FuncName = FuncName;
}
unsigned int file_id() const { return _FileId; }
const std::string &name() const { return _FuncName; }
};
static std::list< GlobalFunction > GlobalFunctions;
static std::list< GlobalFunction > UsedGlobalFunctions;
//---------------------------------------------------------------------------
@ -122,118 +101,6 @@ bool IsStandardType(const char str[])
}
//---------------------------------------------------------------------------
void FillFunctionList(const unsigned int file_id)
{
FunctionList.clear();
std::list<const char *> _usedfunc;
if ( file_id == 0 )
{
GlobalFunctions.clear();
}
bool staticfunc = false;
bool classfunc = false;
int indentlevel = 0;
for ( const TOKEN *tok = tokens; tok; tok = tok->next )
{
if ( tok->str[0] == '{' )
indentlevel++;
else if ( tok->str[0] == '}' )
indentlevel--;
if (indentlevel > 0)
{
if ( CheckCodingStyle )
{
const char *funcname = 0;
if ( Match(tok,"%var% (") )
funcname = tok->str;
else if ( Match(tok, "= %var% ;") ||
Match(tok, "= %var% ,") )
funcname = tok->next->str;
if ( std::find(_usedfunc.begin(), _usedfunc.end(), funcname) == _usedfunc.end() )
_usedfunc.push_back( funcname );
}
continue;
}
if (strchr("};", tok->str[0]))
staticfunc = classfunc = false;
else if ( strcmp( tok->str, "static" ) == 0 )
staticfunc = true;
else if ( strcmp( tok->str, "::" ) == 0 )
classfunc = true;
else if (Match(tok, "%var% ("))
{
// Check if this is the first token of a function implementation..
for ( const TOKEN *tok2 = tok; tok2; tok2 = tok2->next )
{
if ( tok2->str[0] == ';' )
{
tok = tok2;
break;
}
else if ( tok2->str[0] == '{' )
{
break;
}
else if ( tok2->str[0] == ')' )
{
if ( Match(tok2, ") {") )
{
if (CheckCodingStyle && !staticfunc && !classfunc && tok->FileIndex==0)
GlobalFunctions.push_back( GlobalFunction(file_id, tok->str) );
FunctionList.push_back( tok );
tok = tok2;
}
else
{
tok = tok2;
while (tok->next && !strchr(";{", tok->next->str[0]))
tok = tok->next;
}
break;
}
}
}
}
for (std::list<const char *>::const_iterator it = _usedfunc.begin(); it != _usedfunc.end(); ++it)
{
if ( *it != 0 )
{
UsedGlobalFunctions.push_back( GlobalFunction(file_id, *it) );
}
}
}
//---------------------------------------------------------------------------
const TOKEN *GetFunctionTokenByName( const char funcname[] )
{
std::list<const TOKEN *>::const_iterator it;
for ( it = FunctionList.begin(); it != FunctionList.end(); it++ )
{
if ( strcmp( (*it)->str, funcname ) == 0 )
{
return *it;
}
}
return NULL;
}
//--------------------------------------------------------------------------
const char *GetParameterName( const TOKEN *ftok, int par )
{
int _par = 1;
@ -248,75 +115,6 @@ const char *GetParameterName( const TOKEN *ftok, int par )
}
//--------------------------------------------------------------------------
void CheckGlobalFunctionUsage(const std::vector<std::string> &filenames)
{
// Iterator for GlobalFunctions
std::list<GlobalFunction>::const_iterator func;
// Iterator for UsedGlobalFunctions
std::list<GlobalFunction>::const_iterator usedfunc;
unsigned int i1 = 0;
unsigned int i2 = 1;
// Check that every function in GlobalFunctions are used
for ( func = GlobalFunctions.begin(); func != GlobalFunctions.end(); func++ )
{
if ( GlobalFunctions.size() > 100 )
{
++i1;
if ( i1 > (i2 * GlobalFunctions.size()) / 100 )
{
if ( (i2 % 10) == 0 )
std::cout << i2 << "%";
else
std::cout << ".";
std::cout.flush();
++i2;
}
}
const std::string &funcname = func->name();
if ( funcname == "main" || funcname == "WinMain" )
continue;
// Check if this global function is used in any of the other files..
bool UsedOtherFile = false;
bool UsedAnyFile = false;
for ( usedfunc = UsedGlobalFunctions.begin(); usedfunc != UsedGlobalFunctions.end(); usedfunc++ )
{
if ( funcname == usedfunc->name() )
{
UsedAnyFile = true;
if (func->file_id() != usedfunc->file_id())
{
UsedOtherFile = true;
break;
}
}
}
if ( ! UsedAnyFile )
{
std::ostringstream errmsg;
errmsg << "[" << filenames[func->file_id()] << "]: "
<< "The function '" << func->name() << "' is never used.";
ReportErr( errmsg.str() );
}
else if ( ! UsedOtherFile )
{
std::ostringstream errmsg;
errmsg << "[" << filenames[func->file_id()] << "]: "
<< "The linkage of the function '" << func->name() << "' can be local (static) instead of global";
ReportErr( errmsg.str() );
}
}
std::cout << "\n";
}
//---------------------------------------------------------------------------
bool Match(const TOKEN *tok, const char pattern[], const char *varname1[], const char *varname2[])
{
if (!tok)

View File

@ -23,7 +23,7 @@
#define CommonCheckH
//---------------------------------------------------------------------------
#include <list>
#include <string>
#include <sstream>
#include <vector>
@ -47,10 +47,9 @@ bool IsNumber(const char str[]);
bool IsStandardType(const char str[]);
void FillFunctionList(const unsigned int file_id);
const TOKEN *GetFunctionTokenByName( const char funcname[] );
const char *GetParameterName( const TOKEN *ftok, int par );
void CheckGlobalFunctionUsage(const std::vector<std::string> &filenames);
bool Match(const TOKEN *tok, const char pattern[], const char *varname1[]=0, const char *varname2[]=0);

View File

@ -18,7 +18,7 @@
#include "cppcheck.h"
#include "preprocessor.h" // preprocessor.
#include "tokenize.h" // <- Tokenizer
#include "CommonCheck.h"
#include "CheckMemoryLeak.h"
#include "CheckBufferOverrun.h"
@ -87,6 +87,7 @@ void CppCheck::check(int argc, char* argv[])
ShowAll = _settings._showAll;
CheckCodingStyle = _settings._checkCodingStyle;
ErrorsOnly = _settings._errorsOnly;
_tokenizer.settings( _settings );
std::vector<std::string> filenames;
// --recursive was used
@ -174,7 +175,7 @@ void CppCheck::check(int argc, char* argv[])
{
errout.str("");
std::cout << "Checking usage of global functions (this may take several minutes)..\n";
CheckGlobalFunctionUsage(filenames);
_tokenizer.CheckGlobalFunctionUsage(filenames);
if ( ! errout.str().empty() )
{
std::cerr << "\n";
@ -191,28 +192,27 @@ void CppCheck::check(int argc, char* argv[])
void CppCheck::checkFile(const std::string &code, const char FileName[], unsigned int FileId)
{
Tokenizer tokenizer;
OnlyReportUniqueErrors = true;
// Tokenize the file
{
std::istringstream istr(code);
tokenizer.Tokenize(istr, FileName);
_tokenizer.Tokenize(istr, FileName);
}
FillFunctionList(FileId);
_tokenizer.FillFunctionList(FileId);
// 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.
CheckClass checkClass( &tokenizer );
CheckClass checkClass( &_tokenizer );
checkClass.CheckMemset();
// Check for unsigned divisions where one operand is signed
// Very important to run it before 'SimplifyTokenList'
CheckOther checkOther( &tokenizer );
CheckOther checkOther( &_tokenizer );
checkOther.CheckUnsignedDivision();
// Give warning when using char variable as array index
@ -229,14 +229,14 @@ void CppCheck::checkFile(const std::string &code, const char FileName[], unsigne
// }
tokenizer.SimplifyTokenList();
_tokenizer.SimplifyTokenList();
// Memory leak
CheckMemoryLeakClass checkMemoryLeak( &tokenizer );
CheckMemoryLeakClass checkMemoryLeak( &_tokenizer );
checkMemoryLeak.CheckMemoryLeak();
// Buffer overruns..
CheckBufferOverrunClass checkBufferOverrun( &tokenizer );
CheckBufferOverrunClass checkBufferOverrun( &_tokenizer );
checkBufferOverrun.CheckBufferOverrun();
// Check that all class constructors are ok.
@ -303,7 +303,7 @@ void CppCheck::checkFile(const std::string &code, const char FileName[], unsigne
}
// Clean up tokens..
tokenizer.DeallocateTokens();
_tokenizer.DeallocateTokens();
}
//---------------------------------------------------------------------------

View File

@ -21,7 +21,7 @@
#include <string>
#include "settings.h"
#include "tokenize.h" // <- Tokenizer
/**
* This is the base class which will use other classes to do
* static code analysis for C and C++ code to find possible
@ -37,6 +37,7 @@ class CppCheck
private:
void checkFile(const std::string &code, const char FileName[], unsigned int FileId);
Settings _settings;
Tokenizer _tokenizer;
};
#endif // CPPCHECK_H

View File

@ -45,7 +45,10 @@ private:
tokenizer.SimplifyTokenList();
// Fill function list
FillFunctionList(0);
Settings settings;
settings._checkCodingStyle = true;
tokenizer.settings( settings );
tokenizer.FillFunctionList(0);
// Clear the error buffer..
errout.str("");

View File

@ -50,7 +50,10 @@ private:
tokenizer.TokenizeCode( istr );
// Fill function list
FillFunctionList(0);
Settings settings;
settings._checkCodingStyle = true;
tokenizer.settings( settings );
tokenizer.FillFunctionList(0);
// Clear the error buffer..
errout.str("");

View File

@ -50,7 +50,10 @@ private:
// Check for memory leaks..
ShowAll = false;
FillFunctionList(0);
Settings settings;
settings._checkCodingStyle = true;
tokenizer.settings( settings );
tokenizer.FillFunctionList(0);
CheckMemoryLeakClass checkMemoryLeak( &tokenizer );
checkMemoryLeak.CheckMemoryLeak();

View File

@ -28,6 +28,7 @@
#include <string>
#include <cstring>
#include <iostream>
#include <stdlib.h> // <- strtoul
#include <stdio.h>
@ -1115,8 +1116,196 @@ void Tokenizer::DeallocateTokens()
delete dsymlist;
dsymlist = next;
}
Files.clear();
}
//---------------------------------------------------------------------------
const TOKEN *Tokenizer::GetFunctionTokenByName( const char funcname[] ) const
{
std::list<const TOKEN *>::const_iterator it;
for ( it = FunctionList.begin(); it != FunctionList.end(); it++ )
{
if ( strcmp( (*it)->str, funcname ) == 0 )
{
return *it;
}
}
return NULL;
}
void Tokenizer::FillFunctionList(const unsigned int file_id)
{
FunctionList.clear();
std::list<const char *> _usedfunc;
if ( file_id == 0 )
{
GlobalFunctions.clear();
}
bool staticfunc = false;
bool classfunc = false;
int indentlevel = 0;
for ( const TOKEN *tok = tokens; tok; tok = tok->next )
{
if ( tok->str[0] == '{' )
indentlevel++;
else if ( tok->str[0] == '}' )
indentlevel--;
if (indentlevel > 0)
{
if ( _settings._checkCodingStyle )
{
const char *funcname = 0;
if ( Match(tok,"%var% (") )
funcname = tok->str;
else if ( Match(tok, "= %var% ;") ||
Match(tok, "= %var% ,") )
funcname = tok->next->str;
if ( std::find(_usedfunc.begin(), _usedfunc.end(), funcname) == _usedfunc.end() )
_usedfunc.push_back( funcname );
}
continue;
}
if (strchr("};", tok->str[0]))
staticfunc = classfunc = false;
else if ( strcmp( tok->str, "static" ) == 0 )
staticfunc = true;
else if ( strcmp( tok->str, "::" ) == 0 )
classfunc = true;
else if (Match(tok, "%var% ("))
{
// Check if this is the first token of a function implementation..
for ( const TOKEN *tok2 = tok; tok2; tok2 = tok2->next )
{
if ( tok2->str[0] == ';' )
{
tok = tok2;
break;
}
else if ( tok2->str[0] == '{' )
{
break;
}
else if ( tok2->str[0] == ')' )
{
if ( Match(tok2, ") {") )
{
if (_settings._checkCodingStyle && !staticfunc && !classfunc && tok->FileIndex==0)
GlobalFunctions.push_back( GlobalFunction(file_id, tok->str) );
FunctionList.push_back( tok );
tok = tok2;
}
else
{
tok = tok2;
while (tok->next && !strchr(";{", tok->next->str[0]))
tok = tok->next;
}
break;
}
}
}
}
for (std::list<const char *>::const_iterator it = _usedfunc.begin(); it != _usedfunc.end(); ++it)
{
if ( *it != 0 )
{
UsedGlobalFunctions.push_back( GlobalFunction(file_id, *it) );
}
}
}
//--------------------------------------------------------------------------
void Tokenizer::CheckGlobalFunctionUsage(const std::vector<std::string> &filenames)
{
// Iterator for GlobalFunctions
std::list<GlobalFunction>::const_iterator func;
// Iterator for UsedGlobalFunctions
std::list<GlobalFunction>::const_iterator usedfunc;
unsigned int i1 = 0;
unsigned int i2 = 1;
// Check that every function in GlobalFunctions are used
for ( func = GlobalFunctions.begin(); func != GlobalFunctions.end(); func++ )
{
if ( GlobalFunctions.size() > 100 )
{
++i1;
if ( i1 > (i2 * GlobalFunctions.size()) / 100 )
{
if ( (i2 % 10) == 0 )
std::cout << i2 << "%";
else
std::cout << ".";
std::cout.flush();
++i2;
}
}
const std::string &funcname = func->name();
if ( funcname == "main" || funcname == "WinMain" )
continue;
// Check if this global function is used in any of the other files..
bool UsedOtherFile = false;
bool UsedAnyFile = false;
for ( usedfunc = UsedGlobalFunctions.begin(); usedfunc != UsedGlobalFunctions.end(); usedfunc++ )
{
if ( funcname == usedfunc->name() )
{
UsedAnyFile = true;
if (func->file_id() != usedfunc->file_id())
{
UsedOtherFile = true;
break;
}
}
}
if ( ! UsedAnyFile )
{
std::ostringstream errmsg;
errmsg << "[" << filenames[func->file_id()] << "]: "
<< "The function '" << func->name() << "' is never used.";
ReportErr( errmsg.str() );
}
else if ( ! UsedOtherFile )
{
std::ostringstream errmsg;
errmsg << "[" << filenames[func->file_id()] << "]: "
<< "The linkage of the function '" << func->name() << "' can be local (static) instead of global";
ReportErr( errmsg.str() );
}
}
std::cout << "\n";
}
//---------------------------------------------------------------------------
void Tokenizer::settings( const Settings &settings )
{
_settings = settings;
}

View File

@ -21,12 +21,13 @@
#ifndef tokenizeH
#define tokenizeH
//---------------------------------------------------------------------------
#include <list>
#include <string>
#include <map>
#include <vector>
#include <cstdlib>
#include <cstring>
#include "settings.h"
class TOKEN
{
@ -89,10 +90,33 @@ public:
std::vector<std::string> *getFiles();
std::vector<std::string> Files;
void FillFunctionList(const unsigned int file_id);
const TOKEN *GetFunctionTokenByName( const char funcname[] ) const;
void CheckGlobalFunctionUsage(const std::vector<std::string> &filenames);
void settings( const Settings &settings );
private:
class GlobalFunction
{
private:
unsigned int _FileId;
std::string _FuncName;
public:
GlobalFunction( const unsigned int FileId, const char FuncName[] )
{
_FileId = FileId;
_FuncName = FuncName;
}
unsigned int file_id() const { return _FileId; }
const std::string &name() const { return _FuncName; }
};
void Define(const char Name[], const char Value[]);
void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno);
@ -107,6 +131,11 @@ private:
TOKEN *tokens_back;
std::map<std::string, unsigned int> TypeSize;
std::list<const TOKEN *> FunctionList;
std::list< GlobalFunction > GlobalFunctions;
std::list< GlobalFunction > UsedGlobalFunctions;
std::vector<std::string> Files;
Settings _settings;
};