#!/usr/bin/env python3 """Regex rules that repair systematic decompiler output. These are the fixes that recur across many files, where a patch entry per occurrence would be hundreds of lines of noise. Each rule is a named regex with a stated reason, applied to the whole tree. Rules run after the patch file, because they can rewrite lines a patch already touched and it is easier to reason about "patches first, then shape rules". python fix_decompiler_rules.py """ import os import re import sys RULES = [ ( 'super-cast-generic-render', # RenderLiving.doRender takes T. The decompiler inserted a cast to the # supertype, which turns the argument into something that is no longer a T. # Passing the variable as declared is always valid here: the parameter is # the subclass type this renderer was parameterised with. re.compile(r'(super\.\w+\()\((?:Entity|EntityLiving|EntityLivingBase|EntityMob)\)(\s+\w+,)'), r'\1\2', ), ] def main(): if len(sys.argv) != 2: print('usage: fix_decompiler_rules.py ') return 2 tree = sys.argv[1] touched = {} files = set() for root, _dirs, names in os.walk(tree): for name in names: if not name.endswith('.java'): continue path = os.path.join(root, name) with open(path, encoding='utf-8', errors='replace') as handle: text = handle.read() original = text for label, pattern, replacement in RULES: new_text = pattern.sub(replacement, text) if new_text != text: touched[label] = touched.get(label, 0) + \ len(pattern.findall(text)) text = new_text if text != original: files.add(path) with open(path, 'w', encoding='utf-8', newline='\n') as handle: handle.write(text) if not touched: print('decompiler rules: nothing to change') return 0 for label, count in sorted(touched.items()): print(' %s: %d' % (label, count)) print('files rewritten: %d' % len(files)) return 0 if __name__ == '__main__': raise SystemExit(main())