Function list: Remove functions with duplicate names to prevent false positives

This commit is contained in:
Daniel Marjamäki 2008-11-20 19:18:55 +00:00
parent 2e445b195c
commit 63bc26d662
3 changed files with 409 additions and 353 deletions

View File

@ -1,4 +1,4 @@
/* /*
* c++check - c/c++ syntax checking * c++check - c/c++ syntax checking
* Copyright (C) 2007 Daniel Marjamäki * Copyright (C) 2007 Daniel Marjamäki
* *
@ -22,6 +22,7 @@
#include "testsuite.h" #include "testsuite.h"
#define UNIT_TESTING // Get access to "private" data in Tokenizer
#include "tokenize.h" #include "tokenize.h"
class TestTokenizer : public TestFixture class TestTokenizer : public TestFixture
@ -38,6 +39,8 @@ private:
TEST_CASE( longtok ); TEST_CASE( longtok );
TEST_CASE( inlineasm ); TEST_CASE( inlineasm );
TEST_CASE( dupfuncname );
} }
@ -59,9 +62,9 @@ private:
" \"def\"\n"; " \"def\"\n";
// tokenize.. // tokenize..
Tokenizer tokenizer; Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" ); tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata); std::istringstream istr(filedata);
tokenizer.TokenizeCode(istr, 0); tokenizer.TokenizeCode(istr, 0);
// Expected result.. // Expected result..
@ -85,9 +88,9 @@ private:
std::string filedata(10000,'a'); std::string filedata(10000,'a');
// tokenize.. // tokenize..
Tokenizer tokenizer; Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" ); tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata); std::istringstream istr(filedata);
tokenizer.TokenizeCode(istr, 0); tokenizer.TokenizeCode(istr, 0);
// Expected result.. // Expected result..
@ -109,9 +112,9 @@ private:
"}\n"; "}\n";
// tokenize.. // tokenize..
Tokenizer tokenizer; Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" ); tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata); std::istringstream istr(filedata);
tokenizer.TokenizeCode(istr, 0); tokenizer.TokenizeCode(istr, 0);
// Expected result.. // Expected result..
@ -131,6 +134,27 @@ private:
tokenizer.DeallocateTokens(); tokenizer.DeallocateTokens();
} }
void dupfuncname()
{
const char code[] = "void a()\n"
"{ }\n"
"void a(int i)\n"
"{ }\n"
"void b()\n"
"{ }\n";
// tokenize..
Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(code);
tokenizer.TokenizeCode(istr, 0);
tokenizer.FillFunctionList(0);
ASSERT_EQUALS( 1, tokenizer.FunctionList.size() );
ASSERT_EQUALS( std::string("b"), tokenizer.FunctionList[0]->str );
}
}; };
REGISTER_TEST( TestTokenizer ) REGISTER_TEST( TestTokenizer )

View File

@ -1,4 +1,4 @@
/* /*
* c++check - c/c++ syntax checking * c++check - c/c++ syntax checking
* Copyright (C) 2007 Daniel Marjamäki * Copyright (C) 2007 Daniel Marjamäki
* *
@ -27,7 +27,7 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <stdlib.h> // <- strtoul #include <stdlib.h> // <- strtoul
@ -42,41 +42,41 @@
#define _strdup(str) strdup(str) #define _strdup(str) strdup(str)
#endif #endif
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
Tokenizer::Tokenizer() Tokenizer::Tokenizer()
{ {
_tokens = 0; _tokens = 0;
tokens_back = 0; tokens_back = 0;
dsymlist = 0; dsymlist = 0;
} }
Tokenizer::~Tokenizer() Tokenizer::~Tokenizer()
{ {
}
//---------------------------------------------------------------------------
// Helper functions..
TOKEN *Tokenizer::_gettok(TOKEN *tok, int index)
{
while (tok && index>0)
{
tok = tok->next;
index--;
}
return tok;
}
//---------------------------------------------------------------------------
const TOKEN *Tokenizer::tokens() const
{
return _tokens;
} }
//---------------------------------------------------------------------------
// Helper functions..
TOKEN *Tokenizer::_gettok(TOKEN *tok, int index)
{
while (tok && index>0)
{
tok = tok->next;
index--;
}
return tok;
}
//---------------------------------------------------------------------------
const TOKEN *Tokenizer::tokens() const
{
return _tokens;
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Defined symbols. // Defined symbols.
@ -84,11 +84,11 @@ const TOKEN *Tokenizer::tokens() const
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
std::vector<std::string> *Tokenizer::getFiles() std::vector<std::string> *Tokenizer::getFiles()
{ {
return &Files; return &Files;
} }
void Tokenizer::Define(const char Name[], const char Value[]) void Tokenizer::Define(const char Name[], const char Value[])
{ {
@ -755,13 +755,13 @@ void Tokenizer::SimplifyTokenList()
DeleteNextToken(tok); DeleteNextToken(tok);
} }
} }
} }
else if (Match(tok, "sizeof ( * %var% )")) else if (Match(tok, "sizeof ( * %var% )"))
{ {
tok->setstr("100"); tok->setstr("100");
for ( int i = 0; i < 4; ++i ) for ( int i = 0; i < 4; ++i )
DeleteNextToken(tok); DeleteNextToken(tok);
} }
} }
@ -1016,23 +1016,23 @@ void Tokenizer::SimplifyTokenList()
} }
} }
} }
} }
// Replace NULL with 0.. // Replace NULL with 0..
for ( TOKEN *tok = _tokens; tok; tok = tok->next ) for ( TOKEN *tok = _tokens; tok; tok = tok->next )
{ {
if ( Match(tok, "NULL") ) if ( Match(tok, "NULL") )
tok->setstr("0"); tok->setstr("0");
} }
// Replace pointer casts of 0.. "(char *)0" => "0" // Replace pointer casts of 0.. "(char *)0" => "0"
for ( TOKEN *tok = _tokens; tok; tok = tok->next ) for ( TOKEN *tok = _tokens; tok; tok = tok->next )
{ {
if ( Match(tok->next, "( %type% * ) 0") || Match(tok->next,"( %type% %type% * ) 0") ) if ( Match(tok->next, "( %type% * ) 0") || Match(tok->next,"( %type% %type% * ) 0") )
{ {
while (!Match(tok->next,"0")) while (!Match(tok->next,"0"))
DeleteNextToken(tok); DeleteNextToken(tok);
} }
} }
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -1111,197 +1111,225 @@ void Tokenizer::DeallocateTokens()
free(dsymlist->value); free(dsymlist->value);
delete dsymlist; delete dsymlist;
dsymlist = next; dsymlist = next;
} }
Files.clear(); Files.clear();
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const TOKEN *Tokenizer::GetFunctionTokenByName( const char funcname[] ) const const TOKEN *Tokenizer::GetFunctionTokenByName( const char funcname[] ) const
{ {
std::list<const TOKEN *>::const_iterator it; for ( unsigned int i = 0; i < FunctionList.size(); ++i )
for ( it = FunctionList.begin(); it != FunctionList.end(); it++ ) {
{ if ( strcmp( FunctionList[i]->str, funcname ) == 0 )
if ( strcmp( (*it)->str, funcname ) == 0 ) {
{ return FunctionList[i];
return *it; }
} }
} return NULL;
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::FillFunctionList(const unsigned int file_id)
void Tokenizer::CheckGlobalFunctionUsage(const std::vector<std::string> &filenames) {
{ FunctionList.clear();
// Iterator for GlobalFunctions
std::list<GlobalFunction>::const_iterator func; std::list<const char *> _usedfunc;
if ( file_id == 0 )
// Iterator for UsedGlobalFunctions {
std::list<GlobalFunction>::const_iterator usedfunc; GlobalFunctions.clear();
}
unsigned int i1 = 0;
unsigned int i2 = 1; bool staticfunc = false;
bool classfunc = false;
// Check that every function in GlobalFunctions are used
for ( func = GlobalFunctions.begin(); func != GlobalFunctions.end(); func++ ) int indentlevel = 0;
{ for ( const TOKEN *tok = _tokens; tok; tok = tok->next )
if ( GlobalFunctions.size() > 100 ) {
{ if ( tok->str[0] == '{' )
++i1; indentlevel++;
if ( i1 > (i2 * GlobalFunctions.size()) / 100 )
{ else if ( tok->str[0] == '}' )
if ( (i2 % 10) == 0 ) indentlevel--;
std::cout << i2 << "%";
else
std::cout << "."; if (indentlevel > 0)
std::cout.flush(); {
++i2; if ( _settings._checkCodingStyle )
} {
} const char *funcname = 0;
const std::string &funcname = func->name(); if ( Match(tok,"%var% (") )
funcname = tok->str;
if ( funcname == "main" || funcname == "WinMain" ) else if ( Match(tok, "= %var% ;") ||
continue; Match(tok, "= %var% ,") )
funcname = tok->next->str;
// Check if this global function is used in any of the other files..
bool UsedOtherFile = false; if ( std::find(_usedfunc.begin(), _usedfunc.end(), funcname) == _usedfunc.end() )
bool UsedAnyFile = false; _usedfunc.push_back( funcname );
for ( usedfunc = UsedGlobalFunctions.begin(); usedfunc != UsedGlobalFunctions.end(); usedfunc++ ) }
{
if ( funcname == usedfunc->name() ) continue;
{ }
UsedAnyFile = true;
if (func->file_id() != usedfunc->file_id()) if (strchr("};", tok->str[0]))
{ staticfunc = classfunc = false;
UsedOtherFile = true;
break; else if ( strcmp( tok->str, "static" ) == 0 )
} staticfunc = true;
}
} else if ( strcmp( tok->str, "::" ) == 0 )
classfunc = true;
if ( ! UsedAnyFile )
{ else if (Match(tok, "%var% ("))
std::ostringstream errmsg; {
errmsg << "[" << filenames[func->file_id()] << "]: " // Check if this is the first token of a function implementation..
<< "The function '" << func->name() << "' is never used."; for ( const TOKEN *tok2 = tok; tok2; tok2 = tok2->next )
ReportErr( errmsg.str() ); {
} if ( tok2->str[0] == ';' )
else if ( ! UsedOtherFile ) {
{ tok = tok2;
std::ostringstream errmsg; break;
errmsg << "[" << filenames[func->file_id()] << "]: " }
<< "The linkage of the function '" << func->name() << "' can be local (static) instead of global";
ReportErr( errmsg.str() ); else if ( tok2->str[0] == '{' )
} {
} break;
}
std::cout << "\n";
} else if ( tok2->str[0] == ')' )
//--------------------------------------------------------------------------- {
if ( Match(tok2, ") {") )
void Tokenizer::settings( const Settings &settings ) {
{ if (_settings._checkCodingStyle && !staticfunc && !classfunc && tok->FileIndex==0)
_settings = settings; 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;
}
}
}
}
// If the FunctionList functions with duplicate names, remove them
// TODO this will need some better handling
for ( unsigned int func1 = 0; func1 < FunctionList.size(); )
{
bool hasDuplicates = false;
for ( unsigned int func2 = func1 + 1; func2 < FunctionList.size(); )
{
if ( strcmp(FunctionList[func1]->str, FunctionList[func2]->str) == 0 )
{
hasDuplicates = true;
FunctionList.erase( FunctionList.begin() + func2 );
}
else
{
++func2;
}
}
if ( ! hasDuplicates )
{
++func1;
}
else
{
FunctionList.erase( FunctionList.begin() + func1 );
}
}
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

@ -1,4 +1,4 @@
/* /*
* c++check - c/c++ syntax checking * c++check - c/c++ syntax checking
* Copyright (C) 2007 Daniel Marjamäki * Copyright (C) 2007 Daniel Marjamäki
* *
@ -22,11 +22,11 @@
#define tokenizeH #define tokenizeH
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <list> #include <list>
#include <string> #include <string>
#include <map> #include <map>
#include <vector> #include <vector>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include "settings.h" #include "settings.h"
class TOKEN class TOKEN
@ -57,95 +57,99 @@ public:
unsigned int FileIndex; unsigned int FileIndex;
unsigned int linenr; unsigned int linenr;
TOKEN *next; TOKEN *next;
}; };
class Tokenizer class Tokenizer
{ {
public: public:
Tokenizer(); Tokenizer();
~Tokenizer(); ~Tokenizer();
void Tokenize(std::istream &code, const char FileName[]); void Tokenize(std::istream &code, const char FileName[]);
// Deallocate lists.. // Deallocate lists..
void DeallocateTokens(); void DeallocateTokens();
// Simplify tokenlist // Simplify tokenlist
// ----------------------------- // -----------------------------
void SimplifyTokenList(); void SimplifyTokenList();
void TokenizeCode(std::istream &code, const unsigned int FileIndex=0); void TokenizeCode(std::istream &code, const unsigned int FileIndex=0);
// Helper functions for handling the tokens list.. // Helper functions for handling the tokens list..
static const TOKEN *findtoken(const TOKEN *tok1, const char *tokenstr[]); static const TOKEN *findtoken(const TOKEN *tok1, const char *tokenstr[]);
static const TOKEN *gettok(const TOKEN *tok, int index); static const TOKEN *gettok(const TOKEN *tok, int index);
static const char *getstr(const TOKEN *tok, int index); static const char *getstr(const TOKEN *tok, int index);
// Return size. // Return size.
int SizeOfType(const char type[]); int SizeOfType(const char type[]);
void initTokens(); void initTokens();
std::vector<std::string> *getFiles(); std::vector<std::string> *getFiles();
void FillFunctionList(const unsigned int file_id); void FillFunctionList(const unsigned int file_id);
const TOKEN *GetFunctionTokenByName( const char funcname[] ) const; const TOKEN *GetFunctionTokenByName( const char funcname[] ) const;
void CheckGlobalFunctionUsage(const std::vector<std::string> &filenames); void CheckGlobalFunctionUsage(const std::vector<std::string> &filenames);
void settings( const Settings &settings ); void settings( const Settings &settings );
const TOKEN *tokens() const; const TOKEN *tokens() const;
private:
struct DefineSymbol #ifndef UNIT_TESTING
{ private:
char *name; #endif
char *value;
struct DefineSymbol *next; struct DefineSymbol
}; {
char *name;
class GlobalFunction char *value;
{ struct DefineSymbol *next;
private: };
unsigned int _FileId;
std::string _FuncName; class GlobalFunction
{
public: private:
GlobalFunction( const unsigned int FileId, const char FuncName[] ) unsigned int _FileId;
{ std::string _FuncName;
_FileId = FileId;
_FuncName = FuncName; public:
} GlobalFunction( const unsigned int FileId, const char FuncName[] )
{
unsigned int file_id() const { return _FileId; } _FileId = FileId;
const std::string &name() const { return _FuncName; } _FuncName = FuncName;
}; }
void Define(const char Name[], const char Value[]); unsigned int file_id() const { return _FileId; }
const std::string &name() const { return _FuncName; }
void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno); };
void combine_2tokens(TOKEN *tok, const char str1[], const char str2[]); void Define(const char Name[], const char Value[]);
void DeleteNextToken(TOKEN *tok); void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno);
TOKEN *_gettok(TOKEN *tok, int index); void combine_2tokens(TOKEN *tok, const char str1[], const char str2[]);
void InsertTokens(TOKEN *dest, TOKEN *src, unsigned int n); void DeleteNextToken(TOKEN *tok);
TOKEN *tokens_back; TOKEN *_gettok(TOKEN *tok, int index);
std::map<std::string, unsigned int> TypeSize;
std::list<const TOKEN *> FunctionList; void InsertTokens(TOKEN *dest, TOKEN *src, unsigned int n);
std::list< GlobalFunction > GlobalFunctions;
std::list< GlobalFunction > UsedGlobalFunctions; TOKEN *tokens_back;
std::vector<std::string> Files; std::map<std::string, unsigned int> TypeSize;
Settings _settings; std::vector<const TOKEN *> FunctionList;
std::list< GlobalFunction > GlobalFunctions;
std::list< GlobalFunction > UsedGlobalFunctions;
struct DefineSymbol * dsymlist; std::vector<std::string> Files;
TOKEN *_tokens; Settings _settings;
};
struct DefineSymbol * dsymlist;
TOKEN *_tokens;
};