align / code /bugfind /bench /difffuncs.py
viai957's picture
Add files using upload-large-folder tool
4951e76 verified
Raw History Blame Contribute Delete
2.25 kB
"""Find the functions whose bodies changed between two unpacked crate versions (pre-side names), by line-range
overlap of difflib opcodes with a regex-located enclosing `fn`. Heuristic (no parser), documented as such."""
import difflib, os, re
FN = re.compile(r"^\s*(?:pub(?:\([^)]*\))?\s+)?(?:const\s+)?(?:unsafe\s+)?(?:async\s+)?(?:extern\s+\"[^\"]*\"\s+)?fn\s+([A-Za-z_]\w*)")
def modpath(rel):
p = rel[len("src/"):-3].split("/")
if p[-1] in ("mod", "lib"): p = p[:-1]
return p
def changed_functions(pre, post):
out = []
for dp, _, fs in os.walk(os.path.join(pre, "src")):
for f in fs:
if not f.endswith(".rs"): continue
rel = os.path.relpath(os.path.join(dp, f), pre)
if rel.endswith("main.rs") and rel == "src/main.rs": continue
a = open(os.path.join(pre, rel), encoding="utf8", errors="replace").read().splitlines()
pp = os.path.join(post, rel)
b = open(pp, encoding="utf8", errors="replace").read().splitlines() if os.path.exists(pp) else []
if a == b: continue
# skip test code: everything from a `#[cfg(test)]` line on, and fns directly under `#[test]`
cut = next((i for i, l in enumerate(a) if re.match(r"\s*#\[cfg\(test\)\]", l)), len(a))
starts = [(i, FN.match(l).group(1)) for i, l in enumerate(a[:cut]) if FN.match(l) and not any("#[test]" in a[k] for k in range(max(0, i - 3), i))]
if cut < len(a):
a, b = a[:cut], b[: next((i for i, l in enumerate(b) if re.match(r"\s*#\[cfg\(test\)\]", l)), len(b))]
for tag, i1, i2, j1, j2 in difflib.SequenceMatcher(None, a, b, autojunk=False).get_opcodes():
if tag == "equal": continue
for ln in sorted({i1, max(i2 - 1, i1)}):
cands = [s for s in starts if s[0] <= ln]
if cands:
name = cands[-1][1]
# a `fn` header preceding the changed line only counts if the change is within a plausible body (<= 400 lines)
if ln - cands[-1][0] <= 400:
out.append((rel, ".".join(modpath(rel)).replace(".", "::"), name))
return sorted(set(out))