fix: Collect sorrys and type mismatches

This commit is contained in:
Leni Aniva 2024-12-09 20:15:53 -08:00
parent 5ef2b5c118
commit 0b4ded1049
Signed by: aniva
GPG Key ID: 4D9B1C8D10EA4C50
3 changed files with 29 additions and 15 deletions

View File

@ -103,20 +103,25 @@ structure InfoWithContext where
context?: Option Elab.ContextInfo := .none context?: Option Elab.ContextInfo := .none
private def collectSorrysInTree (t : Elab.InfoTree) : IO (List InfoWithContext) := do private def collectSorrysInTree (t : Elab.InfoTree) : IO (List InfoWithContext) := do
let infos ← t.findAllInfoM none true fun i ctx? => match i with let infos ← t.findAllInfoM none fun i ctx? => match i with
| .ofTermInfo { expectedType?, expr, stx, lctx, .. } => do | .ofTermInfo { expectedType?, expr, stx, lctx, .. } => do
let .some expectedType := expectedType? | return false let .some expectedType := expectedType? | return (false, true)
let .some ctx := ctx? | return false let .some ctx := ctx? | return (false, true)
if expr.isSorry ∧ stx.isOfKind `Lean.Parser.Term.sorry then if expr.isSorry ∧ stx.isOfKind `Lean.Parser.Term.sorry then
return true return (true, false)
ctx.runMetaM lctx do let typeMatch ← ctx.runMetaM lctx do
let type ← Meta.inferType expr let type ← Meta.inferType expr
Bool.not <$> Meta.isExprDefEqGuarded type expectedType Meta.isExprDefEqGuarded type expectedType
return match typeMatch, expr.hasSorry with
| false, true => (true, false) -- Types mismatch but has sorry -> collect, halt
| false, false => (true, false) -- Types mistmatch but no sorry -> collect, halt
| true, true => (false, true) -- Types match but has sorry -> continue
| true, false => (false, false) -- Types match but no sorries -> halt
| .ofTacticInfo { stx, goalsBefore, .. } => | .ofTacticInfo { stx, goalsBefore, .. } =>
-- The `sorry` term is distinct from the `sorry` tactic -- The `sorry` term is distinct from the `sorry` tactic
let isSorry := stx.isOfKind `Lean.Parser.Tactic.tacticSorry let isSorry := stx.isOfKind `Lean.Parser.Tactic.tacticSorry
return isSorry ∧ !goalsBefore.isEmpty return (isSorry ∧ !goalsBefore.isEmpty, ¬ isSorry)
| _ => return false | _ => return (false, true)
return infos.map fun (info, context?, _) => { info, context? } return infos.map fun (info, context?, _) => { info, context? }
-- NOTE: Plural deliberately not spelled "sorries" -- NOTE: Plural deliberately not spelled "sorries"

View File

@ -107,18 +107,18 @@ partial def InfoTree.findAllInfo
head ++ tail head ++ tail
| _ => [] | _ => []
/-- Monadic analogue of `findAllInfo` -/ /-- Monadic analogue of `findAllInfo`, but predicate controls whether to recurse. -/
partial def InfoTree.findAllInfoM [Monad m] partial def InfoTree.findAllInfoM [Monad m]
(t : InfoTree) (t : InfoTree)
(context?: Option Elab.ContextInfo) (context?: Option Elab.ContextInfo)
(haltOnMatch : Bool) (pred : Elab.Info → Option Elab.ContextInfo → m (Bool × Bool))
(pred : Elab.Info → Option Elab.ContextInfo → m Bool)
: m (List (Elab.Info × Option Elab.ContextInfo × PersistentArray Elab.InfoTree)) := do : m (List (Elab.Info × Option Elab.ContextInfo × PersistentArray Elab.InfoTree)) := do
match t with match t with
| .context inner t => t.findAllInfoM (inner.mergeIntoOuter? context?) haltOnMatch pred | .context inner t => t.findAllInfoM (inner.mergeIntoOuter? context?) pred
| .node i children => | .node i children =>
let head := if ← pred i context? then [(i, context?, children)] else [] let (flagCollect, flagRecurse) ← pred i context?
let tail := if haltOnMatch ∧ !head.isEmpty then pure [] else children.toList.mapM (fun t => t.findAllInfoM context? haltOnMatch pred) let head := if flagCollect then [(i, context?, children)] else []
let tail := if ¬ flagRecurse then pure [] else children.toList.mapM (fun t => t.findAllInfoM context? pred)
return head ++ (← tail).join return head ++ (← tail).join
| _ => return [] | _ => return []

View File

@ -179,10 +179,19 @@ example (n: Nat) : mystery n + 1 = n + 2 := sorry
def test_capture_type_mismatch : TestT MetaM Unit := do def test_capture_type_mismatch : TestT MetaM Unit := do
let input := " let input := "
def mystery : Nat := true def mystery (k: Nat) : Nat := true
" "
let goalStates ← (collectSorrysFromSource input).run' {} let goalStates ← (collectSorrysFromSource input).run' {}
let [goalState] := goalStates | panic! s!"Incorrect number of states: {goalStates.length}" let [goalState] := goalStates | panic! s!"Incorrect number of states: {goalStates.length}"
checkEq "goals" ((← goalState.serializeGoals (options := {})).map (·.devolatilize)) #[
{
target := { pp? := "Nat" },
vars := #[{
userName := "k",
type? := .some { pp? := "Nat" },
}],
}
]
def collectNewConstants (source: String) : MetaM (List (List Name)) := do def collectNewConstants (source: String) : MetaM (List (List Name)) := do
let filename := "<anonymous>" let filename := "<anonymous>"