cppcheck/lib/checkheaders.cpp

270 lines
9.1 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2010 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 <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
#include "checkheaders.h"
#include "tokenize.h"
#include "filelister.h"
#include "token.h"
#include <algorithm>
#include <list>
#include <sstream>
#include <string>
#include <cstring>
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// HEADERS - No implementation in a header
//---------------------------------------------------------------------------
CheckHeaders::CheckHeaders(const Tokenizer *tokenizer, ErrorLogger *errorLogger)
{
_tokenizer = tokenizer;
_errorLogger = errorLogger;
}
CheckHeaders::~CheckHeaders()
{
}
void CheckHeaders::warningHeaderWithImplementation()
{
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
{
// Only interested in included file
if (tok->fileIndex() == 0)
continue;
if (Token::simpleMatch(tok, ") {"))
{
std::ostringstream ostr;
ostr << _tokenizer->fileLine(tok) << ": Found implementation in header";
// TODO, this check is currently not used, but if it is some day
// it should give correct id and severity by calling proper function
// from errorLogger. It should not call reportErr directly.
// std::list<ErrorLogger::ErrorMessage::FileLocation> empty;
// empty.push_back(FileLocation());
// _errorLogger->reportErr(empty, "severity", ostr.str(), "id");
// Goto next file..
unsigned int fileindex = tok->fileIndex();
while (tok->next() && tok->fileIndex() == fileindex)
tok = tok->next();
}
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// HEADERS - Unneeded include
//---------------------------------------------------------------------------
void CheckHeaders::warningIncludeHeader()
{
// Including..
for (const Token *includetok = _tokenizer->tokens(); includetok; includetok = includetok->next())
{
if (includetok->str() != "#include")
continue;
// Get fileindex of included file..
unsigned int hfile = 0;
const std::string includefile = includetok->strAt(1);
while (hfile < _tokenizer->getFiles()->size())
{
if (getFileLister()->sameFileName(_tokenizer->getFiles()->at(hfile), includefile))
break;
++hfile;
}
if (hfile == _tokenizer->getFiles()->size())
continue;
// This header is needed if:
// * It contains some needed class declaration
// * It contains some needed function declaration
// * It contains some needed constant value
// * It contains some needed variable
// * It contains some needed enum
std::list<std::string> classlist;
std::list<std::string> namelist;
// Extract classes and names in the header..
int indentlevel = 0;
for (const Token *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next())
{
if (tok1->fileIndex() != hfile)
continue;
// I'm only interested in stuff that is declared at indentlevel 0
if (tok1->str() == "{")
++indentlevel;
else if (tok1->str() == "}")
--indentlevel;
if (indentlevel != 0)
continue;
// Class or namespace declaration..
// --------------------------------------
if (Token::Match(tok1, "class %var% {") || Token::Match(tok1, "class %var% :") || Token::Match(tok1, "namespace %var% {"))
classlist.push_back(tok1->strAt(1));
// Variable declaration..
// --------------------------------------
else if (Token::Match(tok1, "%type% %var% ;") || Token::Match(tok1, "%type% %var% ["))
namelist.push_back(tok1->strAt(1));
else if (Token::Match(tok1, "%type% * %var% ;") || Token::Match(tok1, "%type% * %var% ["))
namelist.push_back(tok1->strAt(2));
else if (Token::Match(tok1, "const %type% %var% =") || Token::Match(tok1, "const %type% %var% ["))
namelist.push_back(tok1->strAt(2));
else if (Token::Match(tok1, "const %type% * %var% =") || Token::Match(tok1, "const %type% * %var% ["))
namelist.push_back(tok1->strAt(3));
// enum..
// --------------------------------------
else if (tok1->str() == "enum")
{
tok1 = tok1->next();
while (! Token::Match(tok1, "; %any%"))
{
if (tok1->isName())
namelist.push_back(tok1->str());
tok1 = tok1->next();
}
}
// function..
// --------------------------------------
else if (Token::Match(tok1, "%type% %var% ("))
namelist.push_back(tok1->strAt(1));
else if (Token::Match(tok1, "%type% * %var% ("))
namelist.push_back(tok1->strAt(2));
else if (Token::Match(tok1, "const %type% %var% ("))
namelist.push_back(tok1->strAt(2));
else if (Token::Match(tok1, "const %type% * %var% ("))
namelist.push_back(tok1->strAt(3));
// typedef..
// --------------------------------------
else if (tok1->str() == "typedef")
{
if (tok1->strAt(1) == "enum")
continue;
int parlevel = 0;
while (tok1->next())
{
if (Token::Match(tok1, "[({]"))
++parlevel;
else if (Token::Match(tok1, "[)}]"))
--parlevel;
else if (parlevel == 0)
{
if (tok1->str() == ";")
break;
if (Token::Match(tok1, "%var% ;"))
namelist.push_back(tok1->str());
}
tok1 = tok1->next();
}
}
}
// Check if the extracted names are used...
bool Needed = false;
bool NeedDeclaration = false;
for (const Token *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next())
{
if (tok1->fileIndex() != includetok->fileIndex())
continue;
if (Token::Match(tok1, ": %var% {") || Token::Match(tok1, ": %type% %var% {"))
{
const std::string classname = tok1->strAt(((tok1->strAt(2) != "{")) ? 2 : 1);
if (std::find(classlist.begin(), classlist.end(), classname) != classlist.end())
{
Needed = true;
break;
}
}
if (! tok1->isName())
continue;
if (std::find(namelist.begin(), namelist.end(), tok1->str().c_str()) != namelist.end())
{
Needed = true;
break;
}
if (! NeedDeclaration)
NeedDeclaration = (std::find(classlist.begin(), classlist.end(), tok1->str().c_str()) != classlist.end());
}
// Not a header file?
if (includetok->fileIndex() == 0)
Needed |= NeedDeclaration;
// Not needed!
if (!Needed)
{
std::ostringstream ostr;
ostr << _tokenizer->fileLine(includetok) << ": The included header '" << includefile << "' is not needed";
if (NeedDeclaration)
ostr << " (but a forward declaration is needed)";
// TODO, this check is currently not used, but if it is some day
// it should give correct id and severity by calling proper function
// from errorLogger. It should not call reportErr directly.
// std::list<FileLocation> empty;
// empty.push_back(FileLocation());
// _errorLogger->reportErr(empty, "severity", ostr.str(), "id"); // TODO
}
}
}
//---------------------------------------------------------------------------