cppcheck/tools/reduce.py

287 lines
7.1 KiB
Python
Raw Normal View History

2017-06-04 22:51:48 +02:00
#!/usr/bin/env python
import subprocess
import sys
2019-03-03 20:09:06 +01:00
def show_syntax():
print('Syntax:')
print(' reduce.py --cmd=<full command> --expected=<expected text output> --file=<source file> [--segfault]')
print('')
print("Example. source file = foo/bar.c")
print(" reduce.py --cmd='./cppcheck --enable=style foo/bar.c' --expected=\"Variable 'x' is reassigned\" --file=foo/bar.c")
sys.exit(1)
if len(sys.argv) == 1:
show_syntax()
CMD = None
EXPECTED = None
SEGFAULT = False
FILE = None
BACKUPFILE = None
for arg in sys.argv[1:]:
2016-07-23 13:44:05 +02:00
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:
2016-07-23 13:44:05 +02:00
print('Abort: No --cmd')
2019-03-03 20:09:06 +01:00
show_syntax()
2017-06-04 22:51:48 +02:00
if not SEGFAULT and EXPECTED is None:
2016-07-23 13:44:05 +02:00
print('Abort: No --expected')
2019-03-03 20:09:06 +01:00
show_syntax()
if FILE is None:
2016-07-23 13:44:05 +02:00
print('Abort: No --file')
2019-03-03 20:09:06 +01:00
show_syntax()
2016-07-23 13:44:05 +02:00
print('CMD=' + CMD)
if SEGFAULT:
2016-07-23 13:44:05 +02:00
print('EXPECTED=SEGFAULT')
else:
2016-07-23 13:44:05 +02:00
print('EXPECTED=' + EXPECTED)
print('FILE=' + FILE)
2017-06-04 22:51:48 +02:00
def runtool():
2016-07-23 13:44:05 +02:00
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]
2017-06-05 13:23:00 +02:00
if 'error:' not in out and EXPECTED in out:
2016-07-23 13:44:05 +02:00
return True
return False
2017-06-04 22:51:48 +02:00
def writefile(filename, filedata):
2016-07-23 13:44:05 +02:00
f = open(filename, 'wt')
for line in filedata:
f.write(line)
f.close()
2017-06-04 22:51:48 +02:00
def replaceandrun(what, filedata, i, line):
2016-07-23 13:44:05 +02:00
print(what + ' ' + str(i + 1) + '/' + str(len(filedata)) + '..')
bak = filedata[i]
filedata[i] = line
writefile(FILE, filedata)
2017-06-04 22:51:48 +02:00
if runtool():
2016-07-23 13:44:05 +02:00
print('pass')
writefile(BACKUPFILE, filedata)
return True
print('fail')
filedata[i] = bak
return False
2017-06-04 22:51:48 +02:00
def replaceandrun2(what, filedata, i, line1, line2):
2016-07-23 13:44:05 +02:00
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)
2017-06-04 22:51:48 +02:00
if runtool():
2016-07-23 13:44:05 +02:00
print('pass')
writefile(BACKUPFILE, filedata)
else:
print('fail')
filedata[i] = bak1
filedata[i + 1] = bak2
2017-06-04 22:51:48 +02:00
def clearandrun(what, filedata, i1, i2):
2016-07-23 13:44:05 +02:00
print(what + ' ' + str(i1 + 1) + '/' + str(len(filedata)) + '..')
filedata2 = list(filedata)
i = i1
while i <= i2 and i < len(filedata2):
filedata2[i] = ''
i = i + 1
writefile(FILE, filedata2)
2017-06-04 22:51:48 +02:00
if runtool():
2016-07-23 13:44:05 +02:00
print('pass')
writefile(BACKUPFILE, filedata2)
return filedata2
print('fail')
return filedata
2017-06-04 22:51:48 +02:00
def removecomments(filedata):
2016-07-23 13:44:05 +02:00
for i in range(len(filedata)):
line = filedata[i]
2017-06-04 22:51:48 +02:00
if '//' in line:
2016-07-23 13:44:05 +02:00
replaceandrun('remove comment', filedata, i, line[:line.find('//')].rstrip())
2017-06-04 22:51:48 +02:00
def checkpar(line):
2016-07-23 13:44:05 +02:00
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
2017-06-04 22:51:48 +02:00
def combinelines(filedata):
2016-07-23 13:44:05 +02:00
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, '')
2017-06-04 22:51:48 +02:00
2016-01-27 08:20:58 +01:00
def removedirectives(filedata):
2016-07-23 13:44:05 +02:00
for i in range(len(filedata)):
if filedata[i].lstrip().startswith('#'):
replaceandrun('remove preprocessor directive', filedata, i, '')
2017-06-04 22:51:48 +02:00
def removeblocks(filedata):
2016-07-23 13:44:05 +02:00
if len(filedata) < 3:
return filedata
for i in range(len(filedata)):
strippedline = filedata[i].strip()
if len(strippedline) == 0:
continue
2017-06-04 22:51:48 +02:00
if strippedline[-1] not in ';{}':
2016-07-23 13:44:05 +02:00
continue
i1 = i + 1
while i1 < len(filedata) and filedata[i1].startswith('#'):
i1 = i1 + 1
i2 = i1
indent = 0
while i2 < len(filedata):
for c in filedata[i2]:
if c == '}':
indent = indent - 1
if indent == 0:
indent = -100
elif c == '{':
indent = indent + 1
if indent < 0:
break
i2 = i2 + 1
if indent == -100:
indent = 0
if i2 == i1 or i2 >= len(filedata):
continue
if filedata[i2].strip() != '}' and filedata[i2].strip() != '};':
continue
if indent < 0:
i2 = i2 - 1
filedata = clearandrun('remove codeblock', filedata, i1, i2)
return filedata
2017-06-04 22:51:48 +02:00
2016-01-27 08:20:58 +01:00
def removeline(filedata):
2016-07-23 13:44:05 +02:00
stmt = True
for i in range(len(filedata)):
line = filedata[i]
strippedline = line.strip()
2016-07-23 13:44:05 +02:00
if len(strippedline) == 0:
continue
2017-06-04 22:51:48 +02:00
if stmt and strippedline[-1] == ';' and checkpar(line) and '{' not in line and '}' not in line:
2016-07-23 13:44:05 +02:00
replaceandrun('remove line', filedata, i, '')
2016-01-27 08:20:58 +01:00
2017-06-05 13:23:00 +02:00
elif stmt and '{' in strippedline and strippedline.find('}') == len(strippedline) - 1:
2016-07-23 13:44:05 +02:00
replaceandrun('remove line', filedata, i, '')
2016-01-27 08:20:58 +01:00
2017-06-04 22:51:48 +02:00
if strippedline[-1] in ';{}':
2016-07-23 13:44:05 +02:00
stmt = True
else:
stmt = False
# reduce..
print('Make sure error can be reproduced...')
2017-06-04 22:51:48 +02:00
if not runtool():
2016-07-23 13:44:05 +02:00
print("Cannot reproduce")
sys.exit(1)
f = open(FILE, 'rt')
filedata = f.readlines()
f.close()
writefile(BACKUPFILE, filedata)
while True:
2016-07-23 13:44:05 +02:00
filedata1 = list(filedata)
2016-07-23 13:44:05 +02:00
print('remove comments...')
removecomments(filedata)
2016-07-23 13:44:05 +02:00
print('remove preprocessor directives...')
removedirectives(filedata)
2016-07-23 13:44:05 +02:00
print('remove blocks...')
filedata = removeblocks(filedata)
2016-07-23 13:44:05 +02:00
print('combine lines..')
combinelines(filedata)
2016-07-23 13:44:05 +02:00
print('remove line...')
removeline(filedata)
2016-07-23 13:44:05 +02:00
# if filedata and filedata2 are identical then stop
if len(filedata1) == len(filedata):
i = 0
while i < len(filedata1):
if filedata[i] != filedata1[i]:
break
i = i + 1
if i == len(filedata1):
break
writefile(FILE, filedata)