Skip to content

fix #13961: Additional information for inline suppression in XML output #7622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cli/processexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -249,6 +251,10 @@ 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];
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
Expand Down
27 changes: 21 additions & 6 deletions lib/suppressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,24 @@ 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));
for (auto it = extraComment.begin(); it != extraComment.end();)
it = *it & 0x80 ? extraComment.erase(it) : it + 1;
comment.erase(extraPos);
}

const std::set<std::string> cppchecksuppress{
"cppcheck-suppress",
"cppcheck-suppress-begin",
Expand Down Expand Up @@ -532,6 +541,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=\"" << ErrorLogger::toxml(suppression.extraComment) << "\"";
out << " />" << std::endl;
}
out << " </suppressions>" << std::endl;
Expand Down
1 change: 1 addition & 0 deletions lib/suppressions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions test/cli/inline-suppress_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 '<suppression errorId="some_warning_id" fileName="proj-inline-suppress/2.c" lineNumber="2" inline="true" comment="there should be a unmatchedSuppression warning about this" />' in stderr
assert stdout == ''
assert ret == 0, stdout


def test_unmatched_suppression():
args = [
'-q',
Expand Down
2 changes: 1 addition & 1 deletion test/cli/premium_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 '<suppression errorId="foo" />' in stderr
assert '<suppression errorId="foo" inline="false" />' in stderr


def test_build_dir_hash_cppcheck_product(tmpdir):
Expand Down
11 changes: 11 additions & 0 deletions test/testsuppressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Loading