refactor: add message log capture helpers for tactic evaluation (#11933)
This PR adds utility functions for managing the message log during
tactic
evaluation, and refactors existing code to use them.
**New helpers in `Lean.Elab.Tactic`:**
- `withSuppressedMessages`: executes an action while suppressing new
messages
- `withCapturedMessages`: executes an action and returns any new
messages
- `hasErrorMessages`: checks if a message list contains errors
**Refactored to use these helpers:**
- `LibrarySearch.tryDischarger`: now uses `withSuppressedMessages`
- `Try.evalAndSuggest`: now uses `withSuppressedMessages`
- `Try.evalAndSuggestWithBy`: now uses `withSuppressedMessages`
These helpers provide a standard pattern for tactic validation that
needs to
inspect error messages (e.g., filtering out suggestions that produce
errors).
🤖 Prepared with Claude Code
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
531dbf0e1b
commit
0ad15fe982
3 changed files with 41 additions and 17 deletions
|
|
@ -369,6 +369,33 @@ def withoutRecover (x : TacticM α) : TacticM α :=
|
|||
def withRecover (recover : Bool) (x : TacticM α) : TacticM α :=
|
||||
withReader (fun ctx => { ctx with recover }) x
|
||||
|
||||
/-! ## Message log utilities -/
|
||||
|
||||
/-- Execute an action while suppressing any new messages it generates.
|
||||
Restores the original message log after the action completes.
|
||||
Useful for trying tactics without polluting the message log with errors from failed attempts. -/
|
||||
def withSuppressedMessages (action : TacticM α) : TacticM α := do
|
||||
let initialLog ← Core.getMessageLog
|
||||
try
|
||||
action
|
||||
finally
|
||||
Core.setMessageLog initialLog
|
||||
|
||||
/-- Execute an action and return any new messages it generates.
|
||||
Restores the original message log afterward.
|
||||
Useful for inspecting messages produced by a tactic without committing them. -/
|
||||
def withCapturedMessages (action : TacticM α) : TacticM (α × List Message) := do
|
||||
let initialLog ← Core.getMessageLog
|
||||
let initialMsgCount := initialLog.toList.length
|
||||
let result ← action
|
||||
let newMsgs := (← Core.getMessageLog).toList.drop initialMsgCount
|
||||
Core.setMessageLog initialLog
|
||||
return (result, newMsgs)
|
||||
|
||||
/-- Check if any messages in the list are errors. -/
|
||||
def hasErrorMessages (msgs : List Message) : Bool :=
|
||||
msgs.any (·.severity == .error)
|
||||
|
||||
/--
|
||||
Like `throwErrorAt`, but, if recovery is enabled, logs the error instead.
|
||||
-/
|
||||
|
|
|
|||
|
|
@ -821,13 +821,12 @@ private def addSuggestions (tk : Syntax) (s : Array Tactic.TryThis.Suggestion) :
|
|||
Tactic.TryThis.addSuggestions tk (s.map fun stx => stx) (origSpan? := (← getRef))
|
||||
|
||||
def evalAndSuggest (tk : Syntax) (tac : TSyntax `tactic) (originalMaxHeartbeats : Nat) (config : Try.Config := {}) : TacticM Unit := do
|
||||
let initialLog ← Core.getMessageLog
|
||||
let tac' ← try
|
||||
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
|
||||
catch _ =>
|
||||
throwEvalAndSuggestFailed config
|
||||
-- Restore message log to suppress "Try this" messages from intermediate tactic executions
|
||||
Core.setMessageLog initialLog
|
||||
-- Suppress "Try this" messages from intermediate tactic executions
|
||||
let tac' ← withSuppressedMessages do
|
||||
try
|
||||
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
|
||||
catch _ =>
|
||||
throwEvalAndSuggestFailed config
|
||||
let s := (getSuggestions tac')[*...config.max].toArray
|
||||
if s.isEmpty then
|
||||
throwEvalAndSuggestFailed config
|
||||
|
|
@ -985,13 +984,12 @@ private def wrapSuggestionWithBy (sugg : Tactic.TryThis.Suggestion) : TacticM Ta
|
|||
|
||||
/-- Version of `evalAndSuggest` that wraps tactic suggestions with `by` for term mode. -/
|
||||
private def evalAndSuggestWithBy (tk : Syntax) (tac : TSyntax `tactic) (originalMaxHeartbeats : Nat) (config : Try.Config) : TacticM Unit := do
|
||||
let initialLog ← Core.getMessageLog
|
||||
let tac' ← try
|
||||
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
|
||||
catch _ =>
|
||||
throwEvalAndSuggestFailed config
|
||||
-- Restore message log to suppress "Try this" messages from intermediate tactic executions
|
||||
Core.setMessageLog initialLog
|
||||
-- Suppress "Try this" messages from intermediate tactic executions
|
||||
let tac' ← withSuppressedMessages do
|
||||
try
|
||||
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
|
||||
catch _ =>
|
||||
throwEvalAndSuggestFailed config
|
||||
let suggestions := (getSuggestions tac')[*...config.max].toArray
|
||||
if suggestions.isEmpty then
|
||||
throwEvalAndSuggestFailed config
|
||||
|
|
|
|||
|
|
@ -84,9 +84,8 @@ def tryDischarger (mvarId : MVarId) : MetaM (Option (List MVarId)) := do
|
|||
let tacStx ← `(tactic| try?)
|
||||
let remainingGoals ← Elab.Term.TermElabM.run' <| Elab.Tactic.run subgoal do
|
||||
-- Suppress info messages from try?
|
||||
let initialLog ← Core.getMessageLog
|
||||
Elab.Tactic.evalTactic tacStx
|
||||
Core.setMessageLog initialLog
|
||||
Elab.Tactic.withSuppressedMessages do
|
||||
Elab.Tactic.evalTactic tacStx
|
||||
if remainingGoals.isEmpty then
|
||||
return some []
|
||||
else
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue