From 2e4ea69220db906f465b47ed385c69e1c740edce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 25 Mar 2025 12:29:07 +0100 Subject: [PATCH] test --- cli/cmdlineparser.cpp | 3 + externals/simplecpp/simplecpp.cpp | 164 +++++++++++++++++++++++++++++- externals/simplecpp/simplecpp.h | 10 ++ lib/cppcheck.cpp | 9 ++ lib/preprocessor.cpp | 2 +- lib/preprocessor.h | 2 + lib/settings.h | 3 + 7 files changed, 189 insertions(+), 4 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index e09c3e2dd60..b0cee40a604 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1102,6 +1102,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a mSettings.plistOutput = std::move(path); } + else if (std::strcmp(argv[i], "-pch") == 0) + mSettings.precompileHeader = true; + // Special Cppcheck Premium options else if ((std::strncmp(argv[i], "--premium=", 10) == 0 || std::strncmp(argv[i], "--premium-", 10) == 0) && isCppcheckPremium()) { // valid options --premium=.. diff --git a/externals/simplecpp/simplecpp.cpp b/externals/simplecpp/simplecpp.cpp index 2316c42b95a..9dcf6aba8d8 100755 --- a/externals/simplecpp/simplecpp.cpp +++ b/externals/simplecpp/simplecpp.cpp @@ -1681,6 +1681,13 @@ namespace simplecpp { return invalidHashHash(loc, macroName, "Combining '\\"+ tokenA->str()+ "' and '"+ strAB.substr(tokenA->str().size()) + "' yields universal character '\\" + strAB + "'. This is undefined behavior according to C standard chapter 5.1.1.2, paragraph 4."); } }; + + std::string dump() const { + std::string ret; + for (const Token *tok = nameTokDef; sameline(nameTokDef,tok); tok = tok->next) + ret += "\n" + toString(tok->location.col) + ":" + tok->str(); + return ret.substr(1); + } private: /** Create new token where Token::macro is set for replaced tokens */ Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced, const Token *expandedFromToken=nullptr) const { @@ -3245,7 +3252,18 @@ std::map simplecpp::load(const simplecpp::To continue; std::ifstream f; - const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); + std::string header2; + if (filenames.size() == 1) { + header2 = openHeader(f,dui,sourcefile,header + ".pch",systemheader); + if (f.is_open()) { + const std::string header2WithoutPch = header2.substr(0, header2.size() - 4); + TokenList *tokens = new TokenList(header2WithoutPch, filenames, outputList); + ret[header2WithoutPch] = tokens; + continue; + } + } + if (!f.is_open()) + header2 = openHeader(f,dui,sourcefile,header,systemheader); if (!f.is_open()) continue; f.close(); @@ -3314,8 +3332,82 @@ static std::string getTimeDefine(const struct tm *timep) return std::string("\"").append(buf).append("\""); } -void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector &files, std::map &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage, std::list *ifCond) +static void loadPrecompiledHeader(simplecpp::TokenList& output, + const std::string& pch, + std::istream& f, + std::vector& files, + std::map &filedata, + simplecpp::MacroMap& macros) +{ + simplecpp::TokenList * const macroTokens = new simplecpp::TokenList(files); + filedata[pch] = macroTokens; + enum { FILES, TOKENS, MACROS } section = FILES; + simplecpp::Location loc(files); + std::string line; + while (std::getline(f,line)) { + if (line == "files") { + section = FILES; + continue; + } + if (line == "tokens") { + section = TOKENS; + continue; + } + if (line == "[MACRO]") { + section = MACROS; + loc.fileIndex = 1; + loc.line = 1; + if (macroTokens->cback()) + loc.line = macroTokens->cback()->location.line + 1; + loc.col = 1; + macroTokens->push_back(new simplecpp::Token("#", loc)); + loc.col = 2; + macroTokens->push_back(new simplecpp::Token("define", loc)); + continue; + } + if (section == FILES) { + std::string::size_type pos = line.find(':'); + if (pos >= 1 && pos < line.size()) + files.push_back(line.substr(pos+1)); + continue; + } + if (section == TOKENS) { + std::string::size_type pos = 0; + while (pos < line.size() && line[pos] != ':') { + char c = line[pos++]; + int value = 0; + while (pos < line.size() && std::isdigit(line[pos])) + value = value * 10 + line[pos++] - '0'; + if (c == 'f') + loc.fileIndex = value; + else if (c == 'l') + loc.line = value; + else if (c == 'c') + loc.col = value; + } + if (pos + 1 < line.size() && line[pos] == ':') + output.push_back(new simplecpp::Token(line.substr(pos + 1), loc)); + continue; + } + if (section == MACROS) { + const std::string::size_type pos = line.find(':'); + loc.col = std::atoi(line.substr(0, pos).c_str()); + macroTokens->push_back(new simplecpp::Token(line.substr(pos + 1), loc)); + } + } + for (const simplecpp::Token* tok = macroTokens->cfront(); tok; tok = tok->next) { + if (tok->op == '#' && tok->next && tok->next->str() == DEFINE) { + simplecpp::Macro macro(tok, files); + if (macro.name() != "__DATE__" && macro.name() != "__TIME__") + macros.insert(std::pair(macro.name(), macro)); + } + } +} + +static void simplecppPreprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector &files, std::map &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage, std::list *ifCond, simplecpp::MacroMap& macros) { + using namespace simplecpp; + #ifdef SIMPLECPP_WINDOWS if (dui.clearIncludeCache) nonExistingFilesCache.clear(); @@ -3347,7 +3439,6 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL std::vector dummy; const bool hasInclude = isCpp17OrLater(dui); - MacroMap macros; for (std::list::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { const std::string ¯ostr = *it; const std::string::size_type eq = macrostr.find('='); @@ -3534,6 +3625,13 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL if (header2.empty()) { // try to load file.. std::ifstream f; + if (output.empty()) { + header2 = openHeader(f, dui, rawtok->location.file(), header + ".pch", systemheader); + if (f.is_open()) { + loadPrecompiledHeader(output, header + ".pch", f, files, filedata, macros); + continue; + } + } header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader); if (f.is_open()) { f.close(); @@ -3792,6 +3890,66 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL } } +void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector &files, std::map &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage, std::list *ifCond) +{ + MacroMap macroMap; + simplecppPreprocess(output, + rawtokens, + files, + filedata, + dui, + outputList, + macroUsage, + ifCond, + macroMap); +} + + +std::string simplecpp::precompileHeader(const TokenList &rawtokens, std::vector &files, const DUI &dui, OutputList *outputList) +{ + std::map filedata; + simplecpp::TokenList output(files); + std::list macroUsage; + std::list ifCond; + simplecpp::MacroMap macroMap; + simplecppPreprocess(output, + rawtokens, + files, + filedata, + dui, + outputList, + ¯oUsage, + &ifCond, + macroMap); + + std::string ret; + ret = "files\n"; + for (size_t i = 0; i < files.size(); ++i) + ret += toString(i) + ":" + files[i] + "\n"; + ret += "tokens\n"; + unsigned int fileIndex = 0; + unsigned int line = 0; + unsigned int col = 0; + for (const simplecpp::Token *tok = output.cfront(); tok; tok = tok->next) { + if (tok->location.fileIndex != fileIndex) { + fileIndex = tok->location.fileIndex; + ret += "f" + toString(fileIndex); + } + if (tok->location.line != line) { + line = tok->location.line; + ret += "l" + toString(line); + } + if (tok->location.col != col) { + col = tok->location.col; + ret += "c" + toString(col); + } + ret += ":" + tok->str() + "\n"; + } + for (simplecpp::MacroMap::const_iterator it = macroMap.begin(); it != macroMap.end(); ++it) + ret += "[MACRO]\n" + it->second.dump() + "\n"; + return ret; +} + void simplecpp::cleanup(std::map &filedata) { for (std::map::iterator it = filedata.begin(); it != filedata.end(); ++it) diff --git a/externals/simplecpp/simplecpp.h b/externals/simplecpp/simplecpp.h index f5c69593cb1..fad27cf0a99 100755 --- a/externals/simplecpp/simplecpp.h +++ b/externals/simplecpp/simplecpp.h @@ -365,6 +365,16 @@ namespace simplecpp { */ SIMPLECPP_LIB void preprocess(TokenList &output, const TokenList &rawtokens, std::vector &files, std::map &filedata, const DUI &dui, OutputList *outputList = nullptr, std::list *macroUsage = nullptr, std::list *ifCond = nullptr); + /** + * Precompile header + * @param rawtokens Raw tokenlist for top sourcefile + * @param files internal data of simplecpp + * @param dui defines, undefs, and include paths + * @param outputList output: list that will receive output messages + * @return precompiled header data + */ + SIMPLECPP_LIB std::string precompileHeader(const TokenList &rawtokens, std::vector &files, const DUI &dui, OutputList *outputList = nullptr); + /** * Deallocate data */ diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 7024969117a..cbf703276ce 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -957,6 +957,15 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string return mLogger->exitcode(); } + if (mSettings.precompileHeader) { + std::ofstream fout(file.spath() + ".pch"); + if (fout.is_open()) { + const simplecpp::DUI& dui = Preprocessor::createDUI(mSettings, "", file.spath()); + fout << simplecpp::precompileHeader(tokens1, files, dui, &outputList); + return mLogger->exitcode(); + } + } + Preprocessor preprocessor(mSettings, mErrorLogger); if (!preprocessor.loadFiles(tokens1, files)) diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index db00e18a8d6..d211936604a 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -688,7 +688,7 @@ static void splitcfg(const std::string &cfg, std::list &defines, co } } -static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename) +simplecpp::DUI Preprocessor::createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename) { // TODO: make it possible to specify platform-dependent sizes simplecpp::DUI dui; diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 3086ca4cb0a..1f49d99e5dd 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -125,6 +125,8 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { static void setPlatformInfo(simplecpp::TokenList &tokens, const Settings& settings); + static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename); + simplecpp::TokenList preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError = false); std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool writeLocations); diff --git a/lib/settings.h b/lib/settings.h index 930b8776d18..d6b8ef7a3b2 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -307,6 +307,9 @@ class CPPCHECKLIB WARN_UNUSED Settings { /** Is checker id enabled by premiumArgs */ bool isPremiumEnabled(const char id[]) const; + /** @brief Precompile a header file into a .pch file */ + bool precompileHeader{}; + /** @brief Using -E for debugging purposes */ bool preprocessOnly{};