From 63bc26d6628eed07e9aee959f269605359098350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 20 Nov 2008 19:18:55 +0000 Subject: [PATCH] Function list: Remove functions with duplicate names to prevent false positives --- testtokenize.cpp | 44 +++- tokenize.cpp | 526 +++++++++++++++++++++++++---------------------- tokenize.h | 192 ++++++++--------- 3 files changed, 409 insertions(+), 353 deletions(-) diff --git a/testtokenize.cpp b/testtokenize.cpp index 0444ddc8e..d52f3aa81 100644 --- a/testtokenize.cpp +++ b/testtokenize.cpp @@ -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 ) diff --git a/tokenize.cpp b/tokenize.cpp index 62bb2477d..8f657fc90 100644 --- a/tokenize.cpp +++ b/tokenize.cpp @@ -1,4 +1,4 @@ -/* +/* * c++check - c/c++ syntax checking * Copyright (C) 2007 Daniel Marjamäki * @@ -27,7 +27,7 @@ #include -#include +#include #include #include #include // <- 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 *Tokenizer::getFiles() -{ - return &Files; -} + +std::vector *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_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 _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_iterator it = _usedfunc.begin(); it != _usedfunc.end(); ++it) - { - if ( *it != 0 ) - { - UsedGlobalFunctions.push_back( GlobalFunction(file_id, *it) ); - } - } -} - -//-------------------------------------------------------------------------- - -void Tokenizer::CheckGlobalFunctionUsage(const std::vector &filenames) -{ - // Iterator for GlobalFunctions - std::list::const_iterator func; - - // Iterator for UsedGlobalFunctions - std::list::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 _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_iterator it = _usedfunc.begin(); it != _usedfunc.end(); ++it) + { + if ( *it != 0 ) + { + UsedGlobalFunctions.push_back( GlobalFunction(file_id, *it) ); + } + } +} + +//-------------------------------------------------------------------------- + + +void Tokenizer::CheckGlobalFunctionUsage(const std::vector &filenames) +{ + // Iterator for GlobalFunctions + std::list::const_iterator func; + + // Iterator for UsedGlobalFunctions + std::list::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; +} diff --git a/tokenize.h b/tokenize.h index 19508fbef..eccc839b5 100644 --- a/tokenize.h +++ b/tokenize.h @@ -1,4 +1,4 @@ -/* +/* * c++check - c/c++ syntax checking * Copyright (C) 2007 Daniel Marjamäki * @@ -22,11 +22,11 @@ #define tokenizeH //--------------------------------------------------------------------------- #include -#include +#include #include -#include -#include -#include +#include +#include +#include #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 *getFiles(); - - - - void FillFunctionList(const unsigned int file_id); - const TOKEN *GetFunctionTokenByName( const char funcname[] ) const; - void CheckGlobalFunctionUsage(const std::vector &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 TypeSize; - std::list FunctionList; - std::list< GlobalFunction > GlobalFunctions; - std::list< GlobalFunction > UsedGlobalFunctions; - std::vector 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 *getFiles(); + + + + void FillFunctionList(const unsigned int file_id); + const TOKEN *GetFunctionTokenByName( const char funcname[] ) const; + void CheckGlobalFunctionUsage(const std::vector &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 TypeSize; + std::vector FunctionList; + std::list< GlobalFunction > GlobalFunctions; + std::list< GlobalFunction > UsedGlobalFunctions; + std::vector Files; + Settings _settings; + + + struct DefineSymbol * dsymlist; + TOKEN *_tokens; +}; +