cppcheck/addons/misc.py

165 lines
6.0 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
#
# 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:
2019-04-10 07:31:52 +02:00
VERIFY_ACTUAL.append(str(token.linenr) + ':' + id)
else:
2019-04-14 08:54:53 +02:00
cppcheckdata.reportError(token, severity, msg, 'misc', id)
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
2018-04-16 12:55:27 +02:00
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
2018-04-16 12:55:27 +02:00
for i in range(len(rawTokens)):
if i < 2:
continue
2018-04-16 12:55:27 +02:00
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', '--cli']:
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)