cppcheck/tools/reduce.py

265 lines
6.1 KiB
Python

import subprocess
import os
import sys
CMD = None
EXPECTED = None
SEGFAULT = False
FILE = None
BACKUPFILE = None
for arg in sys.argv[1:]:
if arg.startswith('--cmd='):
CMD = arg[arg.find('=')+1:]
elif arg.startswith('--expected='):
EXPECTED = arg[arg.find('=')+1:]
elif arg.startswith('--file='):
FILE = arg[arg.find('=')+1:]
BACKUPFILE = FILE + '.bak'
elif arg == '--segfault':
SEGFAULT = True
if CMD is None:
print('Abort: No --cmd')
sys.exit(1)
if SEGFAULT == False and EXPECTED is None:
print('Abort: No --expected')
sys.exit(1)
if FILE is None:
print('Abort: No --file')
sys.exit(1)
print('CMD='+CMD)
if SEGFAULT:
print('EXPECTED=SEGFAULT')
else:
print('EXPECTED='+EXPECTED)
print('FILE='+FILE)
def runtool():
p = subprocess.Popen(CMD.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
comm = p.communicate()
if SEGFAULT:
if p.returncode != 0:
return True
elif p.returncode == 0:
out = comm[0] + '\n' + comm[1]
if (out.find('error:') < 0) and (out.find(EXPECTED) > 0):
return True
return False
def writefile(filename, filedata):
f = open(filename, 'wt')
for line in filedata:
f.write(line)
f.close()
def replaceandrun(what, filedata, i, line):
print(what + ' ' + str(i+1) + '/' + str(len(filedata)) + '..')
bak = filedata[i]
filedata[i] = line
writefile(FILE, filedata)
if runtool() == True:
print('pass')
writefile(BACKUPFILE, filedata)
return True
print('fail')
filedata[i] = bak
return False
def replaceandrun2(what, filedata, i, line1, line2):
print(what + ' ' + str(i+1) + '/' + str(len(filedata)) + '..')
bak1 = filedata[i]
bak2 = filedata[i+1]
filedata[i] = line1
filedata[i+1] = line2
writefile(FILE, filedata)
if runtool() == True:
print('pass')
writefile(BACKUPFILE, filedata)
else:
print('fail')
filedata[i] = bak1
filedata[i+1] = bak2
def removecomments(filedata):
for i in range(len(filedata)):
line = filedata[i]
if line.find('//') >= 0:
replaceandrun('remove comment', filedata, i, line[:line.find('//')].rstrip())
def checkpar(line):
par = 0
for c in line:
if c=='(' or c=='[':
par = par + 1
elif c==')' or c==']':
par = par - 1
if par<0:
return False
return par == 0
def combinelines(filedata):
if len(filedata) < 3:
return
lines = []
for i in range(len(filedata)-1):
fd1 = filedata[i].rstrip()
if fd1.endswith(','):
fd2 = filedata[i+1].lstrip()
if fd2 != '':
lines.append(i)
chunksize = len(lines)
while chunksize > 10:
i = 0
while i < len(lines):
i1 = i
i2 = i + chunksize
i = i2
if i2 > len(lines):
i2 = len(lines)
filedata2 = list(filedata)
for line in lines[i1:i2]:
filedata2[line] = filedata2[line].rstrip() + filedata2[line+1].lstrip()
filedata2[line+1] = ''
if replaceandrun('combine lines', filedata2, lines[i1]+1, ''):
filedata = filedata2
lines[i1:i2] = []
i = i1
chunksize = chunksize / 2
for line in lines:
fd1 = filedata[line].rstrip()
fd2 = filedata[line+1].lstrip()
replaceandrun2('combine lines', filedata, line, fd1+fd2, '')
def removeline(filedata):
stmt = True
for i in range(len(filedata)):
line = filedata[i]
strippedline = line.strip()
if len(strippedline) == 0:
continue
if stmt and strippedline[-1]==';' and checkpar(line) and line.find('{')<0 and line.find('}')<0:
replaceandrun('remove line', filedata, i, '')
elif stmt and strippedline.find('{') > 0 and strippedline.find('}') == len(strippedline) - 1:
replaceandrun('remove line', filedata, i, '')
if ';{}'.find(strippedline[-1]) >= 0:
stmt = True
else:
stmt = False
def removeincludes(filedata):
for i in range(len(filedata)):
if filedata[i].startswith('#include '):
replaceandrun('remove #include', filedata, i, '')
def removeemptyblocks(filedata):
if len(filedata) < 3:
return
i = 0
while i < len(filedata):
if filedata[i].strip() == '':
filedata.pop(i)
else:
i = i + 1
for i in range(len(filedata)-1):
fd1 = filedata[i].rstrip()
fd2 = filedata[i+1].strip()
if checkpar(fd1) and fd1.endswith('{') and fd2 == '}':
replaceandrun2('remove block', filedata, i, '', '')
def removenamespaces(filedata):
if len(filedata) < 3:
return filedata
stmt = True
for i in range(len(filedata)):
line = filedata[i]
strippedline = line.strip()
if len(strippedline) == 0:
continue
stmt1 = stmt
if ';{}'.find(strippedline[-1]) >= 0:
stmt = True
else:
stmt = False
if stmt1 == False:
continue
if strippedline.find('}') < 0 and strippedline.find('{') == len(strippedline)-1:
i2 = i + 1
level = 1
while i2 < len(filedata) and level > 0:
#print(str(i2)+':'+str(level)+':'+filedata[i2])
for c in filedata[i2]:
if c == '}':
level = level - 1
if level <= 0:
break
elif c == '{':
level = level + 1
i2 = i2 + 1
if level == 0 and (filedata[i2-1].strip().endswith('}') or filedata[i2-1].strip().endswith('};')):
#print(str(i)+';'+str(i2))
filedata2 = list(filedata)
ii = i
while ii < i2:
filedata2[ii] = ''
ii = ii + 1
if replaceandrun('remove codeblock', filedata2, i, ''):
filedata = filedata2
return filedata
# reduce..
print('Make sure error can be reproduced...')
if runtool() == False:
print("Cannot reproduce")
sys.exit(1)
f = open(FILE, 'rt')
filedata = f.readlines()
f.close()
writefile(BACKUPFILE, filedata)
print('remove comments...')
removecomments(filedata)
print('combine lines..')
combinelines(filedata)
print('remove namespaces...')
filedata = removenamespaces(filedata)
print('remove includes...')
removeincludes(filedata)
print('remove line...')
removeline(filedata)
print('remove includes...')
removeincludes(filedata)
print('remove empty blocks...')
removeemptyblocks(filedata)
print('remove namespaces...')
filedata = removenamespaces(filedata)
writefile(FILE, filedata)