166 lines
6.0 KiB
Python
166 lines
6.0 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Misc: Uncategorized checks that might be moved to some better addon later
|
|
#
|
|
# Example usage of this addon (scan a sourcefile main.cpp)
|
|
# cppcheck --dump main.cpp
|
|
# python misc.py main.cpp.dump
|
|
|
|
import cppcheckdata
|
|
import sys
|
|
import re
|
|
|
|
DEBUG = ('-debug' in sys.argv)
|
|
VERIFY = ('-verify' in sys.argv)
|
|
VERIFY_EXPECTED = []
|
|
VERIFY_ACTUAL = []
|
|
|
|
def reportError(token, severity, msg, id):
|
|
if id == 'debug' and DEBUG == False:
|
|
return
|
|
if VERIFY:
|
|
VERIFY_ACTUAL.append(str(token.linenr) + ':' + id)
|
|
else:
|
|
sys.stderr.write(
|
|
'[' + token.file + ':' + str(token.linenr) + '] (' + severity + '): ' + msg + ' [' + id + ']\n')
|
|
|
|
def simpleMatch(token, pattern):
|
|
for p in pattern.split(' '):
|
|
if not token or token.str != p:
|
|
return False
|
|
token = token.next
|
|
return True
|
|
|
|
# Get function arguments
|
|
def getArgumentsRecursive(tok, arguments):
|
|
if tok is None:
|
|
return
|
|
if tok.str == ',':
|
|
getArgumentsRecursive(tok.astOperand1, arguments)
|
|
getArgumentsRecursive(tok.astOperand2, arguments)
|
|
else:
|
|
arguments.append(tok);
|
|
|
|
def getArguments(ftok):
|
|
arguments = []
|
|
getArgumentsRecursive(ftok.astOperand2, arguments)
|
|
return arguments
|
|
|
|
def isStringLiteral(tokenString):
|
|
return tokenString.startswith('"')
|
|
|
|
# check data
|
|
def stringConcatInArrayInit(configurations, rawTokens):
|
|
# Get all string macros
|
|
stringMacros = []
|
|
for cfg in configurations:
|
|
for directive in cfg.directives:
|
|
res = re.match(r'#define[ ]+([A-Za-z0-9_]+)[ ]+".*', directive.str)
|
|
if res:
|
|
macroName = res.group(1)
|
|
if macroName not in stringMacros:
|
|
stringMacros.append(macroName)
|
|
|
|
# Check code
|
|
arrayInit = False
|
|
for i in range(len(rawTokens)):
|
|
if i < 2:
|
|
continue
|
|
tok1 = rawTokens[i-2].str
|
|
tok2 = rawTokens[i-1].str
|
|
tok3 = rawTokens[i-0].str
|
|
if tok3 == '}':
|
|
arrayInit = False
|
|
elif tok1 == ']' and tok2 == '=' and tok3 == '{':
|
|
arrayInit = True
|
|
elif arrayInit and (tok1 in [',', '{']):
|
|
isString2 = (isStringLiteral(tok2) or (tok2 in stringMacros))
|
|
isString3 = (isStringLiteral(tok3) or (tok3 in stringMacros))
|
|
if isString2 and isString3:
|
|
reportError(rawTokens[i], 'style', 'String concatenation in array initialization, missing comma?', 'stringConcatInArrayInit')
|
|
|
|
|
|
def implicitlyVirtual(data):
|
|
for cfg in data.configurations:
|
|
for function in cfg.functions:
|
|
if function.isImplicitlyVirtual is None:
|
|
continue
|
|
if not function.isImplicitlyVirtual:
|
|
continue
|
|
reportError(function.tokenDef, 'style', 'Function \'' + function.name + '\' overrides base class function but is not marked with \'virtual\' keyword.', 'implicitlyVirtual')
|
|
|
|
def ellipsisStructArg(data):
|
|
for cfg in data.configurations:
|
|
for tok in cfg.tokenlist:
|
|
if tok.str != '(':
|
|
continue
|
|
if tok.astOperand1 is None or tok.astOperand2 is None:
|
|
continue
|
|
if tok.astOperand2.str != ',':
|
|
continue
|
|
if tok.scope.type in ['Global', 'Class']:
|
|
continue
|
|
if tok.astOperand1.function is None:
|
|
continue
|
|
for argnr, argvar in tok.astOperand1.function.argument.items():
|
|
if argnr < 1:
|
|
continue
|
|
if not simpleMatch(argvar.typeStartToken, '. . .'):
|
|
continue
|
|
callArgs = getArguments(tok)
|
|
for i in range(argnr-1, len(callArgs)):
|
|
valueType = callArgs[i].valueType
|
|
if valueType is None:
|
|
argStart = callArgs[i].previous
|
|
while argStart.str != ',':
|
|
if argStart.str == ')':
|
|
argStart = argStart.link
|
|
argStart = argStart.previous
|
|
argEnd = callArgs[i]
|
|
while argEnd.str != ',' and argEnd.str != ')':
|
|
if argEnd.str == '(':
|
|
argEnd = argEnd.link
|
|
argEnd = argEnd.next
|
|
expression = ''
|
|
argStart = argStart.next
|
|
while argStart != argEnd:
|
|
expression = expression + argStart.str
|
|
argStart = argStart.next
|
|
reportError(tok, 'debug', 'Bailout, unknown argument type for argument \'' + expression + '\'.', 'debug')
|
|
continue
|
|
if valueType.pointer > 0:
|
|
continue
|
|
if valueType.type != 'record' and valueType.type != 'container':
|
|
continue
|
|
reportError(tok, 'style', 'Passing record to ellipsis function \'' + tok.astOperand1.function.name + '\'.', 'ellipsisStructArg')
|
|
break
|
|
|
|
for arg in sys.argv[1:]:
|
|
if arg in ['-debug', '-verify']:
|
|
continue
|
|
print('Checking ' + arg + '...')
|
|
data = cppcheckdata.parsedump(arg)
|
|
|
|
if VERIFY:
|
|
VERIFY_ACTUAL = []
|
|
VERIFY_EXPECTED = []
|
|
for tok in data.rawTokens:
|
|
if tok.str.startswith('//'):
|
|
for word in tok.str[2:].split(' '):
|
|
if word in ['stringConcatInArrayInit', 'implicitlyVirtual', 'ellipsisStructArg']:
|
|
VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word)
|
|
|
|
stringConcatInArrayInit(data.configurations, data.rawTokens)
|
|
implicitlyVirtual(data)
|
|
ellipsisStructArg(data)
|
|
|
|
if VERIFY:
|
|
for expected in VERIFY_EXPECTED:
|
|
if expected not in VERIFY_ACTUAL:
|
|
print('Expected but not seen: ' + expected)
|
|
sys.exit(1)
|
|
for actual in VERIFY_ACTUAL:
|
|
if actual not in VERIFY_EXPECTED:
|
|
print('Not expected: ' + actual)
|
|
sys.exit(1)
|