From 65cc0c8b57be17c4797be53d0cc4e83b08beee2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 8 Apr 2025 19:51:25 +0200 Subject: [PATCH 01/13] remove duplicate FileSettings by hashing name and compiler flags --- Makefile | 2 +- cli/cmdlineparser.cpp | 18 +++++++++++++++++- lib/filesettings.h | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8d2d0cc7e73..5570b080a34 100644 --- a/Makefile +++ b/Makefile @@ -665,7 +665,7 @@ cli/executor.o: cli/executor.cpp cli/executor.h lib/addoninfo.h lib/checkers.h l cli/filelister.o: cli/filelister.cpp cli/filelister.h lib/config.h lib/filesettings.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/standards.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/filelister.cpp -cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/config.h lib/errortypes.h lib/filesettings.h lib/mathlib.h lib/path.h lib/platform.h lib/standards.h +cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/config.h lib/errortypes.h lib/filesettings.h lib/mathlib.h lib/path.h lib/platform.h lib/standards.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/main.cpp cli/processexecutor.o: cli/processexecutor.cpp cli/executor.h cli/processexecutor.h lib/addoninfo.h lib/check.h lib/checkers.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index e09c3e2dd60..8eb6a498a3c 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -240,12 +240,28 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) } } - // enforce the language since markup files are special and do not adhere to the enforced language for (auto& fs : fileSettings) { if (mSettings.library.markupFile(fs.filename())) { + // enforce the language since markup files are special and do not adhere to the enforced language fs.file.setLang(Standards::Language::C); } + + fs.computeHash(); + } + + { + auto it = fileSettings.begin(); + while (it != fileSettings.end()) { + fileSettings.erase(std::remove_if(std::next(it), fileSettings.end(), [&](const FileSettings& fs) { + if (fs.hash == it->hash) { + mLogger.printMessage("removing duplicate file " + fs.filename()); + return true; + } + return false; + }), fileSettings.end()); + ++it; + } } // sort the markup last diff --git a/lib/filesettings.h b/lib/filesettings.h index b04f4a1af9e..c28d7e84860 100644 --- a/lib/filesettings.h +++ b/lib/filesettings.h @@ -23,6 +23,7 @@ #include "path.h" #include "platform.h" #include "standards.h" +#include "utils.h" #include #include @@ -88,6 +89,24 @@ struct CPPCHECKLIB FileSettings { : file(std::move(path), lang, size) {} + void computeHash() { + hash = std::hash{}(file.path()); + + hash ^= std::hash{}(standard); + + for (const auto &undef : undefs) + hash ^= std::hash{}(undef); + + for (const auto &includePath : includePaths) + hash ^= std::hash{}(includePath); + + for (const auto &systemIncludePath : systemIncludePaths) + hash ^= std::hash{}(systemIncludePath); + + for (const auto &define : splitString(defines, ';')) + hash ^= std::hash{}(define); + } + std::string cfg; FileWithDetails file; const std::string& filename() const @@ -110,6 +129,7 @@ struct CPPCHECKLIB FileSettings { std::list systemIncludePaths; std::string standard; Platform::Type platformType = Platform::Type::Unspecified; + std::size_t hash; // TODO: get rid of these bool msc{}; bool useMfc{}; From bbf5ae5a5d09696c36a513d41d30ab1a06821797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sun, 13 Apr 2025 11:19:10 +0200 Subject: [PATCH 02/13] index files in files.txt by FileSettings::hash --- lib/analyzerinfo.cpp | 22 +++++++++++----------- lib/analyzerinfo.h | 2 +- lib/cppcheck.cpp | 16 ++++++++++------ lib/cppcheck.h | 2 +- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index 9d5b914c2bd..be459d1086f 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -60,7 +60,7 @@ void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std:: for (const FileSettings &fs : fileSettings) { const std::string afile = getFilename(fs.filename()); - fout << afile << ".a" << (++fileCount[afile]) << ":" << fs.cfg << ":" << Path::simplifyPath(fs.filename()) << std::endl; + fout << afile << ".a" << (++fileCount[afile]) << ":" << std::hex << fs.hash << ":" << Path::simplifyPath(fs.filename()) << std::endl; } } @@ -73,7 +73,7 @@ void AnalyzerInformation::close() } } -static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash, std::list &errors) +static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t filehash, std::list &errors) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(analyzerInfoFile.c_str()); @@ -85,7 +85,7 @@ static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash, return false; const char *attr = rootNode->Attribute("hash"); - if (!attr || attr != std::to_string(hash)) + if (!attr || attr != std::to_string(filehash)) return false; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { @@ -96,10 +96,10 @@ static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash, return true; } -std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg) +std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfghash) { std::string line; - const std::string end(':' + cfg + ':' + Path::simplifyPath(sourcefile)); + const std::string end(':' + cfghash + ':' + Path::simplifyPath(sourcefile)); while (std::getline(filesTxt,line)) { if (line.size() <= end.size() + 2U) continue; @@ -110,11 +110,11 @@ std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& f return ""; } -std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg) +std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfghash) { std::ifstream fin(Path::join(buildDir, "files.txt")); if (fin.is_open()) { - const std::string& ret = getAnalyzerInfoFileFromFilesTxt(fin, sourcefile, cfg); + const std::string& ret = getAnalyzerInfoFileFromFilesTxt(fin, sourcefile, cfghash); if (!ret.empty()) return Path::join(buildDir, ret); } @@ -128,21 +128,21 @@ std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir return Path::join(buildDir, filename) + ".analyzerinfo"; } -bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list &errors) +bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfgHash, std::size_t fileHash, std::list &errors) { if (buildDir.empty() || sourcefile.empty()) return true; close(); - mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg); + mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfgHash); - if (skipAnalysis(mAnalyzerInfoFile, hash, errors)) + if (skipAnalysis(mAnalyzerInfoFile, fileHash, errors)) return false; mOutputStream.open(mAnalyzerInfoFile); if (mOutputStream.is_open()) { mOutputStream << "\n"; - mOutputStream << "\n"; + mOutputStream << "\n"; } else { mAnalyzerInfoFile.clear(); } diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index 04f41ac4e99..06f39f969e2 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -55,7 +55,7 @@ class CPPCHECKLIB AnalyzerInformation { /** Close current TU.analyzerinfo file */ void close(); - bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list &errors); + bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfgHash, std::size_t fileHash, std::list &errors); void reportErr(const ErrorMessage &msg); void setFileInfo(const std::string &check, const std::string &fileInfo); static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg); diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 7024969117a..54799d4ab7a 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -775,7 +775,7 @@ unsigned int CppCheck::check(const FileWithDetails &file) unsigned int CppCheck::check(const FileWithDetails &file, const std::string &content) { std::istringstream iss(content); - return checkFile(file, "", &iss); + return checkFile(file, "", "", &iss); } unsigned int CppCheck::check(const FileSettings &fs) @@ -811,7 +811,9 @@ unsigned int CppCheck::check(const FileSettings &fs) } // need to pass the externally provided ErrorLogger instead of our internal wrapper CppCheck temp(tempSettings, mSuppressions, mErrorLoggerDirect, mUseGlobalSuppressions, mExecuteCommand); - const unsigned int returnValue = temp.checkFile(fs.file, fs.cfg); + std::stringstream hash; + hash << std::hex << fs.hash; + const unsigned int returnValue = temp.checkFile(fs.file, fs.cfg, hash.str()); if (mUnusedFunctionsCheck) mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck); while (!temp.mFileInfo.empty()) { @@ -846,7 +848,7 @@ static std::size_t calculateHash(const Preprocessor& preprocessor, const simplec return preprocessor.calculateHash(tokens, toolinfo.str()); } -unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string &cfgname, std::istream* fileStream) +unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string &cfgname, std::string cfgHash, std::istream* fileStream) { // TODO: move to constructor when CppCheck no longer owns the settings if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) @@ -995,9 +997,9 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string if (analyzerInformation) { // Calculate hash so it can be compared with old hash / future hashes - const std::size_t hash = calculateHash(preprocessor, tokens1, mSettings, mSuppressions); + const std::size_t fileHash = calculateHash(preprocessor, tokens1, mSettings, mSuppressions); std::list errors; - if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, hash, errors)) { + if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgHash, fileHash, errors)) { while (!errors.empty()) { mErrorLogger.reportErr(errors.front()); errors.pop_front(); @@ -1794,7 +1796,9 @@ void CppCheck::executeAddonsWholeProgram(const std::list &files } for (const auto &f: fileSettings) { - const std::string &dumpFileName = getDumpFileName(mSettings, f.filename()); + std::stringstream hash; + hash << std::hex << f.hash; + const std::string &dumpFileName = getDumpFileName(mSettings, f.filename(), hash.str()); ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName)); } diff --git a/lib/cppcheck.h b/lib/cppcheck.h index ea7feeebd55..22d4bd8a772 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -174,7 +174,7 @@ class CPPCHECKLIB CppCheck { * @param fileStream stream the file content can be read from * @return number of errors found */ - unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, std::istream* fileStream = nullptr); + unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, std::string cfgHash = "", std::istream* fileStream = nullptr); /** * @brief Check normal tokens From 45243972dbacb4cbd14305d5354f70f283d43431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 15 Apr 2025 18:19:17 +0200 Subject: [PATCH 03/13] use FileSettings::hash to distinguish dump files --- lib/cppcheck.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 54799d4ab7a..6ff042bdb59 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -331,13 +331,13 @@ static std::vector split(const std::string &str, const std::string return ret; } -static std::string getDumpFileName(const Settings& settings, const std::string& filename) +static std::string getDumpFileName(const Settings& settings, const std::string& filename, std::string cfgHash = "") { std::string extension; - if (settings.dump || !settings.buildDir.empty()) + if ((settings.dump || !settings.buildDir.empty()) && !cfgHash.empty()) extension = ".dump"; else - extension = "." + std::to_string(settings.pid) + ".dump"; + extension = "." + cfgHash + ".dump"; if (!settings.dump && !settings.buildDir.empty()) return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "") + extension; @@ -352,11 +352,12 @@ static std::string getCtuInfoFileName(const std::string &dumpFile) static void createDumpFile(const Settings& settings, const FileWithDetails& file, std::ofstream& fdump, - std::string& dumpFile) + std::string& dumpFile, + std::string cfgHash = "") { if (!settings.dump && settings.addons.empty()) return; - dumpFile = getDumpFileName(settings, file.spath()); + dumpFile = getDumpFileName(settings, file.spath(), cfgHash); fdump.open(dumpFile); if (!fdump.is_open()) @@ -1063,7 +1064,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string // write dump file xml prolog std::ofstream fdump; std::string dumpFile; - createDumpFile(mSettings, file, fdump, dumpFile); + createDumpFile(mSettings, file, fdump, dumpFile, cfgHash); if (fdump.is_open()) { fdump << getLibraryDumpData(); fdump << dumpProlog; From fc62be896be53b05e7e61d361266fce361464fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 15 Apr 2025 19:27:47 +0200 Subject: [PATCH 04/13] fixes --- lib/cppcheck.cpp | 6 +++--- lib/cppcheck.h | 2 +- lib/filesettings.h | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 6ff042bdb59..f664da6deb2 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -331,7 +331,7 @@ static std::vector split(const std::string &str, const std::string return ret; } -static std::string getDumpFileName(const Settings& settings, const std::string& filename, std::string cfgHash = "") +static std::string getDumpFileName(const Settings& settings, const std::string& filename, const std::string &cfgHash = "") { std::string extension; if ((settings.dump || !settings.buildDir.empty()) && !cfgHash.empty()) @@ -353,7 +353,7 @@ static void createDumpFile(const Settings& settings, const FileWithDetails& file, std::ofstream& fdump, std::string& dumpFile, - std::string cfgHash = "") + const std::string &cfgHash = "") { if (!settings.dump && settings.addons.empty()) return; @@ -849,7 +849,7 @@ static std::size_t calculateHash(const Preprocessor& preprocessor, const simplec return preprocessor.calculateHash(tokens, toolinfo.str()); } -unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string &cfgname, std::string cfgHash, std::istream* fileStream) +unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string &cfgname, const std::string &cfgHash, std::istream* fileStream) { // TODO: move to constructor when CppCheck no longer owns the settings if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) diff --git a/lib/cppcheck.h b/lib/cppcheck.h index 22d4bd8a772..0cd4b3a1483 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -174,7 +174,7 @@ class CPPCHECKLIB CppCheck { * @param fileStream stream the file content can be read from * @return number of errors found */ - unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, std::string cfgHash = "", std::istream* fileStream = nullptr); + unsigned int checkFile(const FileWithDetails& file, const std::string &cfgname, const std::string &cfgHash = "", std::istream* fileStream = nullptr); /** * @brief Check normal tokens diff --git a/lib/filesettings.h b/lib/filesettings.h index c28d7e84860..6caaa86ff4d 100644 --- a/lib/filesettings.h +++ b/lib/filesettings.h @@ -83,10 +83,12 @@ class FileWithDetails struct CPPCHECKLIB FileSettings { explicit FileSettings(std::string path) : file(std::move(path)) + , hash(0) {} FileSettings(std::string path, Standards::Language lang, std::size_t size) : file(std::move(path), lang, size) + , hash(0) {} void computeHash() { From 0dc00b3976cc01206efe0ea0185af563b905911c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Apr 2025 06:41:55 +0200 Subject: [PATCH 05/13] account for ordering of options when computing FileSettings::hash --- lib/filesettings.h | 16 ++++++++++++---- lib/utils.h | 6 ++++++ test/testutils.cpp | 9 +++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/filesettings.h b/lib/filesettings.h index 6caaa86ff4d..83cbae694b3 100644 --- a/lib/filesettings.h +++ b/lib/filesettings.h @@ -96,17 +96,25 @@ struct CPPCHECKLIB FileSettings { hash ^= std::hash{}(standard); - for (const auto &undef : undefs) + for (const auto &undef : undefs) { + hash = rotateLeft(hash, 1); hash ^= std::hash{}(undef); + } - for (const auto &includePath : includePaths) + for (const auto &includePath : includePaths) { + hash = rotateLeft(hash, 1); hash ^= std::hash{}(includePath); + } - for (const auto &systemIncludePath : systemIncludePaths) + for (const auto &systemIncludePath : systemIncludePaths) { + hash = rotateLeft(hash, 1); hash ^= std::hash{}(systemIncludePath); + } - for (const auto &define : splitString(defines, ';')) + for (const auto &define : splitString(defines, ';')) { + hash = rotateLeft(hash, 1); hash ^= std::hash{}(define); + } } std::string cfg; diff --git a/lib/utils.h b/lib/utils.h index 18146e91043..15205c8b644 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -397,6 +397,12 @@ static inline T* empty_if_null(T* p) return default_if_null(p, ""); } +template +static inline T rotateLeft(T value, std::size_t amount) +{ + return (value << amount) | (value >> (8 * sizeof(T) - amount % (8 * sizeof(T)))); +} + /** * Split string by given sperator. * @param str The string to split diff --git a/test/testutils.cpp b/test/testutils.cpp index d1150b7b0d2..ef0f971d98d 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -44,6 +44,7 @@ class TestUtils : public TestFixture { TEST_CASE(splitString); TEST_CASE(as_const); TEST_CASE(memoize); + TEST_CASE(rotateLeft); } void isValidGlobPattern() const { @@ -535,6 +536,14 @@ class TestUtils : public TestFixture { ASSERT_EQUALS(1, callF()); ASSERT_EQUALS(1, count); } + + void rotateLeft() const { + uint8_t value_u8 = 0b01010101; + ASSERT_EQUALS(::rotateLeft(value_u8, 1), 0b10101010); + + uint64_t value_u64 = 0xABCDEF0123456789; + ASSERT_EQUALS(::rotateLeft(value_u64, 8), 0xCDEF0123456789AB); + } }; REGISTER_TEST(TestUtils) From ded6cd07dbc51b1ece8a986f1af08530d5d08edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Apr 2025 06:42:20 +0200 Subject: [PATCH 06/13] reset stream flags --- lib/analyzerinfo.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index be459d1086f..94e9c668df0 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -58,10 +58,13 @@ void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std:: fout << afile << ".a" << (++fileCount[afile]) << ":" << userDefines << ":" << Path::simplifyPath(f) << '\n'; } + std::ios savedFlags(0); + savedFlags.copyfmt(fout); for (const FileSettings &fs : fileSettings) { const std::string afile = getFilename(fs.filename()); fout << afile << ".a" << (++fileCount[afile]) << ":" << std::hex << fs.hash << ":" << Path::simplifyPath(fs.filename()) << std::endl; } + fout.copyfmt(savedFlags); } void AnalyzerInformation::close() From ac89b1eba6836394e8185153f6ba06f8911d3874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Apr 2025 10:39:05 +0200 Subject: [PATCH 07/13] include vs config in FileSettings::hash --- lib/filesettings.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/filesettings.h b/lib/filesettings.h index 83cbae694b3..0ad3aeeea38 100644 --- a/lib/filesettings.h +++ b/lib/filesettings.h @@ -115,6 +115,8 @@ struct CPPCHECKLIB FileSettings { hash = rotateLeft(hash, 1); hash ^= std::hash{}(define); } + + hash ^= std::hash{}(cfg); } std::string cfg; From f2c346fddb1b20895f3945189ee7b03ecf6bb66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Apr 2025 15:11:57 +0200 Subject: [PATCH 08/13] add back PID to dumpfile name --- lib/cppcheck.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index f664da6deb2..9caf6b62a53 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -334,10 +334,14 @@ static std::vector split(const std::string &str, const std::string static std::string getDumpFileName(const Settings& settings, const std::string& filename, const std::string &cfgHash = "") { std::string extension; - if ((settings.dump || !settings.buildDir.empty()) && !cfgHash.empty()) - extension = ".dump"; - else - extension = "." + cfgHash + ".dump"; + + if (!settings.dump && settings.buildDir.empty()) + extension += "." + std::to_string(settings.pid); + + if (!cfgHash.empty()) + extension += "." + cfgHash; + + extension += ".dump"; if (!settings.dump && !settings.buildDir.empty()) return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "") + extension; From 5b43ac63293a1772cb498883385547d328a29c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Apr 2025 15:57:55 +0200 Subject: [PATCH 09/13] add --deduplicate option for controlling deduplication of FileSettings --- cli/cmdlineparser.cpp | 7 ++++++- lib/settings.h | 3 +++ man/cppcheck.1.xml | 11 +++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 8eb6a498a3c..a95ac385b60 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -250,7 +250,7 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) fs.computeHash(); } - { + if (mSettings.deduplicate) { auto it = fileSettings.begin(); while (it != fileSettings.end()) { fileSettings.erase(std::remove_if(std::next(it), fileSettings.end(), [&](const FileSettings& fs) { @@ -651,6 +651,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a else if (std::strcmp(argv[i], "--debug-clang-output") == 0) mSettings.debugClangOutput = true; + else if (std::strcmp(argv[i], "--deduplicate") == 0) + mSettings.deduplicate = true; + // Show debug messages for ignored files else if (std::strcmp(argv[i], "--debug-ignore") == 0) mSettings.debugignore = true; @@ -1718,6 +1721,8 @@ void CmdLineParser::printHelp() const " be considered for evaluation.\n" " --config-excludes-file=\n" " A file that contains a list of config-excludes\n" + " --deduplicate Remove duplicate entries when reading project files.\n" + " Takes both compilation flags and filenames in to account.\n" " --disable= Disable individual checks.\n" " Please refer to the documentation of --enable=\n" " for further details.\n" diff --git a/lib/settings.h b/lib/settings.h index 930b8776d18..e1217517ec8 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -220,6 +220,9 @@ class CPPCHECKLIB WARN_UNUSED Settings { /** @brief Do not filter duplicated errors. */ bool emitDuplicates{}; + /** @brief De-duplicate files with identical path and config. */ + bool deduplicate{}; + /** @brief Name of the language that is enforced. Empty per default. */ Standards::Language enforcedLang{}; diff --git a/man/cppcheck.1.xml b/man/cppcheck.1.xml index 1265e0f3511..1ebe2a0da0b 100644 --- a/man/cppcheck.1.xml +++ b/man/cppcheck.1.xml @@ -150,6 +150,9 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + + + @@ -441,6 +444,14 @@ First given path is searched for contained header files first. If paths are rela A file that contains a list of config-excludes. + + + + + + Remove duplicate entries when reading project files. Takes both compilation flags and filenames in to account. + + From ff517942c9193a60797b76c8f92e7402496fc3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Apr 2025 17:12:54 +0200 Subject: [PATCH 10/13] fix some warnings --- lib/analyzerinfo.cpp | 2 +- lib/analyzerinfo.h | 4 ++-- lib/utils.h | 2 +- test/testutils.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index 94e9c668df0..977d9d05608 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -58,7 +58,7 @@ void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std:: fout << afile << ".a" << (++fileCount[afile]) << ":" << userDefines << ":" << Path::simplifyPath(f) << '\n'; } - std::ios savedFlags(0); + std::ios savedFlags(nullptr); savedFlags.copyfmt(fout); for (const FileSettings &fs : fileSettings) { const std::string afile = getFilename(fs.filename()); diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index 06f39f969e2..3403570d284 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -58,9 +58,9 @@ class CPPCHECKLIB AnalyzerInformation { bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfgHash, std::size_t fileHash, std::list &errors); void reportErr(const ErrorMessage &msg); void setFileInfo(const std::string &check, const std::string &fileInfo); - static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg); + static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfghash); protected: - static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg); + static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfghash); private: std::ofstream mOutputStream; std::string mAnalyzerInfoFile; diff --git a/lib/utils.h b/lib/utils.h index 15205c8b644..5f3d50f59f2 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -400,7 +400,7 @@ static inline T* empty_if_null(T* p) template static inline T rotateLeft(T value, std::size_t amount) { - return (value << amount) | (value >> (8 * sizeof(T) - amount % (8 * sizeof(T)))); + return (value << amount) | (value >> ((8 * sizeof(T)) - (amount % (8 * sizeof(T))))); } /** diff --git a/test/testutils.cpp b/test/testutils.cpp index ef0f971d98d..44a6d7f8dfd 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -538,8 +538,8 @@ class TestUtils : public TestFixture { } void rotateLeft() const { - uint8_t value_u8 = 0b01010101; - ASSERT_EQUALS(::rotateLeft(value_u8, 1), 0b10101010); + uint8_t value_u8 = 0x55; + ASSERT_EQUALS(::rotateLeft(value_u8, 1), 0xAA); uint64_t value_u64 = 0xABCDEF0123456789; ASSERT_EQUALS(::rotateLeft(value_u64, 8), 0xCDEF0123456789AB); From 9503fe95110d28d5662a8273faa8b50c2c9b2717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 18 Apr 2025 09:58:28 +0200 Subject: [PATCH 11/13] change option for controlling deduplication deduplicate by default --- cli/cmdlineparser.cpp | 12 +++++++----- lib/settings.h | 4 ++-- man/cppcheck.1.xml | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index a95ac385b60..130b6a85545 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -250,7 +250,7 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) fs.computeHash(); } - if (mSettings.deduplicate) { + if (!mSettings.recheckProjectDuplicates) { auto it = fileSettings.begin(); while (it != fileSettings.end()) { fileSettings.erase(std::remove_if(std::next(it), fileSettings.end(), [&](const FileSettings& fs) { @@ -651,8 +651,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a else if (std::strcmp(argv[i], "--debug-clang-output") == 0) mSettings.debugClangOutput = true; - else if (std::strcmp(argv[i], "--deduplicate") == 0) - mSettings.deduplicate = true; + else if (std::strcmp(argv[i], "--recheck-project-duplicates") == 0) + mSettings.recheckProjectDuplicates = true; // Show debug messages for ignored files else if (std::strcmp(argv[i], "--debug-ignore") == 0) @@ -1721,8 +1721,10 @@ void CmdLineParser::printHelp() const " be considered for evaluation.\n" " --config-excludes-file=\n" " A file that contains a list of config-excludes\n" - " --deduplicate Remove duplicate entries when reading project files.\n" - " Takes both compilation flags and filenames in to account.\n" + " --recheck-project-duplicates\n" + " When using the --project option, files with identical\n" + " names and compiler flags are deduplicated by default.\n" + " This option forces cppcheck to check all imported files.\n" " --disable= Disable individual checks.\n" " Please refer to the documentation of --enable=\n" " for further details.\n" diff --git a/lib/settings.h b/lib/settings.h index e1217517ec8..2c950eb7e6d 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -220,8 +220,8 @@ class CPPCHECKLIB WARN_UNUSED Settings { /** @brief Do not filter duplicated errors. */ bool emitDuplicates{}; - /** @brief De-duplicate files with identical path and config. */ - bool deduplicate{}; + /** @brief Recheck project files with identical path and config. */ + bool recheckProjectDuplicates{}; /** @brief Name of the language that is enforced. Empty per default. */ Standards::Language enforcedLang{}; diff --git a/man/cppcheck.1.xml b/man/cppcheck.1.xml index 1ebe2a0da0b..cad94bdd3f0 100644 --- a/man/cppcheck.1.xml +++ b/man/cppcheck.1.xml @@ -151,7 +151,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ - + @@ -446,10 +446,10 @@ First given path is searched for contained header files first. If paths are rela - + - Remove duplicate entries when reading project files. Takes both compilation flags and filenames in to account. + When using the --project option, files with identical names and compiler flags are deduplicated by default. This option forces cppcheck to check all imported files. From dab6ae6b082848185d68edda9c17ed2faf25229a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 18 Apr 2025 13:59:52 +0200 Subject: [PATCH 12/13] remove warning message --- cli/cmdlineparser.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 130b6a85545..f10b55f9aae 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -254,11 +254,7 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) auto it = fileSettings.begin(); while (it != fileSettings.end()) { fileSettings.erase(std::remove_if(std::next(it), fileSettings.end(), [&](const FileSettings& fs) { - if (fs.hash == it->hash) { - mLogger.printMessage("removing duplicate file " + fs.filename()); - return true; - } - return false; + return fs.hash == it->hash; }), fileSettings.end()); ++it; } From 4937d15c59f6a839ed7fe0041a3da022e9c4e4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 22 Apr 2025 10:39:14 +0200 Subject: [PATCH 13/13] add python tests --- .../compile_commands_duplicates.json | 12 +++++ .../compile_commands_no_duplicates.json | 12 +++++ test/cli/duplicates/main.cpp | 6 +++ test/cli/duplicates_test.py | 52 +++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 test/cli/duplicates/compile_commands_duplicates.json create mode 100644 test/cli/duplicates/compile_commands_no_duplicates.json create mode 100644 test/cli/duplicates/main.cpp create mode 100644 test/cli/duplicates_test.py diff --git a/test/cli/duplicates/compile_commands_duplicates.json b/test/cli/duplicates/compile_commands_duplicates.json new file mode 100644 index 00000000000..4b1b0dea974 --- /dev/null +++ b/test/cli/duplicates/compile_commands_duplicates.json @@ -0,0 +1,12 @@ +[ +{ + "directory": "./.", + "command": "/usr/bin/c++ -std=gnu++11 -c ./main.cpp", + "file": "./main.cpp" +}, +{ + "directory": "./.", + "command": "/usr/bin/c++ -std=gnu++11 -c ./main.cpp", + "file": "./main.cpp" +} +] diff --git a/test/cli/duplicates/compile_commands_no_duplicates.json b/test/cli/duplicates/compile_commands_no_duplicates.json new file mode 100644 index 00000000000..95a7d686e10 --- /dev/null +++ b/test/cli/duplicates/compile_commands_no_duplicates.json @@ -0,0 +1,12 @@ +[ +{ + "directory": "./.", + "command": "/usr/bin/c++ -std=gnu++11 -c ./main.cpp", + "file": "./main.cpp" +}, +{ + "directory": "./.", + "command": "/usr/bin/c++ -DHELLO -std=gnu++11 -c ./main.cpp", + "file": "./main.cpp" +} +] diff --git a/test/cli/duplicates/main.cpp b/test/cli/duplicates/main.cpp new file mode 100644 index 00000000000..17e5b891736 --- /dev/null +++ b/test/cli/duplicates/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) +{ + std::cout << "Hello, world!" << std::endl; +} diff --git a/test/cli/duplicates_test.py b/test/cli/duplicates_test.py new file mode 100644 index 00000000000..8a54fe60938 --- /dev/null +++ b/test/cli/duplicates_test.py @@ -0,0 +1,52 @@ + +# python -m pytest test-helloworld.py + +import os + +from testutils import cppcheck + +__script_dir = os.path.dirname(os.path.abspath(__file__)) +__proj_dir = os.path.join(__script_dir, 'duplicates') + +def test_project_duplicates(): + args = ['--project=compile_commands_duplicates.json'] + ret, stdout, _ = cppcheck(args, cwd=__proj_dir) + assert ret == 0 + assert stdout.count('main.cpp') == 1 + +def test_project_duplicates_recheck(): + args = [ + '--project=compile_commands_duplicates.json', + '--recheck-project-duplicates' + ] + ret, stdout, _ = cppcheck(args, cwd=__proj_dir) + assert ret == 0 + assert stdout.count('main.cpp') > 1 + +def test_project_no_duplicates(): + args = ['--project=compile_commands_no_duplicates.json'] + ret, stdout, _ = cppcheck(args, cwd=__proj_dir) + assert ret == 0 + assert stdout.count('main.cpp') > 1 + +def test_project_duplicates_builddir(tmpdir): + args = [ + '--project=compile_commands_duplicates.json', + f'--cppcheck-build-dir={tmpdir}' + ] + ret, _, _ = cppcheck(args, cwd=__proj_dir) + assert ret == 0 + files = os.listdir(tmpdir) + assert 'main.a1' in files + assert 'main.a2' not in files + +def test_project_no_duplicates_builddir(tmpdir): + args = [ + '--project=compile_commands_no_duplicates.json', + f'--cppcheck-build-dir={tmpdir}' + ] + ret, _, _ = cppcheck(args, cwd=__proj_dir) + assert ret == 0 + files = os.listdir(tmpdir) + assert 'main.a1' in files + assert 'main.a2' in files