From 3c6f2fd34dd819ce13e4e531feabf06dc1bd7081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sun, 15 Jun 2025 12:42:19 +0200 Subject: [PATCH 1/5] add separate report types for misra C editions --- cli/cmdlineparser.cpp | 9 +++++++-- gui/mainwindow.cpp | 17 +++++++++++++--- gui/projectfiledialog.cpp | 16 +++++++-------- gui/projectfiledialog.h | 9 +++++++++ gui/resultstree.cpp | 4 +++- lib/checkers.h | 4 +++- lib/errorlogger.cpp | 42 +++++++++++++++++++++++++++++---------- man/cppcheck.1.xml | 1 + 8 files changed, 77 insertions(+), 25 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 0ad4648d0af..606396b84fe 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1309,8 +1309,12 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a mSettings.reportType = ReportType::certC; } else if (typeStr == "cert-cpp-2016") { mSettings.reportType = ReportType::certCpp; - } else if (typeStr == "misra-c-2012" || typeStr == "misra-c-2023") { - mSettings.reportType = ReportType::misraC; + } else if (typeStr == "misra-c-2012") { + mSettings.reportType = ReportType::misraC2012; + } else if (typeStr == "misra-c-2023") { + mSettings.reportType = ReportType::misraC2023; + } else if (typeStr == "misra-c-2025") { + mSettings.reportType = ReportType::misraC2025; } else if (typeStr == "misra-cpp-2008") { mSettings.reportType = ReportType::misraCpp2008; } else if (typeStr == "misra-cpp-2023") { @@ -1963,6 +1967,7 @@ void CmdLineParser::printHelp() const " * cert-cpp-2016 Cert C++ 2016\n" " * misra-c-2012 Misra C 2012\n" " * misra-c-2023 Misra C 2023\n" + " * misra-c-2025 Misra C 2025\n" " * misra-cpp-2008 Misra C++ 2008\n" " * misra-cpp-2023 Misra C++ 2023\n" " --rule= Match regular expression.\n" diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index af9dbc2a3ba..cefffbebbfe 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -391,7 +391,9 @@ void MainWindow::loadSettings() mUI->mActionReportAutosar->setChecked(reportType == ReportType::autosar); mUI->mActionReportCertC->setChecked(reportType == ReportType::certC); mUI->mActionReportCertCpp->setChecked(reportType == ReportType::certCpp); - mUI->mActionReportMisraC->setChecked(reportType == ReportType::misraC); + mUI->mActionReportMisraC->setChecked(reportType == ReportType::misraC2012 || + reportType == ReportType::misraC2023 || + reportType == ReportType::misraC2025); mUI->mActionReportMisraCpp2008->setChecked(reportType == ReportType::misraCpp2008); mUI->mActionReportMisraCpp2023->setChecked(reportType == ReportType::misraCpp2023); @@ -470,6 +472,15 @@ void MainWindow::loadSettings() } } +static ReportType getMisraCReportType(const QStringList &standards) +{ + if (standards.contains(CODING_STANDARD_MISRA_C_2023)) + return ReportType::misraC2023; + if (standards.contains(CODING_STANDARD_MISRA_C_2025)) + return ReportType::misraC2025; + return ReportType::misraC2012; +} + void MainWindow::saveSettings() const { // Window/dialog sizes @@ -480,7 +491,7 @@ void MainWindow::saveSettings() const const ReportType reportType = mUI->mActionReportAutosar->isChecked() ? ReportType::autosar : mUI->mActionReportCertC->isChecked() ? ReportType::certC : mUI->mActionReportCertCpp->isChecked() ? ReportType::certCpp : - mUI->mActionReportMisraC->isChecked() ? ReportType::misraC : + mUI->mActionReportMisraC->isChecked() ? (mProjectFile ? getMisraCReportType(mProjectFile->getCodingStandards()) : ReportType::misraC2012) : mUI->mActionReportMisraCpp2008->isChecked() ? ReportType::misraCpp2008 : mUI->mActionReportMisraCpp2023->isChecked() ? ReportType::misraCpp2023 : ReportType::normal; @@ -2283,7 +2294,7 @@ void MainWindow::changeReportType() { const ReportType reportType = mUI->mActionReportAutosar->isChecked() ? ReportType::autosar : mUI->mActionReportCertC->isChecked() ? ReportType::certC : mUI->mActionReportCertCpp->isChecked() ? ReportType::certCpp : - mUI->mActionReportMisraC->isChecked() ? ReportType::misraC : + mUI->mActionReportMisraC->isChecked() ? (mProjectFile ? getMisraCReportType(mProjectFile->getCodingStandards()) : ReportType::misraC2012) : mUI->mActionReportMisraCpp2008->isChecked() ? ReportType::misraCpp2008 : mUI->mActionReportMisraCpp2023->isChecked() ? ReportType::misraCpp2023 : ReportType::normal; diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index acb6b520e21..ef7baf18562 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -59,14 +59,14 @@ #include #include -static constexpr char ADDON_MISRA[] = "misra"; -static constexpr char CODING_STANDARD_MISRA_C_2023[] = "misra-c-2023"; -static constexpr char CODING_STANDARD_MISRA_C_2025[] = "misra-c-2025"; -static constexpr char CODING_STANDARD_MISRA_CPP_2008[] = "misra-cpp-2008"; -static constexpr char CODING_STANDARD_MISRA_CPP_2023[] = "misra-cpp-2023"; -static constexpr char CODING_STANDARD_CERT_C[] = "cert-c-2016"; -static constexpr char CODING_STANDARD_CERT_CPP[] = "cert-cpp-2016"; -static constexpr char CODING_STANDARD_AUTOSAR[] = "autosar"; +const char ADDON_MISRA[] = "misra"; +const char CODING_STANDARD_MISRA_C_2023[] = "misra-c-2023"; +const char CODING_STANDARD_MISRA_C_2025[] = "misra-c-2025"; +const char CODING_STANDARD_MISRA_CPP_2008[] = "misra-cpp-2008"; +const char CODING_STANDARD_MISRA_CPP_2023[] = "misra-cpp-2023"; +const char CODING_STANDARD_CERT_C[] = "cert-c-2016"; +const char CODING_STANDARD_CERT_CPP[] = "cert-cpp-2016"; +const char CODING_STANDARD_AUTOSAR[] = "autosar"; /** Return paths from QListWidget */ static QStringList getPaths(const QListWidget *list) diff --git a/gui/projectfiledialog.h b/gui/projectfiledialog.h index 3dfb6b89f93..60de3ae4b7b 100644 --- a/gui/projectfiledialog.h +++ b/gui/projectfiledialog.h @@ -33,6 +33,15 @@ namespace Ui { class ProjectFile; } +extern const char ADDON_MISRA[]; +extern const char CODING_STANDARD_MISRA_C_2023[]; +extern const char CODING_STANDARD_MISRA_C_2025[]; +extern const char CODING_STANDARD_MISRA_CPP_2008[]; +extern const char CODING_STANDARD_MISRA_CPP_2023[]; +extern const char CODING_STANDARD_CERT_C[]; +extern const char CODING_STANDARD_CERT_CPP[]; +extern const char CODING_STANDARD_AUTOSAR[]; + /// @addtogroup GUI /// @{ diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index 3ab9e2602ff..7946dba0609 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -1534,7 +1534,9 @@ bool ResultsTree::isCertReport() const { bool ResultsTree::isAutosarMisraReport() const { return mReportType == ReportType::autosar || - mReportType == ReportType::misraC || + mReportType == ReportType::misraC2012 || + mReportType == ReportType::misraC2023 || + mReportType == ReportType::misraC2025 || mReportType == ReportType::misraCpp2008 || mReportType == ReportType::misraCpp2023; } diff --git a/lib/checkers.h b/lib/checkers.h index c9cd5cccf3e..385b2971ed0 100644 --- a/lib/checkers.h +++ b/lib/checkers.h @@ -31,9 +31,11 @@ enum class ReportType : std::uint8_t { autosar = 1, certC = 2, certCpp = 3, - misraC = 4, + misraC2012 = 4, misraCpp2008 = 5, misraCpp2023 = 6, + misraC2023 = 7, + misraC2025 = 8, }; namespace checkers { diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index c22df228e21..b5b477e766c 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -945,24 +945,40 @@ std::string getClassification(const std::string &guideline, ReportType reportTyp return getClassification(checkers::certCInfo, guideline); case ReportType::certCpp: return getClassification(checkers::certCppInfo, guideline); - case ReportType::misraC: + case ReportType::misraC2012: + case ReportType::misraC2023: + case ReportType::misraC2025: { - auto components = splitString(guideline, '.'); + const bool isDirective = guideline.rfind("Dir ", 0) == 0; + + const std::size_t offset = isDirective ? 4 : 0; + auto components = splitString(guideline.substr(offset), '.'); if (components.size() != 2) return ""; const int a = std::stoi(components[0]); const int b = std::stoi(components[1]); - const std::vector &info = checkers::misraC2012Rules; - const auto it = std::find_if(info.cbegin(), info.cend(), [&](const checkers::MisraInfo &i) { + const std::vector *info = nullptr; + switch (reportType) { + case ReportType::misraC2012: + info = isDirective ? &checkers::misraC2012Directives : &checkers::misraC2012Rules; + break; + case ReportType::misraC2023: + info = isDirective ? &checkers::misraC2023Directives : &checkers::misraC2023Rules; + break; + case ReportType::misraC2025: + info = isDirective ? &checkers::misraC2025Directives : &checkers::misraC2025Rules; + break; + default: + break; + } + + const auto it = std::find_if(info->cbegin(), info->cend(), [&](const checkers::MisraInfo &i) { return i.a == a && i.b == b; }); - if (it == info.cend()) - return ""; - - return it->str; + return it == info->cend() ? "" : it->str; } case ReportType::misraCpp2008: case ReportType::misraCpp2023: @@ -1022,9 +1038,13 @@ std::string getGuideline(const std::string &errId, ReportType reportType, guideline.begin(), static_cast(std::toupper)); } break; - case ReportType::misraC: + case ReportType::misraC2012: + case ReportType::misraC2023: + case ReportType::misraC2025: if (errId.rfind("misra-c20", 0) == 0 || errId.rfind("premium-misra-c-20", 0) == 0) guideline = errId.substr(errId.rfind('-') + 1); + if (errId.find("dir") != std::string::npos) + guideline = "Dir " + guideline; break; case ReportType::misraCpp2008: if (errId.rfind("misra-cpp-2008-", 0) == 0) @@ -1074,7 +1094,9 @@ std::map createGuidelineMapping(ReportType reportType) idMapping1 = &checkers::idMappingCertC; ext1 = "-C"; break; - case ReportType::misraC: + case ReportType::misraC2012: + case ReportType::misraC2023: + case ReportType::misraC2025: idMapping1 = &checkers::idMappingMisraC; break; case ReportType::misraCpp2008: diff --git a/man/cppcheck.1.xml b/man/cppcheck.1.xml index 1265e0f3511..eff9118a2cf 100644 --- a/man/cppcheck.1.xml +++ b/man/cppcheck.1.xml @@ -594,6 +594,7 @@ There are false positives with this option. Each result must be carefully invest cert-cpp-2016Cert C++ 2016 misra-c-2012Misra C 2012 misra-c-2023Misra C 2023 + misra-c-2025Misra C 2025 misra-cpp-2008Misra C++ 2008 misra-cpp-2023Misra C++ 2023 From 1a7290084e4c2003d7c6e1d00d84386143497e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sun, 15 Jun 2025 12:42:51 +0200 Subject: [PATCH 2/5] add/update tests --- gui/test/resultstree/testresultstree.cpp | 4 ++-- test/testcmdlineparser.cpp | 12 ++++++++++-- test/testerrorlogger.cpp | 16 +++++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/gui/test/resultstree/testresultstree.cpp b/gui/test/resultstree/testresultstree.cpp index 8c92da7db8c..0e595bb5b56 100644 --- a/gui/test/resultstree/testresultstree.cpp +++ b/gui/test/resultstree/testresultstree.cpp @@ -157,7 +157,7 @@ void TestResultsTree::testReportType() const QCOMPARE(report.output, "id1,,\nunusedVariable,,"); // switch to Misra C report and check that "id1" is not shown - tree.setReportType(ReportType::misraC); + tree.setReportType(ReportType::misraC2012); tree.saveResults(&report); QCOMPARE(report.output, "unusedVariable,Advisory,2.8"); @@ -187,7 +187,7 @@ void TestResultsTree::testGetGuidelineError() const // normal report with 2 errors ResultsTree tree(nullptr); - tree.setReportType(ReportType::misraC); + tree.setReportType(ReportType::misraC2012); tree.addErrorItem(createErrorItem(Severity::error, "id1")); // error severity => guideline 1.3 tree.saveResults(&report); QCOMPARE(report.output, "id1,Required,1.3"); diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 3d984eb3727..de2d1fe0ba2 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -502,6 +502,7 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(reportTypeCertC); TEST_CASE(reportTypeMisraC2012); TEST_CASE(reportTypeMisraC2023); + TEST_CASE(reportTypeMisraC2025); TEST_CASE(reportTypeMisraCpp2008); TEST_CASE(reportTypeMisraCpp2023); TEST_CASE(invalidReportType); @@ -3456,14 +3457,21 @@ class TestCmdlineParser : public TestFixture { REDIRECT; const char *const argv[] = { "cppcheck", "--report-type=misra-c-2012", "file.cpp" }; ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); - ASSERT_EQUALS_ENUM(ReportType::misraC, settings->reportType); + ASSERT_EQUALS_ENUM(ReportType::misraC2012, settings->reportType); } void reportTypeMisraC2023() { REDIRECT; const char *const argv[] = { "cppcheck", "--report-type=misra-c-2023", "file.cpp" }; ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); - ASSERT_EQUALS_ENUM(ReportType::misraC, settings->reportType); + ASSERT_EQUALS_ENUM(ReportType::misraC2023, settings->reportType); + } + + void reportTypeMisraC2025() { + REDIRECT; + const char *const argv[] = { "cppcheck", "--report-type=misra-c-2025", "file.cpp" }; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); + ASSERT_EQUALS_ENUM(ReportType::misraC2025, settings->reportType); } void reportTypeMisraCpp2008() { diff --git a/test/testerrorlogger.cpp b/test/testerrorlogger.cpp index 52ffdd7a5f2..40ec88c309d 100644 --- a/test/testerrorlogger.cpp +++ b/test/testerrorlogger.cpp @@ -78,6 +78,7 @@ class TestErrorLogger : public TestFixture { TEST_CASE(isCriticalErrorId); TEST_CASE(ErrorMessageReportTypeMisraC); + TEST_CASE(ErrorMessageReportTypeMisraCDirective); TEST_CASE(ErrorMessageReportTypeCertC); } @@ -317,7 +318,7 @@ class TestErrorLogger : public TestFixture { void ErrorMessageReportTypeMisraC() const { std::list locs = { fooCpp5 }; - const auto reportType = ReportType::misraC; + 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); @@ -328,6 +329,19 @@ class TestErrorLogger : public TestFixture { 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); + 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, "")); + } + void ErrorMessageReportTypeCertC() const { std::list locs = { fooCpp5 }; const auto reportType = ReportType::certC; From 70de7b1461f20c030892da9737d0d508de7e8621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 18 Jun 2025 15:48:13 +0200 Subject: [PATCH 3/5] add unreachable statement --- lib/errorlogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index b5b477e766c..aa5b232119f 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -971,7 +971,7 @@ std::string getClassification(const std::string &guideline, ReportType reportTyp info = isDirective ? &checkers::misraC2025Directives : &checkers::misraC2025Rules; break; default: - break; + cppcheck::unreachable(); } const auto it = std::find_if(info->cbegin(), info->cend(), [&](const checkers::MisraInfo &i) { From 99ed1e240042dc122b830784aa747d1a24734419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 18 Jun 2025 16:19:41 +0200 Subject: [PATCH 4/5] more robust substring checking --- lib/errorlogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index aa5b232119f..9055639af55 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -1043,7 +1043,7 @@ std::string getGuideline(const std::string &errId, ReportType reportType, case ReportType::misraC2025: if (errId.rfind("misra-c20", 0) == 0 || errId.rfind("premium-misra-c-20", 0) == 0) guideline = errId.substr(errId.rfind('-') + 1); - if (errId.find("dir") != std::string::npos) + if (errId.find("-dir-") != std::string::npos) guideline = "Dir " + guideline; break; case ReportType::misraCpp2008: From 2cfaa41482855dceb4d501baa8ae640c28e2eea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 19 Jun 2025 11:42:05 +0200 Subject: [PATCH 5/5] add "Dir " prefix to misra directives guideline strings --- lib/errorlogger.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 9055639af55..e1650c3e670 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -1043,8 +1043,6 @@ std::string getGuideline(const std::string &errId, ReportType reportType, case ReportType::misraC2025: if (errId.rfind("misra-c20", 0) == 0 || errId.rfind("premium-misra-c-20", 0) == 0) guideline = errId.substr(errId.rfind('-') + 1); - if (errId.find("-dir-") != std::string::npos) - guideline = "Dir " + guideline; break; case ReportType::misraCpp2008: if (errId.rfind("misra-cpp-2008-", 0) == 0) @@ -1058,8 +1056,11 @@ std::string getGuideline(const std::string &errId, ReportType reportType, break; } - if (!guideline.empty()) + if (!guideline.empty()) { + if (errId.find("-dir-") != std::string::npos) + guideline = "Dir " + guideline; return guideline; + } auto it = guidelineMapping.find(errId);