From 7c4f7683749a77a750e09e32374760091e5891f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 10:52:54 +0200 Subject: [PATCH 1/5] fix #13961 --- cli/processexecutor.cpp | 5 ++++- lib/suppressions.cpp | 25 +++++++++++++++++++------ lib/suppressions.h | 1 + 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index f2a69976e93..4d0d63f4204 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -116,6 +116,8 @@ namespace { suppr_str += suppr.checked ? "1" : "0"; suppr_str += ";"; suppr_str += suppr.matched ? "1" : "0"; + suppr_str += ";"; + suppr_str += suppr.extraComment; return suppr_str; } @@ -239,7 +241,7 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str if (!buf.empty()) { // TODO: avoid string splitting auto parts = splitString(buf, ';'); - if (parts.size() != 3) + if (parts.size() != 4) { // TODO: make this non-fatal std::cerr << "#### ThreadExecutor::handleRead(" << filename << ") adding of inline suppression failed - insufficient data" << std::endl; @@ -249,6 +251,7 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str suppr.isInline = (type == PipeWriter::REPORT_SUPPR_INLINE); suppr.checked = parts[1] == "1"; suppr.matched = parts[2] == "1"; + suppr.extraComment = parts[3]; const std::string err = mSuppressions.nomsg.addSuppression(suppr); if (!err.empty()) { // TODO: only update state if it doesn't exist - otherwise propagate error diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 7f2351878c0..239378c48b8 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -334,15 +334,22 @@ bool SuppressionList::Suppression::parseComment(std::string comment, std::string if (comment.size() < 2) return false; - if (comment.find(';') != std::string::npos) - comment.erase(comment.find(';')); - - if (comment.find("//", 2) != std::string::npos) - comment.erase(comment.find("//",2)); - if (comment.compare(comment.size() - 2, 2, "*/") == 0) comment.erase(comment.size() - 2, 2); + std::string::size_type extraPos = comment.find(';'); + std::string::size_type extraDelimiterSize = 1; + + if (extraPos == std::string::npos) { + extraPos = comment.find("//", 2); + extraDelimiterSize = 2; + } + + if (extraPos != std::string::npos) { + extraComment = trim(comment.substr(extraPos + extraDelimiterSize)); + comment.erase(extraPos); + } + const std::set cppchecksuppress{ "cppcheck-suppress", "cppcheck-suppress-begin", @@ -532,6 +539,12 @@ void SuppressionList::dump(std::ostream & out) const out << " type=\"blockEnd\""; else if (suppression.type == SuppressionList::Type::macro) out << " type=\"macro\""; + if (suppression.isInline) + out << " inline=\"true\""; + else + out << " inline=\"false\""; + if (!suppression.extraComment.empty()) + out << " comment=\"" << suppression.extraComment << "\""; out << " />" << std::endl; } out << " " << std::endl; diff --git a/lib/suppressions.h b/lib/suppressions.h index 5e4cccb3890..11a92b320c2 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -151,6 +151,7 @@ class CPPCHECKLIB SuppressionList { std::string errorId; std::string fileName; + std::string extraComment; int lineNumber = NO_LINE; int lineBegin = NO_LINE; int lineEnd = NO_LINE; From 7920c1bdc7e1187f3b83b27dd376ecc6ed62782f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 10:53:05 +0200 Subject: [PATCH 2/5] add/update tests --- test/cli/inline-suppress_test.py | 14 ++++++++++++++ test/cli/premium_test.py | 2 +- test/testsuppressions.cpp | 11 +++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/test/cli/inline-suppress_test.py b/test/cli/inline-suppress_test.py index a2bf847a2a7..9c7421656d2 100644 --- a/test/cli/inline-suppress_test.py +++ b/test/cli/inline-suppress_test.py @@ -55,6 +55,20 @@ def test_2(): assert ret == 0, stdout +def test_xml(): + args = [ + '-q', + '--template=simple', + '--inline-suppr', + '--xml-version=3', + 'proj-inline-suppress' + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + assert '' in stderr + assert stdout == '' + assert ret == 0, stdout + + def test_unmatched_suppression(): args = [ '-q', diff --git a/test/cli/premium_test.py b/test/cli/premium_test.py index b0df148f22b..2cfd6974c38 100644 --- a/test/cli/premium_test.py +++ b/test/cli/premium_test.py @@ -69,7 +69,7 @@ def test_misra_c_builtin_style_checks(tmpdir): exitcode, _, stderr = cppcheck(['--xml-version=3', '--suppress=foo', test_file], cppcheck_exe=exe) assert exitcode == 0 - assert '' in stderr + assert '' in stderr def test_build_dir_hash_cppcheck_product(tmpdir): diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 5176f2b60d6..980ac3b19f2 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -1014,6 +1014,17 @@ class TestSuppressions : public TestFixture { msg.clear(); ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id */", &msg)); ASSERT_EQUALS("", msg); + ASSERT_EQUALS("", s.extraComment); + + msg.clear(); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id ; extra */", &msg)); + ASSERT_EQUALS("", msg); + ASSERT_EQUALS("extra", s.extraComment); + + msg.clear(); + ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress id // extra */", &msg)); + ASSERT_EQUALS("", msg); + ASSERT_EQUALS("extra", s.extraComment); msg.clear(); ASSERT_EQUALS(true, s.parseComment("/* cppcheck-suppress-file id */", &msg)); From 224991dbe8dadcd8668952d1bb1130b2e62764fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 16:04:59 +0200 Subject: [PATCH 3/5] handle semicolons in inline suppression comment --- cli/processexecutor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index 4d0d63f4204..af9041bd555 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -241,7 +241,7 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str if (!buf.empty()) { // TODO: avoid string splitting auto parts = splitString(buf, ';'); - if (parts.size() != 4) + if (parts.size() < 4) { // TODO: make this non-fatal std::cerr << "#### ThreadExecutor::handleRead(" << filename << ") adding of inline suppression failed - insufficient data" << std::endl; @@ -252,6 +252,9 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str suppr.checked = parts[1] == "1"; suppr.matched = parts[2] == "1"; suppr.extraComment = parts[3]; + for (std::size_t i = 4; i < parts.size(); i++) { + suppr.extraComment += ";" + parts[i]; + } const std::string err = mSuppressions.nomsg.addSuppression(suppr); if (!err.empty()) { // TODO: only update state if it doesn't exist - otherwise propagate error From 36fefc86b9ffe23934fb08bed2aea5e680d839e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 16:14:41 +0200 Subject: [PATCH 4/5] escape special characters in suppression comment --- lib/suppressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 239378c48b8..cdb8eee0d93 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -544,7 +544,7 @@ void SuppressionList::dump(std::ostream & out) const else out << " inline=\"false\""; if (!suppression.extraComment.empty()) - out << " comment=\"" << suppression.extraComment << "\""; + out << " comment=\"" << ErrorLogger::toxml(suppression.extraComment) << "\""; out << " />" << std::endl; } out << " " << std::endl; From cb476723166bbe4281c8087d700e7e0fe070f8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 24 Jun 2025 16:30:37 +0200 Subject: [PATCH 5/5] remove multi-byte characters from suppression comment --- lib/suppressions.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index cdb8eee0d93..6255927cf75 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -347,6 +347,8 @@ bool SuppressionList::Suppression::parseComment(std::string comment, std::string if (extraPos != std::string::npos) { extraComment = trim(comment.substr(extraPos + extraDelimiterSize)); + for (auto it = extraComment.begin(); it != extraComment.end();) + it = *it & 0x80 ? extraComment.erase(it) : it + 1; comment.erase(extraPos); }