2019-11-15 21:38:20 +01:00
#!/usr/bin/env python3
2018-04-13 22:19:47 +02:00
#
# 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
2018-04-19 22:23:34 +02:00
DEBUG = ( ' -debug ' in sys . argv )
2018-04-13 22:19:47 +02:00
VERIFY = ( ' -verify ' in sys . argv )
VERIFY_EXPECTED = [ ]
VERIFY_ACTUAL = [ ]
def reportError ( token , severity , msg , id ) :
2018-04-19 22:23:34 +02:00
if id == ' debug ' and DEBUG == False :
return
2018-04-13 22:19:47 +02:00
if VERIFY :
2019-04-10 07:31:52 +02:00
VERIFY_ACTUAL . append ( str ( token . linenr ) + ' : ' + id )
2018-04-13 22:19:47 +02:00
else :
2019-04-14 08:54:53 +02:00
cppcheckdata . reportError ( token , severity , msg , ' misc ' , id )
2018-04-13 22:19:47 +02:00
def simpleMatch ( token , pattern ) :
for p in pattern . split ( ' ' ) :
if not token or token . str != p :
return False
token = token . next
return True
2018-04-16 13:55:12 +02:00
# Get function arguments
def getArgumentsRecursive ( tok , arguments ) :
if tok is None :
return
if tok . str == ' , ' :
getArgumentsRecursive ( tok . astOperand1 , arguments )
getArgumentsRecursive ( tok . astOperand2 , arguments )
else :
2018-08-05 20:36:21 +02:00
arguments . append ( tok )
2018-04-16 13:55:12 +02:00
def getArguments ( ftok ) :
arguments = [ ]
getArgumentsRecursive ( ftok . astOperand2 , arguments )
return arguments
2018-04-16 12:55:27 +02:00
def isStringLiteral ( tokenString ) :
return tokenString . startswith ( ' " ' )
2018-04-13 22:19:47 +02:00
# check data
2018-04-28 23:01:29 +02:00
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
2018-04-13 22:19:47 +02:00
arrayInit = False
2018-04-16 12:55:27 +02:00
for i in range ( len ( rawTokens ) ) :
2018-04-13 22:19:47 +02:00
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
2018-04-13 22:19:47 +02:00
if tok3 == ' } ' :
arrayInit = False
elif tok1 == ' ] ' and tok2 == ' = ' and tok3 == ' { ' :
arrayInit = True
2018-04-28 23:01:29 +02:00
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 ' )
2018-04-16 13:16:53 +02:00
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 ' )
2018-04-13 22:19:47 +02:00
2018-04-16 13:55:12 +02:00
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
2018-04-19 22:23:34 +02:00
if tok . astOperand2 . str != ' , ' :
continue
if tok . scope . type in [ ' Global ' , ' Class ' ] :
continue
2018-04-16 13:55:12 +02:00
if tok . astOperand1 . function is None :
continue
for argnr , argvar in tok . astOperand1 . function . argument . items ( ) :
if argnr < 1 :
continue
2019-09-04 08:07:30 +02:00
if not simpleMatch ( argvar . typeStartToken , ' ... ' ) :
2018-04-16 13:55:12 +02:00
continue
callArgs = getArguments ( tok )
for i in range ( argnr - 1 , len ( callArgs ) ) :
valueType = callArgs [ i ] . valueType
2018-04-19 22:23:34 +02:00
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 ' )
2018-04-16 13:55:12 +02:00
continue
if valueType . pointer > 0 :
continue
2018-04-19 22:23:34 +02:00
if valueType . type != ' record ' and valueType . type != ' container ' :
2018-04-16 13:55:12 +02:00
continue
reportError ( tok , ' style ' , ' Passing record to ellipsis function \' ' + tok . astOperand1 . function . name + ' \' . ' , ' ellipsisStructArg ' )
break
2018-04-13 22:19:47 +02:00
for arg in sys . argv [ 1 : ] :
2019-04-07 21:07:08 +02:00
if arg in [ ' -debug ' , ' -verify ' , ' --cli ' ] :
2018-04-13 22:19:47 +02:00
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 ( ' ' ) :
2018-04-16 13:55:12 +02:00
if word in [ ' stringConcatInArrayInit ' , ' implicitlyVirtual ' , ' ellipsisStructArg ' ] :
2018-04-13 22:19:47 +02:00
VERIFY_EXPECTED . append ( str ( tok . linenr ) + ' : ' + word )
2018-04-28 23:01:29 +02:00
stringConcatInArrayInit ( data . configurations , data . rawTokens )
2018-04-16 13:16:53 +02:00
implicitlyVirtual ( data )
2018-04-16 13:55:12 +02:00
ellipsisStructArg ( data )
2018-04-13 22:19:47 +02:00
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 )