/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2014 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 .
*/
//---------------------------------------------------------------------------
#include "checkunusedfunctions.h"
#include "tokenize.h"
#include "token.h"
#include "symboldatabase.h"
#include
//---------------------------------------------------------------------------
// Register this check class
namespace {
CheckUnusedFunctions instance;
}
//---------------------------------------------------------------------------
// FUNCTION USAGE - Check for unused functions etc
//---------------------------------------------------------------------------
Check::FileInfo *CheckUnusedFunctions::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const
{
if (!settings->isEnabled("unusedFunction"))
return nullptr;
const SymbolDatabase* symbolDatabase = tokenizer->getSymbolDatabase();
MyFileInfo *fileInfo = new MyFileInfo;
const std::string FileName = tokenizer->list.getFiles().front();
// 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)
continue;
// Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor))
if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction)
continue;
// Don't care about templates
if (func->retDef->str() == "template")
continue;
FunctionUsage &usage = fileInfo->_functions[func->name()];
if (!usage.lineNumber)
usage.lineNumber = func->token->linenr();
// No filename set yet..
if (usage.filename.empty()) {
usage.filename = tokenizer->list.getSourceFilePath();
}
// Multiple files => filename = "+"
else if (usage.filename != tokenizer->list.getSourceFilePath()) {
//func.filename = "+";
usage.usedOtherFile |= usage.usedSameFile;
}
}
// Function usage..
for (const Token *tok = tokenizer->tokens(); tok; tok = tok->next()) {
// parsing of library code to find called functions
if (settings->library.isexecutableblock(FileName, tok->str())) {
const Token * markupVarToken = tok->tokAt(settings->library.blockstartoffset(FileName));
int scope = 0;
bool start = true;
// find all function calls in library code (starts with '(', not if or while etc)
while (scope || start) {
if (markupVarToken->str() == settings->library.blockstart(FileName)) {
scope++;
if (start) {
start = false;
}
} else if (markupVarToken->str() == settings->library.blockend(FileName))
scope--;
else if (!settings->library.iskeyword(FileName, markupVarToken->str())) {
if (fileInfo->_functions.find(markupVarToken->str()) != fileInfo->_functions.end())
fileInfo->_functions[markupVarToken->str()].usedOtherFile = true;
else if (markupVarToken->next()->str() == "(") {
FunctionUsage &func = fileInfo->_functions[markupVarToken->str()];
func.filename = tokenizer->list.getSourceFilePath();
if (func.filename.empty() || func.filename == "+")
func.usedOtherFile = true;
else
func.usedSameFile = true;
}
}
markupVarToken = markupVarToken->next();
}
}
if (!settings->library.markupFile(FileName) // only check source files
&& settings->library.isexporter(tok->str()) && tok->next() != 0) {
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();
if (fileInfo->_functions.find(value) != fileInfo->_functions.end()) {
fileInfo->_functions[value].usedOtherFile = true;
}
}
if (settings->library.isexportedsuffix(tok->str(), propToken->str())) {
const Token* prevPropToken = propToken->previous();
const std::string& value = prevPropToken->str();
if (value != ")" && fileInfo->_functions.find(value) != fileInfo->_functions.end()) {
fileInfo->_functions[value].usedOtherFile = true;
}
}
propToken = propToken->next();
}
}
if (settings->library.markupFile(FileName)
&& settings->library.isimporter(FileName, tok->str()) && tok->next()) {
const Token * propToken = tok->next();
if (propToken->next()) {
propToken = propToken->next();
while (propToken && propToken->str() != ")") {
const std::string& value = propToken->str();
if (!value.empty()) {
fileInfo->_functions[value].usedOtherFile = true;
break;
}
propToken = propToken->next();
}
}
}
if (settings->library.isreflection(tok->str())) {
const int argIndex = settings->library.reflectionArgument(tok->str());
if (argIndex >= 0) {
const Token * funcToken = tok->next();
int index = 0;
std::string value;
while (funcToken) {
if (funcToken->str()==",") {
if (++index == argIndex)
break;
value = "";
} else
value += funcToken->str();
funcToken = funcToken->next();
}
if (index == argIndex) {
value = value.substr(1, value.length() - 2);
fileInfo->_functions[value].usedOtherFile = true;
}
}
}
const Token *funcname = nullptr;
if (tok->scope()->isExecutable() && Token::Match(tok->next(), "%var% (")) {
funcname = tok->next();
}
else if (tok->scope()->isExecutable() && Token::Match(tok->next(), "%var% <") && Token::simpleMatch(tok->linkAt(2), "> (")) {
funcname = tok->next();
}
else if (Token::Match(tok, "[;{}.,()[=+-/|!?:] &| %var% [(),;]:}]")) {
funcname = tok->next();
if (funcname->str() == "&")
funcname = funcname->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;
}
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 = nullptr;
}
if (funcname) {
FunctionUsage &func = fileInfo->_functions[ funcname->str()];
if (func.filename.empty() || func.filename == "+")
func.usedOtherFile = true;
else
func.usedSameFile = true;
}
}
return fileInfo;
}
void CheckUnusedFunctions::analyseWholeProgram(const std::list &fileInfo, ErrorLogger &errorLogger)
{
std::map _functions;
for (std::list::const_iterator it = fileInfo.begin(); it != fileInfo.end(); ++it) {
const MyFileInfo *f = dynamic_cast(*it);
if (f) {
for (std::map::const_iterator it2 = f->_functions.begin(); it2 != f->_functions.end(); ++it2) {
std::map::iterator it3 = _functions.find(it2->first);
if (it3 == _functions.end())
_functions[it2->first] = it2->second;
else {
if (it3->second.filename.empty()) {
it3->second.filename = it2->second.filename;
it3->second.lineNumber = it2->second.lineNumber;
}
it3->second.usedOtherFile |= it2->second.usedOtherFile;
}
}
}
}
for (std::map::const_iterator it = _functions.begin(); it != _functions.end(); ++it) {
const FunctionUsage &func = it->second;
if (func.usedOtherFile || func.filename.empty())
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;
if (! func.usedSameFile) {
std::string filename;
if (func.filename == "+")
filename = "";
else
filename = func.filename;
CheckUnusedFunctions::unusedFunctionError(&errorLogger, filename, func.lineNumber, it->first);
} else if (! func.usedOtherFile) {
/** @todo add error message "function is only used in it can be static" */
/*
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,
const std::string &filename, unsigned int lineNumber,
const std::string &funcname)
{
std::list locationList;
if (!filename.empty()) {
ErrorLogger::ErrorMessage::FileLocation fileLoc;
fileLoc.setfile(filename);
fileLoc.line = lineNumber;
locationList.push_back(fileLoc);
}
const ErrorLogger::ErrorMessage errmsg(locationList, Severity::style, "The function '" + funcname + "' is never used.", "unusedFunction", false);
if (errorLogger)
errorLogger->reportErr(errmsg);
else
reportError(errmsg);
}