Skip to content

Commit 4d124cb

Browse files
committed
fix(scripts/lint-style): improve Mathlib.Init import error messages (leanprover-community#32419)
Improve the `checkInitImports` linter by: 1. Accepting transitive imports (not just direct) for the `Mathlib.Init` check 2. Removing hardcoded exceptions for transitively imported files 3. Making error messages actionable with specific advice ### Two cases with different advice 1. **Transitively imported by `Mathlib.Init`** → add `import Mathlib.Tactic.Linter.Header` 2. **NOT imported by `Mathlib.Init`** → add `import Mathlib.Init` ### Example output **Case 1: Transitively imported by Init** ``` • `Mathlib.Util.SomeFile` is transitively imported by `Mathlib.Init`. Please add `import Mathlib.Tactic.Linter.Header` to `Mathlib.Util.SomeFile`. ``` **Case 2: Not imported by Init** ``` • `Mathlib.Util.TestFile` is NOT imported by `Mathlib.Init`. Please add `import Mathlib.Init` to `Mathlib.Util.TestFile`. ``` Prompted by confusion when working on leanprover-community#32416 - the old error message just said files didn't import `Mathlib.Init` without explaining what to do when adding that import would create a cycle. Discussion: https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Mathlib.2EInit.20linter.20error 🤖 Prepared with Claude Code
1 parent 89e3baf commit 4d124cb

1 file changed

Lines changed: 21 additions & 20 deletions

File tree

scripts/lint-style.lean

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Lake.CLI.Main
88
import Lean.Elab.ParseImportsFast
99
import Batteries.Data.String.Basic
1010
import Mathlib.Tactic.Linter.TextBased
11+
import ImportGraph.FromSource
1112
import Cli.Basic
1213

1314
/-!
@@ -104,38 +105,30 @@ def missingInitImports (opts : LinterOptions) : IO Nat := do
104105

105106
-- Find any file in the Mathlib directory which does not contain any Mathlib import.
106107
-- We simply parse `Mathlib.lean`, as CI ensures this file is up to date.
107-
let allModuleNames := eraseExplicitImports (← findImports "Mathlib.lean")
108+
let allModuleNames := eraseExplicitImports (← findImportsFromSource "Mathlib.lean")
108109
let mut modulesWithoutMathlibImports := #[]
109110
let mut importsHeaderLinter := #[]
110111
for module in allModuleNames do
111112
let path := System.mkFilePath (module.components.map fun n ↦ n.toString)|>.addExtension "lean"
112-
let imports ← findImports path
113+
let imports ← findImportsFromSource path
113114
let hasNoMathlibImport := imports.all fun name ↦ name.getRoot != `Mathlib
114115
if hasNoMathlibImport then
115116
modulesWithoutMathlibImports := modulesWithoutMathlibImports.push module
116117
if imports.contains `Mathlib.Tactic.Linter.Header then
117118
importsHeaderLinter := importsHeaderLinter.push module
118119

119-
-- Every file importing the `header` linter should be imported in `Mathlib/Init.lean` itself.
120+
-- Every file importing the `header` linter should be (transitively) imported by `Mathlib.Init`.
120121
-- (Downstream files should import `Mathlib.Init` and not the header linter.)
121-
-- The only exception are auto-generated import-only files.
122-
let initImportsfindImports ("Mathlib" / "Init.lean")
122+
-- The only exceptions are auto-generated import-only files.
123+
let initTransitiveImportsfindTransitiveImportsFromSource ("Mathlib" / "Init.lean") (some `Mathlib)
123124
let mismatch := importsHeaderLinter.filter (fun mod ↦
124-
![`Mathlib, `Mathlib.Tactic, `Mathlib.Init].contains mod && !initImports.contains mod)
125-
-- These files are transitively imported by `Mathlib.Init`.
126-
|>.erase `Mathlib.Tactic.DeclarationNames
127-
|>.erase `Mathlib.Lean.Elab.Tactic.Meta
128-
|>.erase `Mathlib.Lean.ContextInfo
129-
|>.erase `Mathlib.Tactic.Lia
130-
|>.erase `Mathlib.Tactic.Linter.DirectoryDependency
131-
|>.erase `Mathlib.Lean.Elab.InfoTree
132-
|>.erase `Mathlib.Lean.Environment
133-
|>.erase `Mathlib.Lean.Expr.Basic
125+
![`Mathlib, `Mathlib.Tactic, `Mathlib.Init].contains mod && !initTransitiveImports.contains mod)
134126
if mismatch.size > 0 then
135127
IO.eprintln s!"error: the following {mismatch.size} module(s) import the `header` linter \
136-
directly, but should import Mathlib.Init instead: {mismatch}\n\
137-
The `header` linter is included in Mathlib.Init, and every file in Mathlib \
138-
should import Mathlib.Init.\nPlease adjust the imports accordingly."
128+
directly, but should import Mathlib.Init instead: {mismatch}\n"
129+
for mod in mismatch do
130+
IO.eprintln s!" • `{mod}` is NOT imported by `Mathlib.Init`.\n \
131+
Please replace `import Mathlib.Tactic.Linter.Header` with `import Mathlib.Init`."
139132
return mismatch.size
140133

141134
-- Now, it only remains to check that every module (except for the Header linter itself)
@@ -145,7 +138,15 @@ def missingInitImports (opts : LinterOptions) : IO Nat := do
145138
|>.erase `Mathlib.Tactic.Linter.DirectoryDependency
146139
if missing.size > 0 then
147140
IO.eprintln s!"error: the following {missing.size} module(s) do not import Mathlib.Init: \
148-
{missing}"
141+
{missing}\n"
142+
for mod in missing do
143+
if initTransitiveImports.contains mod then
144+
-- Transitively imported by Init: just needs to import the Header linter
145+
IO.eprintln s!" • `{mod}` is transitively imported by `Mathlib.Init`.\n \
146+
Please add `import Mathlib.Tactic.Linter.Header` to `{mod}`."
147+
else
148+
IO.eprintln s!" • `{mod}` is NOT imported by `Mathlib.Init`.\n \
149+
Please add `import Mathlib.Init` to `{mod}`."
149150
return missing.size
150151
return 0
151152

@@ -240,7 +241,7 @@ def lintStyleCli (args : Cli.Parsed) : IO UInt32 := do
240241
let searchPath ← Lean.getSrcSearchPath
241242
let allModuleNames ← originModules.flatMapM fun mod => do
242243
let imports ← match ← searchPath.findWithExt "lean" mod with
243-
| some file => findImports file
244+
| some file => findImportsFromSource file
244245
| none => throw <| IO.userError s!"could not find module with name {mod}"
245246
pure <| imports.filter (·.components.head! ∈ pkgs)
246247

0 commit comments

Comments
 (0)