File: Mathlib/Tactic/GeneralizeProofs.lean

full_name: Mathlib.Tactic.initFn._@.Mathlib.Tactic.GeneralizeProofs._hyg.6
code: initialize registerTraceClass `Tactic.generalize_proofs
goal: IO Unit

full_name: Mathlib.Tactic.GeneralizeProofs.Config.debug
code: /--
Configuration for the `generalize_proofs` tactic.
-/
structure Config where
  /-- The maximum recursion depth when generalizing proofs.
  When `maxDepth > 0`, then proofs are generalized from the types of the generalized proofs too. -/
  maxDepth : Nat := 8
  /-- When `abstract` is `true`, then the tactic will create universally quantified proofs
  to account for bound variables.
  When it is `false` then such proofs are left alone. -/
  abstract : Bool := true
  /-- (Debugging) When `true`, enables consistency checks. -/
  debug : Bool := false
goal: Bool

full_name: Mathlib.Tactic.GeneralizeProofs.Config.abstract
code: /--
Configuration for the `generalize_proofs` tactic.
-/
structure Config where
  /-- The maximum recursion depth when generalizing proofs.
  When `maxDepth > 0`, then proofs are generalized from the types of the generalized proofs too. -/
  maxDepth : Nat := 8
  /-- When `abstract` is `true`, then the tactic will create universally quantified proofs
  to account for bound variables.
  When it is `false` then such proofs are left alone. -/
  abstract : Bool := true
  /-- (Debugging) When `true`, enables consistency checks. -/
  debug : Bool := false
goal: Bool

full_name: Mathlib.Tactic.GeneralizeProofs.Config
code: /--
Configuration for the `generalize_proofs` tactic.
-/
structure Config where
  /-- The maximum recursion depth when generalizing proofs.
  When `maxDepth > 0`, then proofs are generalized from the types of the generalized proofs too. -/
  maxDepth : Nat := 8
  /-- When `abstract` is `true`, then the tactic will create universally quantified proofs
  to account for bound variables.
  When it is `false` then such proofs are left alone. -/
  abstract : Bool := true
  /-- (Debugging) When `true`, enables consistency checks. -/
  debug : Bool := false
goal: Type

full_name: Mathlib.Tactic.GeneralizeProofs.Config.maxDepth
code: /--
Configuration for the `generalize_proofs` tactic.
-/
structure Config where
  /-- The maximum recursion depth when generalizing proofs.
  When `maxDepth > 0`, then proofs are generalized from the types of the generalized proofs too. -/
  maxDepth : Nat := 8
  /-- When `abstract` is `true`, then the tactic will create universally quantified proofs
  to account for bound variables.
  When it is `false` then such proofs are left alone. -/
  abstract : Bool := true
  /-- (Debugging) When `true`, enables consistency checks. -/
  debug : Bool := false
goal: Nat

full_name: Mathlib.Tactic.GeneralizeProofs.eval._@.Mathlib.Tactic.GeneralizeProofs._hyg.77
code: /-- Elaborates a `Parser.Tactic.config` for `generalize_proofs`. -/
declare_config_elab elabConfig Config
goal: Lean.Expr → Lean.Elab.TermElabM Mathlib.Tactic.GeneralizeProofs.Config

full_name: Mathlib.Tactic.GeneralizeProofs.elabConfig
code: /-- Elaborates a `Parser.Tactic.config` for `generalize_proofs`. -/
declare_config_elab elabConfig Config
goal: Lean.Syntax → Lean.Elab.TermElabM Mathlib.Tactic.GeneralizeProofs.Config

full_name: Mathlib.Tactic.GeneralizeProofs.evalUnsafe._@.Mathlib.Tactic.GeneralizeProofs._hyg.77
code: /-- Elaborates a `Parser.Tactic.config` for `generalize_proofs`. -/
declare_config_elab elabConfig Config
goal: Lean.Expr → Lean.Elab.TermElabM Mathlib.Tactic.GeneralizeProofs.Config

full_name: Mathlib.Tactic.GeneralizeProofs.GState.propToFVar
code: /-- State for the `MGen` monad. -/
structure GState where
  /-- Mapping from propositions to an fvar in the local context with that type. -/
  propToFVar : ExprMap Expr
goal: Lean.ExprMap Lean.Expr

full_name: Mathlib.Tactic.GeneralizeProofs.GState
code: /-- State for the `MGen` monad. -/
structure GState where
  /-- Mapping from propositions to an fvar in the local context with that type. -/
  propToFVar : ExprMap Expr
goal: Type

full_name: Mathlib.Tactic.GeneralizeProofs.MGen
code: /-- Monad used to generalize proofs.
Carries `Mathlib.Tactic.GeneralizeProofs.Config` and `Mathlib.Tactic.GeneralizeProofs.State`. -/
abbrev MGen 
goal: Type

full_name: Mathlib.Tactic.GeneralizeProofs.MGen.insertFVar
code: /-- Inserts a prop/fvar pair into the `propToFVar` map. -/
def MGen.insertFVar (prop fvar : Expr) : MGen Unit 
goal: Mathlib.Tactic.GeneralizeProofs.MGen Unit

full_name: Mathlib.Tactic.GeneralizeProofs.AContext
code: /-- Context for the `MAbs` monad. -/
structure AContext where
  /-- The local fvars corresponding to bound variables.
  Abstraction needs to be sure that these variables do not appear in abstracted terms. -/
  fvars : Array Expr := #[]
  /-- A copy of `propToFVar` from `GState`. -/
  propToFVar : ExprMap Expr
  /-- The recursion depth, for how many times `visit` is called from within `visitProof. -/
  depth : Nat := 0
  /-- The initial local context, for resetting when recursing. -/
  initLCtx : LocalContext
  /-- The tactic configuration. -/
  config : Config
