/-
Copyright 2026 The Formal Conjectures Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-/
module
public import LeanThe Module Docstring Linter
This file implements a linter that enforces module docstring hygiene:
Missing module docstring: warns when a file has no /-! ... -/
block at all (detected on the first non-moduleDoc command).
Duplicate module docstrings: warns when a file has more than one
/-! ... -/ block (subsequent blocks should be regular /- ... -/
comments).
public sectionopen Lean Elab Meta Command Linterregister_option linter.style.moduleDocstring : Bool := {
defValue := true
descr := "enable the module docstring linter"
}namespace ModuleDocstringLinter
Files whose first non-moduleDoc command has already been
processed (so we emit the "missing" warning at most once).
private initialize checkedForDoc :
IO.Ref (Std.HashSet String) ← IO.mkRef {}The module docstring linter checks two things:
A file that has no /-! ... -/ block gets a warning on its
first non-moduleDoc command.
A file that has more than one /-! ... -/ block gets a
warning on each extra block.
def moduleDocstringLinter : Linter where run := withSetOptionIn fun stx ↦ do
let fileName ← getFileName
let env ← getEnv
if stx.getKind == ``Lean.Parser.Command.moduleDoc then
-- Duplicate check
let numDocs := (getMainModuleDoc env).size + (getMainVersoModuleDocs env).snippets.size
if numDocs > 1 then
Lean.Linter.logLintIf linter.style.moduleDocstring stx
"This file has more than one module docstring \
(`/-! ... -/`). Only the first one is treated as \
module documentation; convert additional ones to \
regular comments (`/- ... -/`)."
else
-- Missing check: on the first non-moduleDoc command,
-- warn if no moduleDoc has been seen for this file.
let checked ← checkedForDoc.get
unless checked.contains fileName do
checkedForDoc.modify (·.insert fileName)
let hasDoc := !(getMainModuleDoc env).isEmpty || !(getMainVersoModuleDocs env).snippets.isEmpty
unless hasDoc do
Lean.Linter.logLintIf linter.style.moduleDocstring stx
"This file has no module docstring (`/-! ... -/`). \
Add one after the imports to document the file."initialize addLinter moduleDocstringLinterend ModuleDocstringLinter