2008-12-18 22:28:57 +01:00
|
|
|
/*
|
2009-01-21 21:04:20 +01:00
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2015-01-03 12:14:58 +01:00
|
|
|
* Copyright (C) 2007-2015 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
|
2009-09-27 17:08:31 +02:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2008-12-18 22:28:57 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
2009-06-07 22:12:20 +02:00
|
|
|
#include "checkunusedfunctions.h"
|
2008-12-18 22:28:57 +01:00
|
|
|
#include "tokenize.h"
|
2009-07-13 19:11:31 +02:00
|
|
|
#include "token.h"
|
2014-03-26 08:54:56 +01:00
|
|
|
#include "symboldatabase.h"
|
2011-10-30 09:53:01 +01:00
|
|
|
#include <cctype>
|
2008-12-18 22:28:57 +01:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2014-11-24 06:37:08 +01:00
|
|
|
|
|
|
|
|
2014-04-13 11:51:29 +02:00
|
|
|
// Register this check class
|
2014-11-24 06:37:08 +01:00
|
|
|
CheckUnusedFunctions CheckUnusedFunctions::instance;
|
|
|
|
|
2008-12-18 22:28:57 +01:00
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// FUNCTION USAGE - Check for unused functions etc
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2014-11-24 06:37:08 +01:00
|
|
|
void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings)
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2014-11-24 06:37:08 +01:00
|
|
|
const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase();
|
2009-06-14 07:58:36 +02:00
|
|
|
|
2014-03-26 08:54:56 +01:00
|
|
|
// Function declarations..
|
|
|
|
for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); i++) {
|
|
|
|
const Scope* scope = symbolDatabase->functionScopes[i];
|
|
|
|
const Function* func = scope->function;
|
|
|
|
if (!func || !func->token || scope->classStart->fileIndex() != 0)
|
2012-04-09 11:00:31 +02:00
|
|
|
continue;
|
|
|
|
|
2014-03-14 18:17:21 +01:00
|
|
|
// Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor))
|
2014-03-26 08:54:56 +01:00
|
|
|
if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction)
|
2013-08-30 06:27:46 +02:00
|
|
|
continue;
|
|
|
|
|
2014-03-26 08:54:56 +01:00
|
|
|
// Don't care about templates
|
|
|
|
if (func->retDef->str() == "template")
|
|
|
|
continue;
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2014-11-24 06:37:08 +01:00
|
|
|
FunctionUsage &usage = _functions[func->name()];
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2014-03-26 08:54:56 +01:00
|
|
|
if (!usage.lineNumber)
|
|
|
|
usage.lineNumber = func->token->linenr();
|
2011-09-18 07:13:39 +02:00
|
|
|
|
2014-03-26 08:54:56 +01:00
|
|
|
// No filename set yet..
|
|
|
|
if (usage.filename.empty()) {
|
2014-11-24 06:37:08 +01:00
|
|
|
usage.filename = tokenizer.list.getSourceFilePath();
|
2014-03-26 08:54:56 +01:00
|
|
|
}
|
|
|
|
// Multiple files => filename = "+"
|
2014-11-24 06:37:08 +01:00
|
|
|
else if (usage.filename != tokenizer.list.getSourceFilePath()) {
|
2014-03-26 08:54:56 +01:00
|
|
|
//func.filename = "+";
|
|
|
|
usage.usedOtherFile |= usage.usedSameFile;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function usage..
|
2014-11-24 06:37:08 +01: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));
|
2014-03-11 15:57:28 +01:00
|
|
|
int scope = 0;
|
|
|
|
bool start = true;
|
2013-10-20 14:09:10 +02:00
|
|
|
// find all function calls in library code (starts with '(', not if or while etc)
|
2014-03-11 15:57:28 +01:00
|
|
|
while (scope || start) {
|
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-03-11 15:57:28 +01:00
|
|
|
if (start) {
|
|
|
|
start = false;
|
|
|
|
}
|
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())) {
|
2014-11-24 06:37:08 +01:00
|
|
|
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-11-24 06:37:08 +01:00
|
|
|
FunctionUsage &func = _functions[markupVarToken->str()];
|
|
|
|
func.filename = tokenizer.list.getSourceFilePath();
|
2014-01-02 18:18:24 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-31 19:09:01 +01: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) {
|
2014-09-17 13:52:03 +02:00
|
|
|
const Token * propToken = tok->next();
|
|
|
|
while (propToken && propToken->str() != ")") {
|
|
|
|
if (settings->library.isexportedprefix(tok->str(), propToken->str())) {
|
|
|
|
const Token* nextPropToken = propToken->next();
|
|
|
|
const std::string& value = nextPropToken->str();
|
2014-11-24 06:37:08 +01:00
|
|
|
if (_functions.find(value) != _functions.end()) {
|
|
|
|
_functions[value].usedOtherFile = true;
|
2013-10-20 14:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
2014-09-17 13:52:03 +02:00
|
|
|
if (settings->library.isexportedsuffix(tok->str(), propToken->str())) {
|
|
|
|
const Token* prevPropToken = propToken->previous();
|
|
|
|
const std::string& value = prevPropToken->str();
|
2014-11-24 06:37:08 +01:00
|
|
|
if (value != ")" && _functions.find(value) != _functions.end()) {
|
|
|
|
_functions[value].usedOtherFile = true;
|
2013-10-20 14:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
2014-09-17 13:52:03 +02:00
|
|
|
propToken = propToken->next();
|
2013-10-20 14:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-31 19:09:01 +01:00
|
|
|
if (settings->library.markupFile(FileName)
|
2013-10-20 14:09:10 +02:00
|
|
|
&& settings->library.isimporter(FileName, tok->str()) && tok->next()) {
|
2014-09-17 13:52:03 +02:00
|
|
|
const Token * propToken = tok->next();
|
|
|
|
if (propToken->next()) {
|
|
|
|
propToken = propToken->next();
|
|
|
|
while (propToken && propToken->str() != ")") {
|
|
|
|
const std::string& value = propToken->str();
|
2013-10-20 14:09:10 +02:00
|
|
|
if (!value.empty()) {
|
2014-11-24 06:37:08 +01:00
|
|
|
_functions[value].usedOtherFile = true;
|
2013-10-20 14:09:10 +02:00
|
|
|
break;
|
|
|
|
}
|
2014-09-17 13:52:03 +02:00
|
|
|
propToken = propToken->next();
|
2013-10-20 14:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-11 15:57:28 +01:00
|
|
|
if (settings->library.isreflection(tok->str())) {
|
2014-09-17 13:52:03 +02:00
|
|
|
const int argIndex = settings->library.reflectionArgument(tok->str());
|
|
|
|
if (argIndex >= 0) {
|
2014-03-13 17:43:25 +01:00
|
|
|
const Token * funcToken = tok->next();
|
2014-09-17 13:52:03 +02:00
|
|
|
int index = 0;
|
2014-03-13 17:43:25 +01:00
|
|
|
std::string value;
|
2014-03-14 15:51:15 +01:00
|
|
|
while (funcToken) {
|
2014-03-13 17:43:25 +01:00
|
|
|
if (funcToken->str()==",") {
|
2014-09-17 13:52:03 +02:00
|
|
|
if (++index == argIndex)
|
2014-03-13 17:43:25 +01:00
|
|
|
break;
|
|
|
|
value = "";
|
2014-03-13 17:47:00 +01:00
|
|
|
} else
|
2014-03-13 17:43:25 +01:00
|
|
|
value += funcToken->str();
|
|
|
|
funcToken = funcToken->next();
|
|
|
|
}
|
2014-09-17 13:52:03 +02:00
|
|
|
if (index == argIndex) {
|
2013-10-20 14:09:10 +02:00
|
|
|
value = value.substr(1, value.length() - 2);
|
2014-11-24 06:37:08 +01:00
|
|
|
_functions[value].usedOtherFile = true;
|
2013-10-20 14:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-16 11:47:52 +01:00
|
|
|
const Token *funcname = nullptr;
|
2009-01-03 21:29:20 +01:00
|
|
|
|
2014-12-09 22:04:51 +01:00
|
|
|
if (tok->scope()->isExecutable() && Token::Match(tok, "%var% (")) {
|
|
|
|
funcname = tok;
|
|
|
|
} else if (tok->scope()->isExecutable() && Token::Match(tok, "%var% <") && Token::simpleMatch(tok->linkAt(1), "> (")) {
|
|
|
|
funcname = tok;
|
|
|
|
} else if (Token::Match(tok, "[;{}.,()[=+-/|!?:]")) {
|
2009-02-27 17:36:37 +01:00
|
|
|
funcname = tok->next();
|
2014-12-09 22:04:51 +01:00
|
|
|
if (funcname && funcname->str() == "&")
|
2014-03-26 09:12:41 +01:00
|
|
|
funcname = funcname->next();
|
2014-12-09 22:04:51 +01:00
|
|
|
if (funcname && funcname->str() == "::")
|
2012-06-14 22:04:21 +02:00
|
|
|
funcname = funcname->next();
|
2014-12-09 22:04:51 +01:00
|
|
|
while (Token::Match(funcname, "%var% :: %var%"))
|
2012-06-14 22:04:21 +02:00
|
|
|
funcname = funcname->tokAt(2);
|
2014-12-09 22:04:51 +01:00
|
|
|
|
|
|
|
if (!Token::Match(funcname, "%var% [(),;]:}]"))
|
2012-06-14 22:04:21 +02:00
|
|
|
continue;
|
|
|
|
}
|
2009-05-31 08:01:16 +02:00
|
|
|
|
2014-12-09 22:04:51 +01:00
|
|
|
if (!funcname)
|
2009-06-05 15:02:26 +02:00
|
|
|
continue;
|
|
|
|
|
2013-01-16 15:37:07 +01:00
|
|
|
// funcname ( => Assert that the end parentheses isn't followed by {
|
2013-08-23 06:47:52 +02:00
|
|
|
if (Token::Match(funcname, "%var% (|<")) {
|
|
|
|
const Token *ftok = funcname->next();
|
|
|
|
if (ftok->str() == "<")
|
|
|
|
ftok = ftok->link();
|
|
|
|
if (Token::Match(ftok->linkAt(1), ") const|throw|{"))
|
2014-02-16 10:32:10 +01:00
|
|
|
funcname = nullptr;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (funcname) {
|
2014-11-24 06:37:08 +01:00
|
|
|
FunctionUsage &func = _functions[ funcname->str()];
|
2015-01-07 08:32:49 +01:00
|
|
|
const std::string called_from_file = tokenizer.list.getSourceFilePath();
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2015-01-07 08:32:49 +01:00
|
|
|
if (func.filename.empty() || func.filename == "+" || func.filename != called_from_file)
|
2008-12-18 22:28:57 +01:00
|
|
|
func.usedOtherFile = true;
|
|
|
|
else
|
|
|
|
func.usedSameFile = true;
|
|
|
|
}
|
|
|
|
}
|
2014-11-15 18:44:23 +01:00
|
|
|
}
|
2008-12-18 22:28:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2014-11-15 18:44:23 +01:00
|
|
|
|
2014-11-24 06:37:08 +01:00
|
|
|
void CheckUnusedFunctions::check(ErrorLogger * const errorLogger)
|
|
|
|
{
|
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;
|
2010-04-02 07:30:58 +02:00
|
|
|
if (func.usedOtherFile || func.filename.empty())
|
2008-12-18 22:28:57 +01:00
|
|
|
continue;
|
2011-10-29 20:04:43 +02:00
|
|
|
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])))
|
2009-01-16 17:20:35 +01:00
|
|
|
continue;
|
2011-10-13 20:53:06 +02:00
|
|
|
if (! func.usedSameFile) {
|
2009-01-11 07:44:32 +01:00
|
|
|
std::string filename;
|
2010-04-02 07:30:58 +02:00
|
|
|
if (func.filename == "+")
|
2009-01-11 07:44:32 +01:00
|
|
|
filename = "";
|
|
|
|
else
|
|
|
|
filename = func.filename;
|
2014-11-24 06:37:08 +01:00
|
|
|
unusedFunctionError(errorLogger, filename, func.lineNumber, it->first);
|
2011-10-13 20:53:06 +02:00
|
|
|
} else if (! func.usedOtherFile) {
|
2009-07-14 08:17:12 +02:00
|
|
|
/** @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() );
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-18 07:13:39 +02:00
|
|
|
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-04-04 22:55:28 +02:00
|
|
|
{
|
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;
|
2010-07-17 00:27:40 +02:00
|
|
|
fileLoc.setfile(filename);
|
2011-09-18 07:13:39 +02:00
|
|
|
fileLoc.line = lineNumber;
|
2010-07-08 13:01:53 +02:00
|
|
|
locationList.push_back(fileLoc);
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:40:20 +01:00
|
|
|
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);
|
2010-04-04 22:55:28 +02:00
|
|
|
}
|
2014-12-02 06:41:18 +01:00
|
|
|
|
|
|
|
Check::FileInfo *CheckUnusedFunctions::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const
|
|
|
|
{
|
|
|
|
if (settings->isEnabled("unusedFunction") && settings->_jobs == 1)
|
|
|
|
instance.parseTokens(*tokenizer, tokenizer->list.getFiles().front().c_str(), settings);
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckUnusedFunctions::analyseWholeProgram(const std::list<Check::FileInfo*> &fileInfo, ErrorLogger &errorLogger)
|
|
|
|
{
|
|
|
|
(void)fileInfo;
|
|
|
|
instance.check(&errorLogger);
|
|
|
|
}
|