-
Notifications
You must be signed in to change notification settings - Fork 251
/
Lint.lean
111 lines (90 loc) · 4.11 KB
/
Lint.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/-
Copyright (c) 2023 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import Lean.Linter.Util
import Batteries.Data.Array.Basic
import Batteries.Tactic.Lint
/-!
# Linters for Mathlib
In this file we define additional linters for mathlib.
Perhaps these should be moved to Batteries in the future.
-/
namespace Std.Tactic.Lint
open Lean Meta
/--
Linter that checks whether a structure should be in Prop.
-/
@[env_linter] def structureInType : Linter where
noErrorsFound := "no structures that should be in Prop found."
errorsFound := "FOUND STRUCTURES THAT SHOULD BE IN PROP."
test declName := do
unless isStructure (← getEnv) declName do return none
-- remark: using `Lean.Meta.isProp` doesn't suffice here, because it doesn't (always?)
-- recognize predicates as propositional.
let isProp ← forallTelescopeReducing (← inferType (← mkConstWithLevelParams declName))
fun _ ty => return ty == .sort .zero
if isProp then return none
let projs := (getStructureInfo? (← getEnv) declName).get!.fieldNames
if projs.isEmpty then return none -- don't flag empty structures
let allProofs ← projs.allM (do isProof <| ← mkConstWithLevelParams <| declName ++ ·)
unless allProofs do return none
return m!"all fields are propositional but the structure isn't."
/-- Linter that check that all `deprecated` tags come with `since` dates. -/
@[env_linter] def deprecatedNoSince : Linter where
noErrorsFound := "no `deprecated` tags without `since` dates."
errorsFound := "FOUND `deprecated` tags without `since` dates."
test declName := do
let some info := Lean.Linter.deprecatedAttr.getParam? (← getEnv) declName | return none
match info.since? with
| some _ => return none -- TODO: enforce `YYYY-MM-DD` format
| none => return m!"`deprecated` attribute without `since` date"
end Std.Tactic.Lint
/-!
# `dupNamespace` linter
The `dupNamespace` linter produces a warning when a declaration contains the same namespace
at least twice consecutively.
For instance, `Nat.Nat.foo` and `One.two.two` trigger a warning, while `Nat.One.Nat` does not.
-/
namespace Mathlib.Linter
/--
The `dupNamespace` linter is set on by default. Lean emits a warning on any declaration that
contains the same namespace at least twice consecutively.
For instance, `Nat.Nat.foo` and `One.two.two` trigger a warning, while `Nat.One.Nat` does not.
*Note.*
This linter will not detect duplication in namespaces of autogenerated declarations
(other than the one whose `declId` is present in the source declaration).
-/
register_option linter.dupNamespace : Bool := {
defValue := true
descr := "enable the duplicated namespace linter"
}
namespace DupNamespaceLinter
open Lean Parser Elab Command Meta
/-- Gets the value of the `linter.dupNamespace` option. -/
def getLinterDupNamespace (o : Options) : Bool := Linter.getLinterValue linter.dupNamespace o
/-- `getIds stx` extracts the `declId` nodes from the `Syntax` `stx`.
If `stx` is an `alias` or an `export`, then it extracts an `ident`, instead of a `declId`. -/
partial
def getIds : Syntax → Array Syntax
| .node _ `Batteries.Tactic.Alias.alias args => args[2:3]
| .node _ ``Lean.Parser.Command.export args => (args[3:4] : Array Syntax).map (·[0])
| stx@(.node _ _ args) =>
((args.attach.map fun ⟨a, _⟩ => getIds a).foldl (· ++ ·) #[stx]).filter (·.getKind == ``declId)
| _ => default
@[inherit_doc linter.dupNamespace]
def dupNamespace : Linter where run := withSetOptionIn fun stx => do
if getLinterDupNamespace (← getOptions) then
match getIds stx with
| #[id] =>
let ns := (← getScope).currNamespace
let declName := ns ++ (if id.getKind == ``declId then id[0].getId else id.getId)
let nm := declName.components
let some (dup, _) := nm.zip (nm.tailD []) |>.find? fun (x, y) => x == y
| return
Linter.logLint linter.dupNamespace id
m!"The namespace '{dup}' is duplicated in the declaration '{declName}'"
| _ => return
initialize addLinter dupNamespace
end Mathlib.Linter.DupNamespaceLinter