From a7c459781b34e9ef1ed0422acb1d6a99b63a5385 Mon Sep 17 00:00:00 2001 From: glank Date: Wed, 19 Aug 2026 22:37:31 +0200 Subject: [PATCH] 14974 --- externals/simplecpp/simplecpp.cpp | 15 ++++-- externals/simplecpp/simplecpp.h | 3 +- lib/analyzerinfo.cpp | 56 +++++++++++++++++++++++ lib/analyzerinfo.h | 3 ++ lib/cppcheck.cpp | 76 +++++++++++++++++++------------ lib/preprocessor.cpp | 9 +++- lib/preprocessor.h | 8 ++-- test/helpers.cpp | 2 +- test/testcppcheck.cpp | 2 +- test/testpreprocessor.cpp | 10 ++-- test/testtokenize.cpp | 4 +- 11 files changed, 140 insertions(+), 48 deletions(-) diff --git a/externals/simplecpp/simplecpp.cpp b/externals/simplecpp/simplecpp.cpp index d2c398c49e1..a9793af3e75 100644 --- a/externals/simplecpp/simplecpp.cpp +++ b/externals/simplecpp/simplecpp.cpp @@ -3154,13 +3154,10 @@ std::pair simplecpp::FileDataCache::tryload(FileDat mImpl->mIdMap.emplace(fileId, data); mData.emplace_back(data); - if (mLoadCallback) - mLoadCallback(*data); - return {data, true}; } -std::pair simplecpp::FileDataCache::get(const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader, std::vector &filenames, simplecpp::OutputList *outputList) +std::pair simplecpp::FileDataCache::get_private(const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader, std::vector &filenames, simplecpp::OutputList *outputList) { if (isAbsolutePath(header)) { auto ins = mNameMap.emplace(simplecpp::simplifyPath(header), nullptr); @@ -3206,6 +3203,16 @@ std::pair simplecpp::FileDataCache::get(const std:: return {nullptr, false}; } +std::pair simplecpp::FileDataCache::get(const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader, std::vector &filenames, simplecpp::OutputList *outputList) +{ + auto ret = get_private(sourcefile, header, dui, systemheader, filenames, outputList); + + if (mLoadCallback && ret.first) + mLoadCallback(*ret.first, ret.second); + + return ret; +} + void simplecpp::FileDataCache::clear() { mImpl->clear(); diff --git a/externals/simplecpp/simplecpp.h b/externals/simplecpp/simplecpp.h index 3d6fc5262b9..7c186d4237d 100644 --- a/externals/simplecpp/simplecpp.h +++ b/externals/simplecpp/simplecpp.h @@ -499,7 +499,7 @@ namespace simplecpp { return mData.cend(); } - using load_callback_type = std::function; + using load_callback_type = std::function; void set_load_callback(load_callback_type cb) { mLoadCallback = std::move(cb); @@ -512,6 +512,7 @@ namespace simplecpp { using name_map_type = std::unordered_map; std::pair tryload(name_map_type::iterator &name_it, const DUI &dui, std::vector &filenames, OutputList *outputList); + std::pair get_private(const std::string &sourcefile, const std::string &header, const DUI &dui, bool systemheader, std::vector &filenames, OutputList *outputList); container_type mData; name_map_type mNameMap; diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index 36803b897b1..4b2e2ef2380 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -58,6 +58,61 @@ void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std:: fout << getFilesTxt(sourcefiles, fileSettings); } +void AnalyzerInformation::writeIncludes(const std::set &files) +{ + if (mOutputStream.is_open()) { + mOutputStream << " \n"; + for (const std::string &file : files) { + mOutputStream << " " << file << "\n"; + } + mOutputStream << " \n"; + } +} + +std::set AnalyzerInformation::getIncludes(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId) +{ + if (mOutputStream.is_open()) + throw std::runtime_error("analyzer information file is already open"); + + std::set files; + + if (buildDir.empty() || sourcefile.empty()) + return files; + + const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir, sourcefile, cfg, fsFileId); + + tinyxml2::XMLDocument analyzerInfoDoc; + if (analyzerInfoDoc.LoadFile(analyzerInfoFile.c_str()) != tinyxml2::XML_SUCCESS) + return files; + + const tinyxml2::XMLElement *const rootNode = analyzerInfoDoc.FirstChildElement(); + if (rootNode == nullptr) + return files; + + if (strcmp(rootNode->Name(), "analyzerinfo") != 0) + return files; + + const tinyxml2::XMLElement *cachedfilesNode = nullptr; + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (strcmp(e->Name(), "includes") == 0) { + cachedfilesNode = e; + break; + } + } + + if (cachedfilesNode == nullptr) + return files; + + for (const tinyxml2::XMLElement *e = cachedfilesNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (strcmp(e->Name(), "filename") != 0) + continue; + + files.insert(e->GetText()); + } + + return files; +} + std::string AnalyzerInformation::getFilesTxt(const std::list &sourcefiles, const std::list &fileSettings) { std::ostringstream ret; @@ -172,6 +227,7 @@ bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::st tinyxml2::XMLDocument analyzerInfoDoc; const tinyxml2::XMLError xmlError = analyzerInfoDoc.LoadFile(analyzerInfoFile.c_str()); if (xmlError == tinyxml2::XML_SUCCESS) { + const std::string err = skipAnalysis(analyzerInfoDoc, hash, errors); if (err.empty()) { if (debug) diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index 75674a22f82..72234a5da7b 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -27,6 +27,7 @@ #include #include #include +#include #include class ErrorMessage; @@ -67,6 +68,8 @@ class CPPCHECKLIB AnalyzerInformation { bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId, std::size_t hash, std::list &errors, bool debug = false); void reportErr(const ErrorMessage &msg); void setFileInfo(const std::string &check, const std::string &fileInfo); + void writeIncludes(const std::set &files); + std::set getIncludes(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId); static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId); void reopen(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId); diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 26d98c0c7b4..aed30f70329 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1022,25 +1022,6 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str preprocessor.inlineSuppressions(mSuppressions.nomsg); preprocessor.removeComments(); - if (!mSettings.buildDir.empty()) { - analyzerInformation.reset(new AnalyzerInformation); - mLogger->setAnalyzerInfo(analyzerInformation.get()); - } - - if (analyzerInformation) { - // Calculate hash so it can be compared with old hash / future hashes - const std::size_t hash = calculateHash(preprocessor, file.spath()); - std::list errors; - if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, file.fsFileId(), hash, errors, mSettings.debugainfo)) { - while (!errors.empty()) { - mErrorLogger.reportErr(errors.front()); - errors.pop_front(); - } - mLogger->setAnalyzerInfo(nullptr); - return mLogger->exitcode(); // known results => no need to reanalyze file - } - } - // Get directives std::list directives; preprocessor.createDirectives(directives); @@ -1058,26 +1039,57 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str std::inserter(configDefines, configDefines.end()), getDefineName); - preprocessor.setLoadCallback([&](simplecpp::FileData &data) { - // Do preprocessing on included file - mLogger->addRemarkComments(preprocessor.getRemarkComments(data.tokens)); - preprocessor.inlineSuppressions(data.tokens, mSuppressions.nomsg); - Preprocessor::removeComments(data.tokens); - Preprocessor::createDirectives(data.tokens, directives); - Preprocessor::simplifyPragmaAsm(data.tokens); - // Discover new configurations from included file - if (configurations.size() < maxConfigs) - preprocessor.getConfigs(data.filename, data.tokens, configDefines, configurations); + // Keep track of all included files + std::set includedFiles; + + preprocessor.setLoadCallback([&](simplecpp::FileData &data, bool loaded) { + includedFiles.insert(data.filename); + if (loaded) { + // Do preprocessing on included file + mLogger->addRemarkComments(preprocessor.getRemarkComments(data.tokens)); + preprocessor.inlineSuppressions(data.tokens, mSuppressions.nomsg); + Preprocessor::removeComments(data.tokens); + Preprocessor::createDirectives(data.tokens, directives); + Preprocessor::simplifyPragmaAsm(data.tokens); + // Discover new configurations from included file + if (configurations.size() < maxConfigs) + preprocessor.getConfigs(data.filename, data.tokens, configDefines, configurations); + } }); preprocessor.setPlatformInfo(); + if (!mSettings.buildDir.empty()) { + analyzerInformation.reset(new AnalyzerInformation); + mLogger->setAnalyzerInfo(analyzerInformation.get()); + } + + if (analyzerInformation) { + // Load all included files to get correct hashes and suppressions + for (const std::string &filename : analyzerInformation->getIncludes(mSettings.buildDir, file.spath(), cfgname, file.fsFileId())) + preprocessor.loadFile(files, filename); + // Calculate hash so it can be compared with old hash / future hashes + const std::size_t hash = calculateHash(preprocessor, file.spath()); + std::list errors; + if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, file.fsFileId(), hash, errors, mSettings.debugainfo)) { + while (!errors.empty()) { + mErrorLogger.reportErr(errors.front()); + errors.pop_front(); + } + mLogger->setAnalyzerInfo(nullptr); + return mLogger->exitcode(); // known results => no need to reanalyze file + } + // Clear included file list; we don't want to keep includes that have been removed from the source + // Any includes that are still present will be readded + includedFiles.clear(); + } + // Get configurations.. if (maxConfigs > 1) { Timer::run("Preprocessor::getConfigs", mTimerResults, [&]() { configurations = { "" }; preprocessor.getConfigs(configDefines, configurations); - preprocessor.loadFiles(files); + preprocessor.loadAllIncludes(files); }); } else { configurations = { mSettings.userDefines }; @@ -1300,6 +1312,10 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str mLogger->setPlistFilenames(std::move(files)); } + if (analyzerInformation) { + analyzerInformation->writeIncludes(includedFiles); + } + executeAddons(dumpFile, file); } catch (const TerminateException &) { // Analysis is terminated diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 3e0b33c7f10..00dd57dfa13 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -833,7 +833,7 @@ const simplecpp::Output* Preprocessor::handleErrors(const simplecpp::OutputList& return reportOutput(outputList, showerror); } -bool Preprocessor::loadFiles(std::vector &files) +bool Preprocessor::loadAllIncludes(std::vector &files) { const simplecpp::DUI dui = createDUI(mSettings, "", mLang); @@ -842,6 +842,13 @@ bool Preprocessor::loadFiles(std::vector &files) return !handleErrors(outputList); } +simplecpp::FileData *Preprocessor::loadFile(std::vector &files, const std::string &file) +{ + const simplecpp::DUI dui = createDUI(mSettings, "", mLang); + + return mFileCache.get("", file, dui, false, files, nullptr).first; +} + void Preprocessor::removeComments() { removeComments(mTokens); diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 3a5a8393a3e..c77759418bf 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -122,7 +122,9 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { std::vector getRemarkComments(const simplecpp::TokenList &tokens) const; - bool loadFiles(std::vector &files); + bool loadAllIncludes(std::vector &files); + + simplecpp::FileData *loadFile(std::vector &files, const std::string &file); void removeComments(); @@ -163,6 +165,8 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { mFileCache.set_load_callback(std::move(cb)); } + simplecpp::FileDataCache mFileCache; + private: /** @@ -182,8 +186,6 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { const Settings& mSettings; ErrorLogger &mErrorLogger; - simplecpp::FileDataCache mFileCache; - /** filename for cpp/c file - useful when reporting errors */ std::string mFile0; // TODO: this is never set Standards::Language mLang{Standards::Language::None}; diff --git a/test/helpers.cpp b/test/helpers.cpp index 70437207a84..7eb014062ec 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -117,7 +117,7 @@ void SimpleTokenizer2::preprocess(const char* code, std::size_t size, std::vecto simplecpp::TokenList tokens1({code, size}, files, file0, &outputList); Preprocessor preprocessor(tokens1, tokenizer.getSettings(), errorlogger, Path::identify(tokens1.getFiles()[0], false)); - (void)preprocessor.loadFiles(files); // TODO: check result + (void)preprocessor.loadAllIncludes(files); // TODO: check result simplecpp::TokenList tokens2 = preprocessor.preprocess("", files, outputList); (void)preprocessor.reportOutput(outputList, true); diff --git a/test/testcppcheck.cpp b/test/testcppcheck.cpp index 17c5b8eff3c..d1e02530c16 100644 --- a/test/testcppcheck.cpp +++ b/test/testcppcheck.cpp @@ -573,7 +573,7 @@ class TestCppcheck : public TestFixture { simplecpp::TokenList tokens(code, files, "m1.c"); Preprocessor preprocessor(tokens, settings, errorLogger, Standards::Language::C); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); AddonInfo premiumaddon; premiumaddon.name = "premiumaddon.json"; diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 55fcafd4f4f..52a1a1f9237 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -59,7 +59,7 @@ class TestPreprocessor : public TestFixture { std::vector files; simplecpp::TokenList tokens1 = simplecpp::TokenList(code, files, "file.cpp", &outputList); Preprocessor p(tokens1, settingsDefault, errorLogger, Path::identify(tokens1.getFiles()[0], false)); - ASSERT_LOC(p.loadFiles(files), file, line); + ASSERT_LOC(p.loadAllIncludes(files), file, line); simplecpp::TokenList tokens2 = p.preprocess("", files, outputList); (void)p.reportOutput(outputList, true); return tokens2.stringify(); @@ -410,13 +410,13 @@ class TestPreprocessor : public TestFixture { settings.library.defines().end(), std::inserter(configDefines, configDefines.end()), getDefineName); - preprocessor.setLoadCallback([&](simplecpp::FileData &data) { + preprocessor.setLoadCallback([&](simplecpp::FileData &data, bool) { Preprocessor::removeComments(data.tokens); preprocessor.getConfigs(data.filename, data.tokens, configDefines, configs); }); preprocessor.removeComments(); preprocessor.getConfigs(configDefines, configs); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); ASSERT(!preprocessor.reportOutput(outputList, true)); std::string ret; for (const std::string & config : configs) @@ -429,11 +429,11 @@ class TestPreprocessor : public TestFixture { std::vector files; simplecpp::TokenList tokens(code,files,"test.c"); Preprocessor preprocessor(tokens, settingsDefault, *this, Standards::Language::C); - preprocessor.setLoadCallback([](simplecpp::FileData &data) { + preprocessor.setLoadCallback([](simplecpp::FileData &data, bool) { Preprocessor::removeComments(data.tokens); }); preprocessor.removeComments(); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); return preprocessor.calculateHash(""); } diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 600d457e944..904e16141c4 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -608,11 +608,11 @@ class TestTokenizer : public TestFixture { simplecpp::TokenList tokens1(code, files, filename, &outputList); Preprocessor preprocessor(tokens1, settings, *this, Path::identify(tokens1.getFiles()[0], false)); std::list directives; - preprocessor.setLoadCallback([&](const simplecpp::FileData &data) { + preprocessor.setLoadCallback([&](const simplecpp::FileData &data, bool) { Preprocessor::createDirectives(data.tokens, directives); }); preprocessor.createDirectives(directives); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); (void)preprocessor.reportOutput(outputList, true); TokenList tokenlist{settings, Path::identify(filename, false)};