update scripts
This commit is contained in:
parent
6ba6567ad8
commit
c5b3fafe84
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
129
addons/misra.py
129
addons/misra.py
|
@ -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,6 +4055,9 @@ class MisraChecker:
|
|||
misra_severity = self.ruleTexts[ruleNum].misra_severity
|
||||
cppcheck_severity = self.ruleTexts[ruleNum].cppcheck_severity
|
||||
elif len(self.ruleTexts) == 0:
|
||||
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,6 +4482,7 @@ class MisraChecker:
|
|||
if summary_type == 'MisraInternalIdentifiers':
|
||||
for s in summary_data:
|
||||
if s['name'] in all_internal_identifiers:
|
||||
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
|
||||
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue