Skip to content

Commit 062c4b7

Browse files
committed
feat(scripts): count the number of tactics without a docstring (leanprover-community#32131)
We want to make sure every tactic is well documented. Right now, I count 33 tactics in Mathlib without any docstring at all. Add this number to the weekly technical debt metrics bot so we can track it and fix any regressions. Co-authored-by: Anne C.A. Baanen <vierkantor@vierkantor.com>
1 parent 3cfe59e commit 062c4b7

6 files changed

Lines changed: 68 additions & 1 deletion

File tree

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6637,6 +6637,7 @@ public import Mathlib.Tactic.Linter.OldObtain
66376637
public import Mathlib.Tactic.Linter.PPRoundtrip
66386638
public import Mathlib.Tactic.Linter.PrivateModule
66396639
public import Mathlib.Tactic.Linter.Style
6640+
public import Mathlib.Tactic.Linter.TacticDocumentation
66406641
public import Mathlib.Tactic.Linter.TextBased
66416642
public import Mathlib.Tactic.Linter.UnusedInstancesInType
66426643
public import Mathlib.Tactic.Linter.UnusedTactic

Mathlib/Init.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public import Mathlib.Tactic.Linter.Lint
1717
public import Mathlib.Tactic.Linter.Multigoal
1818
public import Mathlib.Tactic.Linter.OldObtain
1919
public import Mathlib.Tactic.Linter.PrivateModule
20+
public import Mathlib.Tactic.Linter.TacticDocumentation
2021
-- The following import contains the environment extension for the unused tactic linter.
2122
public import Mathlib.Tactic.Linter.UnusedTacticExtension
2223
public import Mathlib.Tactic.Linter.UnusedTactic

Mathlib/Tactic.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ public import Mathlib.Tactic.Linter.OldObtain
176176
public import Mathlib.Tactic.Linter.PPRoundtrip
177177
public import Mathlib.Tactic.Linter.PrivateModule
178178
public import Mathlib.Tactic.Linter.Style
179+
public import Mathlib.Tactic.Linter.TacticDocumentation
179180
public import Mathlib.Tactic.Linter.TextBased
180181
public import Mathlib.Tactic.Linter.UnusedInstancesInType
181182
public import Mathlib.Tactic.Linter.UnusedTactic
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/-
2+
Copyright (c) 2025 Anne Baanen. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Anne Baanen
5+
-/
6+
module
7+
8+
meta import Batteries.Tactic.Lint.Basic
9+
meta import Lean.Elab.Tactic.Doc
10+
import Lean.Parser.Tactic.Doc
11+
import Mathlib.Tactic.Linter.Header
12+
13+
/-! # The `tacticDocs` linter
14+
15+
The `tacticDocs` environment linter checks that all tactics defined in a module come with
16+
a (nonempty) docstring.
17+
-/
18+
19+
open Lean Parser Elab Command
20+
21+
open Tactic.Doc
22+
23+
/-- Does this tactic have a docstring, or an `extensionDoc` defined later? -/
24+
meta def isNonemptyDoc (doc : TacticDoc) : Bool :=
25+
doc.docString.isSome || doc.extensionDocs.any (! ·.isEmpty)
26+
27+
/-- Check that all tactics available in Mathlib have a docstring. -/
28+
@[env_linter] meta def tacticDocs : Batteries.Tactic.Lint.Linter where
29+
noErrorsFound := "No tactics are missing documentation."
30+
errorsFound := "TACTICS ARE MISSING DOCUMENTATION STRINGS:"
31+
test tac := do
32+
let env ← getEnv
33+
34+
-- Only run on unique tactics (where tactics are defined as parsers in the tactic kind).
35+
if !isTactic env tac || (alternativeOfTactic env tac).isSome then
36+
return none
37+
38+
-- Find the associated documentation.
39+
let docs ← Tactic.Doc.allTacticDocs
40+
let docMap : NameMap _ := docs.foldl (init := {}) fun m doc =>
41+
m.insert doc.internalName doc
42+
-- This `get?` should not return `none` for normally declared tactics,
43+
-- but if we do environment manipulation it might give us weird results.
44+
-- So we should allow the case of `none`.
45+
let doc := docMap.get? tac
46+
let name := (doc.map (·.userName)).getD tac.toString
47+
48+
if let some doc := doc then
49+
if isNonemptyDoc doc then
50+
return none
51+
52+
return m!"tactic `{name}` missing documentation string"

scripts/nolints.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,4 +260,15 @@
260260
["docBlame", "Mathlib.Tactic.Coherence.LiftObj.lift"],
261261
["docBlame", "Mathlib.Tactic.GCongr.ForwardExt.eval"],
262262
["docBlame", "Mathlib.Tactic.Monotonicity.mono.side"],
263-
["docBlame", "Mathlib.Tactic.Sat.buildReify.mkPS"]]
263+
["docBlame", "Mathlib.Tactic.Sat.buildReify.mkPS"],
264+
["tacticDocs", "Mathlib.Tactic.tacticMatch_target_"],
265+
["tacticDocs", "Tactic.Elementwise.tacticElementwise!___"],
266+
["tacticDocs", "Tactic.Elementwise.tacticElementwise___"],
267+
["tacticDocs",
268+
"AlgebraicGeometry.ProjIsoSpecTopComponent.FromSpec.tacticMem_tac"],
269+
["tacticDocs",
270+
"AlgebraicGeometry.ProjIsoSpecTopComponent.FromSpec.tacticMem_tac_aux"],
271+
["tacticDocs", "Mathlib.Tactic.Conv.convLHS"],
272+
["tacticDocs", "Mathlib.Tactic.Conv.convRHS"],
273+
["tacticDocs", "Mathlib.Tactic.Find.«tactic#find_»"],
274+
["tacticDocs", "Mathlib.Tactic.GCongr.tacticGcongr_discharger"]]

scripts/technical-debt-metrics.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ doubleDeprecs="$(git grep -A2 -- "set_option linter.deprecated false" -- ":^Math
118118
printf '%s|disabled deprecation lints\n' "$(( deprecs - doubleDeprecs ))"
119119

120120
printf '%s|%s\n' "$(grep -c 'docBlame' scripts/nolints.json)" "documentation nolint entries"
121+
printf '%s|%s\n' "$(grep -c 'tacticDocs' scripts/nolints.json)" "undocumented tactics"
121122
# We count the number of large files, making sure to avoid counting the test file `MathlibTest/Lint.lean`.
122123
printf '%s|%s\n' "$(git grep '^set_option linter.style.longFile [0-9]*' Mathlib | wc -l)" "large files"
123124

0 commit comments

Comments
 (0)