diff --git a/lib/settings.cpp b/lib/settings.cpp index ed604c674..e93c2031f 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -20,6 +20,7 @@ #include "valueflow.h" +#include #include std::atomic Settings::mTerminated; @@ -177,11 +178,53 @@ static std::vector getSummaryFiles(const std::string &filename) return ret; } +static std::vector getSummaryData(const std::string &line, const std::string &data) +{ + std::vector ret; + const std::string::size_type start = line.find(" " + data + ":["); + if (start == std::string::npos) + return ret; + const std::string::size_type end = line.find("]", start); + if (end >= line.size()) + return ret; + + std::string::size_type pos1 = start + 3 + data.size(); + while (pos1 < end) { + std::string::size_type pos2 = line.find_first_of(",]",pos1); + ret.push_back(line.substr(pos1, pos2-pos1-1)); + pos1 = pos2 + 1; + } + + return ret; +} + +static void removeFunctionCalls(const std::string& calledFunction, + std::map> &functionCalledBy, + std::map> &functionCalls, + std::vector &add) +{ + std::vector calledBy = functionCalledBy[calledFunction]; + functionCalledBy.erase(calledFunction); + for (const std::string &c: calledBy) { + std::vector &calls = functionCalls[c]; + calls.erase(std::remove(calls.begin(), calls.end(), calledFunction), calls.end()); + if (calls.empty()) { + add.push_back(calledFunction); + removeFunctionCalls(c, functionCalledBy, functionCalls, add); + } + } +} + void Settings::loadSummaries() { if (buildDir.empty()) return; + std::vector return1; + std::map> functionCalls; + std::map> functionCalledBy; + + // extract "functionNoreturn" and "functionCalledBy" from summaries std::vector summaryFiles = getSummaryFiles(buildDir + "/files.txt"); for (const std::string &filename: summaryFiles) { std::ifstream fin(buildDir + '/' + filename); @@ -193,13 +236,23 @@ void Settings::loadSummaries() const std::string::size_type pos1 = 0; const std::string::size_type pos2 = line.find(" ", pos1); const std::string functionName = (pos2 == std::string::npos) ? line : line.substr(0, pos2); - - // noreturn.. - if (line.find(" noreturn:[") != std::string::npos || line.find(" call:[") != std::string::npos) - summaryNoreturn[functionName] = true; - else - // If there is a value for function already keep it, otherwise insert false - summaryNoreturn.insert(std::pair(functionName, false)); + std::vector call = getSummaryData(line, "call"); + functionCalls[functionName] = call; + if (call.empty()) + return1.push_back(functionName); + else { + for (const std::string &c: call) { + functionCalledBy[c].push_back(functionName); + } + } } } + summaryReturn.insert(return1.cbegin(), return1.cend()); + + // recursively set "summaryNoreturn" + for (const std::string &f: return1) { + std::vector return2; + removeFunctionCalls(f, functionCalledBy, functionCalls, return2); + summaryReturn.insert(return2.cbegin(), return2.cend()); + } } diff --git a/lib/settings.h b/lib/settings.h index e23d7dcd8..f8f0152d6 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -416,7 +416,7 @@ public: return Settings::mTerminated; } - std::map summaryNoreturn; + std::set summaryReturn; void loadSummaries(); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 638fee84d..571e658e3 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8724,10 +8724,8 @@ bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const { std::string unknownFunc; const bool ret = mSettings->library.isScopeNoReturn(endScopeToken,&unknownFunc); - if (!unknownFunc.empty()) { - const std::map::const_iterator it = mSettings->summaryNoreturn.find(unknownFunc); - if (it != mSettings->summaryNoreturn.end() && !it->second) - return false; + if (!unknownFunc.empty() && mSettings->summaryReturn.find(unknownFunc) != mSettings->summaryReturn.end()) { + return false; } if (unknown) *unknown = !unknownFunc.empty();