Skip to content

Commit f775396

Browse files
committed
feat(TacticAnalysis): add a linter for merging consequent intro tactics (#28862)
The linter is triggered by setting the option `linter.tacticAnalysis.introMerge` (which is on by default). When it finds consequent `intro` tactics, it will give a warning suggesting a single use of `intro`. For example: ```lean4 set_option linter.tacticAnalysis.introMerge true /-- warning: Try this: intro a b -/ #guard_msgs in example : ∀ a b : Unit, a = b := by intro a intro b rfl ``` Co-authored-by: Edward van de Meent <edwardvdmeent@gmail.com>
1 parent 14f092f commit f775396

File tree

11 files changed

+73
-23
lines changed

11 files changed

+73
-23
lines changed

Mathlib/CategoryTheory/Idempotents/Basic.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ the equalizer of the identity and this idempotent exists. -/
6060
theorem isIdempotentComplete_iff_hasEqualizer_of_id_and_idempotent :
6161
IsIdempotentComplete C ↔ ∀ (X : C) (p : X ⟶ X), p ≫ p = p → HasEqualizer (𝟙 X) p := by
6262
constructor
63-
· intro
64-
intro X p hp
63+
· intro _ X p hp
6564
rcases IsIdempotentComplete.idempotents_split X p hp with ⟨Y, i, e, ⟨h₁, h₂⟩⟩
6665
exact
6766
⟨Nonempty.intro

Mathlib/CategoryTheory/Limits/MonoCoprod.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ theorem mono_inl_iff {A B : C} {c₁ c₂ : BinaryCofan A B} (hc₁ : IsColimit
7676
∀ (c₁ c₂ : BinaryCofan A B) (_ : IsColimit c₁) (_ : IsColimit c₂) (_ : Mono c₁.inl),
7777
Mono c₂.inl
7878
by exact ⟨fun h₁ => this _ _ hc₁ hc₂ h₁, fun h₂ => this _ _ hc₂ hc₁ h₂⟩
79-
intro c₁ c₂ hc₁ hc₂
80-
intro
79+
intro c₁ c₂ hc₁ hc₂ _
8180
simpa only [IsColimit.comp_coconePointUniqueUpToIso_hom] using
8281
mono_comp c₁.inl (hc₁.coconePointUniqueUpToIso hc₂).hom
8382

Mathlib/CategoryTheory/Sites/Coverage.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,8 @@ it is the infimum of all Grothendieck topologies whose associated coverage conta
288288
theorem toGrothendieck_eq_sInf (K : Coverage C) : toGrothendieck _ K =
289289
sInf {J | K ≤ ofGrothendieck _ J } := by
290290
apply le_antisymm
291-
· apply le_sInf; intro J hJ
292-
intro X S hS
291+
· apply le_sInf
292+
intro J hJ X S hS
293293
induction hS with
294294
| of X S hS => apply hJ; assumption
295295
| top => apply J.top_mem

Mathlib/Data/PNat/Prime.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@ theorem dvd_prime {p m : ℕ+} (pp : p.Prime) : m ∣ p ↔ m = 1 ∨ m = p := b
125125
simp
126126

127127
theorem Prime.ne_one {p : ℕ+} : p.Prime → p ≠ 1 := by
128-
intro pp
129-
intro contra
128+
intro pp contra
130129
apply Nat.Prime.ne_one pp
131130
rw [PNat.coe_eq_one_iff]
132131
apply contra

Mathlib/Data/QPF/Multivariate/Constructions/Fix.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ theorem recF_eq_of_wEquiv (α : TypeVec n) {β : Type u} (u : F (α.append1 β)
8484
apply q.P.w_cases _ x
8585
intro a₀ f'₀ f₀
8686
apply q.P.w_cases _ y
87-
intro a₁ f'₁ f₁
88-
intro h
87+
intro a₁ f'₁ f₁ h
8988
-- Porting note: induction on h doesn't work.
9089
refine @WEquiv.recOn _ _ _ _ (fun a a' _ ↦ recF u a = recF u a') _ _ h ?_ ?_ ?_
9190
· intro a f' f₀ f₁ _h ih; simp only [recF_eq]

Mathlib/Order/Category/NonemptyFinLinOrd.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ def dualEquiv : NonemptyFinLinOrd ≌ NonemptyFinLinOrd where
120120
theorem mono_iff_injective {A B : NonemptyFinLinOrd.{u}} (f : A ⟶ B) :
121121
Mono f ↔ Function.Injective f := by
122122
refine ⟨?_, ConcreteCategory.mono_of_injective f⟩
123-
intro
124-
intro a₁ a₂ h
123+
intro _ a₁ a₂ h
125124
let X := of (ULift (Fin 1))
126125
let g₁ : X ⟶ A := ofHom ⟨fun _ => a₁, fun _ _ _ => by rfl⟩
127126
let g₂ : X ⟶ A := ofHom ⟨fun _ => a₂, fun _ _ _ => by rfl⟩

Mathlib/Order/OmegaCompletePartialOrder.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -585,8 +585,7 @@ theorem ωSup_bind {β γ : Type v} (c : Chain α) (f : α →o Part β) (g : α
585585
simp only [Part.mem_bind_iff, Chain.map_coe,
586586
Function.comp_apply, OrderHom.partBind_coe]
587587
exact ⟨_, hb, hy⟩
588-
· intro i
589-
intro y hy
588+
· intro i y hy
590589
simp only [Part.mem_bind_iff, Chain.map_coe,
591590
Function.comp_apply, OrderHom.partBind_coe] at hy
592591
rcases hy with ⟨b, hb₀, hb₁⟩

Mathlib/RingTheory/RootsOfUnity/Minpoly.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,7 @@ theorem minpoly_eq_pow_coprime {m : ℕ} (hcop : Nat.Coprime m n) :
171171
· intro u hunit _ _
172172
congr
173173
simp [Nat.isUnit_iff.mp hunit]
174-
· intro a p _ hprime
175-
intro hind h hcop
174+
· intro a p _ hprime hind h hcop
176175
rw [hind h (Nat.Coprime.coprime_mul_left hcop)]; clear hind
177176
replace hprime := hprime.nat_prime
178177
have hdiv := (Nat.Prime.coprime_iff_not_dvd hprime).1 (Nat.Coprime.coprime_mul_right hcop)

Mathlib/Tactic/TacticAnalysis/Declarations.lean

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/-
22
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
4-
Authors: Anne Baanen
4+
Authors: Anne Baanen, Edward van de Meent
55
-/
66
import Mathlib.Tactic.TacticAnalysis
77
import Mathlib.Tactic.ExtractGoal
@@ -183,3 +183,30 @@ def terminalToGrind : TacticAnalysis.Config where
183183
logWarningAt stx m!"replace the proof with 'grind': {seq}"
184184
if oldHeartbeats * 2 < newHeartbeats then
185185
logWarningAt stx m!"'grind' is slower than the original: {oldHeartbeats} -> {newHeartbeats}"
186+
187+
-- TODO: add compatibility with `rintro` and `intros`
188+
/-- Suggest merging two adjacent `intro` tactics which don't pattern match. -/
189+
register_option linter.tacticAnalysis.introMerge : Bool := {
190+
defValue := true
191+
}
192+
193+
@[tacticAnalysis linter.tacticAnalysis.introMerge, inherit_doc linter.tacticAnalysis.introMerge]
194+
def introMerge : TacticAnalysis.Config := .ofComplex {
195+
out := Option (TSyntax `tactic)
196+
ctx := Array (Array Term)
197+
trigger ctx stx :=
198+
match stx with
199+
| `(tactic| intro%$x $args*) => .continue ((ctx.getD #[]).push
200+
-- if `intro` is used without arguments, treat it as `intro _`
201+
<| if args.size = 0 then #[⟨mkHole x⟩] else args)
202+
| _ => if let some args := ctx then if args.size > 1 then .accept args else .skip else .skip
203+
test ctx goal := do
204+
let ctxT := ctx.flatten
205+
let tac ← `(tactic| intro $ctxT*)
206+
try
207+
let _ ← Lean.Elab.runTactic goal tac
208+
return some tac
209+
catch _e => -- if for whatever reason we can't run `intro` here.
210+
return none
211+
tell _stx _old _oldHeartbeats new _newHeartbeats :=
212+
if let some tac := new then m!"Try this: {tac}" else none}

MathlibTest/TacticAnalysis.lean

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,36 @@ example {α : Type u} (f : α → Type max u v) : 1 = 1 := by
7474
rfl
7575

7676
end replaceWithGrind
77+
78+
section introMerge
79+
80+
set_option linter.tacticAnalysis.introMerge true
81+
82+
/-- warning: Try this: intro a b -/
83+
#guard_msgs in
84+
example : ∀ a b : Unit, a = b := by
85+
intro a
86+
intro b
87+
rfl
88+
89+
/-- warning: Try this: intro _ b -/
90+
#guard_msgs in
91+
example : ∀ a b : Unit, a = b := by
92+
intro
93+
intro b
94+
rfl
95+
96+
/-- warning: Try this: intro a _ -/
97+
#guard_msgs in
98+
example : ∀ a b : Unit, a = b := by
99+
intro a
100+
intro _
101+
rfl
102+
103+
104+
#guard_msgs in
105+
example : ∀ a b : Unit, a = b := by
106+
intro a b
107+
rfl
108+
109+
end introMerge

0 commit comments

Comments
 (0)