diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index 0b51a762d..963a9463a 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -896,6 +896,8 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const cha } if (!settings.buildDir.empty()) { + settings.loadSummaries(); + std::list fileNames; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) fileNames.emplace_back(i->first); diff --git a/lib/settings.cpp b/lib/settings.cpp index b7d15b9b0..794e70cd9 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -20,6 +20,8 @@ #include "valueflow.h" +#include + std::atomic Settings::mTerminated; const char Settings::SafeChecks::XmlRootName[] = "safe-checks"; @@ -155,3 +157,48 @@ bool Settings::isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck) return false; return true; } + +static std::vector getSummaryFiles(const std::string &filename) +{ + std::vector ret; + std::ifstream fin(filename); + if (!fin.is_open()) + return ret; + std::string line; + while (std::getline(fin, line)) { + std::string::size_type dotA = line.find(".a"); + std::string::size_type colon = line.find(":"); + if (colon > line.size() || dotA > colon) + continue; + std::string f = line.substr(0,colon); + f[dotA + 1] = 's'; + ret.push_back(f); + } + return ret; +} + +void Settings::loadSummaries() +{ + if (buildDir.empty()) + return; + + std::vector summaryFiles = getSummaryFiles(buildDir + "/files.txt"); + for (const std::string &filename: summaryFiles) { + std::ifstream fin(buildDir + '/' + filename); + if (!fin.is_open()) + continue; + std::string line; + while (std::getline(fin, line)) { + // Get function name + 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 (summaryNoreturn.find(functionName) == summaryNoreturn.end()) + summaryNoreturn[functionName] = false; + } + } +} diff --git a/lib/settings.h b/lib/settings.h index 1c852bab6..e23d7dcd8 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -415,6 +415,11 @@ public: static bool terminated() { return Settings::mTerminated; } + + std::map summaryNoreturn; + + void loadSummaries(); + }; /// @} diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index b0e37ffaf..638fee84d 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -19,6 +19,7 @@ //--------------------------------------------------------------------------- #include "tokenize.h" +#include "analyzerinfo.h" #include "check.h" #include "errorlogger.h" #include "library.h" @@ -2377,6 +2378,9 @@ bool Tokenizer::simplifyTokens1(const std::string &configuration) mSymbolDatabase->setValueTypeInTokenList(true); } + if (!mSettings->buildDir.empty()) + createSummaries(configuration); + if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::ValueFlow", mSettings->showtime, mTimerResults); ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); @@ -8720,6 +8724,11 @@ 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 (unknown) *unknown = !unknownFunc.empty(); if (!unknownFunc.empty() && mSettings->checkLibrary && mSettings->isEnabled(Settings::INFORMATION)) { @@ -12064,3 +12073,60 @@ bool Tokenizer::hasIfdef(const Token *start, const Token *end) const return false; } +std::string Tokenizer::createSummaries(const std::string &configuration) const +{ + std::ostringstream ostr; + for (const Scope *scope : mSymbolDatabase->functionScopes) { + const Function *f = scope->function; + if (!f) + continue; + + // Summarize function + std::set noreturn; + std::set globalVars; + std::set calledFunctions; + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->variable() && tok->variable()->isGlobal()) + globalVars.insert(tok->variable()->name()); + if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { + calledFunctions.insert(tok->str()); + if (Token::simpleMatch(tok->linkAt(1), ") ; }")) + noreturn.insert(tok->str()); + } + } + + // Write summary for function + auto join = [](const std::set &data) -> std::string { + std::string ret; + const char *sep = ""; + for (std::string d: data) + { + ret += sep + d; + sep = ","; + } + return ret; + }; + + ostr << f->name(); + if (!globalVars.empty()) + ostr << " global:[" << join(globalVars) << "]"; + if (!calledFunctions.empty()) + ostr << " call:[" << join(calledFunctions) << "]"; + if (!noreturn.empty()) + ostr << " noreturn:[" << join(noreturn) << "]"; + ostr << std::endl; + } + + if (!mSettings->buildDir.empty()) { + std::string filename = AnalyzerInformation::getAnalyzerInfoFile(mSettings->buildDir, list.getSourceFilePath(), configuration); + std::string::size_type pos = filename.rfind(".a"); + if (pos != std::string::npos) { + filename[pos+1] = 's'; + std::ofstream fout(filename); + fout << ostr.str(); + } + } + + return ostr.str(); +} + diff --git a/lib/tokenize.h b/lib/tokenize.h index 5a21be2aa..a123f41df 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -585,6 +585,8 @@ public: bool hasIfdef(const Token *start, const Token *end) const; + std::string createSummaries(const std::string &configuration) const; + private: /** diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 0f862740d..8073b3203 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -520,6 +520,10 @@ private: TEST_CASE(checkHeader1); TEST_CASE(removeExtraTemplateKeywords); + + TEST_CASE(createSummaries1); + TEST_CASE(createSummariesGlobal); + TEST_CASE(createSummariesNoreturn); } std::string tokenizeAndStringify(const char code[], bool simplify = false, bool expand = true, Settings::PlatformType platform = Settings::Native, const char* filename = "test.cpp", bool cpp11 = true) { @@ -8749,6 +8753,30 @@ private: const char expected2[] = "GridView :: Codim < 0 > :: Iterator it ; it = gv . begin < 0 > ( ) ;"; ASSERT_EQUALS(expected2, tokenizeAndStringify(code2, false)); } + + std::string createSummaries(const char code[], const char filename[] = "test.cpp") { + // Clear the error buffer.. + errout.str(""); + + // tokenize.. + Settings settings; + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, filename); + return tokenizer.createSummaries(""); + } + + void createSummaries1() { + ASSERT_EQUALS("foo\n", createSummaries("void foo() {}")); + } + + void createSummariesGlobal() { + ASSERT_EQUALS("foo global:[x]\n", createSummaries("int x; void foo() { x=0; }")); + } + + void createSummariesNoreturn() { + ASSERT_EQUALS("foo call:[bar] noreturn:[bar]\n", createSummaries("void foo() { bar(); }")); + } }; REGISTER_TEST(TestTokenizer)