Skip to content

Commit eab6b48

Browse files
authored
Fix #13144 (regression: misra.json has stopped working since 2.10) (danmar#6946)
1 parent 0545e65 commit eab6b48

9 files changed

+83
-58
lines changed

addons/misra.py

+25-34
Original file line numberDiff line numberDiff line change
@@ -4353,9 +4353,8 @@ def loadRuleTexts(self, filename):
43534353
num1 = 0
43544354
num2 = 0
43554355
appendixA = False
4356-
expect_more = False
43574356

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

43854384
rule = None
4386-
have_severity = False
4387-
severity_loc = 0
4385+
rule_line_number = 0
43884386

43894387
for line in file_stream:
43904388

4391-
line = line.replace('\r', '').replace('\n', '')
4389+
line = line.strip()
4390+
if len(line) == 0:
4391+
continue
43924392

43934393
if not appendixA:
43944394
if line.find('Appendix A') >= 0 and line.find('Summary of guidelines') >= 10:
43954395
appendixA = True
43964396
continue
43974397
if line.find('Appendix B') >= 0:
43984398
break
4399-
if len(line) == 0:
4400-
continue
44014399

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

44054403
if res:
4406-
have_severity = False
4407-
expect_more = False
4408-
severity_loc = 0
4404+
rule_line_number = 0
44094405
num1 = int(res.group(1))
44104406
num2 = int(res.group(2))
44114407
rule = Rule(num1, num2)
44124408

4413-
if not have_severity and rule is not None:
44144409
res = severity_pattern.match(line)
4415-
44164410
if res:
44174411
rule.misra_severity = res.group(1)
4418-
have_severity = True
4419-
else:
4420-
severity_loc += 1
4412+
rule_line_number = 1
4413+
continue
44214414

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

4427-
if severity_loc < 2:
4418+
rule_line_number += 1
4419+
4420+
if rule_line_number == 1:
4421+
res = severity_pattern.match(line)
4422+
4423+
if res:
4424+
rule.misra_severity = res.group(1)
44284425
continue
44294426

4430-
rule.misra_severity = ''
4431-
have_severity = True
4427+
rule_line_number = 2
44324428

4433-
if rule is None:
4429+
# Parse beginning of rule text.
4430+
if not rule.text and xA_Z_pattern.match(line):
4431+
rule.text = line.strip()
4432+
self.ruleTexts[rule.num] = rule
44344433
continue
44354434

44364435
# Parse continuing of rule text.
4437-
if expect_more:
4438-
if a_z_pattern.match(line):
4439-
self.ruleTexts[rule.num].text += ' ' + line
4440-
continue
4441-
4442-
expect_more = False
4436+
if a_z_pattern.match(line):
4437+
self.ruleTexts[rule.num].text += ' ' + line.strip()
44434438
continue
44444439

4445-
# Parse beginning of rule text.
4446-
if xA_Z_pattern.match(line):
4447-
rule.text = line
4448-
self.ruleTexts[rule.num] = rule
4449-
expect_more = True
4440+
rule = None
44504441

44514442
file_stream.close()
44524443

addons/test/misra/misra_rules_structure.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ Here we go:
1111
Appendix A Summary of guidelines
1212

1313
Rule 1.2
14-
Rule text.
14+
Rule text.
15+
16+
Rule 2.1
17+
Rule text for 2.1.
1518

1619
Stop parsing after this line:
1720
Appendix B

addons/test/misra_test.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
# Running the test with Python 2:
2-
# Be sure to install pytest version 4.6.4 (newer should also work)
3-
# Command in cppcheck directory:
4-
# python -m pytest addons/test/test-misra.py
5-
#
61
# Running the test with Python 3:
72
# Command in cppcheck directory:
8-
# PYTHONPATH=./addons python3 -m pytest addons/test/test-misra.py
3+
# PYTHONPATH=./addons python3 -m pytest addons/test/misra_test.py
94

