Files
lean-bench/leanbench/lint.py
T
claude-bot ca98703538
ci / python (pull_request) Successful in 17s
ci / lean (pull_request) Successful in 1m10s
ci / image (pull_request) Has been skipped
Pilot prep: simplex schema, projection, alignment, lint, stats, pilot scripts, protocol v1
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYqwNMyPUHeTbFWg83GQq6
2026-10-22 05:52:28 +00:00

49 lines
2.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Cheap static checks for formalizations that compile and are probably wrong.
These are a baseline for the fidelity filter, not a replacement for one: each looks
at the projected roles and the text of the declaration only, with no model and no
Lean. They catch encoding hazards (natural-number subtraction, integer division),
binders the statement never uses, and goals that say nothing.
"""
from __future__ import annotations
import re
from .project import Projection
from .schema import KIND_FACT, KIND_GOAL, KIND_OBJECT
_NAT = re.compile(r"\b(ℕ|Nat)\b")
_OTHER_NUMBERS = re.compile(r"[ℤℚℝℂ]|\b(Int|Rat|Real|Complex)\b")
_SUBTRACTION = re.compile(r"[\w)\]]\s*-\s*[\w(]")
_DIVISION = re.compile(r"[\w)\]]\s*/\s*[\w(]")
_IDENT = re.compile(r"[A-Za-z_][A-Za-z0-9_']*")
def lint(projection: Projection) -> list[str]:
findings: list[str] = []
if projection.degenerate:
findings.append("degenerate")
texts = [r.type_text for r in projection.roles if r.kind in (KIND_FACT, KIND_GOAL)]
body = " ".join(texts)
declared = " ".join(r.type_text for r in projection.roles if r.kind == KIND_OBJECT)
for role in projection.roles:
if role.kind != KIND_OBJECT or role.attrs.get("binder_kind") != "explicit":
continue
if role.type_text.strip().startswith(("Type", "Sort")):
continue # a carrier: used by the instances that constrain it
used = set(_IDENT.findall(body)) | set(_IDENT.findall(declared.replace(role.type_text, "")))
if role.name != "_" and role.name not in used:
findings.append(f"unused_binder:{role.name}")
nat_only = _NAT.search(declared + " " + body) and not _OTHER_NUMBERS.search(declared + body)
if nat_only and _SUBTRACTION.search(body):
findings.append("nat_subtraction")
if (_NAT.search(declared) or re.search(r"\b(ℤ|Int)\b", declared)) and _DIVISION.search(body):
findings.append("integer_division")
goal = next((r for r in projection.roles if r.kind == KIND_GOAL), None)
if goal is not None and goal.type_text.strip() in ("", "True", "False", "1 = 1", "0 = 0"):
findings.append("trivial_goal")
return findings