misra.py: Add --verify-rule-texts option. (#1909)

* misra.py: Add verify-rule-texts option.

* Capture stdout output in travis.
This commit is contained in:
Georgy Komarov 2019-06-19 22:57:28 +03:00 committed by Daniel Marjamäki
parent a5185c7005
commit 02cc9ffcd8
3 changed files with 94 additions and 15 deletions

View File

@ -481,7 +481,27 @@ class Define:
self.expansionList = res.group(2) self.expansionList = res.group(2)
def getAddonRules():
"""Returns dict of MISRA rules handled by this addon."""
addon_rules = []
compiled = re.compile(r'.*def[ ]+misra_([0-9]+)_([0-9]+)[(].*')
for line in open(__file__):
res = compiled.match(line)
if res is None:
continue
addon_rules.append(res.group(1) + '.' + res.group(2))
return addon_rules
def getCppcheckRules():
"""Returns list of rules handled by cppcheck."""
return ['1.3', '2.1', '2.2', '2.4', '2.6', '8.3', '12.2', '13.2', '13.6',
'14.3', '17.5', '18.1', '18.2', '18.3', '18.6', '20.6',
'22.1', '22.2', '22.4', '22.6']
def generateTable(): def generateTable():
# print table
numberOfRules = {} numberOfRules = {}
numberOfRules[1] = 3 numberOfRules[1] = 3
numberOfRules[2] = 7 numberOfRules[2] = 7
@ -506,23 +526,11 @@ def generateTable():
numberOfRules[21] = 12 numberOfRules[21] = 12
numberOfRules[22] = 6 numberOfRules[22] = 6
# what rules are handled by this addon? # Rules that can be checked with compilers:
addon = []
compiled = re.compile(r'.*def[ ]+misra_([0-9]+)_([0-9]+)[(].*')
for line in open(__file__):
res = compiled.match(line)
if res is None:
continue
addon.append(res.group(1) + '.' + res.group(2))
# rules handled by cppcheck
cppcheck = ['1.3', '2.1', '2.2', '2.4', '2.6', '8.3', '12.2', '13.2', '13.6', '14.3', '17.5',
'18.1', '18.2', '18.3', '18.6', '20.6', '22.1', '22.2', '22.4', '22.6']
# rules that can be checked with compilers
# compiler = ['1.1', '1.2'] # compiler = ['1.1', '1.2']
# print table addon = getAddonRules()
cppcheck = getCppcheckRules()
for i1 in range(1, 23): for i1 in range(1, 23):
for i2 in range(1, numberOfRules[i1] + 1): for i2 in range(1, numberOfRules[i1] + 1):
num = str(i1) + '.' + str(i2) num = str(i1) + '.' + str(i2)
@ -2148,6 +2156,22 @@ class MisraChecker:
else: else:
rule = None rule = None
def verifyRuleTexts(self):
"""Prints rule numbers without rule text."""
rule_texts_rules = []
for rule_num in self.ruleTexts:
rule = self.ruleTexts[rule_num]
rule_texts_rules.append(str(rule.num1) + '.' + str(rule.num2))
all_rules = list(getAddonRules() + getCppcheckRules())
missing_rules = list(set(all_rules) - set(rule_texts_rules))
if len(missing_rules) == 0:
print("Rule texts are correct.")
else:
print("Missing rule texts: " + ', '.join(missing_rules))
def printStatus(self, *args, **kwargs): def printStatus(self, *args, **kwargs):
if not self.settings.quiet: if not self.settings.quiet:
print(*args, **kwargs) print(*args, **kwargs)
@ -2309,6 +2333,7 @@ def get_args():
"""Generates list of command-line arguments acceptable by misra.py script.""" """Generates list of command-line arguments acceptable by misra.py script."""
parser = cppcheckdata.ArgumentParser() parser = cppcheckdata.ArgumentParser()
parser.add_argument("--rule-texts", type=str, help=RULE_TEXTS_HELP) parser.add_argument("--rule-texts", type=str, help=RULE_TEXTS_HELP)
parser.add_argument("--verify-rule-texts", help="Verify that all supported rules texts are present in given file and exit.", action="store_true")
parser.add_argument("--suppress-rules", type=str, help=SUPPRESS_RULES_HELP) parser.add_argument("--suppress-rules", type=str, help=SUPPRESS_RULES_HELP)
parser.add_argument("--quiet", help="Only print something when there is an error", action="store_true") parser.add_argument("--quiet", help="Only print something when there is an error", action="store_true")
parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true") parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true")
@ -2336,6 +2361,13 @@ def main():
print('Fatal error: file is not found: ' + filename) print('Fatal error: file is not found: ' + filename)
sys.exit(1) sys.exit(1)
checker.loadRuleTexts(filename) checker.loadRuleTexts(filename)
if args.verify_rule_texts:
checker.verifyRuleTexts()
sys.exit(0)
if args.verify_rule_texts and not args.rule_texts:
print("Error: Please specify rule texts file with --rule-texts=<file>")
sys.exit(1)
if args.suppress_rules: if args.suppress_rules:
checker.setSuppressionList(args.suppress_rules) checker.setSuppressionList(args.suppress_rules)

View File

@ -0,0 +1,20 @@
Appendix A Summary of guidelines
Rule 3.1 Required
R3.1 text.
Rule 4.1 Required
R4.1 text.
Rule 10.4 Required
R10.4 text.
Rule 11.5 Advisory
R11.5 text.
Rule 15.5 Advisory
R15.5 text.
Rule 15.6 Required
R15.6 text.
Rule 17.7 Required
R17.7 text.
Rule 20.1 Advisory
R20.1 text.
Rule 21.3 Required
R21.3 text.

View File

@ -1,5 +1,24 @@
# python -m pytest addons/test/test-misra.py # python -m pytest addons/test/test-misra.py
import pytest import pytest
import sys
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
class Capturing(object):
def __enter__(self):
self._stdout = sys.stdout
sys.stdout = 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.stdout = self._stdout
@pytest.fixture @pytest.fixture
@ -32,3 +51,11 @@ def test_loadRuleTexts_mutiple_lines(checker):
assert(checker.ruleTexts[105].text == "Should") assert(checker.ruleTexts[105].text == "Should")
assert(checker.ruleTexts[106].text == "Should") assert(checker.ruleTexts[106].text == "Should")
def test_verifyRuleTexts(checker):
checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt")
with Capturing() as output:
checker.verifyRuleTexts()
captured = ''.join(output.captured)
assert("21.3" not in captured)
assert("1.3" in captured)