105
import pytest
116
import re
@@ -48,6 +43,8 @@ def test_loadRuleTexts_structure(checker):
4843
assert(checker.ruleTexts.get(101, None) is None)
4944
assert(checker.ruleTexts[102].text == "Rule text.")
5045
assert(checker.ruleTexts.get(103, None) is None)
46+
assert(checker.ruleTexts[201].text == "Rule text for 2.1.")
47+
assert(checker.ruleTexts.get(202, None) is None)
5148

5249

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

6865

cli/cmdlineparser.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
340340
return Result::Fail;
341341
{
342342
XMLErrorMessagesLogger xmlLogger;
343-
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName, mSettings.xml_version);
343+
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName, 2);
344344
CppCheck::getErrorMessages(xmlLogger);
345-
std::cout << ErrorMessage::getXMLFooter() << std::endl;
345+
std::cout << ErrorMessage::getXMLFooter(2) << std::endl;
346346
}
347347
return Result::Exit;
348348
}

cli/cppcheckexecutor.cpp

+16-11
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,7 @@ int CppCheckExecutor::check_internal(const Settings& settings) const
462462
cppcheck.tooManyConfigsError(emptyString,0U);
463463
}
464464

465-
if (settings.safety || settings.severity.isEnabled(Severity::information) || !settings.checkersReportFilename.empty())
466-
stdLogger.writeCheckersReport();
465+
stdLogger.writeCheckersReport();
467466

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

