misra.py: Fixup rules severity (#1911)
* misra.py: Fixup rules severity * Divide cppcheck and MISRA severity.
This commit is contained in:
parent
bd59999fd0
commit
05bb4a0c8f
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue