misra.py: Fixup rules severity (#1911)

* misra.py: Fixup rules severity

* Divide cppcheck and MISRA severity.
This commit is contained in:
Georgy Komarov 2019-06-23 15:08:05 +03:00 committed by Daniel Marjamäki
parent bd59999fd0
commit 05bb4a0c8f
3 changed files with 91 additions and 21 deletions

View File

@ -566,26 +566,39 @@ def remove_file_prefix(file_path, prefix):
result = file_path
return result
class Rule:
""" Class to keep rule text and metadata """
class Rule(object):
"""Class to keep rule text and metadata"""
MISRA_SEVERIY_LEVELS = ['Required', 'Mandatory', 'Advisory']
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
self.text = ''
self.severity = 'style'
self.misra_severity = ''
@property
def num(self):
return self.num1 * 100 + self.num2
@property
def misra_severity(self):
return self._misra_severity
@misra_severity.setter
def misra_severity(self, val):
if val in self.MISRA_SEVERIY_LEVELS:
self._misra_severity = val
else:
self._misra_severity = ''
@property
def cppcheck_severity(self):
return self.SEVERITY_MAP[self.severity]
return 'style'
def __repr__(self):
return "%d.%d %s" % (self.num1, self.num2, self.severity)
SEVERITY_MAP = { 'Required': 'warning', 'Mandatory': 'error', 'Advisory': 'style', 'style': 'style' }
return "%d.%d (%s)" % (self.num1, self.num2, self.misra_severity)
class MisraSettings(object):
@ -2064,19 +2077,23 @@ class MisraChecker:
return
else:
errorId = 'c2012-' + str(num1) + '.' + str(num2)
severity = 'style'
misra_severity = 'Undefined'
cppcheck_severity = 'style'
if ruleNum in self.ruleTexts:
errmsg = self.ruleTexts[ruleNum].text
severity = self.ruleTexts[ruleNum].cppcheck_severity
if self.ruleTexts[ruleNum].misra_severity:
misra_severity = self.ruleTexts[ruleNum].misra_severity
errmsg += ''.join((' (', self.ruleTexts[ruleNum].misra_severity, ')'))
cppcheck_severity = self.ruleTexts[ruleNum].cppcheck_severity
elif len(self.ruleTexts) == 0:
errmsg = 'misra violation (use --rule-texts=<file> to get proper output)'
else:
return
cppcheckdata.reportError(location, severity, errmsg, 'misra', errorId)
cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId)
if not severity in self.violations:
self.violations[severity] = []
self.violations[severity].append('misra-' + errorId)
if not misra_severity in self.violations:
self.violations[misra_severity] = []
self.violations[misra_severity].append('misra-' + errorId)
def loadRuleTexts(self, filename):
num1 = 0
@ -2086,7 +2103,7 @@ class MisraChecker:
expect_more = False
Rule_pattern = re.compile(r'^Rule ([0-9]+).([0-9]+)')
Choice_pattern = re.compile(r'^[ ]*(Advisory|Required|Mandatory)$')
Choice_pattern = re.compile(r'.*[ ]*(Advisory|Required|Mandatory)$')
xA_Z_pattern = re.compile(r'^[#A-Z].*')
a_z_pattern = re.compile(r'^[a-z].*')
# Try to detect the file encoding
@ -2133,7 +2150,7 @@ class MisraChecker:
rule = Rule(num1, num2)
res = Choice_pattern.match(line)
if res:
self.ruleTexts[rule.num].severity = res.group(1)
rule.misra_severity = res.group(1)
expect_more = False
continue
if rule is None:
@ -2408,12 +2425,13 @@ def main():
exitCode = 1
if settings.show_summary:
print("\nMISRA rule violations found: %s\n" % ("\t".join([ "%s: %d" % (viol, len(checker.get_violations(viol))) for viol in checker.get_violation_types()])))
print("\nMISRA rules violations found:\n\t%s\n" % ("\n\t".join([ "%s: %d" % (viol, len(checker.get_violations(viol))) for viol in checker.get_violation_types()])))
rules_violated = {}
for severity, ids in checker.get_violations():
for misra_id in ids:
rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1
print("Misra rules violated:")
print("MISRA rules violated:")
convert = lambda text: int(text) if text.isdigit() else text
misra_sort = lambda key: [ convert(c) for c in re.split('[\.-]([0-9]*)', key) ]
for misra_id in sorted(rules_violated.keys(), key=misra_sort):
@ -2424,7 +2442,7 @@ def main():
num = int(res.group(1)) * 100 + int(res.group(2))
severity = '-'
if num in checker.ruleTexts:
severity = checker.ruleTexts[num].severity
severity = checker.ruleTexts[num].cppcheck_severity
print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id]))
if args.show_suppressed_rules:

View File

@ -3,7 +3,7 @@ Rule 3.1 Required
R3.1 text.
Rule 4.1 Required
R4.1 text.
Rule 10.4 Required
Rule 10.4 Mandatory
R10.4 text.
Rule 11.5 Advisory
R11.5 text.
@ -17,4 +17,6 @@ Rule 20.1 Advisory
R20.1 text.
Rule 21.3 Required
R21.3 text.
Rule 21.4
R21.4 text.

View File

@ -5,9 +5,10 @@ try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import subprocess
class Capturing(object):
class CapturingStdout(object):
def __enter__(self):
self._stdout = sys.stdout
@ -21,6 +22,37 @@ class Capturing(object):
sys.stdout = self._stdout
class CapturingStderr(object):
def __enter__(self):
self._stderr = sys.stderr
sys.stderr = self._stringio = StringIO()
self.captured = []
return self
def __exit__(self, *args):
self.captured.extend(self._stringio.getvalue().splitlines())
del self._stringio # free up some memory
sys.stderr = self._stderr
TEST_SOURCE_FILES = ['./addons/test/misra-test.c']
def setup_module(module):
for f in TEST_SOURCE_FILES:
p = subprocess.Popen(["./cppcheck", "--dump", "--quiet", f])
p.communicate()[0]
if p.returncode != 0:
raise OSError("cppcheck returns error code: %d" % p.returncode)
subprocess.Popen(["sync"])
def teardown_module(module):
for f in TEST_SOURCE_FILES:
subprocess.Popen(["rm", "-f", f + ".dump"])
@pytest.fixture
def checker():
from addons.misra import MisraChecker, MisraSettings, get_args
@ -54,8 +86,26 @@ def test_loadRuleTexts_mutiple_lines(checker):
def test_verifyRuleTexts(checker):
checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt")
with Capturing() as output:
with CapturingStdout() as output:
checker.verifyRuleTexts()
captured = ''.join(output.captured)
assert("21.3" not in captured)
assert("1.3" in captured)
def test_rules_misra_severity(checker):
checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt")
assert(checker.ruleTexts[1004].misra_severity == 'Mandatory')
assert(checker.ruleTexts[401].misra_severity == 'Required')
assert(checker.ruleTexts[1505].misra_severity == 'Advisory')
assert(checker.ruleTexts[2104].misra_severity == '')
def test_rules_cppcheck_severity(checker):
checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt")
with CapturingStderr() as output:
checker.parseDump("./addons/test/misra-test.c.dump")
captured = ''.join(output.captured)
assert("(error)" not in captured)
assert("(warning)" not in captured)
assert("(style)" in captured)