From f1c33b2964c128ef8747a7a266f41171fe281134 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:21:23 +0100 Subject: [PATCH 001/806] Fix #14366 FN functionStatic with unknown base class (#8085) --- gui/codeeditor.cpp | 2 +- gui/codeeditor.h | 2 +- lib/checkclass.cpp | 4 ++++ test/testclass.cpp | 31 +++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/gui/codeeditor.cpp b/gui/codeeditor.cpp index 7e7f2cbc37c..b6d88a061d3 100644 --- a/gui/codeeditor.cpp +++ b/gui/codeeditor.cpp @@ -246,7 +246,7 @@ void Highlighter::highlightBlock(const QString &text) } } -void Highlighter::applyFormat(HighlightingRule &rule) +void Highlighter::applyFormat(HighlightingRule &rule) const { switch (rule.ruleRole) { case RuleRole::Keyword: diff --git a/gui/codeeditor.h b/gui/codeeditor.h index 4f6d07e1e36..9508c562c41 100644 --- a/gui/codeeditor.h +++ b/gui/codeeditor.h @@ -65,7 +65,7 @@ class Highlighter : public QSyntaxHighlighter { RuleRole ruleRole; }; - void applyFormat(HighlightingRule &rule); + void applyFormat(HighlightingRule &rule) const; QList mHighlightingRules; QList mHighlightingRulesWithSymbols; diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 2949d3c57a7..11951411f8f 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2292,6 +2292,10 @@ bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const if (tok->isKeyword() || tok->isStandardType()) return false; + if (tok->variable() && (tok->variable()->isArgument() || tok->variable()->isLocal())) + return false; + if (tok->function() || tok->type() || tok->enumerator()) + return false; for (const Variable& var : scope->varlist) { if (var.name() == tok->str()) { diff --git a/test/testclass.cpp b/test/testclass.cpp index 21457f15bfc..46c61d41c37 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -6731,6 +6731,7 @@ class TestClass : public TestFixture { "struct S<0> {};\n" "struct D : S<150> {};\n"); // don't hang + ignore_errout(); } void const93() { // #12162 @@ -6785,6 +6786,36 @@ class TestClass : public TestFixture { " B::g(0);\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" // #14366 + " void f();\n" + "};\n" + "struct T : U {\n" + " void g(S* s) {\n" + " s->f();\n" + " }\n" + " void h() {\n" + " S s;\n" + " s.f();\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5:10]: (style) Either there is a missing 'override', or the member function 'T::g' can be static. [functionStatic]\n" + "[test.cpp:8:10]: (style) Either there is a missing 'override', or the member function 'T::h' can be static. [functionStatic]\n", + errout_str()); + + checkConst("enum E { E0 };\n" + "int f();\n" + "struct S : U {\n" + " E g() {\n" + " return E0;\n" + " }\n" + " int h() {\n" + " return f();\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4:7]: (style) Either there is a missing 'override', or the member function 'S::g' can be static. [functionStatic]\n" + "[test.cpp:7:9]: (style) Either there is a missing 'override', or the member function 'S::h' can be static. [functionStatic]\n", + errout_str()); } void const97() { // #13301 From 7a03d5ff169cc0606ee6c74480681b1cf3ae34bd Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:44:01 +0100 Subject: [PATCH 002/806] Fix #13692 fuzzing crash (null-pointer-use) in Tokenizer::simplifyAsm() (#8094) --- lib/tokenize.cpp | 2 ++ .../fuzz-crash/crash-7d409757e9b94fc2eb23758b655e056ec3dd9d09 | 1 + 2 files changed, 3 insertions(+) create mode 100644 test/cli/fuzz-crash/crash-7d409757e9b94fc2eb23758b655e056ec3dd9d09 diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index cc98703011a..db30349dba7 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9976,6 +9976,8 @@ void Tokenizer::simplifyAsm() instruction = tok->stringifyList(endasm); Token::eraseTokens(tok, endasm); } else if (!endasm) { + if (!tok->next()) + syntaxError(tok); instruction = tok->next()->stringifyList(endasm); Token::eraseTokens(tok, endasm); tok->insertToken(";"); diff --git a/test/cli/fuzz-crash/crash-7d409757e9b94fc2eb23758b655e056ec3dd9d09 b/test/cli/fuzz-crash/crash-7d409757e9b94fc2eb23758b655e056ec3dd9d09 new file mode 100644 index 00000000000..8b8929bc573 --- /dev/null +++ b/test/cli/fuzz-crash/crash-7d409757e9b94fc2eb23758b655e056ec3dd9d09 @@ -0,0 +1 @@ +_asm From 558f66e14ed52df1d636bc5c84b15079d26acb82 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:12:06 +0100 Subject: [PATCH 003/806] Fix #13500 fuzzing crash (null-pointer-use) in TemplateSimplifier::useDefaultArgumentValues() (#8096) --- lib/templatesimplifier.cpp | 4 ++++ .../fuzz-crash/crash-7c3e963c9c28dab506696d0dbe8aaf8772d5302f | 1 + 2 files changed, 5 insertions(+) create mode 100644 test/cli/fuzz-crash/crash-7c3e963c9c28dab506696d0dbe8aaf8772d5302f diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 1291993990d..349894b2b89 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -1153,6 +1153,8 @@ void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration) instantiationArgs[index].push_back(tok1); tok1 = tok1->next(); } while (tok1 && tok1 != endLink); + if (!tok1) + syntaxError(end); instantiationArgs[index].push_back(tok1); } else if (tok1->str() == "<" && (tok1->strAt(1) == ">" || (tok1->previous()->isName() && @@ -1162,6 +1164,8 @@ void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration) instantiationArgs[index].push_back(tok1); tok1 = tok1->next(); } while (tok1 && tok1 != endLink); + if (!tok1) + syntaxError(end); instantiationArgs[index].push_back(tok1); } else if (tok1->str() == ",") { ++index; diff --git a/test/cli/fuzz-crash/crash-7c3e963c9c28dab506696d0dbe8aaf8772d5302f b/test/cli/fuzz-crash/crash-7c3e963c9c28dab506696d0dbe8aaf8772d5302f new file mode 100644 index 00000000000..ff8d3a2d0ac --- /dev/null +++ b/test/cli/fuzz-crash/crash-7c3e963c9c28dab506696d0dbe8aaf8772d5302f @@ -0,0 +1 @@ +h>teu<""e<>;templateteu=d From 544303343f9b490e5f485d36b9c230aedd4108ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Thu, 8 Jan 2026 18:22:58 +0100 Subject: [PATCH 004/806] fixed #14208 - read custom manual URL from `cppcheck.cfg` (#8072) --- cli/cmdlineparser.cpp | 7 +------ lib/settings.cpp | 9 +++++++++ lib/settings.h | 2 ++ test/cli/premium_test.py | 1 + test/testcmdlineparser.cpp | 15 +++++++++------ test/testsettings.cpp | 15 +++++++++++++++ 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 4e567e47d9f..47759b75196 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1702,11 +1702,6 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a void CmdLineParser::printHelp(bool premium) const { - // TODO: fetch URL from config like product name? - const std::string manualUrl(premium ? - "https://files.cppchecksolutions.com/manual.pdf" : - "https://cppcheck.sourceforge.io/manual.pdf"); - std::ostringstream oss; // TODO: display product name oss << "Cppcheck - A tool for static C/C++ code analysis\n" @@ -2077,7 +2072,7 @@ void CmdLineParser::printHelp(bool premium) const " cppcheck -I inc1/ -I inc2/ f.cpp\n" "\n" "For more information:\n" - " " << manualUrl << "\n" + " " << mSettings.manualUrl << "\n" "\n" "Many thanks to the 3rd party libraries we use:\n" " * tinyxml2 -- loading project/library/ctu files.\n" diff --git a/lib/settings.cpp b/lib/settings.cpp index 19b8ce5142f..8e4b8d2a6d4 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -117,6 +117,15 @@ std::string Settings::loadCppcheckCfg(Settings& settings, Suppressions& suppress settings.cppcheckCfgProductName = v.get(); } } + { + const auto it = utils::as_const(obj).find("manualUrl"); + if (it != obj.cend()) { + const auto& v = it->second; + if (!v.is()) + return "'manualUrl' is not a string"; + settings.manualUrl = v.get(); + } + } { const auto it = utils::as_const(obj).find("about"); if (it != obj.cend()) { diff --git a/lib/settings.h b/lib/settings.h index cae78e850eb..04541cda08c 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -291,6 +291,8 @@ class CPPCHECKLIB WARN_UNUSED Settings { int loadAverage{}; #endif + std::string manualUrl{"https://cppcheck.sourceforge.io/manual.pdf"}; + /** --max-configs value */ int maxConfigsOption = 0; // "Not Assigned" value diff --git a/test/cli/premium_test.py b/test/cli/premium_test.py index 1bb01260b42..ddf7b1e2fbe 100644 --- a/test/cli/premium_test.py +++ b/test/cli/premium_test.py @@ -29,6 +29,7 @@ def __copy_cppcheck_premium(tmpdir): "addons": [], "productName": "NAME", "about": "NAME", + "manualUrl" : "https://files.cppchecksolutions.com/manual.pdf", "safety": true } """.replace('NAME', __PRODUCT_NAME)) diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 038d1ea0b01..7b5c646e4bf 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -551,7 +551,8 @@ class TestCmdlineParser : public TestFixture { REDIRECT; ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), "{\n" - "\"productName\": \"Cppcheck Premium\"" + "\"productName\": \"Cppcheck Premium\"," + "\"manualUrl\": \"https://docs.notcppcheck.com/manual.pdf\"" "}\n"); const char * const argv[] = {"cppcheck"}; ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parseFromArgs(argv)); @@ -559,7 +560,7 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS(file.path(), *settings->settingsFiles.cbegin()); const std::string log_str = logger->str(); ASSERT_MSG(startsWith(log_str, "Cppcheck - A tool for static C/C++ code analysis"), "header"); - ASSERT_MSG(log_str.find("https://files.cppchecksolutions.com/manual.pdf") != std::string::npos, "help url"); + ASSERT_MSG(log_str.find("https://docs.notcppcheck.com/manual.pdf") != std::string::npos, "help url"); } void nooptionsWithInvalidCfg() { @@ -589,7 +590,8 @@ class TestCmdlineParser : public TestFixture { REDIRECT; ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), "{\n" - "\"productName\": \"Cppcheck Premium\"" + "\"productName\": \"Cppcheck Premium\"," + "\"manualUrl\": \"https://docs.notcppcheck.com/manual.pdf\"" "}\n"); const char * const argv[] = {"cppcheck", "-h"}; ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parseFromArgs(argv)); @@ -597,7 +599,7 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS(file.path(), *settings->settingsFiles.cbegin()); const std::string log_str = logger->str(); ASSERT_MSG(startsWith(log_str, "Cppcheck - A tool for static C/C++ code analysis"), "header"); - ASSERT_MSG(log_str.find("https://files.cppchecksolutions.com/manual.pdf") != std::string::npos, "help url"); + ASSERT_MSG(log_str.find("https://docs.notcppcheck.com/manual.pdf") != std::string::npos, "help url"); } void helplong() { @@ -618,7 +620,8 @@ class TestCmdlineParser : public TestFixture { REDIRECT; ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), "{\n" - "\"productName\": \"Cppcheck Premium\"" + "\"productName\": \"Cppcheck Premium\"," + "\"manualUrl\": \"https://docs.notcppcheck.com/manual.pdf\"" "}\n"); const char * const argv[] = {"cppcheck", "--help"}; ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parseFromArgs(argv)); @@ -626,7 +629,7 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS(file.path(), *settings->settingsFiles.cbegin()); const std::string log_str = logger->str(); ASSERT_MSG(startsWith(log_str, "Cppcheck - A tool for static C/C++ code analysis"), "header"); - ASSERT_MSG(log_str.find("https://files.cppchecksolutions.com/manual.pdf") != std::string::npos, "help url"); + ASSERT_MSG(log_str.find("https://docs.notcppcheck.com/manual.pdf") != std::string::npos, "help url"); } void version() { diff --git a/test/testsettings.cpp b/test/testsettings.cpp index fd8f62c1a82..7bc6aeb0352 100644 --- a/test/testsettings.cpp +++ b/test/testsettings.cpp @@ -236,6 +236,21 @@ class TestSettings : public TestFixture { R"({"suppressions": [1]}\n)"); ASSERT_EQUALS("'suppressions' array entry is not a string", Settings::loadCppcheckCfg(s, supprs)); } + { + Settings s; + Suppressions supprs; + ScopedFile file("cppcheck.cfg", + R"({"manualUrl": "https://docs.notcppcheck.com/manual.pdf"})"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, supprs)); + ASSERT_EQUALS("https://docs.notcppcheck.com/manual.pdf", s.manualUrl); + } + { + Settings s; + Suppressions supprs; + ScopedFile file("cppcheck.cfg", + R"({"manualUrl": 0}\n)"); + ASSERT_EQUALS("'manualUrl' is not a string", Settings::loadCppcheckCfg(s, supprs)); + } // TODO: test with FILESDIR } From 9dfbfbeabafb24ee5b7cecc9b1deb80bb4f1a610 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:35:09 +0100 Subject: [PATCH 005/806] Add test for #4638 FN memleak (pointer converted to bool on return) (#8098) Co-authored-by: chrchr-github --- test/testleakautovar.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index daf738e7b46..7aa64c1cd3c 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -181,6 +181,7 @@ class TestLeakAutoVar : public TestFixture { TEST_CASE(return10); TEST_CASE(return11); // #13098 TEST_CASE(return12); // #12238 + TEST_CASE(return13); // General tests: variable type, allocation type, etc TEST_CASE(test1); @@ -2838,6 +2839,22 @@ class TestLeakAutoVar : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void return13() { // #4638 + CheckOptions options; + options.cpp = true; + check("bool f() {\n" + " int* p = new int;\n" + " return p;\n" + "}\n" + "bool g() {\n" + " void* p = malloc(4);\n" + " return p;\n" + "}\n", options); + ASSERT_EQUALS("[test.cpp:3:5]: (error) Memory leak: p [memleak]\n" + "[test.cpp:7:5]: (error) Memory leak: p [memleak]\n", + errout_str()); + } + void test1() { check("void f(double*&p) {\n" // 3809 " p = malloc(0x100);\n" From 1019e7a523cce8c3910cf233ed0ed5b1575b15c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 8 Jan 2026 20:55:13 +0100 Subject: [PATCH 006/806] Fix #13337 (GUI: In the Misra Report, use proper icons according to misra classifications) (#8092) --- .selfcheck_suppressions | 1 + gui/resultitem.h | 12 ++++++++++ gui/resultstree.cpp | 12 ++++++---- gui/test/resultstree/testresultstree.cpp | 29 ++++++++++++++++++++++++ gui/test/resultstree/testresultstree.h | 1 + 5 files changed, 50 insertions(+), 5 deletions(-) diff --git a/.selfcheck_suppressions b/.selfcheck_suppressions index 1b6dcc1965b..7728225cd44 100644 --- a/.selfcheck_suppressions +++ b/.selfcheck_suppressions @@ -17,6 +17,7 @@ functionStatic:*/ui_fileview.h # --debug-warnings suppressions valueFlowBailout valueFlowBailoutIncompleteVar +valueFlowMaxIterations:gui/resultstree.cpp autoNoType:externals/simplecpp/simplecpp.cpp autoNoType:cli/*.cpp autoNoType:lib/*.cpp diff --git a/gui/resultitem.h b/gui/resultitem.h index a7669d2a3b4..a39606ad23d 100644 --- a/gui/resultitem.h +++ b/gui/resultitem.h @@ -41,9 +41,21 @@ class ResultItem : public QStandardItem Type getType() const { return mType; } + + void setIconFileName(const QString& iconFileName) { + mIconFileName = iconFileName; + if (!mIconFileName.isEmpty()) + setIcon(QIcon(iconFileName)); + } + + // cppcheck-suppress unusedFunction; used in test-resultstree + const QString& getIconFileName() const { + return mIconFileName; + } private: const Type mType; const int mErrorPathIndex; + QString mIconFileName; }; #endif // RESULTITEM_H diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index 78d56929b48..ed4cc8b0535 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -126,6 +126,10 @@ static QStringList getLabels() { QObject::tr("CWE")}; } +static Severity getSeverity(ReportType reportType, const ErrorItem& errorItem) { + return reportType == ReportType::normal ? errorItem.severity : getSeverityFromClassification(errorItem.classification); +} + ResultsTree::ResultsTree(QWidget * parent) : QTreeView(parent), mModel(new QStandardItemModel) @@ -160,6 +164,7 @@ void ResultsTree::setReportType(ReportType reportType) { QSharedPointer& errorItem = dynamic_cast(fileItem->child(j,0))->errorItem; errorItem->guideline = getGuideline(mReportType, mGuideline, errorItem->errorId, errorItem->severity); errorItem->classification = getClassification(mReportType, errorItem->guideline); + dynamic_cast(fileItem->child(j, COLUMN_FILE))->setIconFileName(severityToIcon(getSeverity(reportType, *errorItem))); fileItem->child(j, COLUMN_CERT_LEVEL)->setText(errorItem->classification); fileItem->child(j, COLUMN_CERT_RULE)->setText(errorItem->guideline); fileItem->child(j, COLUMN_MISRA_CLASSIFICATION)->setText(errorItem->classification); @@ -264,7 +269,7 @@ bool ResultsTree::addErrorItem(const ErrorItem& errorItem) ResultItem* stditem = addBacktraceFiles(fileItem, errorItemPtr, !showItem, - severityToIcon(errorItemPtr->severity), + severityToIcon(getSeverity(mReportType, *errorItemPtr)), ResultItem::Type::message, errorItemPtr->getMainLocIndex()); @@ -317,6 +322,7 @@ ResultItem *ResultsTree::addBacktraceFiles(ResultItem *parent, const int numberOfColumns = getLabels().size(); QList columns(numberOfColumns); columns[COLUMN_FILE] = createFilenameItem(errorItem, type, errorPathIndex); + columns[COLUMN_FILE]->setIconFileName(icon); columns[COLUMN_LINE] = createLineNumberItem(loc.line, errorItem, type, errorPathIndex); columns[COLUMN_SEVERITY] = createNormalItem(itemSeverity, errorItem, type, errorPathIndex); columns[COLUMN_SUMMARY] = createNormalItem(text, errorItem, type, errorPathIndex); @@ -340,10 +346,6 @@ ResultItem *ResultsTree::addBacktraceFiles(ResultItem *parent, setRowHidden(parent->rowCount() - 1, parent->index(), hide); - if (!icon.isEmpty()) { - list[COLUMN_FILE]->setIcon(QIcon(icon)); - } - return columns[COLUMN_FILE]; } diff --git a/gui/test/resultstree/testresultstree.cpp b/gui/test/resultstree/testresultstree.cpp index 0873314a41c..ac3ba4ff78c 100644 --- a/gui/test/resultstree/testresultstree.cpp +++ b/gui/test/resultstree/testresultstree.cpp @@ -294,6 +294,35 @@ void TestResultsTree::testReportType() const "missingReturn,Mandatory,17.4"); } +void TestResultsTree::testReportTypeIcon() const { + ResultsTree tree(nullptr); + tree.setReportType(ReportType::misraC2012); + tree.addErrorItem(createErrorItem("file1.c", 10, Severity::style, "some rule text", "premium-misra-c-2012-1.1")); // Required + tree.addErrorItem(createErrorItem("file1.c", 20, Severity::style, "some rule text", "premium-misra-c-2012-1.2")); // Advisory + tree.addErrorItem(createErrorItem("file1.c", 30, Severity::style, "some rule text", "premium-misra-c-2012-9.1")); // Mandatory + + const auto* model = dynamic_cast(tree.model()); + QVERIFY(model != nullptr); + const ResultItem* fileItem = dynamic_cast(model->item(0,0)); + + const ResultItem* err1 = dynamic_cast(fileItem->child(0,0)); + const ResultItem* err2 = dynamic_cast(fileItem->child(1,0)); + const ResultItem* err3 = dynamic_cast(fileItem->child(2,0)); + + QCOMPARE(err1->getIconFileName(), ":images/dialog-warning.png"); + QCOMPARE(err2->getIconFileName(), ":images/applications-development.png"); + QCOMPARE(err3->getIconFileName(), ":images/dialog-error.png"); + + tree.setReportType(ReportType::normal); + QCOMPARE(err1->getIconFileName(), ":images/applications-development.png"); + QCOMPARE(err2->getIconFileName(), ":images/applications-development.png"); + QCOMPARE(err3->getIconFileName(), ":images/applications-development.png"); + + tree.setReportType(ReportType::misraC2012); + QCOMPARE(err1->getIconFileName(), ":images/dialog-warning.png"); + QCOMPARE(err2->getIconFileName(), ":images/applications-development.png"); + QCOMPARE(err3->getIconFileName(), ":images/dialog-error.png"); +} void TestResultsTree::testGetGuidelineError() const { diff --git a/gui/test/resultstree/testresultstree.h b/gui/test/resultstree/testresultstree.h index 108ed098eb5..8604dc81c6b 100644 --- a/gui/test/resultstree/testresultstree.h +++ b/gui/test/resultstree/testresultstree.h @@ -27,6 +27,7 @@ private slots: void multiLineResult() const; void resultsInSameFile() const; void testReportType() const; + void testReportTypeIcon() const; void testGetGuidelineError() const; void misraCReportShowClassifications() const; }; From 7e723efc99dd30ba0d21db3c360df1124b86ea5b Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 9 Jan 2026 14:48:21 +0100 Subject: [PATCH 007/806] Fix #13711 fuzzing crash (null-pointer-use) in Tokenizer::simplifyCompoundStatements() (#8099) --- lib/tokenize.cpp | 2 ++ .../fuzz-crash/crash-be54fe1da04e7a624214b58f3b572b87082a2e93 | 1 + 2 files changed, 3 insertions(+) create mode 100644 test/cli/fuzz-crash/crash-be54fe1da04e7a624214b58f3b572b87082a2e93 diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index db30349dba7..aa4302198e2 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5707,6 +5707,8 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) if (isCPP()) simplifyExternC(); + validate(); + // simplify compound statements: "[;{}] ( { code; } ) ;"->"[;{}] code;" simplifyCompoundStatements(); diff --git a/test/cli/fuzz-crash/crash-be54fe1da04e7a624214b58f3b572b87082a2e93 b/test/cli/fuzz-crash/crash-be54fe1da04e7a624214b58f3b572b87082a2e93 new file mode 100644 index 00000000000..e263f9d8c66 --- /dev/null +++ b/test/cli/fuzz-crash/crash-be54fe1da04e7a624214b58f3b572b87082a2e93 @@ -0,0 +1 @@ +(({}namespace d=n)); From e2640e7bf2b2213b5851cceb4a061466e16d265a Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 9 Jan 2026 19:26:27 +0100 Subject: [PATCH 008/806] Fix #13497 fuzzing crash (null-pointer-use) in Tokenizer::simplifyTypedefCpp() (#8100) --- lib/tokenize.cpp | 2 ++ .../fuzz-crash/crash-0400fbfed5c89e368e436dbac630db0f80db94bd | 1 + 2 files changed, 3 insertions(+) create mode 100644 test/cli/fuzz-crash/crash-0400fbfed5c89e368e436dbac630db0f80db94bd diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index aa4302198e2..c5e810f1148 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1125,6 +1125,8 @@ void Tokenizer::simplifyTypedef() list.front()->deleteThis(); } + validate(); + simplifyTypedefCpp(); } diff --git a/test/cli/fuzz-crash/crash-0400fbfed5c89e368e436dbac630db0f80db94bd b/test/cli/fuzz-crash/crash-0400fbfed5c89e368e436dbac630db0f80db94bd new file mode 100644 index 00000000000..d6e9b32be37 --- /dev/null +++ b/test/cli/fuzz-crash/crash-0400fbfed5c89e368e436dbac630db0f80db94bd @@ -0,0 +1 @@ +typedef i(*const type2)();typedef t(ty)();(v ty)type2() From e031165626cf74d17e0ca26931aced2517325fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 9 Jan 2026 23:39:26 +0100 Subject: [PATCH 009/806] Fix #14370 (False negative: missingReturn in catch) (#8095) --- lib/checkfunctions.cpp | 13 +++++++++++++ test/testfunctions.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/lib/checkfunctions.cpp b/lib/checkfunctions.cpp index 8884a03fc48..1ab21eacfd2 100644 --- a/lib/checkfunctions.cpp +++ b/lib/checkfunctions.cpp @@ -396,6 +396,19 @@ static const Token *checkMissingReturnScope(const Token *tok, const Library &lib if (Token::simpleMatch(tok->tokAt(-2), "} else {")) return checkMissingReturnScope(tok->tokAt(-2), library); return tok; + } else if (tok->scope()->type == ScopeType::eCatch) { + while (tok->str() == "}") { + const Token *errorToken = checkMissingReturnScope(tok, library); + if (errorToken || tok->scope()->type == ScopeType::eTry) + return errorToken; + tok = tok->link(); + if (Token::simpleMatch(tok->previous(), ") {") && Token::simpleMatch(tok->linkAt(-1)->tokAt(-2), "} catch (")) + tok = tok->linkAt(-1)->tokAt(-2); + else + break; + } + // FIXME this should not be reached + return nullptr; } // FIXME return nullptr; diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index da589c765fe..6aa4e6534a2 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -85,6 +85,7 @@ class TestFunctions : public TestFixture { TEST_CASE(checkMissingReturn4); TEST_CASE(checkMissingReturn5); TEST_CASE(checkMissingReturn6); // #13180 + TEST_CASE(checkMissingReturn7); // #14370 - FN try/catch // std::move for locar variable TEST_CASE(returnLocalStdMove1); @@ -1880,6 +1881,43 @@ class TestFunctions : public TestFixture { ASSERT_EQUALS("[test.cpp:3:5]: (error) Found an exit path from function with non-void return type that has missing return statement [missingReturn]\n", errout_str()); } + void checkMissingReturn7() {// #14370 FN try/catch + check("int foo(void) {\n" + " try { return readData(); }\n" + " catch (...) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:18]: (error) Found an exit path from function with non-void return type that has missing return statement [missingReturn]\n", errout_str()); + + check("int foo(void) {\n" + " try { return readData(); }\n" + " catch (const E& e) {}\n" + " catch (...) { return 2; }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:25]: (error) Found an exit path from function with non-void return type that has missing return statement [missingReturn]\n", errout_str()); + + check("int foo(void) {\n" + " try { x=1; }\n" + " catch (...) { return 2; }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:11]: (error) Found an exit path from function with non-void return type that has missing return statement [missingReturn]\n", errout_str()); + + check("int foo(void) {\n" + " try { return readData(); }\n" + " catch (...) { return 0; }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(void)\n" + " try { x=1; }\n" + " catch (...) { return 2; }\n"); + ASSERT_EQUALS("[test.cpp:2:11]: (error) Found an exit path from function with non-void return type that has missing return statement [missingReturn]\n", errout_str()); + + check("int foo(void)\n" + " try { return readData(); }\n" + " catch (...) { }\n"); + ASSERT_EQUALS("[test.cpp:3:19]: (error) Found an exit path from function with non-void return type that has missing return statement [missingReturn]\n", errout_str()); + } + // NRVO check void returnLocalStdMove1() { check("struct A{}; A f() { A var; return std::move(var); }"); From 574fffaeb56155e52bb2349fa6649c623d1d17ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sat, 10 Jan 2026 01:51:39 +0100 Subject: [PATCH 010/806] moved common `files.txt` loading code to `AnalyzerInformation` (#8097) --- Makefile | 4 +-- lib/analyzerinfo.cpp | 34 ++++++++++++++++++ lib/analyzerinfo.h | 4 +++ lib/checkunusedfunctions.cpp | 67 ++++++++++++------------------------ lib/cppcheck.cpp | 55 +++++++++-------------------- lib/ctu.cpp | 1 + oss-fuzz/Makefile | 4 +-- 7 files changed, 81 insertions(+), 88 deletions(-) diff --git a/Makefile b/Makefile index 02482206b9a..3fef3103d96 100644 --- a/Makefile +++ b/Makefile @@ -565,7 +565,7 @@ $(libcppdir)/checktype.o: lib/checktype.cpp lib/addoninfo.h lib/astutils.h lib/c $(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkers.h lib/checknullpointer.h lib/checkuninitvar.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.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/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkuninitvar.cpp -$(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/checkers.h lib/checkunusedfunctions.h lib/config.h lib/errorlogger.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/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h +$(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/astutils.h lib/checkers.h lib/checkunusedfunctions.h lib/config.h lib/errorlogger.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/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedfunctions.cpp $(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkers.h lib/checkunusedvar.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.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/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h @@ -580,7 +580,7 @@ $(libcppdir)/clangimport.o: lib/clangimport.cpp lib/addoninfo.h lib/checkers.h l $(libcppdir)/color.o: lib/color.cpp lib/color.h lib/config.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/color.cpp -$(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkunusedfunctions.h lib/clangimport.h lib/color.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/regex.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h lib/vfvalue.h lib/xml.h +$(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkunusedfunctions.h lib/clangimport.h lib/color.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/regex.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/cppcheck.cpp $(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/config.h lib/ctu.h lib/errorlogger.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/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index 9477f2e290d..6d5ac9a9b15 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -209,3 +209,37 @@ bool AnalyzerInformation::Info::parse(const std::string& filesTxtLine) { return true; } +// TODO: bail out on unexpected data +void AnalyzerInformation::processFilesTxt(const std::string& buildDir, const std::function& handler) +{ + const std::string filesTxt(buildDir + "/files.txt"); + std::ifstream fin(filesTxt.c_str()); + std::string filesTxtLine; + while (std::getline(fin, filesTxtLine)) { + AnalyzerInformation::Info filesTxtInfo; + if (!filesTxtInfo.parse(filesTxtLine)) { + return; + } + + const std::string xmlfile = buildDir + '/' + filesTxtInfo.afile; + + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); + if (error != tinyxml2::XML_SUCCESS) + return; + + const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); + if (rootNode == nullptr) + return; + + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "FileInfo") != 0) + continue; + const char *checkattr = e->Attribute("check"); + if (checkattr == nullptr) + continue; + handler(checkattr, e, filesTxtInfo); + } + } +} + diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index 732587e0a71..04ea1a5b339 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -33,6 +34,7 @@ struct FileSettings; namespace tinyxml2 { class XMLDocument; + class XMLElement; }; /// @addtogroup Core @@ -75,6 +77,8 @@ class CPPCHECKLIB AnalyzerInformation { std::string sourceFile; }; + static void processFilesTxt(const std::string& buildDir, const std::function& handler); + protected: static std::string getFilesTxt(const std::list &sourcefiles, const std::string &userDefines, const std::list &fileSettings); diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index b45200d7128..7edaff6c241 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -20,6 +20,7 @@ //--------------------------------------------------------------------------- #include "checkunusedfunctions.h" +#include "analyzerinfo.h" #include "astutils.h" #include "errorlogger.h" #include "errortypes.h" @@ -34,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -457,55 +458,31 @@ void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLo std::map decls; std::set calls; - const std::string filesTxt(buildDir + "/files.txt"); - std::ifstream fin(filesTxt.c_str()); - std::string filesTxtLine; - while (std::getline(fin, filesTxtLine)) { - const std::string::size_type firstColon = filesTxtLine.find(':'); - if (firstColon == std::string::npos) - continue; - const std::string::size_type secondColon = filesTxtLine.find(':', firstColon+1); - if (secondColon == std::string::npos) - continue; - const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); - const std::string sourcefile = filesTxtLine.substr(secondColon+1); - - tinyxml2::XMLDocument doc; - const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); - if (error != tinyxml2::XML_SUCCESS) - continue; - - const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); - if (rootNode == nullptr) - continue; - - for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { - if (std::strcmp(e->Name(), "FileInfo") != 0) + const auto handler = [&decls, &calls](const char* checkattr, const tinyxml2::XMLElement* e, const AnalyzerInformation::Info& filesTxtInfo) { + if (std::strcmp(checkattr,"CheckUnusedFunctions") != 0) + return; + for (const tinyxml2::XMLElement *e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { + const char* functionName = e2->Attribute("functionName"); + if (functionName == nullptr) continue; - const char *checkattr = e->Attribute("check"); - if (checkattr == nullptr || std::strcmp(checkattr,"CheckUnusedFunctions") != 0) + const char* name = e2->Name(); + if (std::strcmp(name,"functioncall") == 0) { + calls.insert(functionName); continue; - for (const tinyxml2::XMLElement *e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { - const char* functionName = e2->Attribute("functionName"); - if (functionName == nullptr) - continue; - const char* name = e2->Name(); - if (std::strcmp(name,"functioncall") == 0) { - calls.insert(functionName); - continue; - } - if (std::strcmp(name,"functiondecl") == 0) { - const char* lineNumber = e2->Attribute("lineNumber"); - if (lineNumber) { - const char* file = e2->Attribute("file"); - const char* column = default_if_null(e2->Attribute("column"), "0"); - // cppcheck-suppress templateInstantiation - TODO: fix this - see #11631 - decls[functionName] = Location(file ? file : sourcefile, strToInt(lineNumber), strToInt(column)); - } + } + if (std::strcmp(name,"functiondecl") == 0) { + const char* lineNumber = e2->Attribute("lineNumber"); + if (lineNumber) { + const char* file = e2->Attribute("file"); + const char* column = default_if_null(e2->Attribute("column"), "0"); + // cppcheck-suppress templateInstantiation - TODO: fix this - see #11631 + decls[functionName] = Location(file ? file : filesTxtInfo.sourceFile, strToInt(lineNumber), strToInt(column)); } } } - } + }; + + AnalyzerInformation::processFilesTxt(buildDir, handler); for (auto decl = decls.cbegin(); decl != decls.cend(); ++decl) { const std::string &functionName = stripTemplateParameters(decl->first); diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 0fb23203718..c7c6ea65dfc 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -70,10 +70,11 @@ #include #include "json.h" -#include "xml.h" #include +namespace tinyxml2 { class XMLElement; } + static constexpr char Version[] = CPPCHECK_VERSION_STRING; static constexpr char ExtraVersion[] = ""; @@ -1851,50 +1852,26 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st return mLogger->exitcode(); executeAddonsWholeProgram(files, fileSettings, ctuInfo); + std::list fileInfoList; CTU::FileInfo ctuFileInfo; - // Load all analyzer info data.. - const std::string filesTxt(buildDir + "/files.txt"); - std::ifstream fin(filesTxt); - std::string filesTxtLine; - while (std::getline(fin, filesTxtLine)) { - AnalyzerInformation::Info filesTxtInfo; - if (!filesTxtInfo.parse(filesTxtLine)) - continue; - - const std::string xmlfile = buildDir + '/' + filesTxtInfo.afile; - - tinyxml2::XMLDocument doc; - const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); - if (error != tinyxml2::XML_SUCCESS) - continue; - - const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); - if (rootNode == nullptr) - continue; - - for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { - if (std::strcmp(e->Name(), "FileInfo") != 0) - continue; - const char *checkClassAttr = e->Attribute("check"); - if (!checkClassAttr) - continue; - if (std::strcmp(checkClassAttr, "ctu") == 0) { - ctuFileInfo.loadFromXml(e); - continue; - } - // cppcheck-suppress shadowFunction - TODO: fix this - for (const Check *check : Check::instances()) { - if (checkClassAttr == check->name()) { - if (Check::FileInfo* fi = check->loadFileInfoFromXml(e)) { - fi->file0 = filesTxtInfo.sourceFile; - fileInfoList.push_back(fi); - } + const auto handler = [&fileInfoList, &ctuFileInfo](const char* checkattr, const tinyxml2::XMLElement* e, const AnalyzerInformation::Info& filesTxtInfo) { + if (std::strcmp(checkattr, "ctu") == 0) { + ctuFileInfo.loadFromXml(e); + return; + } + for (const Check *check : Check::instances()) { + if (checkattr == check->name()) { + if (Check::FileInfo* fi = check->loadFileInfoFromXml(e)) { + fi->file0 = filesTxtInfo.sourceFile; + fileInfoList.push_back(fi); } } } - } + }; + + AnalyzerInformation::processFilesTxt(buildDir, handler); // Analyse the tokens // cppcheck-suppress shadowFunction - TODO: fix this diff --git a/lib/ctu.cpp b/lib/ctu.cpp index 158c8c1e046..df8ee0f74b6 100644 --- a/lib/ctu.cpp +++ b/lib/ctu.cpp @@ -231,6 +231,7 @@ bool CTU::FileInfo::NestedCall::loadFromXml(const tinyxml2::XMLElement *xmlEleme return !error; } +// TODO: bail out on unexpected data void CTU::FileInfo::loadFromXml(const tinyxml2::XMLElement *xmlElement) { for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index 02d2e7fbde2..ad7fb641024 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -245,7 +245,7 @@ $(libcppdir)/checktype.o: ../lib/checktype.cpp ../lib/addoninfo.h ../lib/astutil $(libcppdir)/checkuninitvar.o: ../lib/checkuninitvar.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkers.h ../lib/checknullpointer.h ../lib/checkuninitvar.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.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/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkuninitvar.cpp -$(libcppdir)/checkunusedfunctions.o: ../lib/checkunusedfunctions.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/astutils.h ../lib/checkers.h ../lib/checkunusedfunctions.h ../lib/config.h ../lib/errorlogger.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/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h +$(libcppdir)/checkunusedfunctions.o: ../lib/checkunusedfunctions.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/astutils.h ../lib/checkers.h ../lib/checkunusedfunctions.h ../lib/config.h ../lib/errorlogger.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/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedfunctions.cpp $(libcppdir)/checkunusedvar.o: ../lib/checkunusedvar.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkers.h ../lib/checkunusedvar.h ../lib/config.h ../lib/errortypes.h ../lib/fwdanalysis.h ../lib/library.h ../lib/mathlib.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/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h @@ -260,7 +260,7 @@ $(libcppdir)/clangimport.o: ../lib/clangimport.cpp ../lib/addoninfo.h ../lib/che $(libcppdir)/color.o: ../lib/color.cpp ../lib/color.h ../lib/config.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/color.cpp -$(libcppdir)/cppcheck.o: ../lib/cppcheck.cpp ../externals/picojson/picojson.h ../externals/simplecpp/simplecpp.h ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/check.h ../lib/checkers.h ../lib/checkunusedfunctions.h ../lib/clangimport.h ../lib/color.h ../lib/config.h ../lib/cppcheck.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/regex.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/version.h ../lib/vfvalue.h ../lib/xml.h +$(libcppdir)/cppcheck.o: ../lib/cppcheck.cpp ../externals/picojson/picojson.h ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/check.h ../lib/checkers.h ../lib/checkunusedfunctions.h ../lib/clangimport.h ../lib/color.h ../lib/config.h ../lib/cppcheck.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/regex.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/version.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/cppcheck.cpp $(libcppdir)/ctu.o: ../lib/ctu.cpp ../externals/tinyxml2/tinyxml2.h ../lib/astutils.h ../lib/check.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.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/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h From 105ea18d115542b2b7d3d0435a8e59322dbd6580 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 10 Jan 2026 11:47:47 +0100 Subject: [PATCH 011/806] Fix #13706 fuzzing crash (assert) in TemplateSimplifier::TokenAndName::TokenAndName() (#8102) Co-authored-by: chrchr-github --- lib/templatesimplifier.cpp | 5 ++--- .../crash-5ac13e244448ff767f6596c220edb9ef7b61fa6d | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 test/cli/fuzz-crash/crash-5ac13e244448ff767f6596c220edb9ef7b61fa6d diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 349894b2b89..e7a096932cf 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -219,9 +219,8 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *token, std::string scope, } // make sure at most only one family flag is set - assert(isClass() ? !(isFunction() || isVariable()) : true); - assert(isFunction() ? !(isClass() || isVariable()) : true); - assert(isVariable() ? !(isClass() || isFunction()) : true); + if (isClass() + isFunction() + isVariable() > 1) + syntaxError(token); if (mToken) mToken->templateSimplifierPointer(this); diff --git a/test/cli/fuzz-crash/crash-5ac13e244448ff767f6596c220edb9ef7b61fa6d b/test/cli/fuzz-crash/crash-5ac13e244448ff767f6596c220edb9ef7b61fa6d new file mode 100644 index 00000000000..7bbc08cfd6d --- /dev/null +++ b/test/cli/fuzz-crash/crash-5ac13e244448ff767f6596c220edb9ef7b61fa6d @@ -0,0 +1 @@ +template<>struct t<>(); From 466839e79e2a44254142ba0358a3765ae2b3d818 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 10 Jan 2026 11:52:01 +0100 Subject: [PATCH 012/806] Fix #13705 fuzzing crash (null-pointer-use) in getEnumType() (#8103) Co-authored-by: chrchr-github --- lib/tokenize.cpp | 2 ++ .../fuzz-crash/crash-cd91802f0554ab425b82191389c9db2be0d50040 | 1 + 2 files changed, 3 insertions(+) create mode 100644 test/cli/fuzz-crash/crash-cd91802f0554ab425b82191389c9db2be0d50040 diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index c5e810f1148..06cb0dbd426 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8955,6 +8955,8 @@ void Tokenizer::findGarbageCode() const if (Token::Match(tok, "!|~ %comp%") && !(cpp && tok->strAt(1) == ">" && Token::simpleMatch(tok->tokAt(-1), "operator"))) syntaxError(tok); + if (Token::Match(tok, "%comp% {") && (!cpp || tok->str() != ">")) + syntaxError(tok); if (Token::Match(tok, "] %name%") && (!cpp || !(tok->tokAt(1)->isKeyword() || (tok->tokAt(-1) && Token::simpleMatch(tok->tokAt(-2), "delete ["))))) { if (tok->next()->isUpperCaseName()) unknownMacroError(tok->next()); diff --git a/test/cli/fuzz-crash/crash-cd91802f0554ab425b82191389c9db2be0d50040 b/test/cli/fuzz-crash/crash-cd91802f0554ab425b82191389c9db2be0d50040 new file mode 100644 index 00000000000..b2a885a35d3 --- /dev/null +++ b/test/cli/fuzz-crash/crash-cd91802f0554ab425b82191389c9db2be0d50040 @@ -0,0 +1 @@ +{f F<{enum{E=1};}>} \ No newline at end of file From 19640019b704e9265c4fa77acf56a011ecbccabb Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 10 Jan 2026 12:33:00 +0100 Subject: [PATCH 013/806] Fix #13501 fuzzing crash (heap-use-after-free) in Tokenizer::simplifyNamespaceAliases() (#8101) Co-authored-by: chrchr-github --- lib/tokenize.cpp | 6 ++++-- .../crash-fb09b3314f55a502c8dd27f3f114122c71dd207e | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 test/cli/fuzz-crash/crash-fb09b3314f55a502c8dd27f3f114122c71dd207e diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 06cb0dbd426..7cb080e071f 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9003,8 +9003,8 @@ void Tokenizer::findGarbageCode() const } } } - if (cpp && tok->str() == "namespace" && tok->tokAt(-1)) { - if (!Token::Match(tok->tokAt(-1), ";|{|}|using|inline")) { + if (cpp && tok->str() == "namespace") { + if (tok->tokAt(-1) && !Token::Match(tok->tokAt(-1), ";|{|}|using|inline")) { if (tok->tokAt(-1)->isUpperCaseName()) unknownMacroError(tok->tokAt(-1)); else if (tok->linkAt(-1) && tok->linkAt(-1)->tokAt(-1) && tok->linkAt(-1)->tokAt(-1)->isUpperCaseName()) @@ -9012,6 +9012,8 @@ void Tokenizer::findGarbageCode() const else syntaxError(tok); } + if (!tok->next() || (Token::Match(tok->next(), "%name% =") && !Token::Match(tok->tokAt(3), "::|%name%"))) + syntaxError(tok); } if (cpp && tok->str() == "using" && !Token::Match(tok->next(), "::|%name%")) syntaxError(tok); diff --git a/test/cli/fuzz-crash/crash-fb09b3314f55a502c8dd27f3f114122c71dd207e b/test/cli/fuzz-crash/crash-fb09b3314f55a502c8dd27f3f114122c71dd207e new file mode 100644 index 00000000000..3a017fdb2e0 --- /dev/null +++ b/test/cli/fuzz-crash/crash-fb09b3314f55a502c8dd27f3f114122c71dd207e @@ -0,0 +1 @@ +;namespace b=i;;namespace b={} From 34b9c45c4c67eb5dfa0aa99c0ce6198faaad4b18 Mon Sep 17 00:00:00 2001 From: clock999 Date: Sun, 11 Jan 2026 17:36:15 +0800 Subject: [PATCH 014/806] Fix #12861 Hang in valueFlowCondition() with huge array (#7757) I tested the fix based on 4617bc25d82ff8d46567e002995ad8f859d5e814. Use the cppcheck to check the below code which is submitted on the ticket #12861. ``` #define ROW A, A, A, A, A, A, A, A, #define ROW8 ROW ROW ROW ROW ROW ROW ROW ROW #define ROW64 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 #define ROW512 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 void f() { static const char A = 'a'; const char a[] = { ROW512 ROW512 ROW512 ROW512 }; } ``` With the fix, the overall time is 1.52582s. Without the fix, the time is 25.9674s. I also tested the testrunner for running the whole of the unit tests. There is some performance improment with the fix, but not remarkable. --- lib/token.h | 10 ++++++++++ lib/tokenlist.cpp | 10 ++++++++++ test/cli/performance_test.py | 23 +++++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/lib/token.h b/lib/token.h index 5fddf510b19..d26c7ac7640 100644 --- a/lib/token.h +++ b/lib/token.h @@ -117,6 +117,7 @@ class CPPCHECKLIB Token { Token* mAstOperand1{}; Token* mAstOperand2{}; Token* mAstParent{}; + Token* mAstTop{}; // symbol database information const Scope* mScope{}; @@ -1557,6 +1558,9 @@ class CPPCHECKLIB Token { * @throws InternalError thrown on cyclic dependency */ void astParent(Token* tok); + void astTop(Token * tok) { + mImpl->mAstTop = tok; + } Token * astOperand1() { return mImpl->mAstOperand1; @@ -1597,6 +1601,9 @@ class CPPCHECKLIB Token { } RET_NONNULL Token *astTop() { + if (mImpl->mAstTop) { + return mImpl->mAstTop; + } Token *ret = this; while (ret->mImpl->mAstParent) ret = ret->mImpl->mAstParent; @@ -1604,6 +1611,9 @@ class CPPCHECKLIB Token { } RET_NONNULL const Token *astTop() const { + if (mImpl->mAstTop) { + return mImpl->mAstTop; + } const Token *ret = this; while (ret->mImpl->mAstParent) ret = ret->mImpl->mAstParent; diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 1e476a14ec5..7fccbb89f1d 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -1860,6 +1860,16 @@ void TokenList::createAst() const throw InternalError(tok, "Syntax Error: Infinite loop when creating AST.", InternalError::AST); tok = nextTok; } + for (Token *tok = mTokensFrontBack->front; tok; tok = tok ? tok->next() : nullptr) { + if (tok->astParent()) + continue; + if (!tok->astOperand1() && !tok->astOperand2()) + continue; + visitAstNodes(tok, [&](Token* child) { + child->astTop(tok); + return ChildrenToVisit::op1_and_op2; + }); + } } namespace { diff --git a/test/cli/performance_test.py b/test/cli/performance_test.py index 9f5141f2bb9..90daf9b58cc 100644 --- a/test/cli/performance_test.py +++ b/test/cli/performance_test.py @@ -221,6 +221,7 @@ def test_slow_many_scopes(tmpdir): }""") cppcheck([filename]) # should not take more than ~1 second + @pytest.mark.skipif(sys.platform == 'darwin', reason='GitHub macOS runners are too slow') @pytest.mark.timeout(20) def test_crash_array_in_namespace(tmpdir): @@ -241,6 +242,28 @@ def test_crash_array_in_namespace(tmpdir): }""") cppcheck([filename]) # should not take more than ~5 seconds + +@pytest.mark.skipif(sys.platform == 'darwin', reason='GitHub macOS runners are too slow') +@pytest.mark.timeout(20) +def test_crash_array_in_array(tmpdir): + # 12861 + filename = os.path.join(tmpdir, 'hang.cpp') + with open(filename, 'wt') as f: + f.write(r""" + #define ROW A, A, A, A, A, A, A, A, + #define ROW8 ROW ROW ROW ROW ROW ROW ROW ROW + #define ROW64 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 + #define ROW512 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 + #define ROW4096 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512 + void f() { + static const char A = 'a'; + const char a[] = { + ROW4096 ROW4096 ROW4096 ROW4096 + }; + }""") + cppcheck([filename]) + + @pytest.mark.timeout(5) def test_slow_bifurcate(tmpdir): # #14134 From 298d602e41c74d62bb571001cba0300a635e6186 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:27:35 +0100 Subject: [PATCH 015/806] Fix #14388 cppcheck.natvis: TokenList visualization is broken [skip ci] (#8111) --- lib/cppcheck.natvis | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cppcheck.natvis b/lib/cppcheck.natvis index 4b3b2d0aea6..6f5b6fd86a2 100644 --- a/lib/cppcheck.natvis +++ b/lib/cppcheck.natvis @@ -10,13 +10,13 @@ - {mTokensFrontBack.front->mStr} - {mTokensFrontBack.back->mStr} + {mTokensFrontBack->front->mStr} - {mTokensFrontBack->back->mStr} mFiles - + - + pCurr @@ -68,4 +68,4 @@ - \ No newline at end of file + From a683c058ed585e171aa7530be19a742ea08c6192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 12 Jan 2026 18:21:46 +0100 Subject: [PATCH 016/806] Fix #14389 (triage: fix triage.pro file) (#8113) --- tools/triage/triage.pro | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/triage/triage.pro b/tools/triage/triage.pro index 1bda43f40a7..213022968cd 100644 --- a/tools/triage/triage.pro +++ b/tools/triage/triage.pro @@ -1,8 +1,7 @@ -lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) QT += core gui widgets TARGET = triage TEMPLATE = app -QMAKE_CXXFLAGS += -std=c++11 +QMAKE_CXXFLAGS += -std=c++17 INCLUDEPATH += ../../gui MOC_DIR = temp OBJECTS_DIR = temp From db22e097baf769f8216f0c3b3db0f260ebba6bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 13 Jan 2026 02:16:44 +0100 Subject: [PATCH 017/806] some `Settings::premium` related cleanups (#8109) --- cli/cmdlineparser.cpp | 8 ++++---- cli/cmdlineparser.h | 2 +- lib/checkersreport.cpp | 6 +----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 47759b75196..48ac7e429d9 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -353,7 +353,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a return Result::Fail; if (argc <= 1) { - printHelp(mSettings.premium); + printHelp(); return Result::Exit; } @@ -388,7 +388,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // Print help if (std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0) { - printHelp(mSettings.premium); + printHelp(); return Result::Exit; } @@ -1700,7 +1700,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a return Result::Success; } -void CmdLineParser::printHelp(bool premium) const +void CmdLineParser::printHelp() const { std::ostringstream oss; // TODO: display product name @@ -1901,7 +1901,7 @@ void CmdLineParser::printHelp(bool premium) const " --plist-output=\n" " Generate Clang-plist output files in folder.\n"; - if (premium) { + if (mSettings.premium) { oss << " --premium=