goal: Type

full_name: Mathlib.Tactic.GeneralizeProofs.AContext.propToFVar
code: /-- Context for the `MAbs` monad. -/
structure AContext where
  /-- The local fvars corresponding to bound variables.
  Abstraction needs to be sure that these variables do not appear in abstracted terms. -/
  fvars : Array Expr := #[]
  /-- A copy of `propToFVar` from `GState`. -/
  propToFVar : ExprMap Expr
  /-- The recursion depth, for how many times `visit` is called from within `visitProof. -/
  depth : Nat := 0
  /-- The initial local context, for resetting when recursing. -/
  initLCtx : LocalContext
  /-- The tactic configuration. -/
  config : Config
goal: Lean.ExprMap Lean.Expr

full_name: Mathlib.Tactic.GeneralizeProofs.AContext.config
code: /-- Context for the `MAbs` monad. -/
structure AContext where
  /-- The local fvars corresponding to bound variables.
  Abstraction needs to be sure that these variables do not appear in abstracted terms. -/
  fvars : Array Expr := #[]
  /-- A copy of `propToFVar` from `GState`. -/
  propToFVar : ExprMap Expr
  /-- The recursion depth, for how many times `visit` is called from within `visitProof. -/
  depth : Nat := 0
  /-- The initial local context, for resetting when recursing. -/
  initLCtx : LocalContext
  /-- The tactic configuration. -/
  config : Config
goal: Mathlib.Tactic.GeneralizeProofs.Config

full_name: Mathlib.Tactic.GeneralizeProofs.AContext.depth
code: /-- Context for the `MAbs` monad. -/
structure AContext where
  /-- The local fvars corresponding to bound variables.
  Abstraction needs to be sure that these variables do not appear in abstracted terms. -/
  fvars : Array Expr := #[]
  /-- A copy of `propToFVar` from `GState`. -/
  propToFVar : ExprMap Expr
  /-- The recursion depth, for how many times `visit` is called from within `visitProof. -/
  depth : Nat := 0
  /-- The initial local context, for resetting when recursing. -/
  initLCtx : LocalContext
  /-- The tactic configuration. -/
  config : Config
goal: Nat

full_name: Mathlib.Tactic.GeneralizeProofs.AContext.initLCtx
code: /-- Context for the `MAbs` monad. -/
structure AContext where
  /-- The local fvars corresponding to bound variables.
  Abstraction needs to be sure that these variables do not appear in abstracted terms. -/
  fvars : Array Expr := #[]
  /-- A copy of `propToFVar` from `GState`. -/
  propToFVar : ExprMap Expr
  /-- The recursion depth, for how many times `visit` is called from within `visitProof. -/
  depth : Nat := 0
  /-- The initial local context, for resetting when recursing. -/
  initLCtx : LocalContext
  /-- The tactic configuration. -/
  config : Config
goal: Lean.LocalContext

full_name: Mathlib.Tactic.GeneralizeProofs.AContext.fvars
code: /-- Context for the `MAbs` monad. -/
structure AContext where
  /-- The local fvars corresponding to bound variables.
  Abstraction needs to be sure that these variables do not appear in abstracted terms. -/
  fvars : Array Expr := #[]
  /-- A copy of `propToFVar` from `GState`. -/
  propToFVar : ExprMap Expr
  /-- The recursion depth, for how many times `visit` is called from within `visitProof. -/
  depth : Nat := 0
  /-- The initial local context, for resetting when recursing. -/
  initLCtx : LocalContext
  /-- The tactic configuration. -/
  config : Config
goal: Array Lean.Expr

full_name: Mathlib.Tactic.GeneralizeProofs.AState.generalizations
code: /-- State for the `MAbs` monad. -/
structure AState where
  /-- The prop/proof triples to add to the local context.
  The proofs must not refer to fvars in `fvars`. -/
  generalizations : Array (Expr × Expr) := #[]
  /-- Map version of `generalizations`. Use `MAbs.findProof?` and `MAbs.insertProof`. -/
  propToProof : ExprMap Expr := {}
goal: Array (Lean.Expr × Lean.Expr)

full_name: Mathlib.Tactic.GeneralizeProofs.AState
code: /-- State for the `MAbs` monad. -/
structure AState where
  /-- The prop/proof triples to add to the local context.
  The proofs must not refer to fvars in `fvars`. -/
  generalizations : Array (Expr × Expr) := #[]
  /-- Map version of `generalizations`. Use `MAbs.findProof?` and `MAbs.insertProof`. -/
  propToProof : ExprMap Expr := {}
goal: Type

full_name: Mathlib.Tactic.GeneralizeProofs.AState.propToProof
code: /-- State for the `MAbs` monad. -/
structure AState where
  /-- The prop/proof triples to add to the local context.
  The proofs must not refer to fvars in `fvars`. -/
  generalizations : Array (Expr × Expr) := #[]
  /-- Map version of `generalizations`. Use `MAbs.findProof?` and `MAbs.insertProof`. -/
  propToProof : ExprMap Expr := {}
goal: Lean.ExprMap Lean.Expr

full_name: Mathlib.Tactic.GeneralizeProofs.MAbs
code: /--
Monad used to abstract proofs, to prepare for generalization.
Has a cache (of expr/type? pairs),
and it also has a reader context `Mathlib.Tactic.GeneralizeProofs.AContext`
and a state `Mathlib.Tactic.GeneralizeProofs.AState`.
-/
abbrev MAbs 
goal: Type

full_name: Mathlib.Tactic.GeneralizeProofs.MGen.runMAbs
code: /-- Runs `MAbs` in `MGen`. Returns the value and the `generalizations`. -/
def MGen.runMAbs {α : Type} (mx : MAbs α) : MGen (α × Array (Expr × Expr)) 
goal: Mathlib.Tactic.GeneralizeProofs.MGen (α × Array (Lean.Expr × Lean.Expr))

full_name: Mathlib.Tactic.GeneralizeProofs.MAbs.findProof?
code: /--
Finds a proof of `prop` by looking at `propToFVar` and `propToProof`.
-/
def MAbs.findProof? (prop : Expr) : MAbs (Option Expr) 
goal: Mathlib.Tactic.GeneralizeProofs.MAbs (Option Lean.Expr)

full_name: Mathlib.Tactic.GeneralizeProofs.MAbs.insertProof
code: /--
Generalize `prop`, where `proof` is its proof.
-/
def MAbs.insertProof (prop pf : Expr) : MAbs Unit 
goal: Mathlib.Tactic.GeneralizeProofs.MAbs Unit

full_name: Mathlib.Tactic.GeneralizeProofs.MAbs.withLocal
code: /-- Runs `x` with an additional local variable. -/
def MAbs.withLocal {α : Type} (fvar : Expr) (x : MAbs α) : MAbs α 
goal: Mathlib.Tactic.GeneralizeProofs.MAbs α

full_name: Mathlib.Tactic.GeneralizeProofs.MAbs.withRecurse
code: /-- Runs `x` with an increased recursion depth and the initial local context, clearing `fvars`. -/
def MAbs.withRecurse {α : Type} (x : MAbs α) : MAbs α 
goal: Mathlib.Tactic.GeneralizeProofs.MAbs α

full_name: Mathlib.Tactic.GeneralizeProofs.appArgExpectedTypes.go
code: /--
Computes expected types for each argument to `f`,
given that the type of `mkAppN f args` is supposed to be `ty?`
(where if `ty?` is none, there's no type to propagate inwards).
-/
def appArgExpectedTypes (f : Expr) (args : Array Expr) (ty? : Option Expr) :
    MetaM (Array (Option Expr)) 
goal: Lean.MetaM (Array (Option Lean.Expr))

full_name: Mathlib.Tactic.GeneralizeProofs.appArgExpectedTypes
code: /--
Computes expected types for each argument to `f`,
given that the type of `mkAppN f args` is supposed to be `ty?`
(where if `ty?` is none, there's no type to propagate inwards).
-/
def appArgExpectedTypes (f : Expr) (args : Array Expr) (ty? : Option Expr) :
    MetaM (Array (Option Expr)) 
goal: Lean.MetaM (Array (Option Lean.Expr))

full_name: Mathlib.Tactic.GeneralizeProofs.mkLambdaFVarsUsedOnly
code: /--
Does `mkLambdaFVars fvars e` but
1. zeta reduces let bindings
2. only includes used fvars
3. returns the list of fvars that were actually abstracted
-/
def mkLambdaFVarsUsedOnly (fvars : Array Expr) (e : Expr) : MetaM (Array Expr × Expr) 
goal: Lean.MetaM (Array Lean.Expr × Lean.Expr)

full_name: Mathlib.Tactic.GeneralizeProofs.abstractProofs.visitProof
code: /--
Abstract proofs occurring in the expression.
A proof is *abstracted* if it is of the form `f a b ...` where `a b ...` are bound variables
(that is, they are variables that are not present in the initial local context)
and where `f` contains no bound variables.
In this form, `f` can be immediately lifted to be a local variable and generalized.
The abstracted proofs are recorded in the state.

This function is careful to track the type of `e` based on where it's used,
since the inferred type might be different.
For example, `(by simp : 1 < [1, 2].length)` has `1 < Nat.succ 1` as the inferred type,
but from knowing it's an argument to `List.nthLe` we can deduce `1 < [1, 2].length`.
-/
partial def abstractProofs (e : Expr) (ty? : Option Expr) : MAbs Expr 
goal: Mathlib.Tactic.GeneralizeProofs.MAbs Lean.Expr

full_name: Mathlib.Tactic.GeneralizeProofs.abstractProofs.visit
code: /--
Abstract proofs occurring in the expression.
A proof is *abstracted* if it is of the form `f a b ...` where `a b ...` are bound variables
(that is, they are variables that are not present in the initial local context)
and where `f` contains no bound variables.
In this form, `f` can be immediately lifted to be a local variable and generalized.
The abstracted proofs are recorded in the state.

This function is careful to track the type of `e` based on where it's used,
since the inferred type might be different.
For example, `(by simp : 1 < [1, 2].length)` has `1 < Nat.succ 1` as the inferred type,
but from knowing it's an argument to `List.nthLe` we can deduce `1 < [1, 2].length`.
-/
partial def abstractProofs (e : Expr) (ty? : Option Expr) : MAbs Expr 
goal: Mathlib.Tactic.GeneralizeProofs.MAbs Lean.Expr

full_name: Mathlib.Tactic.GeneralizeProofs.abstractProofs
code: /--
Abstract proofs occurring in the expression.
A proof is *abstracted* if it is of the form `f a b ...` where `a b ...` are bound variables
(that is, they are variables that are not present in the initial local context)
and where `f` contains no bound variables.
In this form, `f` can be immediately lifted to be a local variable and generalized.
The abstracted proofs are recorded in the state.

This function is careful to track the type of `e` based on where it's used,
since the inferred type might be different.
For example, `(by simp : 1 < [1, 2].length)` has `1 < Nat.succ 1` as the inferred type,
but from knowing it's an argument to `List.nthLe` we can deduce `1 < [1, 2].length`.
-/
partial def abstractProofs (e : Expr) (ty? : Option Expr) : MAbs Expr 
goal: Mathlib.Tactic.GeneralizeProofs.MAbs Lean.Expr

full_name: Mathlib.Tactic.GeneralizeProofs.initialPropToFVar
code: /--
Create a mapping of all propositions in the local context to their fvars.
-/
def initialPropToFVar : MetaM (ExprMap Expr) 
goal: Lean.MetaM (Lean.ExprMap Lean.Expr)

full_name: Mathlib.Tactic.GeneralizeProofs.withGeneralizedProofs
code: /--
Generalizes the proofs in the type `e` and runs `k` in a local context with these propositions.
This continuation `k` is passed
1. an array of fvars for the propositions
2. an array of proof terms (extracted from `e`) that prove these propositions
3. the generalized `e`, which refers to these fvars

The `propToFVar` map is updated with the new proposition fvars.
-/
partial def withGeneralizedProofs {α : Type} [Inhabited α] (e : Expr) (ty? : Option Expr)
    (k : Array Expr → Array Expr → Expr → MGen α) :
    MGen α 
goal: Mathlib.Tactic.GeneralizeProofs.MGen α

full_name: Mathlib.Tactic.GeneralizeProofs.withGeneralizedProofs.go
code: /--
Generalizes the proofs in the type `e` and runs `k` in a local context with these propositions.
This continuation `k` is passed
1. an array of fvars for the propositions
2. an array of proof terms (extracted from `e`) that prove these propositions
3. the generalized `e`, which refers to these fvars

The `propToFVar` map is updated with the new proposition fvars.
-/
partial def withGeneralizedProofs {α : Type} [Inhabited α] (e : Expr) (ty? : Option Expr)
    (k : Array Expr → Array Expr → Expr → MGen α) :
    MGen α 
goal: Mathlib.Tactic.GeneralizeProofs.MGen α

full_name: Mathlib.Tactic.GeneralizeProofs.generalizeProofsCore.go
code: /--
Main loop for `Lean.MVarId.generalizeProofs`.
The `fvars` array is the array of fvars to generalize proofs for,
and `rfvars` is the array of fvars that have been reverted.
The `g` metavariable has all of these fvars reverted.
-/
partial def generalizeProofsCore
    (g : MVarId) (fvars rfvars : Array FVarId) (target : Bool) :
    MGen (Array Expr × MVarId) 
goal: Mathlib.Tactic.GeneralizeProofs.MGen (Array Lean.Expr × Lean.MVarId)

full_name: Mathlib.Tactic.GeneralizeProofs.generalizeProofsCore
code: /--
Main loop for `Lean.MVarId.generalizeProofs`.
The `fvars` array is the array of fvars to generalize proofs for,
and `rfvars` is the array of fvars that have been reverted.
The `g` metavariable has all of these fvars reverted.
-/
partial def generalizeProofsCore
    (g : MVarId) (fvars rfvars : Array FVarId) (target : Bool) :
    MGen (Array Expr × MVarId) 
goal: Mathlib.Tactic.GeneralizeProofs.MGen (Array Lean.Expr × Lean.MVarId)

full_name: Lean.MVarId.generalizeProofs
code: /--
Generalize proofs in the hypotheses `fvars` and, if `target` is true, the target.
Returns the fvars for the generalized proofs and the new goal.

If a hypothesis is a proposition and a `let` binding, this will clear the value of the let binding.

If a hypothesis is a proposition that already appears in the local context, it will be eliminated.

Only *nontrivial* proofs are generalized. These are proofs that aren't of the form `f a b ...`
where `f` is atomic and `a b ...` are bound variables.
These sorts of proofs cannot be meaningfully generalized, and also these are the sorts of proofs
that are left in a term after generalization.
-/
partial def _root_.Lean.MVarId.generalizeProofs
    (g : MVarId) (fvars : Array FVarId) (target : Bool) (config : GeneralizeProofs.Config := {}) :
    MetaM (Array Expr × MVarId) 
goal: Lean.MetaM (Array Lean.Expr × Lean.MVarId)

full_name: Mathlib.Tactic._aux_Mathlib_Tactic_GeneralizeProofs___elabRules_Mathlib_Tactic_generalizeProofsElab_1
code: /--
`generalize_proofs ids* [at locs]?` generalizes proofs in the current goal,
turning them into new local hypotheses.

- `generalize_proofs` generalizes proofs in the target.
- `generalize_proofs at h₁ h₂` generalized proofs in hypotheses `h₁` and `h₂`.
- `generalize_proofs at *` generalizes proofs in the entire local context.
- `generalize_proofs pf₁ pf₂ pf₃` uses names `pf₁`, `pf₂`, and `pf₃` for the generalized proofs.
  These can be `_` to not name proofs.

If a proof is already present in the local context, it will use that rather than create a new
local hypothesis.

When doing `generalize_proofs at h`, if `h` is a let binding, its value is cleared,
and furthermore if `h` duplicates a preceding local hypothesis then it is eliminated.

The tactic is able to abstract proofs from under binders, creating universally quantified
proofs in the local context.
To disable this, use `generalize_proofs (config := { abstract := false })`.
The tactic is also set to recursively abstract proofs from the types of the generalized proofs.
This can be controlled with the `maxDepth` configuration option,
with `generalize_proofs (config := { maxDepth := 0 })` turning this feature off.

For example:
```lean
example : List.nthLe [1, 2] 1 (by simp) = 2 := by
  -- ⊢ [1, 2].nthLe 1 ⋯ = 2
  generalize_proofs h
  -- h : 1 < [1, 2].length
  -- ⊢ [1, 2].nthLe 1 h = 2
