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
* Copyright (C) 2007 Daniel Marjamäki
*
@ -22,6 +22,7 @@
#include "testsuite.h"
#define UNIT_TESTING // Get access to "private" data in Tokenizer
#include "tokenize.h"
class TestTokenizer : public TestFixture
@ -38,6 +39,8 @@ private:
TEST_CASE( longtok );
TEST_CASE( inlineasm );
TEST_CASE( dupfuncname );
}
@ -59,9 +62,9 @@ private:
" \"def\"\n";
// tokenize..
Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata);
Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata);
tokenizer.TokenizeCode(istr, 0);
// Expected result..
@ -85,9 +88,9 @@ private:
std::string filedata(10000,'a');
// tokenize..
Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata);
Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata);
tokenizer.TokenizeCode(istr, 0);
// Expected result..
@ -109,9 +112,9 @@ private:
"}\n";
// tokenize..
Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata);
Tokenizer tokenizer;
tokenizer.getFiles()->push_back( "test.cpp" );
std::istringstream istr(filedata);
tokenizer.TokenizeCode(istr, 0);
// Expected result..
@ -131,6 +134,27 @@ private:
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 )

View File

@ -1,4 +1,4 @@
/*
/*
* c++check - c/c++ syntax checking
* Copyright (C) 2007 Daniel Marjamäki
*
@ -27,7 +27,7 @@
#include <string>
#include <cstring>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <stdlib.h> // <- strtoul
@ -42,41 +42,41 @@
#define _strdup(str) strdup(str)
#endif
//---------------------------------------------------------------------------
Tokenizer::Tokenizer()
{
_tokens = 0;
tokens_back = 0;
dsymlist = 0;
Tokenizer::Tokenizer()
{
_tokens = 0;
tokens_back = 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.
@ -84,11 +84,11 @@ const TOKEN *Tokenizer::tokens() const
//---------------------------------------------------------------------------
std::vector<std::string> *Tokenizer::getFiles()
{
return &Files;
}
std::vector<std::string> *Tokenizer::getFiles()
{
return &Files;
}
void Tokenizer::Define(const char Name[], const char Value[])
{
@ -755,13 +755,13 @@ void Tokenizer::SimplifyTokenList()
DeleteNextToken(tok);
}
}
}
else if (Match(tok, "sizeof ( * %var% )"))
{
tok->setstr("100");
for ( int i = 0; i < 4; ++i )
DeleteNextToken(tok);
}
else if (Match(tok, "sizeof ( * %var% )"))
{
tok->setstr("100");
for ( int i = 0; i < 4; ++i )
DeleteNextToken(tok);
}
}
@ -1016,23 +1016,23 @@ void Tokenizer::SimplifyTokenList()
}
}
}
}
// Replace NULL with 0..
for ( TOKEN *tok = _tokens; tok; tok = tok->next )
{
if ( Match(tok, "NULL") )
tok->setstr("0");
}
// Replace pointer casts of 0.. "(char *)0" => "0"
for ( TOKEN *tok = _tokens; tok; tok = tok->next )
{
if ( Match(tok->next, "( %type% * ) 0") || Match(tok->next,"( %type% %type% * ) 0") )
{
while (!Match(tok->next,"0"))
DeleteNextToken(tok);
}
}
// Replace NULL with 0..
for ( TOKEN *tok = _tokens; tok; tok = tok->next )
{
if ( Match(tok, "NULL") )
tok->setstr("0");
}
// Replace pointer casts of 0.. "(char *)0" => "0"
for ( TOKEN *tok = _tokens; tok; tok = tok->next )
{
if ( Match(tok->next, "( %type% * ) 0") || Match(tok->next,"( %type% %type% * ) 0") )
{
while (!Match(tok->next,"0"))
DeleteNextToken(tok);
}
}
}
//---------------------------------------------------------------------------
@ -1111,197 +1111,225 @@ void Tokenizer::DeallocateTokens()
free(dsymlist->value);
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;
//---------------------------------------------------------------------------
const TOKEN *Tokenizer::GetFunctionTokenByName( const char funcname[] ) const
{
for ( unsigned int i = 0; i < FunctionList.size(); ++i )
{
if ( strcmp( FunctionList[i]->str, funcname ) == 0 )
{
return FunctionList[i];
}
}
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;
}
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;
}
}
}
}
// 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
* Copyright (C) 2007 Daniel Marjamäki
*
@ -22,11 +22,11 @@
#define tokenizeH
//---------------------------------------------------------------------------
#include <list>
#include <string>
#include <string>
#include <map>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <cstdlib>
#include <cstring>
#include "settings.h"
class TOKEN
@ -57,95 +57,99 @@ public:
unsigned int FileIndex;
unsigned int linenr;
TOKEN *next;
};
class Tokenizer
{
public:
Tokenizer();
~Tokenizer();
void Tokenize(std::istream &code, const char FileName[]);
// Deallocate lists..
void DeallocateTokens();
// Simplify tokenlist
// -----------------------------
void SimplifyTokenList();
void TokenizeCode(std::istream &code, const unsigned int FileIndex=0);
// Helper functions for handling the tokens list..
static const TOKEN *findtoken(const TOKEN *tok1, const char *tokenstr[]);
static const TOKEN *gettok(const TOKEN *tok, int index);
static const char *getstr(const TOKEN *tok, int index);
// Return size.
int SizeOfType(const char type[]);
void initTokens();
std::vector<std::string> *getFiles();
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 );
const TOKEN *tokens() const;
private:
struct DefineSymbol
{
char *name;
char *value;
struct DefineSymbol *next;
};
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);
void combine_2tokens(TOKEN *tok, const char str1[], const char str2[]);
void DeleteNextToken(TOKEN *tok);
TOKEN *_gettok(TOKEN *tok, int index);
void InsertTokens(TOKEN *dest, TOKEN *src, unsigned int n);
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;
struct DefineSymbol * dsymlist;
TOKEN *_tokens;
};
};
class Tokenizer
{
public:
Tokenizer();
~Tokenizer();
void Tokenize(std::istream &code, const char FileName[]);
// Deallocate lists..
void DeallocateTokens();
// Simplify tokenlist
// -----------------------------
void SimplifyTokenList();
void TokenizeCode(std::istream &code, const unsigned int FileIndex=0);
// Helper functions for handling the tokens list..
static const TOKEN *findtoken(const TOKEN *tok1, const char *tokenstr[]);
static const TOKEN *gettok(const TOKEN *tok, int index);
static const char *getstr(const TOKEN *tok, int index);
// Return size.
int SizeOfType(const char type[]);
void initTokens();
std::vector<std::string> *getFiles();
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 );
const TOKEN *tokens() const;
#ifndef UNIT_TESTING
private:
#endif
struct DefineSymbol
{
char *name;
char *value;
struct DefineSymbol *next;
};
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);
void combine_2tokens(TOKEN *tok, const char str1[], const char str2[]);
void DeleteNextToken(TOKEN *tok);
TOKEN *_gettok(TOKEN *tok, int index);
void InsertTokens(TOKEN *dest, TOKEN *src, unsigned int n);
TOKEN *tokens_back;
std::map<std::string, unsigned int> TypeSize;
std::vector<const TOKEN *> FunctionList;
std::list< GlobalFunction > GlobalFunctions;
std::list< GlobalFunction > UsedGlobalFunctions;
std::vector<std::string> Files;
Settings _settings;
struct DefineSymbol * dsymlist;
TOKEN *_tokens;
};