From c430a25ab6b3ba358cc42715eec6223a7de6e2e1 Mon Sep 17 00:00:00 2001 From: Allen Winter Date: Tue, 8 Apr 2025 15:54:14 -0400 Subject: [PATCH 01/18] cfg/kde.cfg - add more KDE macros that are widely used --- cfg/kde.cfg | 5 +++++ test/CMakeLists.txt | 2 +- test/cfg/kde.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++- test/cfg/runtests.sh | 2 +- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/cfg/kde.cfg b/cfg/kde.cfg index 7d18998a29f..1131d5e0c50 100644 --- a/cfg/kde.cfg +++ b/cfg/kde.cfg @@ -20,6 +20,11 @@ + + + + + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 467e6e95e4e..3b516f38965 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -148,7 +148,7 @@ if (BUILD_TESTS) add_cfg(gnu.c ADD_LIBRARY posix) add_cfg(googletest.cpp) add_cfg(gtk.c) - add_cfg(kde.cpp) + add_cfg(kde.cpp ADD_LIBRARY qt) add_cfg(libcurl.c) add_cfg(libsigc++.cpp) add_cfg(lua.c) diff --git a/test/cfg/kde.cpp b/test/cfg/kde.cpp index 069aa5d2d48..71533ca781f 100644 --- a/test/cfg/kde.cpp +++ b/test/cfg/kde.cpp @@ -2,7 +2,7 @@ // Test library configuration for kde.cfg // // Usage: -// $ cppcheck --check-library --library=kde --enable=style,information --inconclusive --error-exitcode=1 --inline-suppr test/cfg/kde.cpp +// $ cppcheck --check-library --library=kde --library=qt --enable=style,information --inconclusive --error-exitcode=1 --inline-suppr test/cfg/kde.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // @@ -11,6 +11,7 @@ #include #include #include +#include class k_global_static_testclass1 {}; K_GLOBAL_STATIC(k_global_static_testclass1, k_global_static_testinstance1); @@ -41,3 +42,45 @@ void i18n_test() (void)xi18nc("Text", "Context"); (void)ki18nc("Text", "Context"); } + +class PluginWithoutMetaData : public QObject +{ + Q_OBJECT +public: + // Add a default arg to make sure we do not get an ambiguity compiler error + explicit PluginWithoutMetaData(const QObject *, const QVariantList &args = {}) + : QObject() + { + Q_UNUSED(args) + }; +}; + +K_PLUGIN_CLASS(PluginWithoutMetaData) + +class StaticSimplePluginClass : public QObject +{ + Q_OBJECT + +public: + // Next to the assertion below, ensure that we have no ambiguity! + explicit StaticSimplePluginClass(QObject *parent, const QString &data = {}) + : QObject(parent) + { + // We have added a default arg, but KPluginFactory should still provide the valid metadata instead of the default one + data = QString("foo"); + } +}; +K_PLUGIN_CLASS_WITH_JSON(StaticSimplePluginClass, "data/jsonplugin.json") + +class ClipboardPlugin : public Purpose::PluginBase +{ + Q_OBJECT +public: + using PluginBase::PluginBase; + Purpose::Job *createJob() const override + { + return new ClipboardJob(nullptr); + } +}; + +K_PLUGIN_FACTORY_WITH_JSON(Clipboard, "clipboardplugin.json", foo) diff --git a/test/cfg/runtests.sh b/test/cfg/runtests.sh index 1ff731abe16..bfc5e8f04f3 100755 --- a/test/cfg/runtests.sh +++ b/test/cfg/runtests.sh @@ -507,7 +507,7 @@ function check_file { kde.cpp) # TODO: "kde-4config" is no longer commonly available in recent distros #kde_fn - "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" --library=qt "${DIR}""$f" ;; libcurl.c) libcurl_fn From 10811f782b825a32ba9610ca9ebd7633f4fb4808 Mon Sep 17 00:00:00 2001 From: Allen Winter Date: Mon, 5 May 2025 11:41:21 -0400 Subject: [PATCH 02/18] Add definitions for Q_DECL_DEPRECATED and Q_DECL_DEPRECATED_X Add support for Qt macros Q_DECL_DEPRECATED and Q_DECL_DEPRECATED_X for people use the Qt library. --- cfg/qt.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cfg/qt.cfg b/cfg/qt.cfg index 4b79e1b73bf..cfb53d11db0 100644 --- a/cfg/qt.cfg +++ b/cfg/qt.cfg @@ -5326,6 +5326,8 @@ + + From 45fe1e1d84185322edbf8a66ef7e7f974aa02482 Mon Sep 17 00:00:00 2001 From: Allen Winter Date: Wed, 25 Jun 2025 15:57:13 -0400 Subject: [PATCH 03/18] cfg/qt.cfg - Change the Q_D definition to avoid false positives Change the O_D macro definition by moving the 'const' qualifier to the front of the statement. Without this change we get false positives "style: Variable 'd' can be declared as pointer to const [constVariablePointer]" The current definition follows Qt correctly as we see this define in the `QtCore/qtclasshelpermacros.h` file: Q_D(Class) Class##Private * const d = d_func() but apparently cppcheck doesn't properly recognize that as a const variable pointer. --- cfg/qt.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/qt.cfg b/cfg/qt.cfg index 3578cbe70f8..2ad398be8ed 100644 --- a/cfg/qt.cfg +++ b/cfg/qt.cfg @@ -5315,7 +5315,7 @@ - + From ac301fbc531f59d5f96bf9f648dfc345181f29b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 26 Jun 2025 10:54:57 +0200 Subject: [PATCH 04/18] fix #13960: False negative: no error when freeing struct member twice (regression) (#7626) --- lib/checkleakautovar.cpp | 12 ++++++------ test/testleakautovar.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 6d874e15ebb..3bf447e9da1 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -413,8 +413,6 @@ bool CheckLeakAutoVar::checkScope(const Token * const startToken, // assignment.. if (const Token* const tokAssignOp = isInit ? varTok : isAssignment(varTok)) { - if (Token::simpleMatch(tokAssignOp->astOperand1(), ".")) - continue; // taking address of another variable.. if (Token::Match(tokAssignOp, "= %var% +|;|?|%comp%")) { if (varTok->tokAt(2)->varId() != varTok->varId()) { @@ -730,8 +728,7 @@ bool CheckLeakAutoVar::checkScope(const Token * const startToken, VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC, ftok); if (allocation.type == 0) allocation.status = VarInfo::NOALLOC; - if (Token::simpleMatch(ftok->astParent(), "(") && Token::simpleMatch(ftok->astParent()->astOperand2(), ".")) - continue; + functionCall(ftok, openingPar, varInfo, allocation, af); tok = ftok->linkAt(1); @@ -886,7 +883,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t while (rhs->isCast()) { rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); } - if (rhs->varId() == tok->varId() && isAssignment) { + if ((rhs->str() == "." || rhs->varId() == tok->varId()) && isAssignment) { // simple assignment varInfo.erase(tok->varId()); } else if (rhs->astParent() && rhs->str() == "(" && !mSettings->library.returnValue(rhs->astOperand1()).empty()) { @@ -1024,7 +1021,10 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1(); const Token * const argTypeStartTok = arg; - while (Token::Match(arg, "%name% :: %name%")) + if (Token::simpleMatch(arg, ".")) + arg = arg->next(); + + while (Token::Match(arg, "%name% .|:: %name%")) arg = arg->tokAt(2); if ((Token::Match(arg, "%var% [-,)] !!.") && !(arg->variable() && arg->variable()->isArray())) || diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 41f0e75121b..3ff11ae5293 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -109,6 +109,7 @@ class TestLeakAutoVar : public TestFixture { TEST_CASE(doublefree16); TEST_CASE(doublefree17); // #8109: delete with comma operator TEST_CASE(doublefree18); + TEST_CASE(doublefree19); // #13960 // exit TEST_CASE(exit1); @@ -1814,6 +1815,18 @@ class TestLeakAutoVar : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void doublefree19() { + check("struct S {\n" + " void *x;\n" + "};\n" + "void f(struct S *p)\n" + "{\n" + " free(p->x);\n" + " free(p->x);\n" + "}\n"); + ASSERT_EQUALS("[test.c:6:3] -> [test.c:7:3]: (error) Memory pointed to by 'x' is freed twice. [doubleFree]\n", errout_str()); + } + void exit1() { check("void f() {\n" " char *p = malloc(10);\n" From 48c1424e7efd43299df217381075aafbde07e609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 26 Jun 2025 12:44:29 +0200 Subject: [PATCH 05/18] fix #13961: Additional information for inline suppression in XML output (#7622) --- cli/processexecutor.cpp | 8 +++++++- lib/suppressions.cpp | 27 +++++++++++++++++++++------ lib/suppressions.h | 1 + test/cli/inline-suppress_test.py | 14 ++++++++++++++ test/cli/premium_test.py | 2 +- test/testsuppressions.cpp | 11 +++++++++++ 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index f2a69976e93..af9041bd555 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -116,6 +116,8 @@ namespace { suppr_str += suppr.checked ? "1" : "0"; suppr_str += ";"; suppr_str += suppr.matched ? "1" : "0"; + suppr_str += ";"; + suppr_str += suppr.extraComment; return suppr_str; } @@ -239,7 +241,7 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str if (!buf.empty()) { // TODO: avoid string splitting auto parts = splitString(buf, ';'); - if (parts.size() != 3) + if (parts.size() < 4) { // TODO: make this non-fatal std::cerr << "#### ThreadExecutor::handleRead(" << filename << ") adding of inline suppression failed - insufficient data" << std::endl; @@ -249,6 +251,10 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str suppr.isInline = (type == PipeWriter::REPORT_SUPPR_INLINE); suppr.checked = parts[1] == "1"; suppr.matched = parts[2] == "1"; + suppr.extraComment = parts[3]; + for (std::size_t i = 4; i < parts.size(); i++) { + suppr.extraComment += ";" + parts[i]; + } const std::string err = mSuppressions.nomsg.addSuppression(suppr); if (!err.empty()) { // TODO: only update state if it doesn't exist - otherwise propagate error diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 7f2351878c0..6255927cf75 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -334,15 +334,24 @@ bool SuppressionList::Suppression::parseComment(std::string comment, std::string if (comment.size() < 2) return false; - if (comment.find(';') != std::string::npos) - comment.erase(comment.find(';')); - - if (comment.find("//", 2) != std::string::npos) - comment.erase(comment.find("//",2)); - if (comment.compare(comment.size() - 2, 2, "*/") == 0) comment.erase(comment.size() - 2, 2); + std::string::size_type extraPos = comment.find(';'); + std::string::size_type extraDelimiterSize = 1; + + if (extraPos == std::string::npos) { + extraPos = comment.find("//", 2); + extraDelimiterSize = 2; + } + + if (extraPos != std::string::npos) { + extraComment = trim(comment.substr(extraPos + extraDelimiterSize)); + for (auto it = extraComment.begin(); it != extraComment.end();) + it = *it & 0x80 ? extraComment.erase(it) : it + 1; + comment.erase(extraPos); + } + const std::set cppchecksuppress{ "cppcheck-suppress", "cppcheck-suppress-begin", @@ -532,6 +541,12 @@ void SuppressionList::dump(std::ostream & out) const out << " type=\"blockEnd\""; else if (suppression.type == SuppressionList::Type::macro) out << " type=\"macro\""; + if (suppression.isInline) + out << " inline=\"true\""; + else + out << " inline=\"false\""; + if (!suppression.extraComment.empty()) + out << " comment=\"" << ErrorLogger::toxml(suppression.extraComment) << "\""; out << " />" << std::endl; } out << " " << std::endl; diff --git a/lib/suppressions.h b/lib/suppressions.h index 5e4cccb3890..11a92b320c2 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -151,6 +151,7 @@ class CPPCHECKLIB SuppressionList { std::string errorId; std::string fileName; + std::string extraComment; int lineNumber = NO_LINE; int lineBegin = NO_LINE; int lineEnd = NO_LINE; diff --git a/test/cli/inline-suppress_test.py b/test/cli/inline-suppress_test.py index a2bf847a2a7..9c7421656d2 100644 --- a/test/cli/inline-suppress_test.py +++ b/test/cli/inline-suppress_test.py @@ -55,6 +55,20 @@ def test_2(): assert ret == 0, stdout +def test_xml(): + args = [ + '-q', + '--template=simple', + '--inline-suppr', + '--xml-version=3', + 'proj-inline-suppress' + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + assert '' in stderr + assert stdout == '' + assert ret == 0, stdout + + def test_unmatched_suppression(): args = [ '-q', diff --git a/test/cli/premium_test.py b/test/cli/premium_test.py index b0df148f22b..2cfd6974c38 100644 --- a/test/cli/premium_test.py +++ b/test/cli/premium_test.py @@ -69,7 +69,7 @@ def test_misra_c_builtin_style_checks(tmpdir): exitcode, _, stderr = cppcheck(['--xml-version=3', '--suppress=foo', test_file], cppcheck_exe=exe) assert exitcode == 0 - assert '' in stderr + assert '' in stderr def test_build_dir_hash_cppcheck_product(tmpdir): diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 5176f2b60d6..980ac3b19f2 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1014,6 +1014,17 @@ class TestSuppressions : public TestFixture { msg.clear(); ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id */", &msg)); ASSERT_EQUALS("", msg); + ASSERT_EQUALS("", s.extraComment); + + msg.clear(); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id ; extra */", &msg)); + ASSERT_EQUALS("", msg); + ASSERT_EQUALS("extra", s.extraComment); + + msg.clear(); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id // extra */", &msg)); + ASSERT_EQUALS("", msg); + ASSERT_EQUALS("extra", s.extraComment); msg.clear(); ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-file id */", &msg)); From 819bf138b42decfadadc39cf58a23d0d2f05b1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Thu, 26 Jun 2025 14:24:42 +0200 Subject: [PATCH 06/18] reduced usage of `os.chdir()` in Python scripts (#6510) --- htmlreport/cppcheck-htmlreport | 11 +++-------- tools/test-my-pr.py | 6 ++---- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/htmlreport/cppcheck-htmlreport b/htmlreport/cppcheck-htmlreport index a70fbdb4c83..10300ea50ec 100755 --- a/htmlreport/cppcheck-htmlreport +++ b/htmlreport/cppcheck-htmlreport @@ -441,9 +441,8 @@ def git_blame(errors, path, file, blame_options): full_path = os.path.join(path, file) path, filename = os.path.split(full_path) - cwd = os.getcwd() - if path: - os.chdir(path) + if not path: + path = None # make sure this is None cmd_args = ['git', 'blame', '-L %d,%d' % (first_line, last_line)] if '-w' in blame_options: @@ -453,12 +452,10 @@ def git_blame(errors, path, file, blame_options): cmd_args = cmd_args + ['--porcelain', '--incremental', '--', filename] try: - result = subprocess.check_output(cmd_args) + result = subprocess.check_output(cmd_args, cwd=path) result = result.decode(locale.getpreferredencoding()) except: return [] - finally: - os.chdir(cwd) if result.startswith('fatal'): return [] @@ -704,7 +701,6 @@ def main() -> None: parser.error('No report directory set.') # Get the directory where source code files are located. - cwd = os.getcwd() source_dir = os.getcwd() if options.source_dir: source_dir = options.source_dir @@ -990,7 +986,6 @@ def main() -> None: sys.stderr.write("\nConsider changing source-encoding (for example: \"htmlreport ... --source-encoding=\"iso8859-1\"\"\n") print('Creating style.css file') - os.chdir(cwd) # going back to the cwd to find style.css with io.open(os.path.join(options.report_dir, 'style.css'), 'w') as css_file: css_file.write(STYLE_FILE) diff --git a/tools/test-my-pr.py b/tools/test-my-pr.py index de897c09bd6..4a40f544ee1 100755 --- a/tools/test-my-pr.py +++ b/tools/test-my-pr.py @@ -52,7 +52,7 @@ def format_float(a, b=1): main_dir = os.path.join(work_path, 'tree-main') lib.set_jobs('-j' + str(args.j)) - result_file = os.path.join(work_path, args.o) + result_file = os.path.abspath(os.path.join(work_path, args.o)) (f, ext) = os.path.splitext(result_file) timing_file = f + '_timing' + ext your_repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) @@ -75,8 +75,7 @@ def format_float(a, b=1): sys.exit(1) try: - os.chdir(your_repo_dir) - commit_id = (subprocess.check_output(['git', 'merge-base', 'origin/main', 'HEAD'])).strip().decode('ascii') + commit_id = (subprocess.check_output(['git', 'merge-base', 'origin/main', 'HEAD'], cwd=your_repo_dir)).strip().decode('ascii') with open(result_file, 'a') as myfile: myfile.write('Common ancestor: ' + commit_id + '\n\n') package_width = '140' @@ -85,7 +84,6 @@ def format_float(a, b=1): myfile.write('{:{package_width}} {:{timing_width}} {:{timing_width}} {:{timing_width}}\n'.format( 'Package', 'main', 'your', 'Factor', package_width=package_width, timing_width=timing_width)) - os.chdir(main_dir) subprocess.check_call(['git', 'fetch', '--depth=1', 'origin', commit_id]) subprocess.check_call(['git', 'checkout', '-f', commit_id]) except BaseException as e: From c68a575c5f1bc42c18ceeade788c7e041fbae8dd Mon Sep 17 00:00:00 2001 From: glankk Date: Fri, 27 Jun 2025 13:53:34 +0200 Subject: [PATCH 07/18] Fix #13852 (GUI does not finish analysis properly) (#7633) Create new `QThread`s for each check, as well as for whole-program analysis, instead of restarting finished threads which is undefined behavior (see https://doc.qt.io/qt-6/qthread.html#create). Fixes Trac #13852. --- gui/threadhandler.cpp | 60 +++++++++++++++++++++++++------------------ gui/threadhandler.h | 36 +++++++++++++++++++------- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index fecfb44b23b..7363e167939 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -38,9 +38,7 @@ ThreadHandler::ThreadHandler(QObject *parent) : QObject(parent) -{ - setThreadCount(1); -} +{} ThreadHandler::~ThreadHandler() { @@ -54,7 +52,7 @@ void ThreadHandler::clearFiles() mAnalyseWholeProgram = false; mCtuInfo.clear(); mAddonsAndTools.clear(); - mSuppressions.clear(); + mSuppressionsUI.clear(); } void ThreadHandler::setFiles(const QStringList &files) @@ -83,6 +81,14 @@ void ThreadHandler::setCheckFiles(const QStringList& files) } } +void ThreadHandler::setupCheckThread(CheckThread &thread) const +{ + thread.setAddonsAndTools(mCheckAddonsAndTools); + thread.setSuppressions(mSuppressionsUI); + thread.setClangIncludePaths(mClangIncludePaths); + thread.setSettings(mCheckSettings, mCheckSuppressions); +} + void ThreadHandler::check(const Settings &settings, const std::shared_ptr& supprs) { if (mResults.getFileCount() == 0 || mRunningThreadCount > 0 || settings.jobs == 0) { @@ -91,25 +97,25 @@ void ThreadHandler::check(const Settings &settings, const std::shared_ptrsetAddonsAndTools(addonsAndTools); - mThreads[i]->setSuppressions(mSuppressions); - mThreads[i]->setClangIncludePaths(mClangIncludePaths); - mThreads[i]->setSettings(settings, supprs); + setupCheckThread(*mThreads[i]); mThreads[i]->start(); } @@ -123,14 +129,12 @@ void ThreadHandler::check(const Settings &settings, const std::shared_ptr 0; + return mRunningThreadCount > 0 || mAnalyseWholeProgram; } -void ThreadHandler::setThreadCount(const int count) +void ThreadHandler::createThreads(const int count) { - if (mRunningThreadCount > 0 || - count == mThreads.size() || - count <= 0) { + if (mRunningThreadCount > 0 || count <= 0) { return; } @@ -140,9 +144,9 @@ void ThreadHandler::setThreadCount(const int count) for (int i = mThreads.size(); i < count; i++) { mThreads << new CheckThread(mResults); connect(mThreads.last(), &CheckThread::done, - this, &ThreadHandler::threadDone); + this, &ThreadHandler::threadDone, Qt::QueuedConnection); connect(mThreads.last(), &CheckThread::fileChecked, - &mResults, &ThreadResult::fileChecked); + &mResults, &ThreadResult::fileChecked, Qt::QueuedConnection); } } @@ -151,7 +155,7 @@ void ThreadHandler::removeThreads() { for (CheckThread* thread : mThreads) { if (thread->isRunning()) { - thread->terminate(); + thread->stop(); thread->wait(); } disconnect(thread, &CheckThread::done, @@ -162,19 +166,22 @@ void ThreadHandler::removeThreads() } mThreads.clear(); - mAnalyseWholeProgram = false; } void ThreadHandler::threadDone() { - if (mRunningThreadCount == 1 && mAnalyseWholeProgram) { + mRunningThreadCount--; + + if (mRunningThreadCount == 0 && mAnalyseWholeProgram) { + createThreads(1); + mRunningThreadCount = 1; + setupCheckThread(*mThreads[0]); mThreads[0]->analyseWholeProgram(mLastFiles, mCtuInfo); mAnalyseWholeProgram = false; mCtuInfo.clear(); return; } - mRunningThreadCount--; if (mRunningThreadCount == 0) { emit done(); @@ -185,6 +192,9 @@ void ThreadHandler::threadDone() mLastCheckTime = mCheckStartTime; mCheckStartTime = QDateTime(); } + + mCheckAddonsAndTools.clear(); + mCheckSuppressions.reset(); } } @@ -215,7 +225,7 @@ void ThreadHandler::initialize(const ResultsView *view) void ThreadHandler::loadSettings(const QSettings &settings) { - setThreadCount(settings.value(SETTINGS_CHECK_THREADS, 1).toInt()); + createThreads(settings.value(SETTINGS_CHECK_THREADS, 1).toInt()); } void ThreadHandler::saveSettings(QSettings &settings) const diff --git a/gui/threadhandler.h b/gui/threadhandler.h index 49a326933b0..5a687e74762 100644 --- a/gui/threadhandler.h +++ b/gui/threadhandler.h @@ -20,6 +20,7 @@ #ifndef THREADHANDLER_H #define THREADHANDLER_H +#include "settings.h" #include "suppressions.h" #include "threadresult.h" @@ -37,7 +38,6 @@ class ResultsView; class CheckThread; class QSettings; -class Settings; class ImportProject; class ErrorItem; @@ -55,12 +55,6 @@ class ThreadHandler : public QObject { explicit ThreadHandler(QObject *parent = nullptr); ~ThreadHandler() override; - /** - * @brief Set the number of threads to use - * @param count The number of threads to use - */ - void setThreadCount(int count); - /** * @brief Initialize the threads (connect all signals to resultsview's slots) * @@ -85,7 +79,7 @@ class ThreadHandler : public QObject { } void setSuppressions(const QList &s) { - mSuppressions = s; + mSuppressionsUI = s; } void setClangIncludePaths(const QStringList &s) { @@ -235,12 +229,24 @@ protected slots: */ int mScanDuration{}; + /** + * @brief Create checker threads + * @param count The number of threads to spawn + */ + void createThreads(int count); + /** * @brief Function to delete all threads * */ void removeThreads(); + /* + * @brief Apply check settings to a checker thread + * @param thread The thread to setup + */ + void setupCheckThread(CheckThread &thread) const; + /** * @brief Thread results are stored here * @@ -259,12 +265,24 @@ protected slots: */ int mRunningThreadCount{}; + /** + * @brief A whole program check is queued by check() + */ bool mAnalyseWholeProgram{}; std::string mCtuInfo; QStringList mAddonsAndTools; - QList mSuppressions; + QList mSuppressionsUI; QStringList mClangIncludePaths; + + /// @{ + /** + * @brief Settings specific to the current analysis + */ + QStringList mCheckAddonsAndTools; + Settings mCheckSettings; + std::shared_ptr mCheckSuppressions; + /// @} private: /** From 28c6bea8a4d61240f107d32511cb2aaba8cc36dd Mon Sep 17 00:00:00 2001 From: sdegrande Date: Sat, 28 Jun 2025 21:07:33 +0200 Subject: [PATCH 08/18] IRC chan is now on libera.chat (#7628) In cppcheck html report, points to libera.chat rather than freenode.net --- htmlreport/cppcheck-htmlreport | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htmlreport/cppcheck-htmlreport b/htmlreport/cppcheck-htmlreport index 10300ea50ec..b33699ca2ed 100755 --- a/htmlreport/cppcheck-htmlreport +++ b/htmlreport/cppcheck-htmlreport @@ -383,7 +383,7 @@ HTML_FOOTER = """ From 4d21b97ff617a9eb16bccb4a839ec730ffb47d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 28 Jun 2025 21:12:55 +0200 Subject: [PATCH 09/18] fix #13964: ErrorLogger: Invalid/missing guideline/classification with `--report-type=misra-cpp-2023` (#7624) --- lib/errorlogger.cpp | 8 +++---- test/testerrorlogger.cpp | 51 ++++++++++++++-------------------------- 2 files changed, 22 insertions(+), 37 deletions(-) diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index b31e66c5c32..cb8edb9d72b 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -1045,12 +1045,12 @@ std::string getGuideline(const std::string &errId, ReportType reportType, guideline = errId.substr(errId.rfind('-') + 1); break; case ReportType::misraCpp2008: - if (errId.rfind("misra-cpp-2008-", 0) == 0) - guideline = errId.substr(15); + if (errId.rfind("premium-misra-cpp-2008", 0) == 0) + guideline = errId.substr(23); break; case ReportType::misraCpp2023: - if (errId.rfind("misra-cpp-2023-", 0) == 0) - guideline = errId.substr(15); + if (errId.rfind("premium-misra-cpp-2023", 0) == 0) + guideline = errId.substr(errId.rfind('-') + 1); break; default: break; diff --git a/test/testerrorlogger.cpp b/test/testerrorlogger.cpp index 354fe9909c9..f0a1af7d631 100644 --- a/test/testerrorlogger.cpp +++ b/test/testerrorlogger.cpp @@ -77,9 +77,7 @@ class TestErrorLogger : public TestFixture { TEST_CASE(isCriticalErrorId); - TEST_CASE(ErrorMessageReportTypeMisraC); - TEST_CASE(ErrorMessageReportTypeMisraCDirective); - TEST_CASE(ErrorMessageReportTypeCertC); + TEST_CASE(TestReportType); } void TestPatternSearchReplace(const std::string& idPlaceholder, const std::string& id) const { @@ -316,43 +314,30 @@ class TestErrorLogger : public TestFixture { } } - void ErrorMessageReportTypeMisraC() const { + #define testReportType(reportType, severity, errorId, expectedClassification, expectedGuideline) \ + testReportType_(__FILE__, __LINE__, reportType, severity, errorId, expectedClassification, expectedGuideline) + void testReportType_(const char *file, int line, ReportType reportType, Severity severity, const std::string &errorId, + const std::string &expectedClassification, const std::string &expectedGuideline) const + { std::list locs = { fooCpp5 }; - const auto reportType = ReportType::misraC2012; const auto mapping = createGuidelineMapping(reportType); - const std::string format = "{severity} {id}"; - ErrorMessage msg(std::move(locs), emptyString, Severity::error, "", "unusedVariable", Certainty::normal); - msg.guideline = getGuideline(msg.id, reportType, mapping, msg.severity); - msg.classification = getClassification(msg.guideline, reportType); - ASSERT_EQUALS("Advisory", msg.classification); - ASSERT_EQUALS("2.8", msg.guideline); - ASSERT_EQUALS("Advisory 2.8", msg.toString(true, format, "")); - } - void ErrorMessageReportTypeMisraCDirective() const { - std::list locs = { fooCpp5 }; - const auto reportType = ReportType::misraC2012; - const auto mapping = createGuidelineMapping(reportType); - const std::string format = "{severity} {id}"; - ErrorMessage msg(std::move(locs), emptyString, Severity::style, "", "premium-misra-c-2012-dir-4.6", Certainty::normal); + ErrorMessage msg(std::move(locs), emptyString, severity, "", errorId, Certainty::normal); msg.guideline = getGuideline(msg.id, reportType, mapping, msg.severity); msg.classification = getClassification(msg.guideline, reportType); - ASSERT_EQUALS("Advisory", msg.classification); - ASSERT_EQUALS("Dir 4.6", msg.guideline); - ASSERT_EQUALS("Advisory Dir 4.6", msg.toString(true, format, "")); + + ASSERT_EQUALS_LOC(expectedClassification, msg.classification, file, line); + ASSERT_EQUALS_LOC(expectedGuideline, msg.guideline, file, line); } - void ErrorMessageReportTypeCertC() const { - std::list locs = { fooCpp5 }; - const auto reportType = ReportType::certC; - const auto mapping = createGuidelineMapping(reportType); - const std::string format = "{severity} {id}"; - ErrorMessage msg(std::move(locs), emptyString, Severity::error, "", "resourceLeak", Certainty::normal); - msg.guideline = getGuideline(msg.id, reportType, mapping, msg.severity); - msg.classification = getClassification(msg.guideline, reportType); - ASSERT_EQUALS("L3", msg.classification); - ASSERT_EQUALS("FIO42-C", msg.guideline); - ASSERT_EQUALS("L3 FIO42-C", msg.toString(true, format, "")); + void TestReportType() const { + testReportType(ReportType::misraC2012, Severity::error, "unusedVariable", "Advisory", "2.8"); + testReportType(ReportType::misraCpp2023, Severity::warning, "premium-misra-cpp-2023-6.8.4", "Advisory", "6.8.4"); + testReportType(ReportType::misraCpp2023, Severity::style, "premium-misra-cpp-2023-19.6.1", "Advisory", "19.6.1"); + testReportType(ReportType::misraCpp2008, Severity::style, "premium-misra-cpp-2008-3-4-1", "Required", "3-4-1"); + testReportType(ReportType::misraC2012, Severity::style, "premium-misra-c-2012-dir-4.6", "Advisory", "Dir 4.6"); + testReportType(ReportType::misraC2012, Severity::style, "misra-c2012-dir-4.6", "Advisory", "Dir 4.6"); + testReportType(ReportType::certC, Severity::error, "resourceLeak", "L3", "FIO42-C"); } void CustomFormat() const { From 1bf450544a7828f7581e61841cf00f76d386b090 Mon Sep 17 00:00:00 2001 From: Allen Winter Date: Sat, 28 Jun 2025 18:31:22 -0400 Subject: [PATCH 10/18] Fix #13970 cfg/qt.cfg - add definition for QDate::isValid() (#7630) Add the bool isValid() signature. bool isValid(int, int, int) is already there. --- cfg/qt.cfg | 1 + test/cfg/qt.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/cfg/qt.cfg b/cfg/qt.cfg index 2ad398be8ed..c3e8f4ee639 100644 --- a/cfg/qt.cfg +++ b/cfg/qt.cfg @@ -2737,6 +2737,7 @@ + diff --git a/test/cfg/qt.cpp b/test/cfg/qt.cpp index e1c44398868..a91549a4dad 100644 --- a/test/cfg/qt.cpp +++ b/test/cfg/qt.cpp @@ -847,3 +847,10 @@ namespace { // cppcheck-suppress functionStatic void TestUnusedFunction::doStuff() {} // Should not warn here with unusedFunction } + +int qdateIsValid() +{ + QDate qd(1,1,2025); + Q_ASSERT(qd.isValid()); // Should not warn here with assertWithSideEffect + return qd.month(); +} From f4c5d90924d1ab151d9b17a71e917239188f8a0c Mon Sep 17 00:00:00 2001 From: autoantwort <41973254+autoantwort@users.noreply.github.com> Date: Mon, 30 Jun 2025 07:58:49 +0200 Subject: [PATCH 11/18] Fixes #13962: fi can be nullptr (#7623) --- lib/cppcheck.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 9e584b9ab79..181551e05f6 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -2089,9 +2089,10 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st // cppcheck-suppress shadowFunction - TODO: fix this for (const Check *check : Check::instances()) { if (checkClassAttr == check->name()) { - Check::FileInfo* fi = check->loadFileInfoFromXml(e); - fi->file0 = filesTxtInfo.sourceFile; - fileInfoList.push_back(fi); + if (Check::FileInfo* fi = check->loadFileInfoFromXml(e)) { + fi->file0 = filesTxtInfo.sourceFile; + fileInfoList.push_back(fi); + } } } } From e01e9ed4d573ed6be5835aefc8e2a35ae2c03950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Mon, 30 Jun 2025 10:33:25 +0200 Subject: [PATCH 12/18] iwyu.yml: work around missing includes for Fedora [skip ci] (#7625) --- .github/workflows/iwyu.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index ae084c446f8..316380a50b4 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -20,9 +20,12 @@ jobs: - os: ubuntu-22.04 image: "fedora:latest" stdlib: libstdc++ - - os: ubuntu-22.04 - image: "fedora:latest" - stdlib: libc++ + clang_inc: '-isystem/usr/lib/clang/20/include' + # TODO: disable because it currently fails with "error: tried including but didn't find libc++'s header." + #- os: ubuntu-22.04 + # image: "fedora:latest" + # stdlib: libc++ + # clang_inc: '-isystem/usr/lib/clang/20/include' - os: macos-13 image: "" stdlib: libc++ # no libstdc++ on macOS @@ -144,9 +147,8 @@ jobs: - name: iwyu_tool run: | - # -isystem/usr/lib/clang/17/include # TODO: remove -stdlib= - it should have been taken from the compilation database - iwyu_tool -p cmake.output -j $(nproc) -- -w -Xiwyu --max_line_length=1024 -Xiwyu --comment_style=long -Xiwyu --quoted_includes_first -Xiwyu --update_comments -stdlib=${{ matrix.stdlib }} ${{ matrix.mapping_file_opt }} > iwyu.log + iwyu_tool -p cmake.output -j $(nproc) -- -w -Xiwyu --max_line_length=1024 -Xiwyu --comment_style=long -Xiwyu --quoted_includes_first -Xiwyu --update_comments -stdlib=${{ matrix.stdlib }} ${{ matrix.mapping_file_opt }} ${{ matrix.clang_inc }} > iwyu.log - uses: actions/upload-artifact@v4 if: success() || failure() From 683e4231514337e07cc02195c846266fc27e1834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Mon, 30 Jun 2025 13:50:54 +0200 Subject: [PATCH 13/18] refs #13618 - cleaned up some `--debug` handling and dependencies (#7592) --- cli/cmdlineparser.cpp | 13 +++++++++-- lib/token.cpp | 6 ++--- lib/token.h | 3 ++- lib/tokenize.cpp | 9 ++++--- test/cli/other_test.py | 45 ++++++++++++++++++++++++++++------- test/testcmdlineparser.cpp | 48 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 21 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index c83f55e9818..f8bdc83c262 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -415,6 +415,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a bool def = false; bool maxconfigs = false; + bool debug = false; ImportProject::Type projectType = ImportProject::Type::NONE; ImportProject project; @@ -641,7 +642,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // Show --debug output after the first simplifications else if (std::strcmp(argv[i], "--debug") == 0 || std::strcmp(argv[i], "--debug-normal") == 0) - mSettings.debugnormal = true; + debug = true; // Show debug warnings for lookup for configuration files else if (std::strcmp(argv[i], "--debug-lookup") == 0) @@ -1572,10 +1573,18 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a if (mSettings.force) mSettings.maxConfigs = INT_MAX; - else if ((def || mSettings.preprocessOnly) && !maxconfigs) mSettings.maxConfigs = 1U; + if (debug) { + mSettings.debugnormal = true; + mSettings.debugvalueflow = true; + if (mSettings.verbose) { + mSettings.debugast = true; + mSettings.debugsymdb = true; + } + } + if (mSettings.jobs > 1 && mSettings.buildDir.empty()) { // TODO: bail out instead? if (mSettings.checks.isEnabled(Checks::unusedFunction)) diff --git a/lib/token.cpp b/lib/token.cpp index e914b02c77b..315d952fb48 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1655,7 +1655,7 @@ static void astStringXml(const Token *tok, nonneg int indent, std::ostream &out) } } -void Token::printAst(bool verbose, bool xml, const std::vector &fileNames, std::ostream &out) const +void Token::printAst(bool xml, const std::vector &fileNames, std::ostream &out) const { if (!xml) out << "\n\n##AST" << std::endl; @@ -1672,10 +1672,8 @@ void Token::printAst(bool verbose, bool xml, const std::vector &fil << "\" column=\"" << tok->column() << "\">" << std::endl; astStringXml(tok, 2U, out); out << "" << std::endl; - } else if (verbose) + } else out << "[" << fileNames[tok->fileIndex()] << ":" << tok->linenr() << "]" << std::endl << tok->astStringVerbose() << std::endl; - else - out << tok->astString(" ") << std::endl; if (tok->str() == "(") tok = tok->link(); } diff --git a/lib/token.h b/lib/token.h index 59774747f70..244d560689c 100644 --- a/lib/token.h +++ b/lib/token.h @@ -1544,6 +1544,7 @@ class CPPCHECKLIB Token { mImpl->mValues = nullptr; } + // cppcheck-suppress unusedFunction - used in tests only std::string astString(const char *sep = "") const { std::string ret; if (mImpl->mAstOperand1) @@ -1559,7 +1560,7 @@ class CPPCHECKLIB Token { std::string expressionString() const; - void printAst(bool verbose, bool xml, const std::vector &fileNames, std::ostream &out) const; + void printAst(bool xml, const std::vector &fileNames, std::ostream &out) const; void printValueFlow(const std::vector& files, bool xml, std::ostream &out) const; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index d91e9e7c3b9..35b0ffc06bc 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5941,7 +5941,6 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) } //--------------------------------------------------------------------------- -// TODO: do not depend on --verbose void Tokenizer::printDebugOutput(std::ostream &out) const { if (!list.front()) @@ -5962,14 +5961,14 @@ void Tokenizer::printDebugOutput(std::ostream &out) const if (mSymbolDatabase) { if (xml) mSymbolDatabase->printXml(out); - else if (mSettings.debugsymdb || (mSettings.debugnormal && mSettings.verbose)) + else if (mSettings.debugsymdb) mSymbolDatabase->printOut("Symbol database"); } - if (mSettings.debugast || (mSettings.debugnormal && mSettings.verbose)) - list.front()->printAst(mSettings.verbose, xml, list.getFiles(), out); + if (mSettings.debugast) + list.front()->printAst(xml, list.getFiles(), out); - if (mSettings.debugnormal || mSettings.debugvalueflow) + if (mSettings.debugvalueflow) list.front()->printValueFlow(list.getFiles(), xml, out); if (xml) diff --git a/test/cli/other_test.py b/test/cli/other_test.py index 6a573579c52..8008e2fc862 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -2701,8 +2701,9 @@ def test_debug_verbose_xml(tmp_path): assert len(ast_elem) == 1 +# TODO: remove interaction with --debug? # TODO: test with --xml -def __test_debug_template(tmp_path, verbose): +def __test_debug_template(tmp_path, verbose=False, debug=False): test_file = tmp_path / 'test.cpp' with open(test_file, "w") as f: f.write( @@ -2722,14 +2723,31 @@ def __test_debug_template(tmp_path, verbose): if verbose: args += ['--verbose'] + if debug: + args += ['--debug'] exitcode, stdout, stderr = cppcheck(args) assert exitcode == 0, stdout - assert stdout.find('##file ') == -1 - assert stdout.find('##Value flow') == -1 - assert stdout.find('### Symbol database ###') == -1 - assert stdout.find('##AST') == -1 - assert stdout.find('### Template Simplifier pass ') != -1 + if debug: + assert stdout.find('##file ') != -1 + else: + assert stdout.find('##file ') == -1 + if debug: + assert stdout.find('##Value flow') != -1 + else: + assert stdout.find('##Value flow') == -1 + if debug and verbose: + assert stdout.find('### Symbol database ###') != -1 + else: + assert stdout.find('### Symbol database ###') == -1 + if debug and verbose: + assert stdout.find('##AST') != -1 + else: + assert stdout.find('##AST') == -1 + if debug: + assert stdout.count('### Template Simplifier pass ') == 2 + else: + assert stdout.count('### Template Simplifier pass ') == 1 assert stderr.splitlines() == [ '{}:4:13: error: Null pointer dereference: (int*)nullptr [nullPointer]'.format(test_file) ] @@ -2737,12 +2755,22 @@ def __test_debug_template(tmp_path, verbose): def test_debug_template(tmp_path): - __test_debug_template(tmp_path, False) + __test_debug_template(tmp_path, verbose=False) def test_debug_template_verbose_nodiff(tmp_path): # make sure --verbose does not change the output - assert __test_debug_template(tmp_path, False) == __test_debug_template(tmp_path, True) + assert __test_debug_template(tmp_path, verbose=False) == __test_debug_template(tmp_path, verbose=True) + + +def test_debug_template_debug(tmp_path): + __test_debug_template(tmp_path, debug=True) + + +@pytest.mark.xfail(strict=True) # TODO: remove dependency on --verbose +def test_debug_template_debug_verbose_nodiff(tmp_path): + # make sure --verbose does not change the output + assert __test_debug_template(tmp_path, debug=True, verbose=False) == __test_debug_template(tmp_path, debug=True, verbose=True) def test_file_ignore_2(tmp_path): # #13570 @@ -3467,7 +3495,6 @@ def test_debug_ast(tmp_path): __test_debug_ast(tmp_path, False) -@pytest.mark.xfail(strict=True) # TODO: remove dependency on --verbose def test_debug_ast_verbose_nodiff(tmp_path): # make sure --verbose does not change the output assert __test_debug_ast(tmp_path, False) == __test_debug_ast(tmp_path, True) diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 0bfd0cb5793..64e62408bfc 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -463,6 +463,10 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(debugSymdb); TEST_CASE(debugAst); TEST_CASE(debugValueflow); + TEST_CASE(debugNormal); + TEST_CASE(debugNormalVerbose); + TEST_CASE(debug); + TEST_CASE(debugVerbose); TEST_CASE(ignorepaths1); TEST_CASE(ignorepaths2); @@ -3186,6 +3190,50 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS(true, settings->debugvalueflow); } + void debugNormal() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--debug-normal", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); + ASSERT_EQUALS(true, settings->debugnormal); + ASSERT_EQUALS(false, settings->debugSimplified); + ASSERT_EQUALS(true, settings->debugvalueflow); + ASSERT_EQUALS(false, settings->debugast); + ASSERT_EQUALS(false, settings->debugsymdb); + } + + void debugNormalVerbose() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--debug-normal", "--verbose", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); + ASSERT_EQUALS(true, settings->debugnormal); + ASSERT_EQUALS(false, settings->debugSimplified); + ASSERT_EQUALS(true, settings->debugvalueflow); + ASSERT_EQUALS(true, settings->debugast); + ASSERT_EQUALS(true, settings->debugsymdb); + } + + void debug() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--debug", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); + ASSERT_EQUALS(true, settings->debugnormal); + ASSERT_EQUALS(false, settings->debugSimplified); + ASSERT_EQUALS(true, settings->debugvalueflow); + ASSERT_EQUALS(false, settings->debugast); + ASSERT_EQUALS(false, settings->debugsymdb); + } + + void debugVerbose() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--debug", "--verbose", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); + ASSERT_EQUALS(true, settings->debugnormal); + ASSERT_EQUALS(false, settings->debugSimplified); + ASSERT_EQUALS(true, settings->debugvalueflow); + ASSERT_EQUALS(true, settings->debugast); + ASSERT_EQUALS(true, settings->debugsymdb); + } + void ignorepaths1() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"}; From 6fb79eb2d827905406a3aba00ff82772fdbc23fb Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 30 Jun 2025 15:13:10 +0200 Subject: [PATCH 14/18] Fix #13975 SymbolDatabase: wrongly set function pointer when called constructor is implicitly defined (#7637) --- lib/symboldatabase.cpp | 10 ++++++---- lib/symboldatabase.h | 2 +- test/testsymboldatabase.cpp | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index d007b858e15..97493857429 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -5586,7 +5586,7 @@ bool Scope::hasInlineOrLambdaFunction(const Token** tokStart, bool onlyInline) c }); } -void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const +void Scope::findFunctionInBase(const Token* tok, nonneg int args, std::vector & matches) const { if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { const std::vector &derivedFrom = definedType->derivedFrom; @@ -5596,16 +5596,18 @@ void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::v if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already continue; - auto range = utils::as_const(base->classScope->functionMap).equal_range(name); + auto range = utils::as_const(base->classScope->functionMap).equal_range(tok->str()); for (auto it = range.first; it != range.second; ++it) { const Function *func = it->second; + if (func->isDestructor() && !Token::simpleMatch(tok->tokAt(-1), "~")) + continue; if ((func->isVariadic() && args >= (func->argCount() - 1)) || (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount()))) { matches.push_back(func); } } - base->classScope->findFunctionInBase(name, args, matches); + base->classScope->findFunctionInBase(tok, args, matches); } } } @@ -5791,7 +5793,7 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst, Referen const std::size_t numberOfMatchesNonBase = matches.size(); // check in base classes - findFunctionInBase(tok->str(), args, matches); + findFunctionInBase(tok, args, matches); // Non-call => Do not match parameters if (!isCall) { diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 6d16fddbe84..1ab399762fa 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1205,7 +1205,7 @@ class CPPCHECKLIB Scope { */ bool isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok) const; - void findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const; + void findFunctionInBase(const Token* tok, nonneg int args, std::vector & matches) const; /** @brief initialize varlist */ void getVariableList(const Token *start, const Token *end); diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 147f53b257f..8759db8a31e 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -532,6 +532,7 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(findFunction58); // #13310 TEST_CASE(findFunction59); TEST_CASE(findFunction60); + TEST_CASE(findFunction61); TEST_CASE(findFunctionRef1); TEST_CASE(findFunctionRef2); // #13328 TEST_CASE(findFunctionContainer); @@ -8650,6 +8651,19 @@ class TestSymbolDatabase : public TestFixture { ASSERT(fun && !fun->function()); } + void findFunction61() { + GET_SYMBOL_DB("namespace N {\n" // #13975 + " struct B {\n" + " virtual ~B() = default;\n" + " };\n" + " struct D : B {\n" + " D() : B() {}\n" + " };\n" + "}\n"); + const Token* fun = Token::findsimplematch(tokenizer.tokens(), "B ( ) {"); + ASSERT(fun && !fun->function()); + } + void findFunctionRef1() { GET_SYMBOL_DB("struct X {\n" " const std::vector getInts() const & { return mInts; }\n" From f5896ba7bd6c2eda151acf517210a337c5d9b644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Mon, 30 Jun 2025 17:30:57 +0200 Subject: [PATCH 15/18] refs #13914 - apply enforced language for non-project inputs in GUI (#7627) --- cli/cmdlineparser.cpp | 35 +-------------------------- frontend/frontend.cpp | 38 ++++++++++++++++++++++++++++++ frontend/frontend.h | 2 ++ gui/checkthread.cpp | 12 +++------- gui/checkthread.h | 7 +++--- gui/mainwindow.cpp | 35 +++++++++++++++++++-------- gui/mainwindow.h | 5 ++++ gui/threadhandler.cpp | 23 +++++++++--------- gui/threadhandler.h | 10 ++++---- gui/threadresult.cpp | 11 ++++----- gui/threadresult.h | 3 +-- test/testfrontend.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 156 insertions(+), 80 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index f8bdc83c262..2c879daa904 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -293,40 +293,7 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) files = std::move(filesResolved); } - if (mEnforcedLang != Standards::Language::None) - { - // apply enforced language - for (auto& f : files) - { - if (mSettings.library.markupFile(f.path())) - continue; - f.setLang(mEnforcedLang); - } - } - else - { - // identify remaining files - for (auto& f : files) - { - if (f.lang() != Standards::Language::None) - continue; - if (mSettings.library.markupFile(f.path())) - continue; - bool header = false; - f.setLang(Path::identify(f.path(), mSettings.cppHeaderProbe, &header)); - // unknown extensions default to C++ - if (!header && f.lang() == Standards::Language::None) - f.setLang(Standards::Language::CPP); - } - } - - // enforce the language since markup files are special and do not adhere to the enforced language - for (auto& f : files) - { - if (mSettings.library.markupFile(f.path())) { - f.setLang(Standards::Language::C); - } - } + frontend::applyLang(files, mSettings, mEnforcedLang); // sort the markup last std::copy_if(files.cbegin(), files.cend(), std::inserter(mFiles, mFiles.end()), [&](const FileWithDetails& entry) { diff --git a/frontend/frontend.cpp b/frontend/frontend.cpp index eaaf34f33fd..bd95488a9a5 100644 --- a/frontend/frontend.cpp +++ b/frontend/frontend.cpp @@ -63,4 +63,42 @@ namespace frontend { } } } + + void applyLang(std::list& files, const Settings& settings, Standards::Language enforcedLang) + { + if (enforcedLang != Standards::Language::None) + { + // apply enforced language + for (auto& f : files) + { + if (settings.library.markupFile(f.path())) + continue; + f.setLang(enforcedLang); + } + } + else + { + // identify remaining files + for (auto& f : files) + { + if (f.lang() != Standards::Language::None) + continue; + if (settings.library.markupFile(f.path())) + continue; + bool header = false; + f.setLang(Path::identify(f.path(), settings.cppHeaderProbe, &header)); + // unknown extensions default to C++ + if (!header && f.lang() == Standards::Language::None) + f.setLang(Standards::Language::CPP); + } + } + + // enforce the language since markup files are special and do not adhere to the enforced language + for (auto& f : files) + { + if (settings.library.markupFile(f.path())) { + f.setLang(Standards::Language::C); + } + } + } } diff --git a/frontend/frontend.h b/frontend/frontend.h index 1a8a92743b2..71af18b3b3f 100644 --- a/frontend/frontend.h +++ b/frontend/frontend.h @@ -25,6 +25,7 @@ struct FileSettings; class Settings; +class FileWithDetails; namespace frontend { @@ -32,6 +33,7 @@ namespace frontend Applies the enforced language as all as identifying remaining files - also taking markup files into consideration. */ void applyLang(std::list &fileSettings, const Settings &settings, Standards::Language enforcedLang); + void applyLang(std::list &files, const Settings &settings, Standards::Language enforcedLang); } #endif // FRONTEND_H diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index 773aab95a6d..872227aa9e3 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -25,7 +25,6 @@ #include "errorlogger.h" #include "errortypes.h" #include "filesettings.h" -#include "path.h" #include "settings.h" #include "standards.h" #include "threadresult.h" @@ -121,7 +120,7 @@ void CheckThread::setSettings(const Settings &settings, std::shared_ptr &files, const std::string& ctuInfo) { mFiles = files; mAnalyseWholeProgram = true; @@ -136,17 +135,12 @@ void CheckThread::run() CppCheck cppcheck(mSettings, *mSuppressions, mResult, true, executeCommand); - if (!mFiles.isEmpty() || mAnalyseWholeProgram) { + if (!mFiles.empty() || mAnalyseWholeProgram) { mAnalyseWholeProgram = false; std::string ctuInfo; ctuInfo.swap(mCtuInfo); qDebug() << "Whole program analysis"; - std::list files2; - std::transform(mFiles.cbegin(), mFiles.cend(), std::back_inserter(files2), [&](const QString& file) { - // TODO: apply enforcedLanguage - return FileWithDetails{file.toStdString(), Path::identify(file.toStdString(), mSettings.cppHeaderProbe), 0}; - }); - cppcheck.analyseWholeProgram(mSettings.buildDir, files2, {}, ctuInfo); + cppcheck.analyseWholeProgram(mSettings.buildDir, mFiles, {}, ctuInfo); mFiles.clear(); emit done(); return; diff --git a/gui/checkthread.h b/gui/checkthread.h index 827e75f1247..892bdf5258d 100644 --- a/gui/checkthread.h +++ b/gui/checkthread.h @@ -20,11 +20,13 @@ #ifndef CHECKTHREAD_H #define CHECKTHREAD_H +#include "filesettings.h" #include "settings.h" #include "suppressions.h" #include #include +#include #include #include #include @@ -36,7 +38,6 @@ #include class ThreadResult; -struct FileSettings; /// @addtogroup GUI /// @{ @@ -63,7 +64,7 @@ class CheckThread : public QThread { * @param files All files * @param ctuInfo Ctu info for addons */ - void analyseWholeProgram(const QStringList &files, const std::string& ctuInfo); + void analyseWholeProgram(const std::list &files, const std::string& ctuInfo); void setAddonsAndTools(const QStringList &addonsAndTools) { mAddonsAndTools = addonsAndTools; @@ -142,7 +143,7 @@ class CheckThread : public QThread { bool isSuppressed(const SuppressionList::ErrorMessage &errorMessage) const; - QStringList mFiles; + std::list mFiles; bool mAnalyseWholeProgram{}; std::string mCtuInfo; QStringList mAddonsAndTools; diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 4b7970d3b47..ce900e4230a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -57,6 +57,8 @@ #include "frontend.h" #include +#include +#include #include #include #include @@ -661,9 +663,11 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrar return; } - mUI->mResults->checkingStarted(fileNames.count()); + std::list fdetails = enrichFilesForAnalysis(fileNames, checkSettings); - mThread->setFiles(fileNames); + // TODO: lock UI here? + mUI->mResults->checkingStarted(fdetails.size()); + mThread->setFiles(std::move(fdetails)); if (mProjectFile && !checkConfiguration) mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools()); mThread->setSuppressions(mProjectFile ? mProjectFile->getCheckingSuppressions() : QList()); @@ -701,9 +705,9 @@ void MainWindow::analyzeCode(const QString& code, const QString& filename) if (!getCppcheckSettings(checkSettings, supprs)) return; + // TODO: split ErrorLogger from ThreadResult // Initialize dummy ThreadResult as ErrorLogger ThreadResult result; - result.setFiles(QStringList(filename)); connect(&result, SIGNAL(progress(int,QString)), mUI->mResults, SLOT(progress(int,QString))); connect(&result, SIGNAL(error(ErrorItem)), @@ -720,7 +724,7 @@ void MainWindow::analyzeCode(const QString& code, const QString& filename) checkLockDownUI(); clearResults(); mUI->mResults->checkingStarted(1); - // TODO: apply enforcedLanguage + // TODO: apply enforcedLanguage? cppcheck.check(FileWithDetails(filename.toStdString(), Path::identify(filename.toStdString(), false), 0), code.toStdString()); analysisDone(); @@ -1380,10 +1384,12 @@ void MainWindow::reAnalyzeSelected(const QStringList& files) pathList.addPathList(files); if (mProjectFile) pathList.addExcludeList(mProjectFile->getExcludedPaths()); - QStringList fileNames = pathList.getFileList(); + + std::list fdetails = enrichFilesForAnalysis(pathList.getFileList(), checkSettings); + checkLockDownUI(); // lock UI while checking - mUI->mResults->checkingStarted(fileNames.size()); - mThread->setCheckFiles(fileNames); + mUI->mResults->checkingStarted(fdetails.size()); + mThread->setCheckFiles(std::move(fdetails)); // Saving last check start time, otherwise unchecked modified files will not be // considered in "Modified Files Check" performed after "Selected Files Check" @@ -1396,7 +1402,7 @@ void MainWindow::reAnalyzeSelected(const QStringList& files) void MainWindow::reAnalyze(bool all) { - const QStringList files = mThread->getReCheckFiles(all); + const std::list files = mThread->getReCheckFiles(all); if (files.empty()) return; @@ -1409,8 +1415,8 @@ void MainWindow::reAnalyze(bool all) mUI->mResults->clear(all); // Clear results for changed files - for (int i = 0; i < files.size(); ++i) - mUI->mResults->clear(files[i]); + for (const auto& f : files) + mUI->mResults->clear(QString::fromStdString(f.path())); checkLockDownUI(); // lock UI while checking mUI->mResults->checkingStarted(files.size()); @@ -2349,3 +2355,12 @@ void MainWindow::changeReportType() { } } +std::list MainWindow::enrichFilesForAnalysis(const QStringList& fileNames, const Settings& settings) const { + std::list fdetails; + std::transform(fileNames.cbegin(), fileNames.cend(), std::back_inserter(fdetails), [](const QString& f) { + return FileWithDetails{f.toStdString(), Standards::Language::None, static_cast(QFile(f).size())}; + }); + const Standards::Language enforcedLang = static_cast(mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt()); + frontend::applyLang(fdetails, settings, enforcedLang); + return fdetails; +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h index ae58e5412ae..4999f101f80 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -23,6 +23,7 @@ #include "platforms.h" #include +#include #include #include @@ -45,6 +46,7 @@ class QNetworkAccessManager; class QNetworkReply; class Settings; struct Suppressions; +class FileWithDetails; namespace Ui { class MainWindow; } @@ -423,6 +425,9 @@ private slots: */ void removeProjectMRU(const QString &project); + /** @brief Generate list of detailed files from list of filenames. */ + std::list enrichFilesForAnalysis(const QStringList& fileNames, const Settings& settings) const; + /** @brief Program settings */ QSettings *mSettings; diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index 7363e167939..f077d3c7b2c 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -20,6 +20,7 @@ #include "checkthread.h" #include "common.h" +#include "filesettings.h" #include "resultsview.h" #include "settings.h" @@ -55,10 +56,10 @@ void ThreadHandler::clearFiles() mSuppressionsUI.clear(); } -void ThreadHandler::setFiles(const QStringList &files) +void ThreadHandler::setFiles(std::list files) { - mResults.setFiles(files); mLastFiles = files; + mResults.setFiles(std::move(files)); } void ThreadHandler::setProject(const ImportProject &prj) @@ -74,10 +75,10 @@ void ThreadHandler::setCheckFiles(bool all) } } -void ThreadHandler::setCheckFiles(const QStringList& files) +void ThreadHandler::setCheckFiles(std::list files) { if (mRunningThreadCount == 0) { - mResults.setFiles(files); + mResults.setFiles(std::move(files)); } } @@ -172,6 +173,7 @@ void ThreadHandler::threadDone() { mRunningThreadCount--; + // TODO: also run with projects? if (mRunningThreadCount == 0 && mAnalyseWholeProgram) { createThreads(1); mRunningThreadCount = 1; @@ -235,7 +237,7 @@ void ThreadHandler::saveSettings(QSettings &settings) const bool ThreadHandler::hasPreviousFiles() const { - return !mLastFiles.isEmpty(); + return !mLastFiles.empty(); } int ThreadHandler::getPreviousFilesCount() const @@ -248,7 +250,7 @@ int ThreadHandler::getPreviousScanDuration() const return mScanDuration; } -QStringList ThreadHandler::getReCheckFiles(bool all) const +std::list ThreadHandler::getReCheckFiles(bool all) const { if (mLastCheckTime.isNull() || all) return mLastFiles; @@ -256,11 +258,10 @@ QStringList ThreadHandler::getReCheckFiles(bool all) const std::set modified; std::set unmodified; - QStringList files; - for (int i = 0; i < mLastFiles.size(); ++i) { - if (needsReCheck(mLastFiles[i], modified, unmodified)) - files.push_back(mLastFiles[i]); - } + std::list files; + std::copy_if(mLastFiles.cbegin(), mLastFiles.cend(), std::back_inserter(files), [&](const FileWithDetails &f) { + return needsReCheck(QString::fromStdString(f.path()), modified, unmodified); + }); return files; } diff --git a/gui/threadhandler.h b/gui/threadhandler.h index 5a687e74762..cc578bd5643 100644 --- a/gui/threadhandler.h +++ b/gui/threadhandler.h @@ -24,6 +24,7 @@ #include "suppressions.h" #include "threadresult.h" +#include #include #include #include @@ -40,6 +41,7 @@ class CheckThread; class QSettings; class ImportProject; class ErrorItem; +class FileWithDetails; /// @addtogroup GUI /// @{ @@ -97,7 +99,7 @@ class ThreadHandler : public QObject { * * @param files files to check */ - void setFiles(const QStringList &files); + void setFiles(std::list files); /** * @brief Set project to check @@ -126,7 +128,7 @@ class ThreadHandler : public QObject { * * @param files list of files to be checked */ - void setCheckFiles(const QStringList& files); + void setCheckFiles(std::list files); /** * @brief Is checking running? @@ -160,7 +162,7 @@ class ThreadHandler : public QObject { * @brief Get files that should be rechecked because they have been * changed. */ - QStringList getReCheckFiles(bool all) const; + std::list getReCheckFiles(bool all) const; /** * @brief Get start time of last check @@ -207,7 +209,7 @@ protected slots: * @brief List of files checked last time (used when rechecking) * */ - QStringList mLastFiles; + std::list mLastFiles; /** @brief date and time when current checking started */ QDateTime mCheckStartTime; diff --git a/gui/threadresult.cpp b/gui/threadresult.cpp index a8d198228ea..39e6f48f6cd 100644 --- a/gui/threadresult.cpp +++ b/gui/threadresult.cpp @@ -25,6 +25,7 @@ #include "importproject.h" #include +#include #include @@ -80,18 +81,14 @@ void ThreadResult::getNextFileSettings(const FileSettings*& fs) ++mItNextFileSettings; } -void ThreadResult::setFiles(const QStringList &files) +void ThreadResult::setFiles(std::list files) { std::lock_guard locker(mutex); - std::list fdetails; - std::transform(files.cbegin(), files.cend(), std::back_inserter(fdetails), [](const QString& f) { - return FileWithDetails{f.toStdString(), Path::identify(f.toStdString(), false), static_cast(QFile(f).size())}; // TODO: provide Settings::cppHeaderProbe - }); - mFiles = std::move(fdetails); + mTotalFiles = files.size(); + mFiles = std::move(files); mItNextFile = mFiles.cbegin(); mProgress = 0; mFilesChecked = 0; - mTotalFiles = files.size(); // Determine the total size of all of the files to check, so that we can // show an accurate progress estimate diff --git a/gui/threadresult.h b/gui/threadresult.h index 33dc8290134..64c26d03d0c 100644 --- a/gui/threadresult.h +++ b/gui/threadresult.h @@ -31,7 +31,6 @@ #include #include #include -#include class ErrorItem; class ImportProject; @@ -59,7 +58,7 @@ class ThreadResult : public QObject, public ErrorLogger { * @brief Set list of files to check * @param files List of files to check */ - void setFiles(const QStringList &files); + void setFiles(std::list files); void setProject(const ImportProject &prj); diff --git a/test/testfrontend.cpp b/test/testfrontend.cpp index 941394863f3..ef6b774181a 100644 --- a/test/testfrontend.cpp +++ b/test/testfrontend.cpp @@ -33,6 +33,7 @@ class TestFrontend : public TestFixture { private: void run() override { TEST_CASE(applyLangFS); + TEST_CASE(applyLangFiles); } void applyLangFS() const { @@ -87,6 +88,60 @@ class TestFrontend : public TestFixture { ASSERT_EQUALS_ENUM((it++)->file.lang(), Standards::Language::C); // markup files are always C } } + + void applyLangFiles() const + { + const char xmldata[] = R"()"; + const Settings s = settingsBuilder().libraryxml(xmldata).build(); + + const std::list fs = { + {"nolang", Standards::Language::None, 0 }, + {"c", Standards::Language::C, 0 }, + {"cpp", Standards::Language::CPP, 0 }, + {"nolang.c", Standards::Language::None, 0 }, + {"nolang.cpp", Standards::Language::None, 0 }, + {"nolang.ml", Standards::Language::None, 0 } + }; + + // no language to enforce - identify only + { + std::list fs1 = fs; + frontend::applyLang(fs1, s, Standards::Language::None); + auto it = fs1.cbegin(); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); // unknown defaults to C++ + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); // markup files are always C + } + + // language to enforce (C) + { + std::list fs1 = fs; + frontend::applyLang(fs1, s, Standards::Language::C); + auto it = fs1.cbegin(); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); // markup files are always C + } + + // language to enforce (C++) + { + std::list fs1 = fs; + frontend::applyLang(fs1, s, Standards::Language::CPP); + auto it = fs1.cbegin(); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::CPP); + ASSERT_EQUALS_ENUM((it++)->lang(), Standards::Language::C); // markup files are always C + } + } }; REGISTER_TEST(TestFrontend) From 9a1d0bf51a8d34f5f765fc539f706c9ef8646c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Mon, 30 Jun 2025 19:43:56 +0200 Subject: [PATCH 16/18] cleaned up includes based on include-what-you-use (#7638) also cleaned up some `externals` includes and the order. --- Makefile | 2 +- gui/resultsview.h | 2 ++ gui/threadresult.cpp | 4 ++++ lib/analyzerinfo.cpp | 1 + lib/checknullpointer.cpp | 1 + lib/checkuninitvar.cpp | 1 + lib/cppcheck.h | 1 + lib/errorlogger.h | 1 - lib/importproject.h | 3 +++ lib/library.cpp | 1 - lib/settings.cpp | 2 +- lib/settings.h | 2 -- lib/symboldatabase.h | 1 + lib/token.cpp | 3 ++- lib/valueflow.h | 1 + oss-fuzz/Makefile | 2 +- test/seh/test-sehwrapper.cpp | 2 +- test/testanalyzerinformation.cpp | 7 +++++-- test/testbufferoverrun.cpp | 1 - test/testclangimport.cpp | 4 +++- test/testcmdlineparser.cpp | 3 ++- test/testcondition.cpp | 1 - test/testcppcheck.cpp | 8 ++++++-- test/testexecutor.cpp | 1 + test/testimportproject.cpp | 1 + test/testincompletestatement.cpp | 1 - test/testleakautovar.cpp | 1 - test/testmathlib.cpp | 1 + test/testnullpointer.cpp | 2 +- test/testoptions.cpp | 1 - test/testother.cpp | 1 - test/testsimplifytemplate.cpp | 2 +- test/testsizeof.cpp | 1 - test/teststring.cpp | 1 - test/testtype.cpp | 1 - test/testunusedprivfunc.cpp | 1 - test/testvarid.cpp | 1 - 37 files changed, 42 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index dea7b5b7cd6..188529d82a9 100644 --- a/Makefile +++ b/Makefile @@ -594,7 +594,7 @@ $(libcppdir)/infer.o: lib/infer.cpp lib/calculate.h lib/config.h lib/errortypes. $(libcppdir)/keywords.o: lib/keywords.cpp lib/config.h lib/keywords.h lib/standards.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/keywords.cpp -$(libcppdir)/library.o: lib/library.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/checkers.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h +$(libcppdir)/library.o: lib/library.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/library.cpp $(libcppdir)/mathlib.o: lib/mathlib.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errortypes.h lib/mathlib.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h diff --git a/gui/resultsview.h b/gui/resultsview.h index 18168b8a5ce..b28fcffe0f4 100644 --- a/gui/resultsview.h +++ b/gui/resultsview.h @@ -23,6 +23,8 @@ #include "report.h" #include "showtypes.h" +#include + #include #include #include diff --git a/gui/threadresult.cpp b/gui/threadresult.cpp index 39e6f48f6cd..ce2a4736718 100644 --- a/gui/threadresult.cpp +++ b/gui/threadresult.cpp @@ -23,7 +23,11 @@ #include "errorlogger.h" #include "errortypes.h" #include "importproject.h" +#include "path.h" +#include +#include +#include #include #include diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index f303783c737..cc259c8a12d 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -24,6 +24,7 @@ #include "utils.h" #include +#include #include #include diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index 9c26490d027..7defd07cf04 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include //--------------------------------------------------------------------------- diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 41175218e48..2c01613f1bf 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include //--------------------------------------------------------------------------- diff --git a/lib/cppcheck.h b/lib/cppcheck.h index 19274b3a990..d56d7fa6b4f 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -24,6 +24,7 @@ #include "check.h" #include "config.h" +#include #include #include #include diff --git a/lib/errorlogger.h b/lib/errorlogger.h index 86b2caf666a..44e3b733cdc 100644 --- a/lib/errorlogger.h +++ b/lib/errorlogger.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/lib/importproject.h b/lib/importproject.h index f0434ecdd23..8a03474528a 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -36,6 +36,9 @@ class Settings; struct Suppressions; +namespace tinyxml2 { + class XMLDocument; +} /// @addtogroup Core /// @{ diff --git a/lib/library.cpp b/lib/library.cpp index 943ab93822f..1000b4d77f6 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -22,7 +22,6 @@ #include "errortypes.h" #include "mathlib.h" #include "path.h" -#include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenlist.h" diff --git a/lib/settings.cpp b/lib/settings.cpp index 64a9f9df6bf..5d659742adf 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include "json.h" diff --git a/lib/settings.h b/lib/settings.h index eb33f44540b..41651dbbeb3 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -32,9 +32,7 @@ #include #include #include -#include #include -#include #include #include #include diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 1ab399762fa..a66c2dfd017 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -47,6 +47,7 @@ class Settings; class SymbolDatabase; class Tokenizer; class ValueType; +class ErrorLogger; enum class Reference : std::uint8_t { None, diff --git a/lib/token.cpp b/lib/token.cpp index 315d952fb48..98f79b4bb1d 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -23,7 +23,6 @@ #include "library.h" #include "mathlib.h" #include "settings.h" -#include "simplecpp.h" #include "symboldatabase.h" #include "tokenlist.h" #include "utils.h" @@ -48,6 +47,8 @@ #include #include +#include + namespace { struct less { template diff --git a/lib/valueflow.h b/lib/valueflow.h index 557e9ca5378..1b73f2167da 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -25,6 +25,7 @@ #include "errortypes.h" #include "mathlib.h" +#include #include #include #include diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index 69a7147e895..88449d09051 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -282,7 +282,7 @@ $(libcppdir)/infer.o: ../lib/infer.cpp ../lib/calculate.h ../lib/config.h ../lib $(libcppdir)/keywords.o: ../lib/keywords.cpp ../lib/config.h ../lib/keywords.h ../lib/standards.h ../lib/utils.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/keywords.cpp -$(libcppdir)/library.o: ../lib/library.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/astutils.h ../lib/checkers.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h +$(libcppdir)/library.o: ../lib/library.cpp ../externals/tinyxml2/tinyxml2.h ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/library.cpp $(libcppdir)/mathlib.o: ../lib/mathlib.cpp ../externals/simplecpp/simplecpp.h ../lib/config.h ../lib/errortypes.h ../lib/mathlib.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/vfvalue.h diff --git a/test/seh/test-sehwrapper.cpp b/test/seh/test-sehwrapper.cpp index 5c00418fd5b..a92753b8423 100644 --- a/test/seh/test-sehwrapper.cpp +++ b/test/seh/test-sehwrapper.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include "config.h" +#include "config.h" // IWYU pragma: keep #if defined(USE_WINDOWS_SEH) #include "sehwrapper.h" diff --git a/test/testanalyzerinformation.cpp b/test/testanalyzerinformation.cpp index e6dab0fd156..a1802006a8d 100644 --- a/test/testanalyzerinformation.cpp +++ b/test/testanalyzerinformation.cpp @@ -18,12 +18,15 @@ #include "analyzerinfo.h" +#include "errorlogger.h" #include "filesettings.h" #include "fixture.h" -#include "xml.h" +#include "standards.h" +#include #include -#include + +#include "xml.h" class TestAnalyzerInformation : public TestFixture, private AnalyzerInformation { public: diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index b13746a5b4b..16f0f4c3f72 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -29,7 +29,6 @@ #include #include #include -#include class TestBufferOverrun : public TestFixture { public: diff --git a/test/testclangimport.cpp b/test/testclangimport.cpp index a3f3d54a38c..e0c5a3d63a7 100644 --- a/test/testclangimport.cpp +++ b/test/testclangimport.cpp @@ -15,12 +15,14 @@ // along with this program. If not, see . #include "clangimport.h" +#include "fixture.h" #include "platform.h" #include "settings.h" +#include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" -#include "fixture.h" +#include "tokenlist.h" #include #include diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 64e62408bfc..c0c0dde9786 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -22,6 +22,8 @@ #include "config.h" #include "errorlogger.h" #include "errortypes.h" +#include "filesettings.h" +#include "fixture.h" #include "helpers.h" #include "path.h" #include "platform.h" @@ -29,7 +31,6 @@ #include "settings.h" #include "standards.h" #include "suppressions.h" -#include "fixture.h" #include "timer.h" #include "utils.h" diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 4e19837f37c..6d13655d840 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -26,7 +26,6 @@ #include #include #include -#include class TestCondition : public TestFixture { public: diff --git a/test/testcppcheck.cpp b/test/testcppcheck.cpp index c7944844541..b085cdcedf5 100644 --- a/test/testcppcheck.cpp +++ b/test/testcppcheck.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include "addoninfo.h" #include "color.h" #include "cppcheck.h" #include "errorlogger.h" @@ -25,15 +26,18 @@ #include "path.h" #include "preprocessor.h" #include "settings.h" +#include "standards.h" #include "suppressions.h" -#include "simplecpp.h" - #include +#include #include +#include #include #include +#include + class TestCppcheck : public TestFixture { public: TestCppcheck() : TestFixture("TestCppcheck") {} diff --git a/test/testexecutor.cpp b/test/testexecutor.cpp index ab4b4778a33..33fb454f5a4 100644 --- a/test/testexecutor.cpp +++ b/test/testexecutor.cpp @@ -23,6 +23,7 @@ #include "fixture.h" #include "helpers.h" #include "settings.h" +#include "standards.h" #include "suppressions.h" #include diff --git a/test/testimportproject.cpp b/test/testimportproject.cpp index 1cc14f2d2c7..843c42c0d11 100644 --- a/test/testimportproject.cpp +++ b/test/testimportproject.cpp @@ -21,6 +21,7 @@ #include "importproject.h" #include "redirect.h" #include "settings.h" +#include "standards.h" #include "suppressions.h" #include "xml.h" diff --git a/test/testincompletestatement.cpp b/test/testincompletestatement.cpp index f8e44e19133..637aa3d121e 100644 --- a/test/testincompletestatement.cpp +++ b/test/testincompletestatement.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include -#include class TestIncompleteStatement : public TestFixture { public: diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 3ff11ae5293..9667e8b8285 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -23,7 +23,6 @@ #include #include -#include class TestLeakAutoVar : public TestFixture { public: diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index dbd4a5e8837..08bf728c345 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -19,6 +19,7 @@ #include "config.h" #include "fixture.h" #include "mathlib.h" +#include "standards.h" #include "token.h" #include "tokenlist.h" diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 1028c527275..638d0a3adf9 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -24,12 +24,12 @@ #include "helpers.h" #include "library.h" #include "settings.h" +#include "standards.h" #include "token.h" #include #include #include -#include class TestNullPointer : public TestFixture { public: diff --git a/test/testoptions.cpp b/test/testoptions.cpp index 909dcb277b8..7c72b2de336 100644 --- a/test/testoptions.cpp +++ b/test/testoptions.cpp @@ -20,7 +20,6 @@ #include "options.h" #include "utils.h" -#include #include #include diff --git a/test/testother.cpp b/test/testother.cpp index 464889ce012..8c037fe5288 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -26,7 +26,6 @@ #include #include -#include class TestOther : public TestFixture { public: diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 69f919c50fe..65ad80e802c 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -19,8 +19,8 @@ #include "errortypes.h" #include "fixture.h" #include "helpers.h" -#include "path.h" #include "settings.h" +#include "standards.h" #include "templatesimplifier.h" #include "token.h" #include "tokenize.h" diff --git a/test/testsizeof.cpp b/test/testsizeof.cpp index e063ad22505..2080996fbb9 100644 --- a/test/testsizeof.cpp +++ b/test/testsizeof.cpp @@ -24,7 +24,6 @@ #include #include -#include class TestSizeof : public TestFixture { public: diff --git a/test/teststring.cpp b/test/teststring.cpp index 5d7a191afe4..3d07b4774a9 100644 --- a/test/teststring.cpp +++ b/test/teststring.cpp @@ -24,7 +24,6 @@ #include "fixture.h" #include -#include class TestString : public TestFixture { public: diff --git a/test/testtype.cpp b/test/testtype.cpp index 2f15f816a39..d85e6d8ca74 100644 --- a/test/testtype.cpp +++ b/test/testtype.cpp @@ -26,7 +26,6 @@ #include #include -#include class TestType : public TestFixture { public: diff --git a/test/testunusedprivfunc.cpp b/test/testunusedprivfunc.cpp index d90e46daab0..32df6a4d494 100644 --- a/test/testunusedprivfunc.cpp +++ b/test/testunusedprivfunc.cpp @@ -24,7 +24,6 @@ #include "fixture.h" #include -#include class TestUnusedPrivateFunction : public TestFixture { public: diff --git a/test/testvarid.cpp b/test/testvarid.cpp index b30b56c2bfb..27efdeed0d1 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -25,7 +25,6 @@ #include #include -#include class TestVarID : public TestFixture { public: From 8e6726c0cf20309ff42edaeb9c741713238457e3 Mon Sep 17 00:00:00 2001 From: Allen Winter Date: Tue, 8 Apr 2025 15:54:14 -0400 Subject: [PATCH 17/18] cfg/kde.cfg - add more KDE macros that are widely used --- test/cfg/runtests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cfg/runtests.sh b/test/cfg/runtests.sh index 7598ffb9165..7a3bada4fac 100755 --- a/test/cfg/runtests.sh +++ b/test/cfg/runtests.sh @@ -522,7 +522,7 @@ function check_file { kde.cpp) # TODO: "kde-4config" is no longer commonly available in recent distros #kde_fn - cppcheck_run --library="$lib" --library=qt "${DIR}""$f" + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" --library=qt "${DIR}""$f" ;; libcurl.c) libcurl_fn From 21d0f2c81a34077b274f9c5eb4fbef2a1a8c1f05 Mon Sep 17 00:00:00 2001 From: Allen Winter Date: Mon, 30 Jun 2025 15:28:25 -0400 Subject: [PATCH 18/18] test/cfg/runtests.sh - fix --- test/cfg/runtests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cfg/runtests.sh b/test/cfg/runtests.sh index 7a3bada4fac..7598ffb9165 100755 --- a/test/cfg/runtests.sh +++ b/test/cfg/runtests.sh @@ -522,7 +522,7 @@ function check_file { kde.cpp) # TODO: "kde-4config" is no longer commonly available in recent distros #kde_fn - "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" --library=qt "${DIR}""$f" + cppcheck_run --library="$lib" --library=qt "${DIR}""$f" ;; libcurl.c) libcurl_fn