diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index b9f4948e0..f3a94b1f9 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -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(); diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index 54dc833a1..ba1d798ee 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -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 *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 = ""; diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index 73d904168..7e68c16fd 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -53,6 +53,7 @@ public: bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, unsigned long long checksum, std::list *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; diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index 5ee62f1da..9ae22694c 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -22,6 +22,7 @@ #include "tokenize.h" #include "token.h" #include "symboldatabase.h" +#include "analyzerinfo.h" #include #include //--------------------------------------------------------------------------- @@ -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 &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 decls; + std::set calls; + + for (std::map::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::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); } } } diff --git a/lib/checkunusedfunctions.h b/lib/checkunusedfunctions.h index 366e96f09..d59cc6efe 100644 --- a/lib/checkunusedfunctions.h +++ b/lib/checkunusedfunctions.h @@ -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 &files); private: diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 8200a06ff..b0a813626 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -729,6 +729,14 @@ void CppCheck::analyseWholeProgram() (*it)->analyseWholeProgram(fileInfo, _settings, *this); } +void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map &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")); diff --git a/lib/cppcheck.h b/lib/cppcheck.h index dfb0e5ae4..27cf8561b 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -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 &files); + /** Check if the user wants to check for unused functions * and if it's possible at all */ bool isUnusedFunctionCheckEnabled() const;