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
|
result = file_path
|
||||||
return result
|
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):
|
def __init__(self, num1, num2):
|
||||||
self.num1 = num1
|
self.num1 = num1
|
||||||
self.num2 = num2
|
self.num2 = num2
|
||||||
self.text = ''
|
self.text = ''
|
||||||
self.severity = 'style'
|
self.misra_severity = ''
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def num(self):
|
def num(self):
|
||||||
return self.num1 * 100 + self.num2
|
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
|
@property
|
||||||
def cppcheck_severity(self):
|
def cppcheck_severity(self):
|
||||||
return self.SEVERITY_MAP[self.severity]
|
return 'style'
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%d.%d %s" % (self.num1, self.num2, self.severity)
|
return "%d.%d (%s)" % (self.num1, self.num2, self.misra_severity)
|
||||||
|
|
||||||
SEVERITY_MAP = { 'Required': 'warning', 'Mandatory': 'error', 'Advisory': 'style', 'style': 'style' }
|
|
||||||
|
|
||||||
|
|
||||||
class MisraSettings(object):
|
class MisraSettings(object):
|
||||||
|
@ -2064,19 +2077,23 @@ class MisraChecker:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
errorId = 'c2012-' + str(num1) + '.' + str(num2)
|
errorId = 'c2012-' + str(num1) + '.' + str(num2)
|
||||||
severity = 'style'
|
misra_severity = 'Undefined'
|
||||||
|
cppcheck_severity = 'style'
|
||||||
if ruleNum in self.ruleTexts:
|
if ruleNum in self.ruleTexts:
|
||||||
errmsg = self.ruleTexts[ruleNum].text
|
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:
|
elif len(self.ruleTexts) == 0:
|
||||||
errmsg = 'misra violation (use --rule-texts=<file> to get proper output)'
|
errmsg = 'misra violation (use --rule-texts=<file> to get proper output)'
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
cppcheckdata.reportError(location, severity, errmsg, 'misra', errorId)
|
cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId)
|
||||||
|
|
||||||
if not severity in self.violations:
|
if not misra_severity in self.violations:
|
||||||
self.violations[severity] = []
|
self.violations[misra_severity] = []
|
||||||
self.violations[severity].append('misra-' + errorId)
|
self.violations[misra_severity].append('misra-' + errorId)
|
||||||
|
|
||||||
def loadRuleTexts(self, filename):
|
def loadRuleTexts(self, filename):
|
||||||
num1 = 0
|
num1 = 0
|
||||||
|
@ -2086,7 +2103,7 @@ class MisraChecker:
|
||||||
expect_more = False
|
expect_more = False
|
||||||
|
|
||||||
Rule_pattern = re.compile(r'^Rule ([0-9]+).([0-9]+)')
|
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].*')
|
xA_Z_pattern = re.compile(r'^[#A-Z].*')
|
||||||
a_z_pattern = re.compile(r'^[a-z].*')
|
a_z_pattern = re.compile(r'^[a-z].*')
|
||||||
# Try to detect the file encoding
|
# Try to detect the file encoding
|
||||||
|
@ -2133,7 +2150,7 @@ class MisraChecker:
|
||||||
rule = Rule(num1, num2)
|
rule = Rule(num1, num2)
|
||||||
res = Choice_pattern.match(line)
|
res = Choice_pattern.match(line)
|
||||||
if res:
|
if res:
|
||||||
self.ruleTexts[rule.num].severity = res.group(1)
|
rule.misra_severity = res.group(1)
|
||||||
expect_more = False
|
expect_more = False
|
||||||
continue
|
continue
|
||||||
if rule is None:
|
if rule is None:
|
||||||
|
@ -2408,12 +2425,13 @@ def main():
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
|
|
||||||
if settings.show_summary:
|
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 = {}
|
rules_violated = {}
|
||||||
for severity, ids in checker.get_violations():
|
for severity, ids in checker.get_violations():
|
||||||
for misra_id in ids:
|
for misra_id in ids:
|
||||||
rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1
|
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
|
convert = lambda text: int(text) if text.isdigit() else text
|
||||||
misra_sort = lambda key: [ convert(c) for c in re.split('[\.-]([0-9]*)', key) ]
|
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):
|
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))
|
num = int(res.group(1)) * 100 + int(res.group(2))
|
||||||
severity = '-'
|
severity = '-'
|
||||||
if num in checker.ruleTexts:
|
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]))
|
print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id]))
|
||||||
|
|
||||||
if args.show_suppressed_rules:
|
if args.show_suppressed_rules:
|
||||||
|
|
|
@ -3,7 +3,7 @@ Rule 3.1 Required
|
||||||
R3.1 text.
|
R3.1 text.
|
||||||
Rule 4.1 Required
|
Rule 4.1 Required
|
||||||
R4.1 text.
|
R4.1 text.
|
||||||
Rule 10.4 Required
|
Rule 10.4 Mandatory
|
||||||
R10.4 text.
|
R10.4 text.
|
||||||
Rule 11.5 Advisory
|
Rule 11.5 Advisory
|
||||||
R11.5 text.
|
R11.5 text.
|
||||||
|
@ -17,4 +17,6 @@ Rule 20.1 Advisory
|
||||||
R20.1 text.
|
R20.1 text.
|
||||||
Rule 21.3 Required
|
Rule 21.3 Required
|
||||||
R21.3 text.
|
R21.3 text.
|
||||||
|
Rule 21.4
|
||||||
|
R21.4 text.
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ try:
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
class Capturing(object):
|
class CapturingStdout(object):
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self._stdout = sys.stdout
|
self._stdout = sys.stdout
|
||||||
|
@ -21,6 +22,37 @@ class Capturing(object):
|
||||||
sys.stdout = self._stdout
|
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
|
@pytest.fixture
|
||||||
def checker():
|
def checker():
|
||||||
from addons.misra import MisraChecker, MisraSettings, get_args
|
from addons.misra import MisraChecker, MisraSettings, get_args
|
||||||
|
@ -54,8 +86,26 @@ def test_loadRuleTexts_mutiple_lines(checker):
|
||||||
|
|
||||||
def test_verifyRuleTexts(checker):
|
def test_verifyRuleTexts(checker):
|
||||||
checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt")
|
checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt")
|
||||||
with Capturing() as output:
|
with CapturingStdout() as output:
|
||||||
checker.verifyRuleTexts()
|
checker.verifyRuleTexts()
|
||||||
captured = ''.join(output.captured)
|
captured = ''.join(output.captured)
|
||||||
assert("21.3" not in captured)
|
assert("21.3" not in captured)
|
||||||
assert("1.3" 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