update scripts

This commit is contained in:
Daniel Marjamäki 2022-03-10 20:13:11 +01:00
parent 6ba6567ad8
commit c5b3fafe84
3 changed files with 103 additions and 60 deletions

View File

@ -13,6 +13,7 @@ import argparse
import cppcheckdata
import sys
import re
import subprocess
VERIFY = ('-verify' in sys.argv)
VERIFY_EXPECTED = []
@ -195,7 +196,9 @@ def int31(data, platform):
if to_value_type is None or not from_values:
continue
bits = None
if to_value_type.type == 'char':
if token.valueType.pointer > 0:
bits = platform.pointer_bit
elif to_value_type.type == 'char':
bits = platform.char_bit
elif to_value_type.type == 'short':
bits = platform.short_bit
@ -399,6 +402,8 @@ if __name__ == '__main__':
parser = get_args_parser()
args = parser.parse_args()
path_premium_addon = cppcheckdata.get_path_premium_addon()
if args.verify:
VERIFY = True
@ -411,6 +416,14 @@ if __name__ == '__main__':
if not args.quiet:
print('Checking %s...' % dumpfile)
if path_premium_addon:
premium_command = [path_premium_addon, '--cert', dumpfile]
if args.cli:
premium_command.append('--cli')
for line in subprocess.check_output(premium_command).decode('ascii').split('\n'):
if line.find('cert-') > 0:
print(line.strip())
data = cppcheckdata.CppcheckData(dumpfile)
if VERIFY:

View File

@ -8,6 +8,8 @@ License: No restrictions, use this as you need.
import argparse
import json
import os
import pathlib
import sys
from xml.etree import ElementTree
@ -1364,3 +1366,14 @@ def reportSummary(dumpfile, summary_type, summary_data):
with open(ctu_info_file, 'at') as f:
msg = {'summary': summary_type, 'data': summary_data}
f.write(json.dumps(msg) + '\n')
def get_path_premium_addon():
p = pathlib.Path(sys.argv[0]).parent.parent
for ext in ('.exe', ''):
p1 = os.path.join(p, 'premiumaddon' + ext)
p2 = os.path.join(p, 'cppcheck' + ext)
if os.path.isfile(p1) and os.path.isfile(p2):
return p1
return None

View File