```
-/
elab (name := generalizeProofsElab) "generalize_proofs" config?:(Parser.Tactic.config)?
    hs:(ppSpace colGt binderIdent)* loc?:(location)? : tactic => withMainContext do
  let config ← GeneralizeProofs.elabConfig (mkOptionalNode config?)
  let (fvars, target) ←
    match expandOptLocation (Lean.mkOptionalNode loc?) with
    | .wildcard => pure ((← getLCtx).getFVarIds, true)
    | .targets t target => pure (← getFVarIds t, target)
  liftMetaTactic1 fun g => do
    let (pfs, g) ← g.generalizeProofs fvars target config
    -- Rename the proofs using `hs` and record info
    g.withContext do
      let mut lctx ← getLCtx
      for h in hs, fvar in pfs do
        if let `(binderIdent| $s:ident) := h then
          lctx := lctx.setUserName fvar.fvarId! s.getId
        Expr.addLocalVarInfoForBinderIdent fvar h
      withLCtx lctx (← getLocalInstances) do
        let g' ← mkFreshExprSyntheticOpaqueMVar (← g.getType) (← g.getTag)
        g.assign g'
        return g'.mvarId!
goal: Lean.Elab.Tactic.Tactic

full_name: Mathlib.Tactic.generalizeProofsElab
code: /--
`generalize_proofs ids* [at locs]?` generalizes proofs in the current goal,
turning them into new local hypotheses.