480479
void StdLogger::writeCheckersReport()
481480
{
481+
const bool summary = mSettings.safety || mSettings.severity.isEnabled(Severity::information);
482+
const bool xmlReport = mSettings.xml && mSettings.xml_version == 3;
483+
const bool textReport = !mSettings.checkersReportFilename.empty();
484+
485+
if (!summary && !xmlReport && !textReport)
486+
return;
487+
482488
CheckersReport checkersReport(mSettings, mActiveCheckers);
483489

484-
bool suppressed = false;
485-
for (const SuppressionList::Suppression& s : mSettings.supprs.nomsg.getSuppressions()) {
486-
if (s.errorId == "checkersReport")
487-
suppressed = true;
488-
}
490+
const auto& suppressions = mSettings.supprs.nomsg.getSuppressions();
491+
const bool summarySuppressed = std::any_of(suppressions.cbegin(), suppressions.cend(), [](const SuppressionList::Suppression& s) {
492+
return s.errorId == "checkersReport";
493+
});
489494

490-
if (!suppressed) {
495+
if (summary && !summarySuppressed) {
491496
ErrorMessage msg;
492497
msg.severity = Severity::information;
493498
msg.id = "checkersReport";
@@ -500,20 +505,20 @@ void StdLogger::writeCheckersReport()
500505
what = std::to_string(activeCheckers) + "/" + std::to_string(totalCheckers);
501506
else
502507
what = "There was critical errors";
503-
if (mSettings.checkersReportFilename.empty())
508+
if (!xmlReport && !textReport)
504509
what += " (use --checkers-report=<filename> to see details)";
505510
msg.setmsg("Active checkers: " + what);
506511

507512
reportErr(msg);
508513
}
509514

510-
if (!mSettings.checkersReportFilename.empty()) {
515+
if (textReport) {
511516
std::ofstream fout(mSettings.checkersReportFilename);
512517
if (fout.is_open())
513518
fout << checkersReport.getReport(mCriticalErrors);
514519
}
515520

516-
if (mSettings.xml && mSettings.xml_version == 3) {
521+
if (xmlReport) {
517522
reportErr(" </errors>\n");
518523
reportErr(checkersReport.getXmlReport(mCriticalErrors));
519524
}

lib/checkersreport.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ static int getMisraCVersion(const Settings& settings) {
4040
return 2023;
4141
if (settings.addons.count("misra"))
4242
return 2012;
43+
const bool misraAddonInfo = std::any_of(settings.addonInfos.cbegin(), settings.addonInfos.cend(), [](const AddonInfo& addonInfo) {
44+
return addonInfo.name == "misra";
45+
});
46+
if (misraAddonInfo)
47+
return 2012;
4348
return 0;
4449
}
4550

lib/errorlogger.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class CPPCHECKLIB ErrorMessage {
143143
std::string toXML() const;
144144

145145
static std::string getXMLHeader(std::string productName, int xmlVersion = 2);
146-
static std::string getXMLFooter(int xmlVersion = 2);
146+
static std::string getXMLFooter(int xmlVersion);
147147

148148
/**
149149
* Format the error message into a string.

test/cli/other_test.py

+13
Original file line numberDiff line numberDiff line change
@@ -1970,6 +1970,19 @@ def test_checkers_report(tmpdir):
19701970
assert '--checkers-report' not in stderr
19711971

19721972

1973+
def test_checkers_report_misra_json(tmpdir):
1974+
"""check that misra checkers are reported properly when --addon=misra.json is used"""
1975+
test_file = os.path.join(tmpdir, 'test.c')
1976+
with open(test_file, 'wt') as f:
1977+
f.write('x=1;')
1978+
misra_json = os.path.join(tmpdir, 'misra.json')
1979+
with open(misra_json, 'wt') as f:
1980+
f.write('{"script":"misra.py"}')
1981+
exitcode, stdout, stderr = cppcheck('--enable=style --addon=misra.json --xml-version=3 test.c'.split(), cwd=tmpdir)
1982+
assert exitcode == 0, stdout
1983+
assert '<checker id="Misra C 2012: 8.1"/>' in stderr
1984+
1985+
19731986
def test_ignore(tmpdir):
19741987
os.mkdir(os.path.join(tmpdir, 'src'))
19751988
test_file = os.path.join(tmpdir, 'src', 'test.cpp')

test/testerrorlogger.cpp

+13-2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class TestErrorLogger : public TestFixture {
5454
TEST_CASE(ToXmlV2Locations);
5555
TEST_CASE(ToXmlV2Encoding);
5656
TEST_CASE(FromXmlV2);
57+
TEST_CASE(ToXmlV3);
5758

5859
// Inconclusive results in xml reports..
5960
TEST_CASE(InconclusiveXml);
@@ -232,7 +233,7 @@ class TestErrorLogger : public TestFixture {
232233
header += CppCheck::version();
233234
header += "\"/>\n <errors>";
234235
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader(""));
235-
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter());
236+
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter(2));
236237
std::string message(" <error id=\"errorId\" severity=\"error\"");
237238
message += " msg=\"Programming error.\" verbose=\"Verbose error\">\n";
238239
message += " <location file=\"foo.cpp\" line=\"5\" column=\"1\"/>\n </error>";
@@ -253,7 +254,7 @@ class TestErrorLogger : public TestFixture {
253254
header += CppCheck::version();
254255
header += "\"/>\n <errors>";
255256
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader(""));
256-
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter());
257+
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter(2));
257258
std::string message(" <error id=\"errorId\" severity=\"error\"");
258259
message += " msg=\"Programming error.\" verbose=\"Verbose error\">\n";
259260
message += " <location file=\"bar.cpp\" line=\"8\" column=\"1\" info=\"\\303\\244\"/>\n";
@@ -312,6 +313,16 @@ class TestErrorLogger : public TestFixture {
312313
ASSERT_EQUALS(1u, msg.callStack.back().column);
313314
}
314315

316+
void ToXmlV3() const {
317+
std::string header("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<results version=\"3\">\n");
318+
header += " <cppcheck version=\"";
319+
header += CppCheck::version();
320+
header += "\"/>\n <errors>";
321+
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader("", 3));
322+
323+
ASSERT_EQUALS("</results>", ErrorMessage::getXMLFooter(3));
324+
}
325+
315326
void InconclusiveXml() const {
316327
// Location
317328
std::list<ErrorMessage::FileLocation> locs(1, fooCpp5);

0 commit comments

Comments
 (0)