Skip to content

Commit bcbcbff

Browse files
committed
fix: make the allowed unused tactics extensible (#14557)
This PR allows to change the tactics that the unused tactic linter ignores. The main use-case is allowing `done` at the end of proofs, which is very useful for teaching purposes. See for instance [this Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Disable.20.60linter.2EunusedTactic.60.20warning.20for.20.60done.60).
1 parent e0e0819 commit bcbcbff

File tree

2 files changed

+60
-26
lines changed

2 files changed

+60
-26
lines changed

Mathlib/Tactic/Linter/UnusedTactic.lean

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -66,31 +66,48 @@ namespace UnusedTactic
6666
/-- The monad for collecting the ranges of the syntaxes that do not modify any goal. -/
6767
abbrev M := StateRefT (HashMap String.Range Syntax) IO
6868

69-
/-- `Parser`s allowed to not change the tactic state. -/
70-
def allowed : HashSet SyntaxNodeKind := HashSet.empty
71-
|>.insert `Mathlib.Tactic.Says.says
72-
|>.insert `Batteries.Tactic.«tacticOn_goal-_=>_»
73-
-- attempt to speed up, by ignoring more tactics
74-
|>.insert `by
75-
|>.insert `null
76-
|>.insert `«]»
77-
|>.insert ``Lean.Parser.Term.byTactic
78-
|>.insert ``Lean.Parser.Tactic.tacticSeq
79-
|>.insert ``Lean.Parser.Tactic.tacticSeq1Indented
80-
|>.insert ``Lean.Parser.Tactic.tacticTry_
81-
82-
-- the following `SyntaxNodeKind`s play a role in silencing `test`s
83-
|>.insert ``Lean.Parser.Tactic.guardHyp
84-
|>.insert ``Lean.Parser.Tactic.guardTarget
85-
|>.insert ``Lean.Parser.Tactic.failIfSuccess
86-
|>.insert `Mathlib.Tactic.successIfFailWithMsg
87-
|>.insert `Mathlib.Tactic.failIfNoProgress
88-
|>.insert `Mathlib.Tactic.ExtractGoal.extractGoal
89-
|>.insert `Mathlib.Tactic.Propose.propose'
90-
|>.insert `Lean.Parser.Tactic.traceState
91-
|>.insert `Mathlib.Tactic.tacticMatch_target_
92-
|>.insert `change?
93-
|>.insert `«tactic#adaptation_note_»
69+
/-- `Parser`s allowed to not change the tactic state.
70+
This can be increased dynamically, using `#allow_unused_tactic`.
71+
-/
72+
initialize allowedRef : IO.Ref (HashSet SyntaxNodeKind) ←
73+
IO.mkRef <| HashSet.empty
74+
|>.insert `Mathlib.Tactic.Says.says
75+
|>.insert `Batteries.Tactic.«tacticOn_goal-_=>_»
76+
-- attempt to speed up, by ignoring more tactics
77+
|>.insert `by
78+
|>.insert `null
79+
|>.insert `«]»
80+
|>.insert ``Lean.Parser.Term.byTactic
81+
|>.insert ``Lean.Parser.Tactic.tacticSeq
82+
|>.insert ``Lean.Parser.Tactic.tacticSeq1Indented
83+
|>.insert ``Lean.Parser.Tactic.tacticTry_
84+
-- the following `SyntaxNodeKind`s play a role in silencing `test`s
85+
|>.insert ``Lean.Parser.Tactic.guardHyp
86+
|>.insert ``Lean.Parser.Tactic.guardTarget
87+
|>.insert ``Lean.Parser.Tactic.failIfSuccess
88+
|>.insert `Mathlib.Tactic.successIfFailWithMsg
89+
|>.insert `Mathlib.Tactic.failIfNoProgress
90+
|>.insert `Mathlib.Tactic.ExtractGoal.extractGoal
91+
|>.insert `Mathlib.Tactic.Propose.propose'
92+
|>.insert `Lean.Parser.Tactic.traceState
93+
|>.insert `Mathlib.Tactic.tacticMatch_target_
94+
|>.insert `change?
95+
|>.insert `«tactic#adaptation_note_»
96+
97+
/-- `#allow_unused_tactic` takes an input a space-separated list of identifiers.
98+
These identifiers are then allowed by the unused tactic linter:
99+
even if these tactics do not modify goals, there will be no warning emitted.
100+
Note: for this to work, these identifiers should be the `SyntaxNodeKind` of each tactic.
101+
102+
For instance, you can allow the `done` and `skip` tactics using
103+
```lean
104+
#allow_unused_tactic Lean.Parser.Tactic.done Lean.Parser.Tactic.skip
105+
```
106+
Notice that you should use the `SyntaxNodeKind` of the tactic.
107+
-/
108+
elab "#allow_unused_tactic " ids:ident* : command => do
109+
let ids := ← Command.liftCoreM do ids.mapM realizeGlobalConstNoOverload
110+
allowedRef.modify (·.insertMany ids)
94111

95112
/--
96113
A list of blacklisted syntax kinds, which are expected to have subterms that contain
@@ -161,7 +178,7 @@ partial def eraseUsedTactics : InfoTree → M Unit
161178
let stx := i.stx
162179
let kind := stx.getKind
163180
if let some r := stx.getRange? true then
164-
if allowed.contains kind
181+
if (← allowedRef.get).contains kind
165182
-- if the tactic is allowed to not change the goals
166183
then modify (·.erase r)
167184
else

test/UnusedTactic.lean

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,27 @@ set_option linter.unusedTactic false
1515
/--
1616
warning: 'congr' tactic does nothing
1717
note: this linter can be disabled with `set_option linter.unusedTactic false`
18+
---
19+
warning: 'done' tactic does nothing
20+
note: this linter can be disabled with `set_option linter.unusedTactic false`
1821
-/
1922
#guard_msgs in
2023
set_option linter.unusedTactic true in
2124
-- the linter notices that `congr` is unused
2225
example : True := by
2326
congr
2427
constructor
28+
done
29+
30+
section allowing_more_unused_tactics
31+
-- test that allowing more unused tactics has the desired effect of silencing the linter
32+
#allow_unused_tactic Lean.Parser.Tactic.done Lean.Parser.Tactic.skip
33+
34+
#guard_msgs in
35+
set_option linter.unusedTactic true in
36+
example : True := by
37+
skip
38+
constructor
39+
done
40+
41+
end allowing_more_unused_tactics

0 commit comments

Comments
 (0)