"""Find List/Set.remove(int) misdecompilations. FernFlower sometimes resolves List.remove(Object) to remove(int index) when the element type is Integer. The bytecode always boxes with Integer.valueOf first, so a source-level remove() on an Integer collection without that box is a bug: it deletes by position, and throws IndexOutOfBounds on an empty list. python find_remove_index_bugs.py """ import io import os import re import sys FIELD = re.compile(r'\b(?:private|public|protected)?\s*(?:final\s+)?' r'((?:java\.util\.)?(?:List|Set|Collection|ArrayList|LinkedList|Queue|Deque))\s*' r'<\s*(?:java\.lang\.)?Integer\s*>\s+(\w+)\s*[;=]') REMOVE = re.compile(r'\b(\w+)\.remove\(([^()]*)\)') def main(): root = sys.argv[1] hits = [] for base, _dirs, files in os.walk(root): for name in files: if not name.endswith('.java'): continue path = os.path.join(base, name) with io.open(path, encoding='utf-8') as handle: text = handle.read() fields = {} for match in FIELD.finditer(text): fields[match.group(2)] = match.group(1) if not fields: continue for number, line in enumerate(text.split('\n'), 1): for match in REMOVE.finditer(line): receiver, argument = match.group(1), match.group(2).strip() if receiver not in fields: continue if 'valueOf' in argument or 'Integer' in argument: continue hits.append((os.path.relpath(path, root).replace(os.sep, '/'), number, receiver, argument, line.strip())) if not hits: print('no List.remove(int) candidates found') return 0 print('%d candidate(s):' % len(hits)) for path, number, receiver, argument, line in hits: print(' %s:%d %s.remove(%s)' % (path, number, receiver, argument)) print(' %s' % line) return 0 if __name__ == '__main__': raise SystemExit(main())