- `generalize_proofs` generalizes proofs in the target.
- `generalize_proofs at h₁ h₂` generalized proofs in hypotheses `h₁` and `h₂`.
- `generalize_proofs at *` generalizes proofs in the entire local context.
- `generalize_proofs pf₁ pf₂ pf₃` uses names `pf₁`, `pf₂`, and `pf₃` for the generalized proofs.
  These can be `_` to not name proofs.

If a proof is already present in the local context, it will use that rather than create a new
local hypothesis.

When doing `generalize_proofs at h`, if `h` is a let binding, its value is cleared,
and furthermore if `h` duplicates a preceding local hypothesis then it is eliminated.

The tactic is able to abstract proofs from under binders, creating universally quantified
proofs in the local context.
To disable this, use `generalize_proofs (config := { abstract := false })`.
The tactic is also set to recursively abstract proofs from the types of the generalized proofs.
This can be controlled with the `maxDepth` configuration option,
with `generalize_proofs (config := { maxDepth := 0 })` turning this feature off.

For example:
```lean
example : List.nthLe [1, 2] 1 (by simp) = 2 := by
  -- ⊢ [1, 2].nthLe 1 ⋯ = 2
  generalize_proofs h
  -- h : 1 < [1, 2].length
  -- ⊢ [1, 2].nthLe 1 h = 2
```
-/
elab (name := generalizeProofsElab) "generalize_proofs" config?:(Parser.Tactic.config)?
    hs:(ppSpace colGt binderIdent)* loc?:(location)? : tactic => withMainContext do
  let config ← GeneralizeProofs.elabConfig (mkOptionalNode config?)
  let (fvars, target) ←
    match expandOptLocation (Lean.mkOptionalNode loc?) with
    | .wildcard => pure ((← getLCtx).getFVarIds, true)
    | .targets t target => pure (← getFVarIds t, target)
  liftMetaTactic1 fun g => do
    let (pfs, g) ← g.generalizeProofs fvars target config
    -- Rename the proofs using `hs` and record info
    g.withContext do
      let mut lctx ← getLCtx
      for h in hs, fvar in pfs do
        if let `(binderIdent| $s:ident) := h then
          lctx := lctx.setUserName fvar.fvarId! s.getId
        Expr.addLocalVarInfoForBinderIdent fvar h
      withLCtx lctx (← getLocalInstances) do
        let g' ← mkFreshExprSyntheticOpaqueMVar (← g.getType) (← g.getTag)
        g.assign g'
        return g'.mvarId!
goal: Lean.ParserDescr