cppcheck/lib/checkunusedfunctions.cpp

288 lines
11 KiB
C++
Raw Normal View History

2008-12-18 22:28:57 +01:00
/*
* Cppcheck - A tool for static C/C++ code analysis
2014-02-15 07:45:39 +01:00
* Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team.
2008-12-18 22:28:57 +01:00
*
* 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/>.
2008-12-18 22:28:57 +01:00
*/
//---------------------------------------------------------------------------
#include "checkunusedfunctions.h"
2008-12-18 22:28:57 +01:00
#include "tokenize.h"
#include "token.h"
2011-10-30 09:53:01 +01:00
#include <cctype>
2008-12-18 22:28:57 +01:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// FUNCTION USAGE - Check for unused functions etc
//---------------------------------------------------------------------------
2013-10-20 14:09:10 +02:00
void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings)
2008-12-18 22:28:57 +01:00
{
// Function declarations..
2011-10-13 20:53:06 +02:00
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->fileIndex() != 0)
2008-12-18 22:28:57 +01:00
continue;
// token contains a ':' => skip to next ; or {
2011-10-13 20:53:06 +02:00
if (tok->str().find(":") != std::string::npos) {
while (tok && tok->str().find_first_of(";{"))
tok = tok->next();
if (tok)
continue;
break;
}
// If this is a template function, skip it
if (tok->previous() && tok->previous()->str() == ">")
continue;
const Token *funcname = 0;
2008-12-18 22:28:57 +01:00
if (Token::Match(tok, "%type% %var% ("))
funcname = tok->next();
else if (Token::Match(tok, "%type% *|& %var% ("))
2008-12-18 22:28:57 +01:00
funcname = tok->tokAt(2);
else if (Token::Match(tok, "%type% :: %var% (") && !Token::Match(tok, tok->strAt(2).c_str()))
2008-12-18 22:28:57 +01:00
funcname = tok->tokAt(2);
// Don't assume throw as a function name: void foo() throw () {}
if (Token::Match(tok->previous(), ")|const") || funcname == 0)
continue;
// Don't warn about functions that are marked by __attribute__((constructor))
if (tok->isAttributeConstructor() || funcname->isAttributeConstructor())
continue;
tok = funcname->linkAt(1);
2008-12-18 22:28:57 +01:00
// Check that ") {" is found..
if (! Token::Match(tok, ") const| {") &&
! Token::Match(tok, ") const| throw ( ) {"))
funcname = 0;
2008-12-18 22:28:57 +01:00
2011-10-13 20:53:06 +02:00
if (funcname) {
FunctionUsage &func = _functions[ funcname->str()];
2008-12-18 22:28:57 +01:00
2011-09-18 18:58:28 +02:00
if (!func.lineNumber)
func.lineNumber = funcname->linenr();
2008-12-18 22:28:57 +01:00
// No filename set yet..
2011-10-13 20:53:06 +02:00
if (func.filename.empty()) {
func.filename = tokenizer.getSourceFilePath();
}
2008-12-18 22:28:57 +01:00
// Multiple files => filename = "+"
else if (func.filename != tokenizer.getSourceFilePath()) {
//func.filename = "+";
2008-12-18 22:28:57 +01:00
func.usedOtherFile |= func.usedSameFile;
}
}
}
// Function usage..
const Token *scopeEnd = NULL;
2011-10-13 20:53:06 +02:00
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
2013-10-20 14:09:10 +02:00
// parsing of library code to find called functions
if (settings->library.isexecutableblock(FileName, tok->str())) {
2014-01-02 18:18:24 +01:00
const Token * markupVarToken = tok->tokAt(settings->library.blockstartoffset(FileName));
2013-10-20 14:09:10 +02:00
int scope = 1;
// find all function calls in library code (starts with '(', not if or while etc)
while (scope) {
2014-01-02 18:18:24 +01:00
if (markupVarToken->str() == settings->library.blockstart(FileName)) {
2013-10-20 14:09:10 +02:00
scope++;
2014-01-02 18:18:24 +01:00
} else if (markupVarToken->str() == settings->library.blockend(FileName))
2013-10-20 14:09:10 +02:00
scope--;
2014-01-02 18:18:24 +01:00
else if (!settings->library.iskeyword(FileName, markupVarToken->str())) {
if (_functions.find(markupVarToken->str()) != _functions.end())
_functions[markupVarToken->str()].usedOtherFile = true;
2014-01-02 21:47:35 +01:00
else if (markupVarToken->next()->str() == "(") {
2014-01-02 18:18:24 +01:00
FunctionUsage &func = _functions[markupVarToken->str()];
func.filename = tokenizer.getSourceFilePath();
if (func.filename.empty() || func.filename == "+")
func.usedOtherFile = true;
else
func.usedSameFile = true;
}
2013-10-20 14:09:10 +02:00
}
2014-01-02 18:18:24 +01:00
markupVarToken = markupVarToken->next();
2013-10-20 14:09:10 +02:00
}
}
if (!settings->library.markupFile(FileName) // only check source files
2013-10-20 14:09:10 +02:00
&& settings->library.isexporter(tok->str()) && tok->next() != 0) {
const Token * qPropToken = tok;
qPropToken = qPropToken->next();
while (qPropToken && qPropToken->str() != ")") {
if (settings->library.isexportedprefix(tok->str(), qPropToken->str())) {
const Token* qNextPropToken = qPropToken->next();
const std::string value = qNextPropToken->str();
if (_functions.find(value) != _functions.end()) {
_functions[value].usedOtherFile = true;
}
}
if (settings->library.isexportedsuffix(tok->str(), qPropToken->str())) {
const Token* qNextPropToken = qPropToken->previous();
const std::string value = qNextPropToken->str();
if (value != ")" && _functions.find(value) != _functions.end()) {
_functions[value].usedOtherFile = true;
}
}
qPropToken = qPropToken->next();
}
}
if (settings->library.markupFile(FileName)
2013-10-20 14:09:10 +02:00
&& settings->library.isimporter(FileName, tok->str()) && tok->next()) {
const Token * qPropToken = tok;
qPropToken = qPropToken->next();
if (qPropToken->next()) {
qPropToken = qPropToken->next();
while (qPropToken && qPropToken->str() != ")") {
const std::string value = qPropToken->str();
if (!value.empty()) {
_functions[value].usedOtherFile = true;
break;
}
qPropToken = qPropToken->next();
}
}
}
if (settings->library.isreflection(FileName, tok->str())) {
const int index = settings->library.reflectionArgument(FileName, tok->str());
if (index >= 0) {
const Token * funcToken = tok->tokAt(index);
if (funcToken) {
std::string value = funcToken->str();
value = value.substr(1, value.length() - 2);
_functions[value].usedOtherFile = true;
}
}
}
if (scopeEnd == NULL) {
if (!Token::Match(tok, ")|= const| {"))
continue;
scopeEnd = tok;
while (scopeEnd->str() != "{")
scopeEnd = scopeEnd->next();
scopeEnd = scopeEnd->link();
} else if (tok == scopeEnd) {
scopeEnd = NULL;
continue;
}
const Token *funcname = 0;
2011-10-13 20:53:06 +02:00
if (Token::Match(tok->next(), "%var% (")) {
funcname = tok->next();
}
else if (Token::Match(tok->next(), "%var% <") && Token::simpleMatch(tok->linkAt(2), "> (")) {
funcname = tok->next();
}
else if (Token::Match(tok, "[;{}.,()[=+-/&|!?:] %var% [(),;:}]"))
2008-12-18 22:28:57 +01:00
funcname = tok->next();
else if (Token::Match(tok, "[=(,] &| %var% :: %var%")) {
funcname = tok->next();
if (funcname->str() == "&")
funcname = funcname->next();
while (Token::Match(funcname,"%var% :: %var%"))
funcname = funcname->tokAt(2);
if (!Token::Match(funcname, "%var% [,);]"))
continue;
}
2009-06-05 15:02:26 +02:00
else
continue;
// funcname ( => Assert that the end parentheses isn't followed by {
if (Token::Match(funcname, "%var% (|<")) {
const Token *ftok = funcname->next();
if (ftok->str() == "<")
ftok = ftok->link();
if (Token::Match(ftok->linkAt(1), ") const|throw|{"))
funcname = NULL;
2008-12-18 22:28:57 +01:00
}
2011-10-13 20:53:06 +02:00
if (funcname) {
FunctionUsage &func = _functions[ funcname->str()];
2008-12-18 22:28:57 +01:00
if (func.filename.empty() || func.filename == "+")
2008-12-18 22:28:57 +01:00
func.usedOtherFile = true;
else
func.usedSameFile = true;
}
}
}
2010-07-08 13:01:53 +02:00
void CheckUnusedFunctions::check(ErrorLogger * const errorLogger)
2008-12-18 22:28:57 +01:00
{
2011-10-13 20:53:06 +02:00
for (std::map<std::string, FunctionUsage>::const_iterator it = _functions.begin(); it != _functions.end(); ++it) {
2008-12-18 22:28:57 +01:00
const FunctionUsage &func = it->second;
if (func.usedOtherFile || func.filename.empty())
2008-12-18 22:28:57 +01:00
continue;
if (it->first == "main" ||
it->first == "WinMain" ||
it->first == "_tmain" ||
it->first == "if" ||
(it->first.compare(0, 8, "operator") == 0 && it->first.size() > 8 && !std::isalnum(it->first[8])))
continue;
2011-10-13 20:53:06 +02:00
if (! func.usedSameFile) {
2009-01-11 07:44:32 +01:00
std::string filename;
if (func.filename == "+")
2009-01-11 07:44:32 +01:00
filename = "";
else
filename = func.filename;
unusedFunctionError(errorLogger, filename, func.lineNumber, it->first);
2011-10-13 20:53:06 +02:00
} else if (! func.usedOtherFile) {
/** @todo add error message "function is only used in <file> it can be static" */
/*
2008-12-18 22:28:57 +01:00
std::ostringstream errmsg;
errmsg << "The function '" << it->first << "' is only used in the file it was declared in so it should have local linkage.";
_errorLogger->reportErr( errmsg.str() );
*/
}
}
}
void CheckUnusedFunctions::unusedFunctionError(ErrorLogger * const errorLogger,
2011-09-18 18:58:28 +02:00
const std::string &filename, unsigned int lineNumber,
const std::string &funcname)
{
2010-07-08 13:01:53 +02:00
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
2011-10-13 20:53:06 +02:00
if (!filename.empty()) {
2010-07-08 13:01:53 +02:00
ErrorLogger::ErrorMessage::FileLocation fileLoc;
fileLoc.setfile(filename);
fileLoc.line = lineNumber;
2010-07-08 13:01:53 +02:00
locationList.push_back(fileLoc);
}
const ErrorLogger::ErrorMessage errmsg(locationList, Severity::style, "The function '" + funcname + "' is never used.", "unusedFunction", false);
2010-07-08 13:01:53 +02:00
if (errorLogger)
errorLogger->reportErr(errmsg);
else
reportError(errmsg);
}