@@ -12,6 +12,7 @@ public meta import Lean.Server.InfoUtils
1212public meta import Mathlib.Tactic.Linter.Header -- shake: keep
1313public import Lean.Parser.Command
1414public import Mathlib.Tactic.DeclarationNames
15+ public import Batteries.Tactic.Lint.Basic
1516
1617/-!
1718## Style linters
@@ -38,6 +39,10 @@ This file defines the following linters:
3839- the `openClassical` linter checks for `open (scoped) Classical` statements which are not
3940 scoped to a single declaration
4041- the `show` linter checks for `show`s that change the goal and should be replaced by `change`
42+ - the `nameCheck` linter checks for declarations whose names are in non-standard style, such as
43+ by containing a double underscore. The `defsWithUnderscore` environment linter checks for
44+ definitions whose name contains an underscore: that is also very likely to be a violation of
45+ mathlib's naming convention.
4146
4247 All of these linters are enabled in mathlib by default, but disabled globally
4348since they enforce conventions which are inherently subjective.
@@ -518,6 +523,51 @@ def doubleUnderscore : Linter where run := withSetOptionIn fun stx => do
518523
519524initialize addLinter doubleUnderscore
520525
526+ /-- Check `name` is a `Name` containing an underscore that should be linted against
527+ by the `defsWithUnderscore` linter. Namely, we do not lint
528+ * names ending in an underscore, or in a namespace ending with an underscore,
529+ * names containing guillemets `«»` (these tend to be `term<something>` declarations,
530+ i.e. internal names for notation, not user-facing commands),
531+ * names with a component starting with `term` (e.g. `Nat.term_!`)
532+ * names starting with `Mathlib.Tactic`, `Mathlib.Parser` or containing a `Simps` component
533+ (these are probably custom simps projections, i.e. affect how `simps` names its auto-generated
534+ lemmas: we usually prefer a generated name `coe_support` over `coeSupport`, which requires a
535+ projection named `coe_support`)
536+ * names that end in `_<number>` or in `_mathlib` (these tend to be autogenerated instance names),
537+ * library notes.
538+ -/
539+ public def isBadNameWithUnderscore (name : Name) : Bool := Id.run do
540+ let s := name.toString
541+ let declName := name
542+ let last := declName.components.getLast?.getD `Default |>.toString
543+ if last.endsWith '_' ||
544+ s.contains '«' || declName.components.any (·.toString.startsWith "term" ) ||
545+ (`LibraryNote).isPrefixOf declName ||
546+ (`Mathlib.Tactic).isPrefixOf declName || (`Mathlib.Parser).isPrefixOf declName ||
547+ declName.components.any (· == `Simps) ||
548+ last.endsWith "_1" || last.endsWith "_2" || last.endsWith "_mathlib" ||
549+ declName.components.any (·.toString.endsWith '_' ) then
550+ return false
551+ if declName.toString.contains "_" then return true
552+ else return false
553+
554+ open Batteries.Tactic.Lint in
555+ /-- Linter that checks for definitions whose name contains an underscore:
556+ such names violate the naming convention. -/
557+ @[env_linter] public def defsWithUnderscore : Batteries.Tactic.Lint.Linter where
558+ noErrorsFound := "no definitions with an underscore in their name found."
559+ errorsFound := "FOUND definitions with an underscore in their name."
560+ test declName := do
561+ unless ((← getEnv).find? declName).get!.isDefinition && !(← isAutoDecl declName) do return none
562+ -- We also exclude simprocs: these should be named like normal lemmas.
563+ -- check if their type is `Lean.Meta.Simp.Simproc`.
564+ if ((← getEnv).find? declName).get!.type.isConstOf `Lean.Meta.Simp.Simproc then return none
565+ if isBadNameWithUnderscore declName then
566+ return m! "The definition `{ declName} ` contains an underscore. \
567+ This almost surely violates mathlib's naming convention; \
568+ use lowerCamelCase or UpperCamelCase instead."
569+ else return none
570+
521571end Style.nameCheck
522572
523573/-! ### The "openClassical" linter -/
0 commit comments