From aa31b8b80c36dff80ac2ca37fa9f67527cbe8bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Fri, 9 Jan 2026 16:33:32 +0100 Subject: [PATCH 01/14] Support polyspace suppressions --- Makefile | 2 +- lib/preprocessor.cpp | 9 ++ lib/suppressions.cpp | 261 +++++++++++++++++++++++++++++++++++++++++++ lib/suppressions.h | 49 ++++++++ oss-fuzz/Makefile | 2 +- 5 files changed, 321 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d5e3f74f6d1..de600d25d1e 100644 --- a/Makefile +++ b/Makefile @@ -652,7 +652,7 @@ $(libcppdir)/standards.o: lib/standards.cpp externals/simplecpp/simplecpp.h lib/ $(libcppdir)/summaries.o: lib/summaries.cpp lib/addoninfo.h lib/analyzerinfo.h lib/checkers.h lib/config.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/summaries.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)/summaries.cpp -$(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/smallvector.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h +$(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/checkers.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/smallvector.h lib/standards.h lib/suppressions.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)/suppressions.cpp $(libcppdir)/templatesimplifier.o: lib/templatesimplifier.cpp lib/addoninfo.h lib/checkers.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 123ef5ca898..fc7b082144a 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -200,12 +200,19 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett bool onlyComments = true; + polyspace::Parser polyspaceParser(settings); + for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (!tok->comment) { onlyComments = false; continue; } + if (polyspace::isPolyspaceComment(tok->str())) { + polyspaceParser.parse(tok->str(), tok->location.line, tokens.file(tok->location)); + continue; + } + std::list inlineSuppressions; if (!parseInlineSuppressionCommentToken(tokens, tok, inlineSuppressions, bad)) continue; @@ -310,6 +317,8 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett for (const SuppressionList::Suppression & suppr: inlineSuppressionsBlockBegin) // cppcheck-suppress useStlAlgorithm bad.emplace_back(suppr.fileName, suppr.lineNumber, 0, "Suppress Begin: No matching end"); // TODO: set column + + polyspaceParser.collect(suppressions); } void Preprocessor::inlineSuppressions(SuppressionList &suppressions) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index b91db635478..fcbe6ff07a7 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -26,6 +26,7 @@ #include "token.h" #include "tokenize.h" #include "tokenlist.h" +#include "settings.h" #include #include // std::isdigit, std::isalnum, etc @@ -647,3 +648,263 @@ std::string SuppressionList::Suppression::toString() const } return s; } + +std::string polyspace::Parser::peekToken() +{ + if (!mHasPeeked) { + mPeeked = nextToken(); + mHasPeeked = true; + } + return mPeeked; +} + +std::string polyspace::Parser::nextToken() +{ + if (mHasPeeked) { + mHasPeeked = false; + return mPeeked; + } + + if (mComment.compare(0, 2, "/*") == 0 || mComment.compare(0, 2, "//") == 0) + mComment = mComment.substr(2); + + std::string::size_type pos = 0; + while (mComment[pos] == ' ') { + pos++; + if (pos == mComment.size()) { + mComment = ""; + return ""; + } + } + + if (mComment.compare(0, 2, "*/") == 0) { + mComment = ""; + return ""; + } + + if (mComment[pos] == ':') { + mComment = mComment.substr(pos + 1); + return ":"; + } + + if (mComment[pos] == ',') { + mComment = mComment.substr(pos + 1); + return ","; + } + + const char *stopChars; + std::string::size_type skip; + switch (mComment[pos]) { + case '\"': + stopChars = "\""; + skip = 1; + break; + case '[': + stopChars = "]"; + skip = 1; + break; + default: + stopChars = " :,"; + skip = 0; + break; + } + + const std::string::size_type start = pos; + pos += skip; + + if (pos == mComment.size()) { + mComment = ""; + return ""; + } + + while (std::strchr(stopChars, mComment[pos]) == nullptr) { + pos++; + if (pos == mComment.size()) + break; + } + + if (pos == mComment.size()) + skip = 0; + + const std::string token = mComment.substr(start, pos - start + skip); + mComment = mComment.substr(pos + skip); + + return token; +} + +void polyspace::Parser::finishSuppression() +{ + Suppression suppr = { mFamily, mResultName, mFilename, 0, 0 }; + + switch (mKind) { + case CommentKind::Regular: + { + suppr.lineBegin = mLine; + suppr.lineEnd = mLine + mRange; + mDone.push_back(suppr); + return; + } + case CommentKind::Begin: + { + suppr.lineBegin = mLine; + mStarted.push_back(suppr); + return; + } + case CommentKind::End: + { + auto it = std::find_if( + mStarted.begin(), + mStarted.end(), + [&] (const Suppression &other) { + return suppr.matches(other); + } + ); + + if (it == mStarted.end()) + return; + + suppr.lineBegin = it->lineBegin; + suppr.lineEnd = mLine; + mStarted.erase(it); + mDone.push_back(suppr); + return; + } + } +} + +bool polyspace::Parser::parseEntry() +{ + mFamily = nextToken(); + if (mFamily.empty()) + return false; + + if (nextToken() != ":") + return false; + + // Parse result name, multiple names may be separated by commas + while (!mComment.empty()) { + mResultName = nextToken(); + if (mResultName.empty()) + return false; + + finishSuppression(); + + if (peekToken() == ",") { + (void) nextToken(); + continue; + } + + break; + } + + // Skip status and severity + if (!peekToken().empty() && mPeeked[0] == '[') + (void) nextToken(); + + return true; +} + +void polyspace::Parser::collect(SuppressionList &suppressions) const +{ + for (const auto &polyspaceSuppr : mDone) { + SuppressionList::Suppression suppr; + if (polyspaceSuppr.convert(mSettings, suppr)) + suppressions.addSuppression(std::move(suppr)); + } +} + +void polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename) +{ + mComment = comment; + mLine = line; + mFilename = filename; + mHasPeeked = false; + + while (true) { + const std::string kindStr = nextToken(); + if (kindStr.empty()) + return; + + if (kindStr == "polyspace") mKind = CommentKind::Regular; + else if (kindStr == "polyspace-begin") mKind = CommentKind::Begin; + else if (kindStr == "polyspace-end") mKind = CommentKind::End; + else return; + + mRange = 0; + if (peekToken()[0] == '+') { + try { mRange = std::stoi(mPeeked.substr(1)); } catch (...) { return; } + (void) nextToken(); + } + + while (parseEntry()) { + if (peekToken().empty() || mPeeked[0] == '\"') + break; + } + + if (!peekToken().empty() && mPeeked[0] == '\"') { + (void) nextToken(); + if (peekToken().empty()) + return; + continue; + } + + break; + } +} + +bool polyspace::isPolyspaceComment(const std::string &comment) +{ + const std::string polyspace = "polyspace"; + const std::string::size_type pos = comment.find_first_not_of("/* "); + if (pos == std::string::npos) + return false; + return comment.compare(pos, polyspace.size(), polyspace, 0, polyspace.size()) == 0; +} + +bool polyspace::Suppression::matches(const polyspace::Suppression &other) const +{ + return family == other.family && resultName == other.resultName; +} + +bool polyspace::Suppression::convert(const Settings &settings, SuppressionList::Suppression &suppr) const +{ + static const std::map map = { + { "MISRA-C-2023", "premium-misra-c-2023-" }, + { "MISRA-CPP", "premium-misra-cpp-2008-" }, + { "MISRA-CPP-2023", "premium-misra-cpp-2023-" }, + { "CERT-C", "premium-cert-c-" }, + { "CERT-CPP", "premium-cert-cpp-" }, + { "AUTOSAR-CPP14", "premium-autosar-" }, + }; + + const auto it = map.find(family); + std::string prefix; + if (it == map.cend()) { + if (family == "MISRA-C3" || family == "MISRA2012") { + if (settings.premiumArgs.empty()) { + prefix = "misra-c2012-"; + } else { + prefix = "premium-misra-c-2012-"; + } + } else { + return false; + } + } else { + prefix = it->second; + } + + suppr.errorId = prefix + resultName; + suppr.isInline = true; + suppr.fileName = filename; + + suppr.lineNumber = lineBegin; + if (lineBegin == lineEnd) { + suppr.type = SuppressionList::Type::unique; + } else { + suppr.type = SuppressionList::Type::block; + suppr.lineBegin = lineBegin; + suppr.lineEnd = lineEnd; + } + + return true; +} diff --git a/lib/suppressions.h b/lib/suppressions.h index 022a492c51f..756e03e056c 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -36,6 +36,7 @@ class Tokenizer; class ErrorMessage; enum class Certainty : std::uint8_t; class FileWithDetails; +class Settings; /// @addtogroup Core /// @{ @@ -294,6 +295,54 @@ struct Suppressions SuppressionList nofail; }; +namespace polyspace { + + struct CPPCHECKLIB Suppression { + std::string family; + std::string resultName; + std::string filename; + int lineBegin; + int lineEnd; + + bool matches(const Suppression &other) const; + bool convert(const Settings &settings, SuppressionList::Suppression &suppr) const; + }; + + class CPPCHECKLIB Parser { + public: + Parser() = delete; + explicit Parser(const Settings &settings) : mSettings(settings) {} + void collect(SuppressionList &suppressions) const; + void parse(const std::string &comment, int line, const std::string &filename); + + private: + std::string peekToken(); + std::string nextToken(); + void finishSuppression(); + bool parseEntry(); + + enum class CommentKind : std::uint8_t { + Regular, Begin, End, + }; + + std::list mStarted; + std::list mDone; + std::string mComment; + std::string mFilename; + int mLine{}; + int mRange{}; + CommentKind mKind{}; + std::string mFamily; + std::string mResultName; + std::string mPeeked; + bool mHasPeeked{}; + const Settings &mSettings; + }; + + bool CPPCHECKLIB isPolyspaceComment(const std::string &comment); + +} + /// @} //--------------------------------------------------------------------------- #endif // suppressionsH diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index ad7fb641024..43f093810da 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -332,7 +332,7 @@ $(libcppdir)/standards.o: ../lib/standards.cpp ../externals/simplecpp/simplecpp. $(libcppdir)/summaries.o: ../lib/summaries.cpp ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/checkers.h ../lib/config.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/summaries.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)/summaries.cpp -$(libcppdir)/suppressions.o: ../lib/suppressions.cpp ../externals/tinyxml2/tinyxml2.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/mathlib.h ../lib/path.h ../lib/pathmatch.h ../lib/platform.h ../lib/smallvector.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h +$(libcppdir)/suppressions.o: ../lib/suppressions.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/checkers.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/pathmatch.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/standards.h ../lib/suppressions.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)/suppressions.cpp $(libcppdir)/templatesimplifier.o: ../lib/templatesimplifier.cpp ../lib/addoninfo.h ../lib/checkers.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/standards.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h From 65c527579b4f35bde0f2faa992b5ae0f7395f2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 10 Jan 2026 10:56:05 +0100 Subject: [PATCH 02/14] Add unmatchedPolyspaceSuppression --- cli/cppcheckexecutor.cpp | 6 +++++- lib/suppressions.cpp | 1 + lib/suppressions.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index a16f76183c0..b0925a013f8 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -329,7 +329,11 @@ static bool reportUnmatchedSuppressions(const std::list Date: Fri, 9 Jan 2026 17:47:27 +0100 Subject: [PATCH 03/14] Add tests --- test/testsuppressions.cpp | 207 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 58ee72f28b3..6452c313a61 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -115,6 +115,21 @@ class TestSuppressions : public TestFixture { TEST_CASE(suppressionFromErrorMessage); TEST_CASE(suppressionWildcard); + + TEST_CASE(polyspaceMisraC2012); + TEST_CASE(polyspacePremiumMisraC2012); + TEST_CASE(polyspaceMisraC2023); + TEST_CASE(polyspaceMisraCpp2008); + TEST_CASE(polyspaceMisraCpp2023); + TEST_CASE(polyspaceCertC); + TEST_CASE(polyspaceCertCpp); + TEST_CASE(polyspaceAutosar); + TEST_CASE(polyspaceIgnored); + TEST_CASE(polyspaceMultiple1); + TEST_CASE(polyspaceMultiple2); + TEST_CASE(polyspaceMultiple3); + TEST_CASE(polyspaceRange); + TEST_CASE(polyspaceBlock); } void suppressionsBadId1() const { @@ -1895,6 +1910,198 @@ class TestSuppressions : public TestFixture { ASSERT(!suppressions.getUnmatchedGlobalSuppressions().empty()); } } + + void polyspaceMisraC2012() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("/* polyspace MISRA2012 : 2.7 */", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT(s->isInline); + ASSERT(s->isPolyspace); + ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS(1, s->lineNumber); + ASSERT_EQUALS_ENUM(SuppressionList::Type::unique, s->type); + ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineBegin); + ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineEnd); + } + + void polyspacePremiumMisraC2012() const { + SuppressionList list; + Settings settings; + settings.premiumArgs = "--misra-c-2012"; + polyspace::Parser parser(settings); + parser.parse("/* polyspace MISRA2012 : 2.7 */", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); + } + + void polyspaceMisraC2023() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("// polyspace MISRA-C-2023 : *", 2, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT_EQUALS("premium-misra-c-2023-*", s->errorId); + ASSERT_EQUALS(2, s->lineNumber); + ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineBegin); + ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineEnd); + } + + void polyspaceMisraCpp2008() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("// polyspace MISRA-CPP : 7-1-1", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); + } + + void polyspaceMisraCpp2023() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("// polyspace MISRA-CPP-2023 : 4.6.1", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT_EQUALS("premium-misra-cpp-2023-4.6.1", s->errorId); + } + + void polyspaceCertC() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("// polyspace CERT-C : PRE30", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT_EQUALS("premium-cert-c-PRE30", s->errorId); + } + + void polyspaceCertCpp() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("// polyspace CERT-CPP : CTR51", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT_EQUALS("premium-cert-cpp-CTR51", s->errorId); + } + + void polyspaceAutosar() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("// polyspace AUTOSAR-CPP14 : a2-10-1", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT_EQUALS("premium-autosar-a2-10-1", s->errorId); + } + + void polyspaceIgnored() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("// polyspace DEFECT : INT_OVFL", 1, "file.c"); + parser.collect(list); + ASSERT(list.getSuppressions().empty()); + } + + void polyspaceMultiple1() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("/* polyspace MISRA2012 : 2.7, 9.1 */", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(2, supprs.size()); + auto s = supprs.cbegin(); + ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + s++; + ASSERT_EQUALS("misra-c2012-9.1", s->errorId); + } + + void polyspaceMultiple2() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("/* polyspace MISRA2012 : 2.7 MISRA-CPP : 7-1-1 */", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(2, supprs.size()); + auto s = supprs.cbegin(); + ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + s++; + ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); + } + + void polyspaceMultiple3() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("/* polyspace MISRA2012 : 2.7 [Justified:Low] \"comment 1\" polyspace MISRA-CPP : 7-1-1 \"comment 2\"*/", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(2, supprs.size()); + auto s = supprs.cbegin(); + ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + s++; + ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); + } + + void polyspaceRange() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("/* polyspace +3 MISRA2012 : 2.7 */", 1, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT(s->isInline); + ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS(1, s->lineNumber); + ASSERT_EQUALS_ENUM(SuppressionList::Type::block, s->type); + ASSERT_EQUALS(1, s->lineBegin); + ASSERT_EQUALS(4, s->lineEnd); + } + + void polyspaceBlock() const { + SuppressionList list; + const Settings settings; + polyspace::Parser parser(settings); + parser.parse("/* polyspace-begin MISRA2012 : 2.7 */", 1, "file.c"); + parser.parse("/* polyspace-end MISRA2012 : 2.7 */", 5, "file.c"); + parser.collect(list); + const auto &supprs = list.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto s = supprs.cbegin(); + ASSERT(s->isInline); + ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS(1, s->lineNumber); + ASSERT_EQUALS_ENUM(SuppressionList::Type::block, s->type); + ASSERT_EQUALS(1, s->lineBegin); + ASSERT_EQUALS(5, s->lineEnd); + } }; REGISTER_TEST(TestSuppressions) From 7682da1c4fae274715ffda7edc8900c5a0f3fc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 15 Jan 2026 13:22:00 +0100 Subject: [PATCH 04/14] Enable suppressions conditionally --- lib/suppressions.cpp | 110 +++++++++++++++++++++++++------------------ lib/suppressions.h | 6 +-- 2 files changed, 67 insertions(+), 49 deletions(-) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index d553f6ab0eb..a7860c370f8 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -649,6 +649,51 @@ std::string SuppressionList::Suppression::toString() const return s; } +polyspace::Parser::Parser(const Settings &settings) +{ + const auto matchArg = [&](const std::string &arg) { + const std::string args = settings.premiumArgs; + const std::string::size_type pos = args.find(arg); + + if (pos == std::string::npos) + return false; + + if (pos > 0 && args[pos - 1] != ' ') + return false; + + return pos == args.size() - arg.size() || args[pos + arg.size()] == ' '; + }; + + if (settings.addons.count("misra") != 0) { + mFamilyMap["MISRA-C3"] = "misra-c2012-"; + mFamilyMap["MISRA2012"] = "misra-c2012-"; + } + + if (matchArg("--misra-c-2012")) { + mFamilyMap["MISRA-C3"] = "premium-misra-c-2012-"; + mFamilyMap["MISRA2012"] = "premium-misra-c-2012-"; + } + + if (matchArg("--misra-c-2023")) + mFamilyMap["MISRA-C-2023"] = "premium-misra-c-2023-"; + + if (matchArg("--misra-cpp-2008") || matchArg("--misra-c++-2008")) + mFamilyMap["MISRA-CPP"] = "premium-misra-cpp-2008-"; + + if (matchArg("--misra-cpp-2023") || matchArg("--misra-c++-2023")) + mFamilyMap["MISRA-CPP-2023"] = "premium-misra-cpp-2023-"; + + if (matchArg("--cert-c") || matchArg("--cert-c-2016")) + mFamilyMap["CERT-C"] = "premium-cert-c-"; + + if (matchArg("--cert-cpp") || matchArg("--cert-c++") || + matchArg("--cert-cpp-2016") || matchArg("--cert-c++-2016")) + mFamilyMap["CERT-CPP"] = "premium-cert-cpp-"; + + if (matchArg("--autosar")) + mFamilyMap["AUTOSAR-CPP14"] = "premium-autosar-"; +} + std::string polyspace::Parser::peekToken() { if (!mHasPeeked) { @@ -807,9 +852,26 @@ bool polyspace::Parser::parseEntry() void polyspace::Parser::collect(SuppressionList &suppressions) const { for (const auto &polyspaceSuppr : mDone) { + const auto it = mFamilyMap.find(polyspaceSuppr.family); + if (it == mFamilyMap.cend()) + continue; + SuppressionList::Suppression suppr; - if (polyspaceSuppr.convert(mSettings, suppr)) - suppressions.addSuppression(std::move(suppr)); + suppr.errorId = it->second + polyspaceSuppr.resultName; + suppr.isInline = true; + suppr.isPolyspace = true; + suppr.fileName = polyspaceSuppr.filename; + + suppr.lineNumber = polyspaceSuppr.lineBegin; + if (polyspaceSuppr.lineBegin == polyspaceSuppr.lineEnd) { + suppr.type = SuppressionList::Type::unique; + } else { + suppr.type = SuppressionList::Type::block; + suppr.lineBegin = polyspaceSuppr.lineBegin; + suppr.lineEnd = polyspaceSuppr.lineEnd; + } + + suppressions.addSuppression(std::move(suppr)); } } @@ -865,47 +927,3 @@ bool polyspace::Suppression::matches(const polyspace::Suppression &other) const { return family == other.family && resultName == other.resultName; } - -bool polyspace::Suppression::convert(const Settings &settings, SuppressionList::Suppression &suppr) const -{ - static const std::map map = { - { "MISRA-C-2023", "premium-misra-c-2023-" }, - { "MISRA-CPP", "premium-misra-cpp-2008-" }, - { "MISRA-CPP-2023", "premium-misra-cpp-2023-" }, - { "CERT-C", "premium-cert-c-" }, - { "CERT-CPP", "premium-cert-cpp-" }, - { "AUTOSAR-CPP14", "premium-autosar-" }, - }; - - const auto it = map.find(family); - std::string prefix; - if (it == map.cend()) { - if (family == "MISRA-C3" || family == "MISRA2012") { - if (settings.premiumArgs.empty()) { - prefix = "misra-c2012-"; - } else { - prefix = "premium-misra-c-2012-"; - } - } else { - return false; - } - } else { - prefix = it->second; - } - - suppr.errorId = prefix + resultName; - suppr.isInline = true; - suppr.isPolyspace = true; - suppr.fileName = filename; - - suppr.lineNumber = lineBegin; - if (lineBegin == lineEnd) { - suppr.type = SuppressionList::Type::unique; - } else { - suppr.type = SuppressionList::Type::block; - suppr.lineBegin = lineBegin; - suppr.lineEnd = lineEnd; - } - - return true; -} diff --git a/lib/suppressions.h b/lib/suppressions.h index b891568632c..c32cfabdb48 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -31,6 +31,7 @@ #include #include #include +#include class Tokenizer; class ErrorMessage; @@ -306,13 +307,12 @@ namespace polyspace { int lineEnd; bool matches(const Suppression &other) const; - bool convert(const Settings &settings, SuppressionList::Suppression &suppr) const; }; class CPPCHECKLIB Parser { public: Parser() = delete; - explicit Parser(const Settings &settings) : mSettings(settings) {} + explicit Parser(const Settings &settings); void collect(SuppressionList &suppressions) const; void parse(const std::string &comment, int line, const std::string &filename); @@ -337,7 +337,7 @@ namespace polyspace { std::string mResultName; std::string mPeeked; bool mHasPeeked{}; - const Settings &mSettings; + std::map mFamilyMap; }; bool CPPCHECKLIB isPolyspaceComment(const std::string &comment); From af3fb8224432e28e8aaf4b2973a15ae9fbb6c6ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 15 Jan 2026 13:58:37 +0100 Subject: [PATCH 05/14] Update tests --- test/testsuppressions.cpp | 48 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 6452c313a61..d829ef21154 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1913,7 +1913,8 @@ class TestSuppressions : public TestFixture { void polyspaceMisraC2012() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.addons.emplace("misra"); polyspace::Parser parser(settings); parser.parse("/* polyspace MISRA2012 : 2.7 */", 1, "file.c"); parser.collect(list); @@ -1944,7 +1945,8 @@ class TestSuppressions : public TestFixture { void polyspaceMisraC2023() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-c-2023"; polyspace::Parser parser(settings); parser.parse("// polyspace MISRA-C-2023 : *", 2, "file.c"); parser.collect(list); @@ -1959,7 +1961,8 @@ class TestSuppressions : public TestFixture { void polyspaceMisraCpp2008() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-cpp-2008"; polyspace::Parser parser(settings); parser.parse("// polyspace MISRA-CPP : 7-1-1", 1, "file.c"); parser.collect(list); @@ -1971,7 +1974,8 @@ class TestSuppressions : public TestFixture { void polyspaceMisraCpp2023() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-cpp-2023"; polyspace::Parser parser(settings); parser.parse("// polyspace MISRA-CPP-2023 : 4.6.1", 1, "file.c"); parser.collect(list); @@ -1983,7 +1987,8 @@ class TestSuppressions : public TestFixture { void polyspaceCertC() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--cert-c"; polyspace::Parser parser(settings); parser.parse("// polyspace CERT-C : PRE30", 1, "file.c"); parser.collect(list); @@ -1995,7 +2000,8 @@ class TestSuppressions : public TestFixture { void polyspaceCertCpp() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--cert-cpp"; polyspace::Parser parser(settings); parser.parse("// polyspace CERT-CPP : CTR51", 1, "file.c"); parser.collect(list); @@ -2007,7 +2013,8 @@ class TestSuppressions : public TestFixture { void polyspaceAutosar() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--autosar"; polyspace::Parser parser(settings); parser.parse("// polyspace AUTOSAR-CPP14 : a2-10-1", 1, "file.c"); parser.collect(list); @@ -2028,49 +2035,53 @@ class TestSuppressions : public TestFixture { void polyspaceMultiple1() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-c-2012"; polyspace::Parser parser(settings); parser.parse("/* polyspace MISRA2012 : 2.7, 9.1 */", 1, "file.c"); parser.collect(list); const auto &supprs = list.getSuppressions(); ASSERT_EQUALS(2, supprs.size()); auto s = supprs.cbegin(); - ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); s++; - ASSERT_EQUALS("misra-c2012-9.1", s->errorId); + ASSERT_EQUALS("premium-misra-c-2012-9.1", s->errorId); } void polyspaceMultiple2() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-c-2012 --misra-cpp-2008"; polyspace::Parser parser(settings); parser.parse("/* polyspace MISRA2012 : 2.7 MISRA-CPP : 7-1-1 */", 1, "file.c"); parser.collect(list); const auto &supprs = list.getSuppressions(); ASSERT_EQUALS(2, supprs.size()); auto s = supprs.cbegin(); - ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); s++; ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); } void polyspaceMultiple3() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-c-2012 --misra-cpp-2008"; polyspace::Parser parser(settings); parser.parse("/* polyspace MISRA2012 : 2.7 [Justified:Low] \"comment 1\" polyspace MISRA-CPP : 7-1-1 \"comment 2\"*/", 1, "file.c"); parser.collect(list); const auto &supprs = list.getSuppressions(); ASSERT_EQUALS(2, supprs.size()); auto s = supprs.cbegin(); - ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); s++; ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); } void polyspaceRange() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-c-2012"; polyspace::Parser parser(settings); parser.parse("/* polyspace +3 MISRA2012 : 2.7 */", 1, "file.c"); parser.collect(list); @@ -2078,7 +2089,7 @@ class TestSuppressions : public TestFixture { ASSERT_EQUALS(1, supprs.size()); const auto s = supprs.cbegin(); ASSERT(s->isInline); - ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); ASSERT_EQUALS(1, s->lineNumber); ASSERT_EQUALS_ENUM(SuppressionList::Type::block, s->type); ASSERT_EQUALS(1, s->lineBegin); @@ -2087,7 +2098,8 @@ class TestSuppressions : public TestFixture { void polyspaceBlock() const { SuppressionList list; - const Settings settings; + Settings settings; + settings.premiumArgs = "--misra-c-2012"; polyspace::Parser parser(settings); parser.parse("/* polyspace-begin MISRA2012 : 2.7 */", 1, "file.c"); parser.parse("/* polyspace-end MISRA2012 : 2.7 */", 5, "file.c"); @@ -2096,7 +2108,7 @@ class TestSuppressions : public TestFixture { ASSERT_EQUALS(1, supprs.size()); const auto s = supprs.cbegin(); ASSERT(s->isInline); - ASSERT_EQUALS("misra-c2012-2.7", s->errorId); + ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); ASSERT_EQUALS(1, s->lineNumber); ASSERT_EQUALS_ENUM(SuppressionList::Type::block, s->type); ASSERT_EQUALS(1, s->lineBegin); From be6d9dcec27651c39d45c4e575f60bd241703543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 17 Jan 2026 16:26:54 +0100 Subject: [PATCH 06/14] Remove comment prefix before parsing --- lib/suppressions.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index a7860c370f8..4898379682c 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -710,9 +710,6 @@ std::string polyspace::Parser::nextToken() return mPeeked; } - if (mComment.compare(0, 2, "/*") == 0 || mComment.compare(0, 2, "//") == 0) - mComment = mComment.substr(2); - std::string::size_type pos = 0; while (mComment[pos] == ' ') { pos++; @@ -877,7 +874,7 @@ void polyspace::Parser::collect(SuppressionList &suppressions) const void polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename) { - mComment = comment; + mComment = comment.substr(2); mLine = line; mFilename = filename; mHasPeeked = false; From e33033af858137eb6a19c709c97ed1a8585b5181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 17 Jan 2026 17:16:22 +0100 Subject: [PATCH 07/14] Cancel early if mFamilyMap is empty --- lib/suppressions.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 4898379682c..2b5c4f658b1 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -874,6 +874,9 @@ void polyspace::Parser::collect(SuppressionList &suppressions) const void polyspace::Parser::parse(const std::string &comment, int line, const std::string &filename) { + if (mFamilyMap.empty()) + return; + mComment = comment.substr(2); mLine = line; mFilename = filename; From 0d7072274ec63fc1708bc2cc033b62a127c119eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 17 Jan 2026 17:19:10 +0100 Subject: [PATCH 08/14] Simplify error message --- cli/cppcheckexecutor.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index b0925a013f8..716478f0baf 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -329,11 +329,8 @@ static bool reportUnmatchedSuppressions(const std::list Date: Sat, 17 Jan 2026 17:35:39 +0100 Subject: [PATCH 09/14] Check for misra addon in Settings::addonInfos --- lib/suppressions.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 2b5c4f658b1..7128433b202 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -651,6 +651,17 @@ std::string SuppressionList::Suppression::toString() const polyspace::Parser::Parser(const Settings &settings) { + const auto it = std::find_if(settings.addonInfos.cbegin(), + settings.addonInfos.cend(), + [] (const AddonInfo &info) { + return info.name == "misra"; + }); + + if (it != settings.addonInfos.cend()) { + mFamilyMap["MISRA-C3"] = "misra-c2012-"; + mFamilyMap["MISRA2012"] = "misra-c2012-"; + } + const auto matchArg = [&](const std::string &arg) { const std::string args = settings.premiumArgs; const std::string::size_type pos = args.find(arg); @@ -664,11 +675,6 @@ polyspace::Parser::Parser(const Settings &settings) return pos == args.size() - arg.size() || args[pos + arg.size()] == ' '; }; - if (settings.addons.count("misra") != 0) { - mFamilyMap["MISRA-C3"] = "misra-c2012-"; - mFamilyMap["MISRA2012"] = "misra-c2012-"; - } - if (matchArg("--misra-c-2012")) { mFamilyMap["MISRA-C3"] = "premium-misra-c-2012-"; mFamilyMap["MISRA2012"] = "premium-misra-c-2012-"; From cd884fcdcd36175bc5803bcb399eb09ebf6d13bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Sat, 17 Jan 2026 17:12:26 +0100 Subject: [PATCH 10/14] Refactor tests --- test/testsuppressions.cpp | 273 +++++++++++++++++++------------------- 1 file changed, 138 insertions(+), 135 deletions(-) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index d829ef21154..2a893c4218c 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1911,208 +1911,211 @@ class TestSuppressions : public TestFixture { } } - void polyspaceMisraC2012() const { + struct PolyspaceComment { + std::string text; + int line; + + PolyspaceComment(const std::string &&text, int line) + : text(text) + , line(line) + {} + }; + + struct PolyspaceParseResult { + std::string errorId; + int lineNumber; + SuppressionList::Type type = SuppressionList::Type::unique; + int lineBegin = SuppressionList::Suppression::NO_LINE; + int lineEnd = SuppressionList::Suppression::NO_LINE; + + PolyspaceParseResult(const std::string &&errorId, + int lineNumber, + SuppressionList::Type type = SuppressionList::Type::unique, + int lineBegin = SuppressionList::Suppression::NO_LINE, + int lineEnd = SuppressionList::Suppression::NO_LINE) + : errorId(errorId) + , lineNumber(lineNumber) + , type(type) + , lineBegin(lineBegin) + , lineEnd(lineEnd) + {} + }; + + void testPolyspaceSuppression(const Settings &settings, + std::initializer_list comments, + std::initializer_list results) const + { SuppressionList list; - Settings settings; - settings.addons.emplace("misra"); polyspace::Parser parser(settings); - parser.parse("/* polyspace MISRA2012 : 2.7 */", 1, "file.c"); + + const std::string fileName = "file.c"; + for (const auto &comment : comments) + parser.parse(comment.text, comment.line, fileName); + parser.collect(list); const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT(s->isInline); - ASSERT(s->isPolyspace); - ASSERT_EQUALS("misra-c2012-2.7", s->errorId); - ASSERT_EQUALS(1, s->lineNumber); - ASSERT_EQUALS_ENUM(SuppressionList::Type::unique, s->type); - ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineBegin); - ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineEnd); + + ASSERT_EQUALS(results.size(), supprs.size()); + + auto supprIt = supprs.cbegin(); + const auto *resultIt = results.begin(); + + for (; supprIt != supprs.cend(); supprIt++, resultIt++) { + ASSERT(supprIt->isPolyspace); + ASSERT(supprIt->isInline); + ASSERT_EQUALS(fileName, supprIt->fileName); + ASSERT_EQUALS(resultIt->errorId, supprIt->errorId); + ASSERT_EQUALS_ENUM(resultIt->type, supprIt->type); + ASSERT_EQUALS(resultIt->lineNumber, supprIt->lineNumber); + ASSERT_EQUALS(resultIt->lineBegin, supprIt->lineBegin); + ASSERT_EQUALS(resultIt->lineEnd, supprIt->lineEnd); + } + } + + void polyspaceMisraC2012() const { + Settings settings; + AddonInfo info; + info.name = "misra"; + settings.addonInfos.push_back(info); + + testPolyspaceSuppression( + settings, + { { "/* polyspace MISRA2012 : 2.7 */", 1 } }, + { { "misra-c2012-2.7", 1 } } + ); } void polyspacePremiumMisraC2012() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-c-2012"; - polyspace::Parser parser(settings); - parser.parse("/* polyspace MISRA2012 : 2.7 */", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); + testPolyspaceSuppression( + settings, + { { "/* polyspace MISRA2012 : 2.7 */", 1 } }, + { { "premium-misra-c-2012-2.7", 1 } } + ); } void polyspaceMisraC2023() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-c-2023"; - polyspace::Parser parser(settings); - parser.parse("// polyspace MISRA-C-2023 : *", 2, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-misra-c-2023-*", s->errorId); - ASSERT_EQUALS(2, s->lineNumber); - ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineBegin); - ASSERT_EQUALS(SuppressionList::Suppression::NO_LINE, s->lineEnd); + testPolyspaceSuppression( + settings, + { { "// polyspace MISRA-C-2023 : *", 2 } }, + { { "premium-misra-c-2023-*", 2 } } + ); } void polyspaceMisraCpp2008() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-cpp-2008"; - polyspace::Parser parser(settings); - parser.parse("// polyspace MISRA-CPP : 7-1-1", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); + testPolyspaceSuppression( + settings, + { { "// polyspace MISRA-CPP : 7-1-1", 1 } }, + { { "premium-misra-cpp-2008-7-1-1", 1 } } + ); } void polyspaceMisraCpp2023() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-cpp-2023"; - polyspace::Parser parser(settings); - parser.parse("// polyspace MISRA-CPP-2023 : 4.6.1", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-misra-cpp-2023-4.6.1", s->errorId); + testPolyspaceSuppression( + settings, + { { "// polyspace MISRA-CPP-2023 : 4.6.1", 1 } }, + { { "premium-misra-cpp-2023-4.6.1", 1 } } + ); } void polyspaceCertC() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--cert-c"; - polyspace::Parser parser(settings); - parser.parse("// polyspace CERT-C : PRE30", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-cert-c-PRE30", s->errorId); + testPolyspaceSuppression( + settings, + { { "// polyspace CERT-C : PRE30", 1 } }, + { { "premium-cert-c-PRE30", 1 } } + ); } void polyspaceCertCpp() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--cert-cpp"; - polyspace::Parser parser(settings); - parser.parse("// polyspace CERT-CPP : CTR51", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-cert-cpp-CTR51", s->errorId); + testPolyspaceSuppression( + settings, + { { "// polyspace CERT-CPP : CTR51", 1 } }, + { { "premium-cert-cpp-CTR51", 1 } } + ); } void polyspaceAutosar() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--autosar"; - polyspace::Parser parser(settings); - parser.parse("// polyspace AUTOSAR-CPP14 : a2-10-1", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-autosar-a2-10-1", s->errorId); + testPolyspaceSuppression( + settings, + { { "// polyspace AUTOSAR-CPP14 : a2-10-1", 1 } }, + { { "premium-autosar-a2-10-1", 1 } } + ); } void polyspaceIgnored() const { - SuppressionList list; - const Settings settings; - polyspace::Parser parser(settings); - parser.parse("// polyspace DEFECT : INT_OVFL", 1, "file.c"); - parser.collect(list); - ASSERT(list.getSuppressions().empty()); + Settings settings; + testPolyspaceSuppression( + settings, + { { "// polyspace DEFECT : INT_OVFL AUTOSAR-CPP14 : a2-10-1", 1 } }, + {} + ); } void polyspaceMultiple1() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-c-2012"; - polyspace::Parser parser(settings); - parser.parse("/* polyspace MISRA2012 : 2.7, 9.1 */", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(2, supprs.size()); - auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); - s++; - ASSERT_EQUALS("premium-misra-c-2012-9.1", s->errorId); + testPolyspaceSuppression( + settings, + { { "/* polyspace MISRA2012 : 2.7, 9.1 */", 1 } }, + { { "premium-misra-c-2012-2.7", 1 }, + { "premium-misra-c-2012-9.1", 1 } } + ); } void polyspaceMultiple2() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-c-2012 --misra-cpp-2008"; - polyspace::Parser parser(settings); - parser.parse("/* polyspace MISRA2012 : 2.7 MISRA-CPP : 7-1-1 */", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(2, supprs.size()); - auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); - s++; - ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); + testPolyspaceSuppression( + settings, + { { "/* polyspace MISRA2012 : 2.7 MISRA-CPP : 7-1-1 */", 1 } }, + { { "premium-misra-c-2012-2.7", 1 }, + { "premium-misra-cpp-2008-7-1-1", 1 } } + ); } void polyspaceMultiple3() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-c-2012 --misra-cpp-2008"; - polyspace::Parser parser(settings); - parser.parse("/* polyspace MISRA2012 : 2.7 [Justified:Low] \"comment 1\" polyspace MISRA-CPP : 7-1-1 \"comment 2\"*/", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(2, supprs.size()); - auto s = supprs.cbegin(); - ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); - s++; - ASSERT_EQUALS("premium-misra-cpp-2008-7-1-1", s->errorId); + testPolyspaceSuppression( + settings, + { { "/* polyspace MISRA2012 : 2.7 [Justified:Low] \"comment 1\" polyspace MISRA-CPP : 7-1-1 \"comment 2\"*/", 1 } }, + { { "premium-misra-c-2012-2.7", 1 }, + { "premium-misra-cpp-2008-7-1-1", 1 }, } + ); } void polyspaceRange() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-c-2012"; - polyspace::Parser parser(settings); - parser.parse("/* polyspace +3 MISRA2012 : 2.7 */", 1, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT(s->isInline); - ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); - ASSERT_EQUALS(1, s->lineNumber); - ASSERT_EQUALS_ENUM(SuppressionList::Type::block, s->type); - ASSERT_EQUALS(1, s->lineBegin); - ASSERT_EQUALS(4, s->lineEnd); + testPolyspaceSuppression( + settings, + { { "/* polyspace +3 MISRA2012 : 2.7 */", 1 } }, + { { "premium-misra-c-2012-2.7", 1, SuppressionList::Type::block, 1, 4 } } + ); } void polyspaceBlock() const { - SuppressionList list; Settings settings; settings.premiumArgs = "--misra-c-2012"; - polyspace::Parser parser(settings); - parser.parse("/* polyspace-begin MISRA2012 : 2.7 */", 1, "file.c"); - parser.parse("/* polyspace-end MISRA2012 : 2.7 */", 5, "file.c"); - parser.collect(list); - const auto &supprs = list.getSuppressions(); - ASSERT_EQUALS(1, supprs.size()); - const auto s = supprs.cbegin(); - ASSERT(s->isInline); - ASSERT_EQUALS("premium-misra-c-2012-2.7", s->errorId); - ASSERT_EQUALS(1, s->lineNumber); - ASSERT_EQUALS_ENUM(SuppressionList::Type::block, s->type); - ASSERT_EQUALS(1, s->lineBegin); - ASSERT_EQUALS(5, s->lineEnd); + testPolyspaceSuppression( + settings, + { { "/* polyspace-begin MISRA2012 : 2.7 */", 1 }, + { "/* polyspace-end MISRA2012 : 2.7 */", 5 } }, + { { "premium-misra-c-2012-2.7", 1, SuppressionList::Type::block, 1, 5 } } + ); } }; From 2850299264a5003ffc2522d9ef571dab8e70205d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 19 Jan 2026 16:37:37 +0100 Subject: [PATCH 11/14] Rewrite parser to handle extra comments --- lib/suppressions.cpp | 201 ++++++++++++++++++++++++++----------------- lib/suppressions.h | 30 ++++--- 2 files changed, 142 insertions(+), 89 deletions(-) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 7128433b202..7d463801069 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -651,13 +651,13 @@ std::string SuppressionList::Suppression::toString() const polyspace::Parser::Parser(const Settings &settings) { - const auto it = std::find_if(settings.addonInfos.cbegin(), - settings.addonInfos.cend(), - [] (const AddonInfo &info) { + const auto haveMisraAddon = std::any_of(settings.addonInfos.cbegin(), + settings.addonInfos.cend(), + [] (const AddonInfo &info) { return info.name == "misra"; }); - if (it != settings.addonInfos.cend()) { + if (haveMisraAddon) { mFamilyMap["MISRA-C3"] = "misra-c2012-"; mFamilyMap["MISRA2012"] = "misra-c2012-"; } @@ -780,64 +780,27 @@ std::string polyspace::Parser::nextToken() return token; } -void polyspace::Parser::finishSuppression() +bool polyspace::Parser::parseAnnotation(polyspace::Annotation &annotation) { - Suppression suppr = { mFamily, mResultName, mFilename, 0, 0 }; + annotation.family = nextToken(); + annotation.resultNames.clear(); + annotation.extraComment = ""; - switch (mKind) { - case CommentKind::Regular: - { - suppr.lineBegin = mLine; - suppr.lineEnd = mLine + mRange; - mDone.push_back(suppr); - return; - } - case CommentKind::Begin: - { - suppr.lineBegin = mLine; - mStarted.push_back(suppr); - return; - } - case CommentKind::End: - { - auto it = std::find_if( - mStarted.begin(), - mStarted.end(), - [&] (const Suppression &other) { - return suppr.matches(other); - } - ); - - if (it == mStarted.end()) - return; - - suppr.lineBegin = it->lineBegin; - suppr.lineEnd = mLine; - mStarted.erase(it); - mDone.push_back(suppr); - return; - } - } -} - -bool polyspace::Parser::parseEntry() -{ - mFamily = nextToken(); - if (mFamily.empty()) + if (annotation.family.empty()) return false; if (nextToken() != ":") return false; - // Parse result name, multiple names may be separated by commas - while (!mComment.empty()) { - mResultName = nextToken(); - if (mResultName.empty()) + for (;;) { + const std::string resultName = nextToken(); + + if (resultName.empty()) return false; - finishSuppression(); + annotation.resultNames.push_back(resultName); - if (peekToken() == ",") { + if (peekToken().substr(0, 1) == ",") { (void) nextToken(); continue; } @@ -845,13 +808,105 @@ bool polyspace::Parser::parseEntry() break; } - // Skip status and severity - if (!peekToken().empty() && mPeeked[0] == '[') + if (peekToken().substr(0, 1) == "[") (void) nextToken(); + if (peekToken().substr(0, 1) == "\"") { + std::string extraComment = nextToken().substr(1); + + if (extraComment.size() > 1) + extraComment.pop_back(); + + annotation.extraComment = extraComment; + } + return true; } +polyspace::CommentKind polyspace::Parser::parseKind() +{ + const std::string token = nextToken(); + + if (token == "polyspace") + return CommentKind::Regular; + + if (token == "polyspace-begin") + return CommentKind::Begin; + + if (token == "polyspace-end") + return CommentKind::End; + + return CommentKind::Invalid; +} + +int polyspace::Parser::parseRange() +{ + if (peekToken()[0] == '+') { + try { + const int range = std::stoi(peekToken().substr(1)); + (void) nextToken(); + return range; + } catch (...) { + return -1; + } + } + + return 0; +} + +void polyspace::Parser::handleAnnotation(const polyspace::Annotation &annotation) +{ + for (const auto &resultName : annotation.resultNames) { + Suppression suppr = { + annotation.family, + resultName, + annotation.filename, + annotation.extraComment, + 0, + 0, + }; + + switch (annotation.kind) { + case CommentKind::Regular: + { + suppr.lineBegin = annotation.line; + suppr.lineEnd = annotation.line + annotation.range; + mDone.push_back(suppr); + break; + } + case CommentKind::Begin: + { + suppr.lineBegin = annotation.line; + mStarted.push_back(suppr); + break; + } + case CommentKind::End: + { + auto it = std::find_if( + mStarted.begin(), + mStarted.end(), + [&] (const Suppression &other) { + return suppr.matches(other); + } + ); + + if (it == mStarted.end()) + break; + + suppr.lineBegin = it->lineBegin; + suppr.lineEnd = annotation.line; + mStarted.erase(it); + mDone.push_back(suppr); + break; + } + case CommentKind::Invalid: + { + assert(false); // Invalid comments are not handled + } + } + } +} + void polyspace::Parser::collect(SuppressionList &suppressions) const { for (const auto &polyspaceSuppr : mDone) { @@ -864,6 +919,7 @@ void polyspace::Parser::collect(SuppressionList &suppressions) const suppr.isInline = true; suppr.isPolyspace = true; suppr.fileName = polyspaceSuppr.filename; + suppr.extraComment = polyspaceSuppr.extraComment; suppr.lineNumber = polyspaceSuppr.lineBegin; if (polyspaceSuppr.lineBegin == polyspaceSuppr.lineEnd) { @@ -884,39 +940,28 @@ void polyspace::Parser::parse(const std::string &comment, int line, const std::s return; mComment = comment.substr(2); - mLine = line; - mFilename = filename; mHasPeeked = false; while (true) { - const std::string kindStr = nextToken(); - if (kindStr.empty()) + const CommentKind kind = parseKind(); + if (kind == CommentKind::Invalid) return; - if (kindStr == "polyspace") mKind = CommentKind::Regular; - else if (kindStr == "polyspace-begin") mKind = CommentKind::Begin; - else if (kindStr == "polyspace-end") mKind = CommentKind::End; - else return; + const int range = parseRange(); + if (range < 0) + return; - mRange = 0; - if (peekToken()[0] == '+') { - try { mRange = std::stoi(mPeeked.substr(1)); } catch (...) { return; } - (void) nextToken(); - } + Annotation annotation; + annotation.filename = filename; + annotation.kind = kind; + annotation.line = line; + annotation.range = range; - while (parseEntry()) { - if (peekToken().empty() || mPeeked[0] == '\"') + while (parseAnnotation(annotation)) { + handleAnnotation(annotation); + if (!annotation.extraComment.empty()) break; } - - if (!peekToken().empty() && mPeeked[0] == '\"') { - (void) nextToken(); - if (peekToken().empty()) - return; - continue; - } - - break; } } diff --git a/lib/suppressions.h b/lib/suppressions.h index c32cfabdb48..656a8d755d9 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -303,12 +303,27 @@ namespace polyspace { std::string family; std::string resultName; std::string filename; + std::string extraComment; int lineBegin; int lineEnd; bool matches(const Suppression &other) const; }; + enum class CommentKind : std::uint8_t { + Invalid, Regular, Begin, End, + }; + + struct CPPCHECKLIB Annotation { + std::string family; + std::vector resultNames; + std::string extraComment; + std::string filename; + CommentKind kind; + int line; + int range; + }; + class CPPCHECKLIB Parser { public: Parser() = delete; @@ -319,22 +334,15 @@ namespace polyspace { private: std::string peekToken(); std::string nextToken(); - void finishSuppression(); - bool parseEntry(); - enum class CommentKind : std::uint8_t { - Regular, Begin, End, - }; + void handleAnnotation(const Annotation &annotation); + bool parseAnnotation(Annotation &annotation); + CommentKind parseKind(); + int parseRange(); std::list mStarted; std::list mDone; std::string mComment; - std::string mFilename; - int mLine{}; - int mRange{}; - CommentKind mKind{}; - std::string mFamily; - std::string mResultName; std::string mPeeked; bool mHasPeeked{}; std::map mFamilyMap; From 56de4d21b45ea4e74e748456db7a9e140197ad62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 19 Jan 2026 17:26:14 +0100 Subject: [PATCH 12/14] Check extra comments in tests --- test/testsuppressions.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 2a893c4218c..154d86f46ce 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1924,17 +1924,20 @@ class TestSuppressions : public TestFixture { struct PolyspaceParseResult { std::string errorId; int lineNumber; + std::string extraComment; SuppressionList::Type type = SuppressionList::Type::unique; int lineBegin = SuppressionList::Suppression::NO_LINE; int lineEnd = SuppressionList::Suppression::NO_LINE; PolyspaceParseResult(const std::string &&errorId, int lineNumber, + const std::string &&extraComment = "", SuppressionList::Type type = SuppressionList::Type::unique, int lineBegin = SuppressionList::Suppression::NO_LINE, int lineEnd = SuppressionList::Suppression::NO_LINE) : errorId(errorId) , lineNumber(lineNumber) + , extraComment(extraComment) , type(type) , lineBegin(lineBegin) , lineEnd(lineEnd) @@ -1965,6 +1968,7 @@ class TestSuppressions : public TestFixture { ASSERT(supprIt->isInline); ASSERT_EQUALS(fileName, supprIt->fileName); ASSERT_EQUALS(resultIt->errorId, supprIt->errorId); + ASSERT_EQUALS(resultIt->extraComment, supprIt->extraComment); ASSERT_EQUALS_ENUM(resultIt->type, supprIt->type); ASSERT_EQUALS(resultIt->lineNumber, supprIt->lineNumber); ASSERT_EQUALS(resultIt->lineBegin, supprIt->lineBegin); @@ -2092,8 +2096,8 @@ class TestSuppressions : public TestFixture { testPolyspaceSuppression( settings, { { "/* polyspace MISRA2012 : 2.7 [Justified:Low] \"comment 1\" polyspace MISRA-CPP : 7-1-1 \"comment 2\"*/", 1 } }, - { { "premium-misra-c-2012-2.7", 1 }, - { "premium-misra-cpp-2008-7-1-1", 1 }, } + { { "premium-misra-c-2012-2.7", 1, "comment 1" }, + { "premium-misra-cpp-2008-7-1-1", 1, "comment 2" }, } ); } @@ -2103,7 +2107,7 @@ class TestSuppressions : public TestFixture { testPolyspaceSuppression( settings, { { "/* polyspace +3 MISRA2012 : 2.7 */", 1 } }, - { { "premium-misra-c-2012-2.7", 1, SuppressionList::Type::block, 1, 4 } } + { { "premium-misra-c-2012-2.7", 1, "", SuppressionList::Type::block, 1, 4 } } ); } @@ -2114,7 +2118,7 @@ class TestSuppressions : public TestFixture { settings, { { "/* polyspace-begin MISRA2012 : 2.7 */", 1 }, { "/* polyspace-end MISRA2012 : 2.7 */", 5 } }, - { { "premium-misra-c-2012-2.7", 1, SuppressionList::Type::block, 1, 5 } } + { { "premium-misra-c-2012-2.7", 1, "", SuppressionList::Type::block, 1, 5 } } ); } }; From e7afafdc0c5cd2c9af65649244050794533fab17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 19 Jan 2026 17:26:25 +0100 Subject: [PATCH 13/14] Add test for extra comments --- test/testsuppressions.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 154d86f46ce..a9f8e0603a7 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -130,6 +130,7 @@ class TestSuppressions : public TestFixture { TEST_CASE(polyspaceMultiple3); TEST_CASE(polyspaceRange); TEST_CASE(polyspaceBlock); + TEST_CASE(polyspaceExtraComments); } void suppressionsBadId1() const { @@ -2121,6 +2122,19 @@ class TestSuppressions : public TestFixture { { { "premium-misra-c-2012-2.7", 1, "", SuppressionList::Type::block, 1, 5 } } ); } + + void polyspaceExtraComments() const { + Settings settings; + settings.premiumArgs = "--misra-c-2012 --misra-cpp-2008"; + testPolyspaceSuppression( + settings, + { { "/* polyspace MISRA2012 : 2.7 MISRA-CPP : 7-1-1 \"comment 1\" polyspace MISRA2012 : 9.1, 8.13 \"comment 2\" */", 1 }, }, + { { "premium-misra-c-2012-2.7", 1, "" }, + { "premium-misra-cpp-2008-7-1-1", 1, "comment 1" }, + { "premium-misra-c-2012-9.1", 1, "comment 2" }, + { "premium-misra-c-2012-8.13", 1, "comment 2" }, } + ); + } }; REGISTER_TEST(TestSuppressions) From 9826d2ca5951adf09ffc1123ee957436a126252b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 19 Jan 2026 21:02:57 +0100 Subject: [PATCH 14/14] Use relative filename --- lib/preprocessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index fc7b082144a..f920d3a7ed2 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -209,7 +209,7 @@ static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Sett } if (polyspace::isPolyspaceComment(tok->str())) { - polyspaceParser.parse(tok->str(), tok->location.line, tokens.file(tok->location)); + polyspaceParser.parse(tok->str(), tok->location.line, getRelativeFilename(tokens, tok, settings)); continue; }