This PR fixes spurious unused variable warnings for variables used in non-atomic match discriminants in `do` notation. For example, in `match Json.parse s >>= fromJson? with`, the variable `s` would be reported as unused. The root cause is that `expandNonAtomicDiscrs?` eagerly elaborates the discriminant via `Term.elabTerm`, which creates TermInfo for variable references. The result is then passed to `elabDoElem` for further elaboration. When the match elaboration is postponed (e.g. because the discriminant type contains an mvar from `fromJson?`), the result is a postponed synthetic mvar. The `withTermInfoContext'` wrapper in `elabDoElemFns` checks `isTacticOrPostponedHole?` on this result, detects a postponed mvar, and replaces the info subtree with a `hole` node — discarding all the TermInfo that was accumulated during discriminant elaboration. The fix applies `mkSaveInfoAnnotation` to the result, which prevents `isTacticOrPostponedHole?` from recognizing it as a hole. This is the same mechanism that `elabLetMVar` uses to preserve info trees when the body is a metavariable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
44 lines
1.3 KiB
Text
44 lines
1.3 KiB
Text
module
|
|
|
|
import Init.Control.Do
|
|
import Lean.Data.Json
|
|
|
|
set_option linter.unusedVariables true
|
|
set_option backward.do.legacy false
|
|
|
|
-- Regression tests: variables used in non-atomic match discriminants in `do`
|
|
-- should not trigger unused variable warnings.
|
|
|
|
open Lean in
|
|
def test1 (s? : Option String) : IO (Option Nat) := do
|
|
let r ← s?.mapM fun s => do
|
|
let s ← pure (s ++ "")
|
|
match Json.parse s >>= fromJson? with
|
|
| .ok v => pure v
|
|
| .error e => throw <| IO.userError s!"bad: {e}"
|
|
return r
|
|
|
|
open Lean in
|
|
def test2 (s? : Option String) : IO (Option Nat) := do
|
|
let r ← s?.mapM fun s => do
|
|
let s ← pure (s ++ "")
|
|
match Json.parse s >>= fromJson? with
|
|
| .ok v => pure v
|
|
| .error _ => pure 0
|
|
return r
|
|
|
|
open Lean in
|
|
def test3 (env : IO (Option String)) : IO (Option Nat) := do
|
|
let some urlMapStr ← env | return none
|
|
match Json.parse urlMapStr >>= fromJson? with
|
|
| .ok urlMap => return urlMap
|
|
| .error e => throw <| IO.userError s!"invalid JSON: {e}"
|
|
|
|
open Lean in
|
|
def test4 (header? : Option String) : IO (Option Nat) := do
|
|
let header ← header?.mapM fun header => do
|
|
let header ← if header == "" then pure "default" else pure header
|
|
match Json.parse header >>= fromJson? with
|
|
| .ok header => pure header
|
|
| .error e => throw <| IO.userError s!"failed to parse: {e}"
|
|
return header
|