Skip to content

Commit

Permalink
Fix #13144 (regression: misra.json has stopped working since 2.10) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
danmar authored Oct 24, 2024
1 parent 0545e65 commit eab6b48
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 58 deletions.
59 changes: 25 additions & 34 deletions addons/misra.py
Original file line number Diff line number Diff line change
Expand Up @@ -4353,9 +4353,8 @@ def loadRuleTexts(self, filename):
num1 = 0
num2 = 0
appendixA = False
expect_more = False

Rule_pattern = re.compile(r'^Rule ([0-9]+).([0-9]+)')
Rule_pattern = re.compile(r'^Rule ([0-9]+)\.([0-9]+)')
severity_pattern = re.compile(r'.*[ ]*(Advisory|Required|Mandatory)$')
xA_Z_pattern = re.compile(r'^[#A-Z].*')
a_z_pattern = re.compile(r'^[a-z].*')
Expand Down Expand Up @@ -4383,70 +4382,62 @@ def loadRuleTexts(self, filename):
file_stream = open(filename, 'rt')

rule = None
have_severity = False
severity_loc = 0
rule_line_number = 0

for line in file_stream:

line = line.replace('\r', '').replace('\n', '')
line = line.strip()
if len(line) == 0:
continue

if not appendixA:
if line.find('Appendix A') >= 0 and line.find('Summary of guidelines') >= 10:
appendixA = True
continue
if line.find('Appendix B') >= 0:
break
if len(line) == 0:
continue

# Parse rule declaration.
res = Rule_pattern.match(line)

if res:
have_severity = False
expect_more = False
severity_loc = 0
rule_line_number = 0
num1 = int(res.group(1))
num2 = int(res.group(2))
rule = Rule(num1, num2)

if not have_severity and rule is not None:
res = severity_pattern.match(line)

if res:
rule.misra_severity = res.group(1)
have_severity = True
else:
severity_loc += 1
rule_line_number = 1
continue

# Only look for severity on the Rule line
# or the next non-blank line after
# If it's not in either of those locations then
# assume a severity was not provided.
if rule is None:
continue

if severity_loc < 2:
rule_line_number += 1

if rule_line_number == 1:
res = severity_pattern.match(line)

if res:
rule.misra_severity = res.group(1)
continue

rule.misra_severity = ''
have_severity = True
rule_line_number = 2

if rule is None:
# Parse beginning of rule text.
if not rule.text and xA_Z_pattern.match(line):
rule.text = line.strip()
self.ruleTexts[rule.num] = rule
continue

# Parse continuing of rule text.
if expect_more:
if a_z_pattern.match(line):
self.ruleTexts[rule.num].text += ' ' + line
continue

expect_more = False
if a_z_pattern.match(line):
self.ruleTexts[rule.num].text += ' ' + line.strip()
continue

# Parse beginning of rule text.
if xA_Z_pattern.match(line):
rule.text = line
self.ruleTexts[rule.num] = rule
expect_more = True
rule = None

file_stream.close()

Expand Down
5 changes: 4 additions & 1 deletion addons/test/misra/misra_rules_structure.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ Here we go:
Appendix A Summary of guidelines

Rule 1.2
Rule text.
Rule text.

Rule 2.1
Rule text for 2.1.

Stop parsing after this line:
Appendix B
Expand Down
11 changes: 4 additions & 7 deletions addons/test/misra_test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
# Running the test with Python 2:
# Be sure to install pytest version 4.6.4 (newer should also work)
# Command in cppcheck directory:
# python -m pytest addons/test/test-misra.py
#
# Running the test with Python 3:
# Command in cppcheck directory:
# PYTHONPATH=./addons python3 -m pytest addons/test/test-misra.py
# PYTHONPATH=./addons python3 -m pytest addons/test/misra_test.py

import pytest
import re
Expand Down Expand Up @@ -48,6 +43,8 @@ def test_loadRuleTexts_structure(checker):
assert(checker.ruleTexts.get(101, None) is None)
assert(checker.ruleTexts[102].text == "Rule text.")
assert(checker.ruleTexts.get(103, None) is None)
assert(checker.ruleTexts[201].text == "Rule text for 2.1.")
assert(checker.ruleTexts.get(202, None) is None)


def test_loadRuleTexts_empty_lines(checker):
Expand All @@ -62,7 +59,7 @@ def test_loadRuleTexts_mutiple_lines(checker):
assert(checker.ruleTexts[102].text == "Multiple lines text.")
assert(checker.ruleTexts[103].text == "Multiple lines text.")
assert(checker.ruleTexts[104].text == "Should")
assert(checker.ruleTexts[105].text == "Should")
assert(checker.ruleTexts[105].text == "Should starts from lowercase letter.")
assert(checker.ruleTexts[106].text == "Can contain empty lines.")


Expand Down
4 changes: 2 additions & 2 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
return Result::Fail;
{
XMLErrorMessagesLogger xmlLogger;
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName, mSettings.xml_version);
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName, 2);
CppCheck::getErrorMessages(xmlLogger);
std::cout << ErrorMessage::getXMLFooter() << std::endl;
std::cout << ErrorMessage::getXMLFooter(2) << std::endl;
}
return Result::Exit;
}
Expand Down
27 changes: 16 additions & 11 deletions cli/cppcheckexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,7 @@ int CppCheckExecutor::check_internal(const Settings& settings) const
cppcheck.tooManyConfigsError(emptyString,0U);
}

if (settings.safety || settings.severity.isEnabled(Severity::information) || !settings.checkersReportFilename.empty())
stdLogger.writeCheckersReport();
stdLogger.writeCheckersReport();

if (settings.xml) {
stdLogger.reportErr(ErrorMessage::getXMLFooter(settings.xml_version));
Expand All @@ -479,15 +478,21 @@ int CppCheckExecutor::check_internal(const Settings& settings) const

void StdLogger::writeCheckersReport()
{
const bool summary = mSettings.safety || mSettings.severity.isEnabled(Severity::information);
const bool xmlReport = mSettings.xml && mSettings.xml_version == 3;
const bool textReport = !mSettings.checkersReportFilename.empty();

if (!summary && !xmlReport && !textReport)
return;

CheckersReport checkersReport(mSettings, mActiveCheckers);

bool suppressed = false;
for (const SuppressionList::Suppression& s : mSettings.supprs.nomsg.getSuppressions()) {
if (s.errorId == "checkersReport")
suppressed = true;
}
const auto& suppressions = mSettings.supprs.nomsg.getSuppressions();
const bool summarySuppressed = std::any_of(suppressions.cbegin(), suppressions.cend(), [](const SuppressionList::Suppression& s) {
return s.errorId == "checkersReport";
});

if (!suppressed) {
if (summary && !summarySuppressed) {
ErrorMessage msg;
msg.severity = Severity::information;
msg.id = "checkersReport";
Expand All @@ -500,20 +505,20 @@ void StdLogger::writeCheckersReport()
what = std::to_string(activeCheckers) + "/" + std::to_string(totalCheckers);
else
what = "There was critical errors";
if (mSettings.checkersReportFilename.empty())
if (!xmlReport && !textReport)
what += " (use --checkers-report=<filename> to see details)";
msg.setmsg("Active checkers: " + what);

reportErr(msg);
}

if (!mSettings.checkersReportFilename.empty()) {
if (textReport) {
std::ofstream fout(mSettings.checkersReportFilename);
if (fout.is_open())
fout << checkersReport.getReport(mCriticalErrors);
}

if (mSettings.xml && mSettings.xml_version == 3) {
if (xmlReport) {
reportErr(" </errors>\n");
reportErr(checkersReport.getXmlReport(mCriticalErrors));
}
Expand Down
5 changes: 5 additions & 0 deletions lib/checkersreport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ static int getMisraCVersion(const Settings& settings) {
return 2023;
if (settings.addons.count("misra"))
return 2012;
const bool misraAddonInfo = std::any_of(settings.addonInfos.cbegin(), settings.addonInfos.cend(), [](const AddonInfo& addonInfo) {
return addonInfo.name == "misra";
});
if (misraAddonInfo)
return 2012;
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/errorlogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class CPPCHECKLIB ErrorMessage {
std::string toXML() const;

static std::string getXMLHeader(std::string productName, int xmlVersion = 2);
static std::string getXMLFooter(int xmlVersion = 2);
static std::string getXMLFooter(int xmlVersion);

/**
* Format the error message into a string.
Expand Down
13 changes: 13 additions & 0 deletions test/cli/other_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1970,6 +1970,19 @@ def test_checkers_report(tmpdir):
assert '--checkers-report' not in stderr


def test_checkers_report_misra_json(tmpdir):
"""check that misra checkers are reported properly when --addon=misra.json is used"""
test_file = os.path.join(tmpdir, 'test.c')
with open(test_file, 'wt') as f:
f.write('x=1;')
misra_json = os.path.join(tmpdir, 'misra.json')
with open(misra_json, 'wt') as f:
f.write('{"script":"misra.py"}')
exitcode, stdout, stderr = cppcheck('--enable=style --addon=misra.json --xml-version=3 test.c'.split(), cwd=tmpdir)
assert exitcode == 0, stdout
assert '<checker id="Misra C 2012: 8.1"/>' in stderr


def test_ignore(tmpdir):
os.mkdir(os.path.join(tmpdir, 'src'))
test_file = os.path.join(tmpdir, 'src', 'test.cpp')
Expand Down
15 changes: 13 additions & 2 deletions test/testerrorlogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class TestErrorLogger : public TestFixture {
TEST_CASE(ToXmlV2Locations);
TEST_CASE(ToXmlV2Encoding);
TEST_CASE(FromXmlV2);
TEST_CASE(ToXmlV3);

// Inconclusive results in xml reports..
TEST_CASE(InconclusiveXml);
Expand Down Expand Up @@ -232,7 +233,7 @@ class TestErrorLogger : public TestFixture {
header += CppCheck::version();
header += "\"/>\n <errors>";
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader(""));
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter());
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter(2));
std::string message(" <error id=\"errorId\" severity=\"error\"");
message += " msg=\"Programming error.\" verbose=\"Verbose error\">\n";
message += " <location file=\"foo.cpp\" line=\"5\" column=\"1\"/>\n </error>";
Expand All @@ -253,7 +254,7 @@ class TestErrorLogger : public TestFixture {
header += CppCheck::version();
header += "\"/>\n <errors>";
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader(""));
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter());
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter(2));
std::string message(" <error id=\"errorId\" severity=\"error\"");
message += " msg=\"Programming error.\" verbose=\"Verbose error\">\n";
message += " <location file=\"bar.cpp\" line=\"8\" column=\"1\" info=\"\\303\\244\"/>\n";
Expand Down Expand Up @@ -312,6 +313,16 @@ class TestErrorLogger : public TestFixture {
ASSERT_EQUALS(1u, msg.callStack.back().column);
}

void ToXmlV3() const {
std::string header("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<results version=\"3\">\n");
header += " <cppcheck version=\"";
header += CppCheck::version();
header += "\"/>\n <errors>";
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader("", 3));

ASSERT_EQUALS("</results>", ErrorMessage::getXMLFooter(3));
}

void InconclusiveXml() const {
// Location
std::list<ErrorMessage::FileLocation> locs(1, fooCpp5);
Expand Down

0 comments on commit eab6b48

Please sign in to comment.