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(); returnValue = executor.check();
} }
cppcheck.analyseWholeProgram(_settings->buildDir, _files);
if (settings.isEnabled("information") || settings.checkConfiguration) { if (settings.isEnabled("information") || settings.checkConfiguration) {
const bool enableUnusedFunctionCheck = cppcheck.isUnusedFunctionCheckEnabled(); const bool enableUnusedFunctionCheck = cppcheck.isUnusedFunctionCheckEnabled();

View File

@ -58,21 +58,27 @@ static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long
return true; 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) 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()) if (buildDir.empty() || sourcefile.empty())
return true; return true;
close(); close();
analyzerInfoFile = Path::fromNativeSeparators(buildDir); analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile);
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";
const std::string start = "<analyzerinfo checksum=\"" + std::to_string(checksum) + "\">"; 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); 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 reportErr(const ErrorLogger::ErrorMessage &msg, bool verbose);
void setFileInfo(const std::string &check, const std::string &fileInfo); void setFileInfo(const std::string &check, const std::string &fileInfo);
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile);
private: private:
std::ofstream fout; std::ofstream fout;
std::string analyzerInfoFile; std::string analyzerInfoFile;

View File

@ -22,6 +22,7 @@
#include "tokenize.h" #include "tokenize.h"
#include "token.h" #include "token.h"
#include "symboldatabase.h" #include "symboldatabase.h"
#include "analyzerinfo.h"
#include <cctype> #include <cctype>
#include <tinyxml2.h> #include <tinyxml2.h>
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -303,25 +304,56 @@ std::string CheckUnusedFunctions::analyzerInfo() const
return ret.str(); return ret.str();
} }
namespace {
void CheckUnusedFunctions::clear() struct Location {
{ Location() : fileName(""), lineNumber(0) {}
instance._functions.clear(); 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()) { std::map<std::string, Location> decls;
if (std::strcmp(e->Name(), "functiondecl")==0) { std::set<std::string> calls;
FunctionUsage &func = _functions[e->Attribute("functionName")];
func.filename = filename; for (std::map<std::string, std::size_t>::const_iterator it = files.begin(); it != files.end(); ++it) {
func.lineNumber = std::atoi(e->Attribute("lineNumber")); const std::string &sourcefile = it->first;
} else if (std::strcmp(e->Name(), "functioncall")==0) { const std::string xmlfile = AnalyzerInformation::getAnalyzerInfoFile(buildDir, sourcefile);
FunctionUsage &func = _functions[e->Attribute("functionName")];
if (func.filename == filename) tinyxml2::XMLDocument doc;
func.usedSameFile = true; tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
else if (error != tinyxml2::XML_SUCCESS)
func.usedOtherFile = true; 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; std::string analyzerInfo() const;
/** @brief Combine and analyze all analyzerInfos for all TUs */ /** @brief Combine and analyze all analyzerInfos for all TUs */
void clear(); void analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir, const std::map<std::string, std::size_t> &files);
void loadInfo(const tinyxml2::XMLElement *info, const std::string &filename);
private: private:

View File

@ -729,6 +729,14 @@ void CppCheck::analyseWholeProgram()
(*it)->analyseWholeProgram(fileInfo, _settings, *this); (*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 bool CppCheck::isUnusedFunctionCheckEnabled() const
{ {
return ((_settings.jobs == 1 || !_settings.buildDir.empty()) && _settings.isEnabled("unusedFunction")); return ((_settings.jobs == 1 || !_settings.buildDir.empty()) && _settings.isEnabled("unusedFunction"));

View File

@ -126,9 +126,15 @@ public:
_simplify = false; _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(); 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 /** Check if the user wants to check for unused functions
* and if it's possible at all */ * and if it's possible at all */
bool isUnusedFunctionCheckEnabled() const; bool isUnusedFunctionCheckEnabled() const;