@ -25,6 +25,7 @@ import argparse
import codecs
import string
import copy
import subprocess
try:
from itertools import izip as zip
@ -422,6 +423,28 @@ def get_type_conversion_to_from(token):
return None
def is_composite_expr(expr, composite_operator=False):
"""MISRA C 2012, section 8.10.3"""
if expr is None:
return False
if not composite_operator:
if (expr.str in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~')):
return is_composite_expr(expr.astOperand1,True) or is_composite_expr(expr.astOperand2, True)
if expr.str == '?' and simpleMatch(expr.astOperand2, ':'):
colon = expr.astOperand2
return is_composite_expr(colon.astOperand1,True) or is_composite_expr(colon.astOperand2, True)
return False
# non constant expression?
if expr.isNumber:
return False
if expr.astOperand1 or expr.astOperand2:
return is_composite_expr(expr.astOperand1,True) or is_composite_expr(expr.astOperand2, True)
return True
def getEssentialTypeCategory(expr):
if not expr:
return None
@ -1009,16 +1032,14 @@ def isNoReturnScope(tok):
# Return the token which the value is assigned to
def getAssignedVariableToken(valueToken):
if not valueToken:
def getAssignedVariableToken(vartok):
if not vartok:
return None
if not valueToken.astParent:
return None
operator = valueToken.astParent
if operator.isAssignmentOp:
return operator.astOperand1
if operator.isArithmeticalOp:
return getAssignedVariableToken(operator)
parent = vartok.astParent
while parent and parent.isArithmeticalOp:
parent = parent.astParent
if parent and parent.isAssignmentOp:
return parent.astOperand1
return None
# If the value is used as a return value, return the function definition
@ -1297,6 +1318,8 @@ class MisraChecker:
self._ctu_summary_identifiers = False
self._ctu_summary_usage = False
self.path_premium_addon = None
def __repr__(self):
attrs = ["settings", "verify_expected", "verify_actual", "violations",
"ruleTexts", "suppressedRules", "filePrefix",
@ -1372,7 +1395,9 @@ class MisraChecker:
local_identifiers.append(identifier(var.nameToken))
elif var.isStatic:
names.append(var.nameToken.str)
internal_identifiers.append(identifier(var.nameToken))
i = identifier(var.nameToken)
i['inlinefunc'] = False
internal_identifiers.append(i)
else:
names.append(var.nameToken.str)
i = identifier(var.nameToken)
@ -1383,9 +1408,14 @@ class MisraChecker:
if func.tokenDef is None:
continue
if func.isStatic:
internal_identifiers.append(identifier(func.tokenDef))
else:
i = identifier(func.tokenDef)
i['inlinefunc'] = func.isInlineKeyword
internal_identifiers.append(i)
else:
if func.token is None:
i = identifier(func.tokenDef)
else:
i = identifier(func.token)
i['decl'] = func.token is None
external_identifiers.append(i)
@ -1404,12 +1434,12 @@ class MisraChecker:
continue
if token.function and token.scope.isExecutable:
if (not token.function.isStatic) and (token.str not in names):
names.append(token.str)
names.append({'name': token.str, 'file': token.file})
elif token.variable:
if token == token.variable.nameToken:
continue
if token.variable.access == 'Global' and (not token.variable.isStatic) and (token.str not in names):
names.append(token.str)
names.append({'name': token.str, 'file': token.file})
if len(names) > 0:
cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names)
@ -1759,41 +1789,9 @@ class MisraChecker:
self.reportError(tok, 7, 1)
def misra_7_2(self, data):
# Large constant numbers that are assigned to a variable should have an
# u/U suffix if the variable type is unsigned.
def reportErrorIfMissingSuffix(variable, value):
if 'U' in value.str.upper():
return
if value and value.isNumber:
if variable and variable.valueType and variable.valueType.sign == 'unsigned':
if variable.valueType.type in ['char', 'short', 'int', 'long', 'long long']:
limit = 1 << (bitsOfEssentialType(variable.valueType.type) -1)
v = value.getKnownIntValue()
if v is not None and v >= limit:
self.reportError(value, 7, 2)
for token in data.tokenlist:
# Check normal variable assignment
if token.valueType and token.isNumber:
variable = getAssignedVariableToken(token)
reportErrorIfMissingSuffix(variable, token)
# Check use as function parameter
if isFunctionCall(token) and token.astOperand1 and token.astOperand1.function:
functionDeclaration = token.astOperand1.function
if functionDeclaration.tokenDef:
if functionDeclaration.tokenDef is token.astOperand1:
# Token is not a function call, but it is the definition of the function
continue
parametersUsed = getArguments(token)
for i in range(len(parametersUsed)):
usedParameter = parametersUsed[i]
if usedParameter.isNumber:
parameterDefinition = functionDeclaration.argument.get(i+1)
if parameterDefinition and parameterDefinition.nameToken:
reportErrorIfMissingSuffix(parameterDefinition.nameToken, usedParameter)
if token.isInt and ('U' not in token.str.upper()) and token.valueType and token.valueType.sign == 'unsigned':
self.reportError(token, 7, 2)
def misra_7_3(self, rawTokens):
compiled = re.compile(r'^[0-9.]+[Uu]*l+[Uu]*$')
@ -2277,8 +2275,7 @@ class MisraChecker:
for token in data.tokenlist:
if token.str != '=' or not token.astOperand1 or not token.astOperand2:
continue
if (token.astOperand2.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~') and
not isCast(token.astOperand2)):
if not is_composite_expr(token.astOperand2):
continue
vt1 = token.astOperand1.valueType
vt2 = token.astOperand2.valueType
@ -4058,7 +4055,10 @@ class MisraChecker:
misra_severity = 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)'
if self.path_premium_addon:
errmsg = subprocess.check_output([self.path_premium_addon, '--get-rule-text=' + errorId]).strip().decode('ascii')
else:
errmsg = 'misra violation (use --rule-texts=<file> to get proper output)'
else:
errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum)
@ -4392,6 +4392,10 @@ class MisraChecker:
self.executeCheck(2209, self.misra_22_9, cfg)
self.executeCheck(2210, self.misra_22_10, cfg)
# Premium MISRA checking, deep analysis
if cfgNumber == 0 and self.path_premium_addon:
subprocess.call([self.path_premium_addon, '--misra', dumpfile])
def analyse_ctu_info(self, ctu_info_files):
all_typedef_info = []
all_tagname_info = []
@ -4400,7 +4404,7 @@ class MisraChecker:
all_external_identifiers_def = {}
all_internal_identifiers = {}
all_local_identifiers = {}
all_usage_count = {}
all_usage_files = {}
from cppcheckdata import Location
@ -4478,8 +4482,9 @@ class MisraChecker:
if summary_type == 'MisraInternalIdentifiers':
for s in summary_data:
if s['name'] in all_internal_identifiers:
self.reportError(Location(s), 5, 9)
self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9)
if not s['inlinefunc'] or s['file'] != all_internal_identifiers[s['name']]['file']:
self.reportError(Location(s), 5, 9)
self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9)
all_internal_identifiers[s['name']] = s
if summary_type == 'MisraLocalIdentifiers':
@ -4488,10 +4493,10 @@ class MisraChecker:
if summary_type == 'MisraUsage':
for s in summary_data:
if s in all_usage_count:
all_usage_count[s] += 1
if s['name'] in all_usage_files:
all_usage_files[s['name']].append(s['file'])
else:
all_usage_count[s] = 1
all_usage_files[s['name']] = [s['file']]
for ti in all_typedef_info:
if not ti['used']:
@ -4518,9 +4523,12 @@ class MisraChecker:
self.reportError(Location(local_identifier), 5, 8)
self.reportError(Location(external_identifier), 5, 8)
for name, count in all_usage_count.items():
for name, files in all_usage_files.items():
#print('%s:%i' % (name, count))
if count != 1:
count = len(files)
if count != 1 or name not in all_external_identifiers_def:
continue
if files[0] != Location(all_external_identifiers_def[name]).file:
continue
if name in all_external_identifiers:
self.reportError(Location(all_external_identifiers[name]), 8, 7)
@ -4584,6 +4592,8 @@ def main():
settings = MisraSettings(args)
checker = MisraChecker(settings)
checker.path_premium_addon = cppcheckdata.get_path_premium_addon()
if args.generate_table:
generateTable()
sys.exit(0)
@ -4645,6 +4655,13 @@ def main():
checker.analyse_ctu_info(ctu_info_files)
if args.file_list and checker.path_premium_addon:
premium_command = [checker.path_premium_addon, '--misra', '--file-list', args.file_list]
if args.cli:
premium_command.append('--cli')
for line in subprocess.check_output(premium_command).decode('ascii').split('\n'):
print(line.strip())
if settings.verify:
sys.exit(exitCode)