From 38051d115f669badf5e5205bac5e69bff52fca6e 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 1/5] 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 3fef3103d96..59524a37779 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 6003364ebad52a39527b3bd2babca1b598902d6d 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 2/5] 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 c5daf5079e9..c69a4a9f56e 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 3/5] Add tests --- test/testsuppressions.cpp | 207 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 1f572bd2eb2..8758c0167ad 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -116,6 +116,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 { @@ -1896,6 +1911,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 2450cd94b27c1a5c3deccd725e809d4218ef4b65 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 4/5] 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 3510cffeeb514f3b2f4433e509f37df2a0dfd5ed 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 5/5] Update tests --- test/testsuppressions.cpp | 48 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 8758c0167ad..79c3f37ffd2 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1914,7 +1914,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); @@ -1945,7 +1946,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); @@ -1960,7 +1962,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); @@ -1972,7 +1975,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); @@ -1984,7 +1988,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); @@ -1996,7 +2001,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); @@ -2008,7 +2014,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); @@ -2029,49 +2036,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); @@ -2079,7 +2090,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); @@ -2088,7 +2099,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"); @@ -2097,7 +2109,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);