improve misra analysis perf for misra_5_4, misra_5_5, misra_20_1 (#2329)

fix bad indent
This commit is contained in:
elfel19 2020-01-08 14:54:43 +09:00 committed by Daniel Marjamäki
parent d75d9c1866
commit a288ea3c1f
1 changed files with 80 additions and 71 deletions

View File

@ -32,7 +32,7 @@ except ImportError:
def grouped(iterable, n): def grouped(iterable, n):
"""s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ...""" """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."""
return zip(*[iter(iterable)]*n) return zip(*[iter(iterable)] * n)
typeBits = { typeBits = {
@ -119,7 +119,7 @@ def getEssentialTypeCategory(expr):
# TODO this is incomplete # TODO this is incomplete
e1 = getEssentialTypeCategory(expr.astOperand1) e1 = getEssentialTypeCategory(expr.astOperand1)
e2 = getEssentialTypeCategory(expr.astOperand2) e2 = getEssentialTypeCategory(expr.astOperand2)
#print('{0}: {1} {2}'.format(expr.str, e1, e2)) # print('{0}: {1} {2}'.format(expr.str, e1, e2))
if e1 and e2 and e1 == e2: if e1 and e2 and e1 == e2:
return e1 return e1
if expr.valueType: if expr.valueType:
@ -166,7 +166,8 @@ def getEssentialType(expr):
return typeToken.str return typeToken.str
typeToken = typeToken.next typeToken = typeToken.next
elif expr.astOperand1 and expr.astOperand2 and expr.str in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":"): elif expr.astOperand1 and expr.astOperand2 and expr.str in (
'+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":"):
if expr.astOperand1.valueType and expr.astOperand1.valueType.pointer > 0: if expr.astOperand1.valueType and expr.astOperand1.valueType.pointer > 0:
return None return None
if expr.astOperand2.valueType and expr.astOperand2.valueType.pointer > 0: if expr.astOperand2.valueType and expr.astOperand2.valueType.pointer > 0:
@ -600,7 +601,6 @@ class Define:
) )
def getAddonRules(): def getAddonRules():
"""Returns dict of MISRA rules handled by this addon.""" """Returns dict of MISRA rules handled by this addon."""
addon_rules = [] addon_rules = []
@ -964,56 +964,55 @@ class MisraChecker:
macro = {} macro = {}
compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)') compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)')
compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]') compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]')
short_names = {}
macro_w_arg = []
for dir in data.directives: for dir in data.directives:
res1 = compile_name.match(dir.str) res1 = compile_name.match(dir.str)
if res1: if res1:
if dir not in macro: if dir not in macro:
macro.setdefault(dir, {})["name"] = [] macro.setdefault(dir, {})["name"] = []
macro.setdefault(dir, {})["params"] = [] macro.setdefault(dir, {})["params"] = []
macro[dir]["name"] = res1.group(1) full_name = res1.group(1)
macro[dir]["name"] = full_name
short_name = full_name[:num_sign_chars]
if short_name in short_names:
_dir = short_names[short_name]
if full_name != macro[_dir]["name"]:
self.reportError(dir, 5, 4)
else:
short_names[short_name] = dir
res2 = compile_param.match(dir.str) res2 = compile_param.match(dir.str)
if res2: if res2:
res_gp2 = res2.group(2).split(",") res_gp2 = res2.group(2).split(",")
res_gp2 = [macroname.replace(" ", "") for macroname in res_gp2] res_gp2 = [macroname.replace(" ", "") for macroname in res_gp2]
macro[dir]["params"].extend(res_gp2) macro[dir]["params"].extend(res_gp2)
for mvar in macro: macro_w_arg.append(dir)
if len(macro[mvar]["params"]) > 0: for mvar in macro_w_arg:
for i, macroparam1 in enumerate(macro[mvar]["params"]): for i, macroparam1 in enumerate(macro[mvar]["params"]):
for j, macroparam2 in enumerate(macro[mvar]["params"]): for j, macroparam2 in enumerate(macro[mvar]["params"]):
if j > i and macroparam1[:num_sign_chars] == macroparam2[:num_sign_chars]: if j > i and macroparam1[:num_sign_chars] == macroparam2[:num_sign_chars]:
self.reportError(mvar, 5, 4) self.reportError(mvar, 5, 4)
param = macroparam1
for x, m_var1 in enumerate(macro): if param[:num_sign_chars] in short_names:
for y, m_var2 in enumerate(macro): m_var1 = short_names[param[:num_sign_chars]]
if x < y and macro[m_var1]["name"] != macro[m_var2]["name"] and \ if m_var1.linenr > mvar.linenr:
macro[m_var1]["name"][:num_sign_chars] == macro[m_var2]["name"][:num_sign_chars]:
if m_var1.linenr > m_var2.linenr:
self.reportError(m_var1, 5, 4) self.reportError(m_var1, 5, 4)
else: else:
self.reportError(m_var2, 5, 4) self.reportError(mvar, 5, 4)
for param in macro[m_var2]["params"]:
if macro[m_var1]["name"][:num_sign_chars] == param[:num_sign_chars]:
if m_var1.linenr > m_var2.linenr:
self.reportError(m_var1, 5, 4)
else:
self.reportError(m_var2, 5, 4)
def misra_5_5(self, data): def misra_5_5(self, data):
num_sign_chars = self.get_num_significant_naming_chars(data) num_sign_chars = self.get_num_significant_naming_chars(data)
macroNames = [] macroNames = {}
compiled = re.compile(r'#define ([A-Za-z0-9_]+)') compiled = re.compile(r'#define ([A-Za-z0-9_]+)')
for dir in data.directives: for dir in data.directives:
res = compiled.match(dir.str) res = compiled.match(dir.str)
if res: if res:
macroNames.append(res.group(1)) macroNames[res.group(1)[:num_sign_chars]] = dir
for var in data.variables: for var in data.variables:
for macro in macroNames: if var.nameToken and var.nameToken.str[:num_sign_chars] in macroNames:
if var.nameToken is not None:
if var.nameToken.str[:num_sign_chars] == macro[:num_sign_chars]:
self.reportError(var.nameToken, 5, 5) self.reportError(var.nameToken, 5, 5)
for scope in data.scopes: for scope in data.scopes:
for macro in macroNames: if scope.className and scope.className[:num_sign_chars] in macroNames:
if scope.className and scope.className[:num_sign_chars] == macro[:num_sign_chars]:
self.reportError(scope.bodyStart, 5, 5) self.reportError(scope.bodyStart, 5, 5)
def misra_7_1(self, rawTokens): def misra_7_1(self, rawTokens):
@ -1216,7 +1215,7 @@ class MisraChecker:
continue continue
if vt2.pointer > 0 and vt1.pointer == 0 and (vt1.isIntegral() or vt1.isEnum()) and vt2.type != 'void': if vt2.pointer > 0 and vt1.pointer == 0 and (vt1.isIntegral() or vt1.isEnum()) and vt2.type != 'void':
self.reportError(token, 11, 4) self.reportError(token, 11, 4)
elif vt1.pointer > 0 and vt2.pointer == 0 and (vt2.isIntegral() or vt2.isEnum())and vt1.type != 'void': elif vt1.pointer > 0 and vt2.pointer == 0 and (vt2.isIntegral() or vt2.isEnum()) and vt1.type != 'void':
self.reportError(token, 11, 4) self.reportError(token, 11, 4)
def misra_11_5(self, data): def misra_11_5(self, data):
@ -1230,7 +1229,8 @@ class MisraChecker:
if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void':
self.reportError(token, 11, 5) self.reportError(token, 11, 5)
continue continue
if token.astOperand1.astOperand1 and token.astOperand1.astOperand1.str in ('malloc', 'calloc', 'realloc', 'free'): if token.astOperand1.astOperand1 and token.astOperand1.astOperand1.str in (
'malloc', 'calloc', 'realloc', 'free'):
continue continue
vt1 = token.valueType vt1 = token.valueType
vt2 = token.astOperand1.valueType vt2 = token.astOperand1.valueType
@ -1462,7 +1462,6 @@ class MisraChecker:
if state == STATE_CHECK and tok.str == ',': if state == STATE_CHECK and tok.str == ',':
self.reportError(tok, 12, 3) self.reportError(tok, 12, 3)
def misra_12_4(self, data): def misra_12_4(self, data):
if typeBits['INT'] == 16: if typeBits['INT'] == 16:
max_uint = 0xffff max_uint = 0xffff
@ -1583,7 +1582,6 @@ class MisraChecker:
self.reportError(tn, 14, 2) self.reportError(tn, 14, 2)
tn = tn.next tn = tn.next
def misra_14_4(self, data): def misra_14_4(self, data):
for token in data.tokenlist: for token in data.tokenlist:
if token.str != '(': if token.str != '(':
@ -1805,7 +1803,8 @@ class MisraChecker:
def misra_17_1(self, data): def misra_17_1(self, data):
for token in data.tokenlist: for token in data.tokenlist:
if isFunctionCall(token) and token.astOperand1.str in ('va_list', 'va_arg', 'va_start', 'va_end', 'va_copy'): if isFunctionCall(token) and token.astOperand1.str in (
'va_list', 'va_arg', 'va_start', 'va_end', 'va_copy'):
self.reportError(token, 17, 1) self.reportError(token, 17, 1)
elif token.str == 'va_list': elif token.str == 'va_list':
self.reportError(token, 17, 1) self.reportError(token, 17, 1)
@ -1953,15 +1952,20 @@ class MisraChecker:
self.reportError(token, 19, 2) self.reportError(token, 19, 2)
def misra_20_1(self, data): def misra_20_1(self, data):
token_in_file = {}
for token in data.tokenlist:
if token.file not in token_in_file:
token_in_file[token.file] = int(token.linenr)
else:
token_in_file[token.file] = min(token_in_file[token.file], int(token.linenr))
for directive in data.directives: for directive in data.directives:
if not directive.str.startswith('#include'): if not directive.str.startswith('#include'):
continue continue
for token in data.tokenlist: if directive.file not in token_in_file:
if token.file != directive.file:
continue continue
if int(token.linenr) < int(directive.linenr): if token_in_file[directive.file] < int(directive.linenr):
self.reportError(directive, 20, 1) self.reportError(directive, 20, 1)
break
def misra_20_2(self, data): def misra_20_2(self, data):
for directive in data.directives: for directive in data.directives:
@ -2052,7 +2056,8 @@ class MisraChecker:
# the size increases when there are inner #if directives. # the size increases when there are inner #if directives.
ifStack = [] ifStack = []
for directive in data.directives: for directive in data.directives:
if directive.str.startswith('#if ') or directive.str.startswith('#ifdef ') or directive.str.startswith('#ifndef '): if directive.str.startswith('#if ') or directive.str.startswith('#ifdef ') or directive.str.startswith(
'#ifndef '):
ifStack.append(directive) ifStack.append(directive)
elif directive.str == '#else' or directive.str.startswith('#elif '): elif directive.str == '#else' or directive.str.startswith('#elif '):
if len(ifStack) == 0: if len(ifStack) == 0:
@ -2191,9 +2196,9 @@ class MisraChecker:
return self.violations.keys() return self.violations.keys()
def addSuppressedRule(self, ruleNum, def addSuppressedRule(self, ruleNum,
fileName = None, fileName=None,
lineNumber = None, lineNumber=None,
symbolName = None): symbolName=None):
""" """
Add a suppression to the suppressions data structure Add a suppression to the suppressions data structure
@ -2384,7 +2389,8 @@ class MisraChecker:
else: else:
item_str = str(item[0]) item_str = str(item[0])
outlist.append("%s: %s: %s (%d locations suppressed)" % (float(ruleNum)/100, fname, item_str, self.suppressionStats.get(ruleNum, 0))) outlist.append("%s: %s: %s (%d locations suppressed)" % (
float(ruleNum) / 100, fname, item_str, self.suppressionStats.get(ruleNum, 0)))
for line in sorted(outlist, reverse=True): for line in sorted(outlist, reverse=True):
print(" %s" % line) print(" %s" % line)
@ -2408,7 +2414,7 @@ class MisraChecker:
if res: if res:
num1 = int(res.group(1)) num1 = int(res.group(1))
num2 = int(res.group(2)) num2 = int(res.group(2))
ruleNum = (num1*100)+num2 ruleNum = (num1 * 100) + num2
self.addSuppressedRule(ruleNum) self.addSuppressedRule(ruleNum)
@ -2730,7 +2736,9 @@ 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("--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("--no-summary", help="Hide summary of violations", action="store_true") parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true")
parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true")
@ -2808,7 +2816,8 @@ def main():
if settings.show_summary: if settings.show_summary:
print("\nMISRA rules violations found:\n\t%s\n" % ( 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()]))) "\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():
@ -2816,7 +2825,7 @@ def main():
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(r'[\.-]([0-9]*)', key) ] misra_sort = lambda key: [convert(c) for c in re.split(r'[\.-]([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):
res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id) res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id)
if res is None: if res is None: