feat: filter out deprecated lemmas from suggestions in exact?/rw? (#11918)

This PR filters deprecated lemmas from `exact?` and `rw?` suggestions.

Previously, both tactics would suggest deprecated lemmas, which could be
confusing for users since using the suggestion would trigger a
deprecation warning.

Now, lemmas marked with `@[deprecated]` are filtered out in the
`addImport` functions that populate the discrimination trees used by
these tactics.

**Example (before this PR):**
```lean
import Mathlib.Logic.Basic

example (h : ∃ n : Nat, n > 0) : True := by
  choose (n : Nat) (hn : n > 0 + 0) using h
  guard_hyp hn : n > 0  -- `rw?` would suggest `Eq.rec_eq_cast` which is deprecated
```

Zulip discussion:
https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/deprecated.20lemma.20from.20rw.3F/near/554106870

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kim Morrison 2026-01-08 10:45:04 +11:00 committed by GitHub
parent f7de0c408f
commit 975a81cdb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 45 additions and 1 deletions

View file

@ -13,6 +13,7 @@ public import Lean.Util.Heartbeats
import Init.Grind.Util
import Init.Try
import Lean.Elab.Tactic.Basic
import Lean.Linter.Deprecated
public section
@ -138,7 +139,9 @@ to find candidate lemmas.
open LazyDiscrTree (InitEntry findMatches)
private def addImport (name : Name) (constInfo : ConstantInfo) :
MetaM (Array (InitEntry (Name × DeclMod))) :=
MetaM (Array (InitEntry (Name × DeclMod))) := do
-- Don't report deprecated lemmas.
if Linter.isDeprecated (← getEnv) name then return #[]
-- Don't report lemmas from metaprogramming namespaces.
if name.isMetaprogramming then return #[] else
forallTelescope constInfo.type fun _ type => do

View file

@ -13,6 +13,7 @@ public import Lean.Meta.Tactic.Refl
public import Lean.Meta.Tactic.SolveByElim
public import Lean.Meta.Tactic.TryThis
public import Lean.Util.Heartbeats
import Lean.Linter.Deprecated
public section
@ -47,6 +48,8 @@ private def addImport (name : Name) (constInfo : ConstantInfo) :
MetaM (Array (InitEntry (Name × RwDirection))) := do
if constInfo.isUnsafe then return #[]
if !allowCompletion (←getEnv) name then return #[]
-- Don't report deprecated lemmas.
if Linter.isDeprecated (← getEnv) name then return #[]
-- We now remove some injectivity lemmas which are not useful to rewrite by.
match name with
| .str _ n => if n = "injEq" n = "sizeOf_spec" n.endsWith "_inj" n.endsWith "_inj'" then return #[]

View file

@ -0,0 +1,38 @@
/-!
# Test that `exact?` and `rw?` do not suggest deprecated lemmas
-/
-- Create a deprecated lemma and its non-deprecated replacement
@[deprecated "use myLemma2" (since := "2025-01-01")]
theorem myLemma1 : True := trivial
theorem myLemma2 : True := trivial
-- Test that exact? suggests myLemma2, not the deprecated myLemma1
/--
info: Try this:
[apply] exact myLemma2
-/
#guard_msgs in
example : True := by exact?
-- Create a unique type so our lemmas are the only matches
structure MyType where
val : Nat
def myFn (x : MyType) : MyType := x
-- Create deprecated and non-deprecated rewrite lemmas with unique signature
@[deprecated "use myRwLemma2" (since := "2025-01-01")]
theorem myRwLemma1 (x : MyType) : myFn (myFn x) = myFn x := rfl
theorem myRwLemma2 (x : MyType) : myFn (myFn x) = myFn x := rfl
-- Test that rw? suggests myRwLemma2, not the deprecated myRwLemma1
/--
info: Try this:
[apply] rw [myRwLemma2]
-- no goals
-/
#guard_msgs in
example (x : MyType) : myFn (myFn x) = myFn x := by rw?