update scripts
This commit is contained in:
parent
6ba6567ad8
commit
c5b3fafe84
|
@ -13,6 +13,7 @@ import argparse
|
||||||
import cppcheckdata
|
import cppcheckdata
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
VERIFY = ('-verify' in sys.argv)
|
VERIFY = ('-verify' in sys.argv)
|
||||||
VERIFY_EXPECTED = []
|
VERIFY_EXPECTED = []
|
||||||
|
@ -195,7 +196,9 @@ def int31(data, platform):
|
||||||
if to_value_type is None or not from_values:
|
if to_value_type is None or not from_values:
|
||||||
continue
|
continue
|
||||||
bits = None
|
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
|
bits = platform.char_bit
|
||||||
elif to_value_type.type == 'short':
|
elif to_value_type.type == 'short':
|
||||||
bits = platform.short_bit
|
bits = platform.short_bit
|
||||||
|
@ -399,6 +402,8 @@ if __name__ == '__main__':
|
||||||
parser = get_args_parser()
|
parser = get_args_parser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
path_premium_addon = cppcheckdata.get_path_premium_addon()
|
||||||
|
|
||||||
if args.verify:
|
if args.verify:
|
||||||
VERIFY = True
|
VERIFY = True
|
||||||
|
|
||||||
|
@ -411,6 +416,14 @@ if __name__ == '__main__':
|
||||||
if not args.quiet:
|
if not args.quiet:
|
||||||
print('Checking %s...' % dumpfile)
|
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)
|
data = cppcheckdata.CppcheckData(dumpfile)
|
||||||
|
|
||||||
if VERIFY:
|
if VERIFY:
|
||||||
|
|
|
@ -8,6 +8,8 @@ License: No restrictions, use this as you need.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
@ -1364,3 +1366,14 @@ def reportSummary(dumpfile, summary_type, summary_data):
|
||||||
with open(ctu_info_file, 'at') as f:
|
with open(ctu_info_file, 'at') as f:
|
||||||
msg = {'summary': summary_type, 'data': summary_data}
|
msg = {'summary': summary_type, 'data': summary_data}
|
||||||
f.write(json.dumps(msg) + '\n')
|
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
|
||||||
|
|
135
addons/misra.py
135
addons/misra.py
|
@ -25,6 +25,7 @@ import argparse
|
||||||
import codecs
|
import codecs
|
||||||
import string
|
import string
|
||||||
import copy
|
import copy
|
||||||
|
import subprocess
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from itertools import izip as zip
|
from itertools import izip as zip
|
||||||
|
@ -422,6 +423,28 @@ def get_type_conversion_to_from(token):
|
||||||
|
|
||||||
return None
|
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):
|
def getEssentialTypeCategory(expr):
|
||||||
if not expr:
|
if not expr:
|
||||||
return None
|
return None
|
||||||
|
@ -1009,16 +1032,14 @@ def isNoReturnScope(tok):
|
||||||
|
|
||||||
|
|
||||||
# Return the token which the value is assigned to
|
# Return the token which the value is assigned to
|
||||||
def getAssignedVariableToken(valueToken):
|
def getAssignedVariableToken(vartok):
|
||||||
if not valueToken:
|
if not vartok:
|
||||||
return None
|
return None
|
||||||
if not valueToken.astParent:
|
parent = vartok.astParent
|
||||||
return None
|
while parent and parent.isArithmeticalOp:
|
||||||
operator = valueToken.astParent
|
parent = parent.astParent
|
||||||
if operator.isAssignmentOp:
|
if parent and parent.isAssignmentOp:
|
||||||
return operator.astOperand1
|
return parent.astOperand1
|
||||||
if operator.isArithmeticalOp:
|
|
||||||
return getAssignedVariableToken(operator)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# If the value is used as a return value, return the function definition
|
# 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_identifiers = False
|
||||||
self._ctu_summary_usage = False
|
self._ctu_summary_usage = False
|
||||||
|
|
||||||
|
self.path_premium_addon = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
attrs = ["settings", "verify_expected", "verify_actual", "violations",
|
attrs = ["settings", "verify_expected", "verify_actual", "violations",
|
||||||
"ruleTexts", "suppressedRules", "filePrefix",
|
"ruleTexts", "suppressedRules", "filePrefix",
|
||||||
|
@ -1372,7 +1395,9 @@ class MisraChecker:
|
||||||
local_identifiers.append(identifier(var.nameToken))
|
local_identifiers.append(identifier(var.nameToken))
|
||||||
elif var.isStatic:
|
elif var.isStatic:
|
||||||
names.append(var.nameToken.str)
|
names.append(var.nameToken.str)
|
||||||
internal_identifiers.append(identifier(var.nameToken))
|
i = identifier(var.nameToken)
|
||||||
|
i['inlinefunc'] = False
|
||||||
|
internal_identifiers.append(i)
|
||||||
else:
|
else:
|
||||||
names.append(var.nameToken.str)
|
names.append(var.nameToken.str)
|
||||||
i = identifier(var.nameToken)
|
i = identifier(var.nameToken)
|
||||||
|
@ -1383,9 +1408,14 @@ class MisraChecker:
|
||||||
if func.tokenDef is None:
|
if func.tokenDef is None:
|
||||||
continue
|
continue
|
||||||
if func.isStatic:
|
if func.isStatic:
|
||||||
internal_identifiers.append(identifier(func.tokenDef))
|
|
||||||
else:
|
|
||||||
i = identifier(func.tokenDef)
|
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
|
i['decl'] = func.token is None
|
||||||
external_identifiers.append(i)
|
external_identifiers.append(i)
|
||||||
|
|
||||||
|
@ -1404,12 +1434,12 @@ class MisraChecker:
|
||||||
continue
|
continue
|
||||||
if token.function and token.scope.isExecutable:
|
if token.function and token.scope.isExecutable:
|
||||||
if (not token.function.isStatic) and (token.str not in names):
|
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:
|
elif token.variable:
|
||||||
if token == token.variable.nameToken:
|
if token == token.variable.nameToken:
|
||||||
continue
|
continue
|
||||||
if token.variable.access == 'Global' and (not token.variable.isStatic) and (token.str not in names):
|
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:
|
if len(names) > 0:
|
||||||
cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names)
|
cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names)
|
||||||
|
@ -1759,41 +1789,9 @@ class MisraChecker:
|
||||||
self.reportError(tok, 7, 1)
|
self.reportError(tok, 7, 1)
|
||||||
|
|
||||||
def misra_7_2(self, data):
|
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:
|
for token in data.tokenlist:
|
||||||
# Check normal variable assignment
|
if token.isInt and ('U' not in token.str.upper()) and token.valueType and token.valueType.sign == 'unsigned':
|
||||||
if token.valueType and token.isNumber:
|
self.reportError(token, 7, 2)
|
||||||
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)
|
|
||||||
|
|
||||||
def misra_7_3(self, rawTokens):
|
def misra_7_3(self, rawTokens):
|
||||||
compiled = re.compile(r'^[0-9.]+[Uu]*l+[Uu]*$')
|
compiled = re.compile(r'^[0-9.]+[Uu]*l+[Uu]*$')
|
||||||
|
@ -2277,8 +2275,7 @@ class MisraChecker:
|
||||||
for token in data.tokenlist:
|
for token in data.tokenlist:
|
||||||
if token.str != '=' or not token.astOperand1 or not token.astOperand2:
|
if token.str != '=' or not token.astOperand1 or not token.astOperand2:
|
||||||
continue
|
continue
|
||||||
if (token.astOperand2.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~') and
|
if not is_composite_expr(token.astOperand2):
|
||||||
not isCast(token.astOperand2)):
|
|
||||||
continue
|
continue
|
||||||
vt1 = token.astOperand1.valueType
|
vt1 = token.astOperand1.valueType
|
||||||
vt2 = token.astOperand2.valueType
|
vt2 = token.astOperand2.valueType
|
||||||
|
@ -4058,7 +4055,10 @@ class MisraChecker:
|
||||||
misra_severity = self.ruleTexts[ruleNum].misra_severity
|
misra_severity = self.ruleTexts[ruleNum].misra_severity
|
||||||
cppcheck_severity = self.ruleTexts[ruleNum].cppcheck_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)'
|
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:
|
else:
|
||||||
errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum)
|
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(2209, self.misra_22_9, cfg)
|
||||||
self.executeCheck(2210, self.misra_22_10, 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):
|
def analyse_ctu_info(self, ctu_info_files):
|
||||||
all_typedef_info = []
|
all_typedef_info = []
|
||||||
all_tagname_info = []
|
all_tagname_info = []
|
||||||
|
@ -4400,7 +4404,7 @@ class MisraChecker:
|
||||||
all_external_identifiers_def = {}
|
all_external_identifiers_def = {}
|
||||||
all_internal_identifiers = {}
|
all_internal_identifiers = {}
|
||||||
all_local_identifiers = {}
|
all_local_identifiers = {}
|
||||||
all_usage_count = {}
|
all_usage_files = {}
|
||||||
|
|
||||||
from cppcheckdata import Location
|
from cppcheckdata import Location
|
||||||
|
|
||||||
|
@ -4478,8 +4482,9 @@ class MisraChecker:
|
||||||
if summary_type == 'MisraInternalIdentifiers':
|
if summary_type == 'MisraInternalIdentifiers':
|
||||||
for s in summary_data:
|
for s in summary_data:
|
||||||
if s['name'] in all_internal_identifiers:
|
if s['name'] in all_internal_identifiers:
|
||||||
self.reportError(Location(s), 5, 9)
|
if not s['inlinefunc'] or s['file'] != all_internal_identifiers[s['name']]['file']:
|
||||||
self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9)
|
self.reportError(Location(s), 5, 9)
|
||||||
|
self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9)
|
||||||
all_internal_identifiers[s['name']] = s
|
all_internal_identifiers[s['name']] = s
|
||||||
|
|
||||||
if summary_type == 'MisraLocalIdentifiers':
|
if summary_type == 'MisraLocalIdentifiers':
|
||||||
|
@ -4488,10 +4493,10 @@ class MisraChecker:
|
||||||
|
|
||||||
if summary_type == 'MisraUsage':
|
if summary_type == 'MisraUsage':
|
||||||
for s in summary_data:
|
for s in summary_data:
|
||||||
if s in all_usage_count:
|
if s['name'] in all_usage_files:
|
||||||
all_usage_count[s] += 1
|
all_usage_files[s['name']].append(s['file'])
|
||||||
else:
|
else:
|
||||||
all_usage_count[s] = 1
|
all_usage_files[s['name']] = [s['file']]
|
||||||
|
|
||||||
for ti in all_typedef_info:
|
for ti in all_typedef_info:
|
||||||
if not ti['used']:
|
if not ti['used']:
|
||||||
|
@ -4518,9 +4523,12 @@ class MisraChecker:
|
||||||
self.reportError(Location(local_identifier), 5, 8)
|
self.reportError(Location(local_identifier), 5, 8)
|
||||||
self.reportError(Location(external_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))
|
#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
|
continue
|
||||||
if name in all_external_identifiers:
|
if name in all_external_identifiers:
|
||||||
self.reportError(Location(all_external_identifiers[name]), 8, 7)
|
self.reportError(Location(all_external_identifiers[name]), 8, 7)
|
||||||
|
@ -4584,6 +4592,8 @@ def main():
|
||||||
settings = MisraSettings(args)
|
settings = MisraSettings(args)
|
||||||
checker = MisraChecker(settings)
|
checker = MisraChecker(settings)
|
||||||
|
|
||||||
|
checker.path_premium_addon = cppcheckdata.get_path_premium_addon()
|
||||||
|
|
||||||
if args.generate_table:
|
if args.generate_table:
|
||||||
generateTable()
|
generateTable()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@ -4645,6 +4655,13 @@ def main():
|
||||||
|
|
||||||
checker.analyse_ctu_info(ctu_info_files)
|
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:
|
if settings.verify:
|
||||||
sys.exit(exitCode)
|
sys.exit(exitCode)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue