UnusedFunctions: Perform checking using analyzeinfo

This commit is contained in:
Daniel Marjamäki 2016-11-05 21:26:56 +01:00
parent da956a5278
commit 350e5a7142
7 changed files with 82 additions and 28 deletions

View File

@ -854,6 +854,8 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const cha
returnValue = executor.check();
}
cppcheck.analyseWholeProgram(_settings->buildDir, _files);
if (settings.isEnabled("information") || settings.checkConfiguration) {
const bool enableUnusedFunctionCheck = cppcheck.isUnusedFunctionCheckEnabled();

View File

@ -58,21 +58,27 @@ static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long
return true;
}
std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile)
{
std::string filename = Path::fromNativeSeparators(buildDir);
if (filename.back() != '/')
filename += '/';
const std::string::size_type pos = sourcefile.rfind("/");
if (pos == std::string::npos)
filename += sourcefile;
else
filename += sourcefile.substr(pos+1);
filename += ".analyzerinfo";
return filename;
}
bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, unsigned long long checksum, std::list<ErrorLogger::ErrorMessage> *errors)
{
if (buildDir.empty() || sourcefile.empty())
return true;
close();
analyzerInfoFile = Path::fromNativeSeparators(buildDir);
if (analyzerInfoFile.back() != '/')
analyzerInfoFile += '/';
const std::string::size_type pos = sourcefile.rfind("/");
if (pos == std::string::npos)
analyzerInfoFile += sourcefile;
else
analyzerInfoFile += sourcefile.substr(pos+1);
analyzerInfoFile += ".analyzerinfo";
analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile);
const std::string start = "<analyzerinfo checksum=\"" + std::to_string(checksum) + "\">";

View File

@ -53,6 +53,7 @@ public:
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, unsigned long long checksum, std::list<ErrorLogger::ErrorMessage> *errors);
void reportErr(const ErrorLogger::ErrorMessage &msg, bool verbose);
void setFileInfo(const std::string &check, const std::string &fileInfo);
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile);
private:
std::ofstream fout;
std::string analyzerInfoFile;

View File

@ -22,6 +22,7 @@
#include "tokenize.h"
#include "token.h"
#include "symboldatabase.h"
#include "analyzerinfo.h"
#include <cctype>
#include <tinyxml2.h>
//---------------------------------------------------------------------------
@ -303,25 +304,56 @@ std::string CheckUnusedFunctions::analyzerInfo() const
return ret.str();
}
void CheckUnusedFunctions::clear()
{
instance._functions.clear();
namespace {
struct Location {
Location() : fileName(""), lineNumber(0) {}
Location(std::string f, int l) : fileName(f), lineNumber(l) {}
std::string fileName;
int lineNumber;
};
}
void CheckUnusedFunctions::loadInfo(const tinyxml2::XMLElement *info, const std::string &filename)
void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir, const std::map<std::string, std::size_t> &files)
{
for (const tinyxml2::XMLElement *e = info->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (std::strcmp(e->Name(), "functiondecl")==0) {
FunctionUsage &func = _functions[e->Attribute("functionName")];
func.filename = filename;
func.lineNumber = std::atoi(e->Attribute("lineNumber"));
} else if (std::strcmp(e->Name(), "functioncall")==0) {
FunctionUsage &func = _functions[e->Attribute("functionName")];
if (func.filename == filename)
func.usedSameFile = true;
else
func.usedOtherFile = true;
std::map<std::string, Location> decls;
std::set<std::string> calls;
for (std::map<std::string, std::size_t>::const_iterator it = files.begin(); it != files.end(); ++it) {
const std::string &sourcefile = it->first;
const std::string xmlfile = AnalyzerInformation::getAnalyzerInfoFile(buildDir, sourcefile);
tinyxml2::XMLDocument doc;
tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
if (error != tinyxml2::XML_SUCCESS)
continue;
const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement();
if (rootNode == nullptr)
continue;
for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (std::strcmp(e->Name(), "FileInfo") == 0) {
const char *checkattr = e->Attribute("check");
if (checkattr && std::strcmp(checkattr,"CheckUnusedFunctions")==0) {
for (const tinyxml2::XMLElement *e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) {
if (!e2->Attribute("functionName"))
continue;
if (std::strcmp(e2->Name(),"functiondecl")==0 && e2->Attribute("lineNumber")) {
decls[e2->Attribute("functionName")] = Location(sourcefile, std::atoi(e2->Attribute("lineNumber")));
} else if (std::strcmp(e2->Name(),"functioncall")==0) {
calls.insert(e2->Attribute("functionName"));
}
}
}
}
}
}
for (std::map<std::string, Location>::const_iterator decl = decls.begin(); decl != decls.end(); ++decl) {
const std::string &functionName = decl->first;
if (calls.find(functionName) == calls.end()) {
const Location &loc = decl->second;
unusedFunctionError(errorLogger, loc.fileName, loc.lineNumber, functionName);
}
}
}

View File

@ -62,8 +62,7 @@ public:
std::string analyzerInfo() const;
/** @brief Combine and analyze all analyzerInfos for all TUs */
void clear();
void loadInfo(const tinyxml2::XMLElement *info, const std::string &filename);
void analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir, const std::map<std::string, std::size_t> &files);
private:

View File

@ -729,6 +729,14 @@ void CppCheck::analyseWholeProgram()
(*it)->analyseWholeProgram(fileInfo, _settings, *this);
}
void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map<std::string, std::size_t> &files)
{
if (buildDir.empty())
return;
if (_settings.isEnabled("unusedFunctions"))
CheckUnusedFunctions::instance.analyseWholeProgram(this, buildDir, files);
}
bool CppCheck::isUnusedFunctionCheckEnabled() const
{
return ((_settings.jobs == 1 || !_settings.buildDir.empty()) && _settings.isEnabled("unusedFunction"));

View File

@ -126,9 +126,15 @@ public:
_simplify = false;
}
/** analyse whole program, run this after all TUs has been scanned. */
/** Analyse whole program, run this after all TUs has been scanned.
* This is deprecated and the plan is to remove this when
* .analyzeinfo is good enough
*/
void analyseWholeProgram();
/** analyse whole program use .analyzeinfo files */
void analyseWholeProgram(const std::string &buildDir, const std::map<std::string, std::size_t> &files);
/** Check if the user wants to check for unused functions
* and if it's possible at all */
bool isUnusedFunctionCheckEnabled() const;