@@ -5,6 +5,7 @@ Authors: Michael Rothgang
5
5
-/
6
6
7
7
import Batteries.Data.String.Basic
8
+ import Cli.Basic
8
9
import Mathlib.Init.Data.Nat.Notation
9
10
10
11
/-!
@@ -30,10 +31,27 @@ inductive StyleError where
30
31
| fileTooLong (number_lines : ℕ) (new_size_limit : ℕ) : StyleError
31
32
deriving BEq
32
33
34
+ /-- How to format style errors -/
35
+ inductive ErrorFormat
36
+ /-- Produce style error output aimed at humans: no error code, clickable file name -/
37
+ | humanReadable : ErrorFormat
38
+ /-- Produce an entry in the style-exceptions file: mention the error code, slightly uglier
39
+ than humand-readable output -/
40
+ | exceptionsFile : ErrorFormat
41
+ /-- Produce output suitable for Github error annotations: in particular,
42
+ duplicate the file path, line number and error code -/
43
+ | github : ErrorFormat
44
+ deriving BEq
45
+
33
46
/-- Create the underlying error message for a given `StyleError`. -/
34
- def StyleError.errorMessage (err : StyleError) : String := match err with
47
+ def StyleError.errorMessage (err : StyleError) (style : ErrorFormat) : String := match err with
35
48
| StyleError.fileTooLong current_size size_limit =>
36
- s! "{ size_limit} file contains { current_size} lines, try to split it up"
49
+ match style with
50
+ | ErrorFormat.github =>
51
+ s! "file contains { current_size} lines (at most { size_limit} allowed), try to split it up"
52
+ | ErrorFormat.exceptionsFile =>
53
+ s! "{ size_limit} file contains { current_size} lines, try to split it up"
54
+ | ErrorFormat.humanReadable => s! "file contains { current_size} lines, try to split it up"
37
55
38
56
/-- The error code for a given style error. Keep this in sync with `parse?_errorContext` below! -/
39
57
-- FUTURE: we're matching the old codes in `lint-style.py` for compatibility;
@@ -65,14 +83,24 @@ instance : BEq ErrorContext where
65
83
-- We normalise errors before comparing them.
66
84
&& (ctx.error).normalise == (ctx'.error).normalise
67
85
68
- /-- Output the formatted error message, containing its context. -/
69
- def outputMessage (errctx : ErrorContext) : String :=
70
- -- We are outputting for github: duplicate file path, line number and error code,
71
- -- so that they are also visible in the plain text output.
72
- let path := errctx.path
73
- let nr := errctx.lineNumber
74
- let code := errctx.error.errorCode
75
- s! "::ERR file={ path} ,line={ nr} ,code={ code} ::{ path} :{ nr} { code} : { errctx.error.errorMessage} "
86
+ /-- Output the formatted error message, containing its context.
87
+ `style` specifies if the error should be formatted for humans or for github output matchers -/
88
+ def outputMessage (errctx : ErrorContext) (style : ErrorFormat) : String :=
89
+ let error_message := errctx.error.errorMessage style
90
+ match style with
91
+ | ErrorFormat.github =>
92
+ -- We are outputting for github: duplicate file path, line number and error code,
93
+ -- so that they are also visible in the plain text output.
94
+ let path := errctx.path
95
+ let nr := errctx.lineNumber
96
+ let code := errctx.error.errorCode
97
+ s! "::ERR file={ path} ,line={ nr} ,code={ code} ::{ path} :{ nr} { code} : { error_message} "
98
+ | ErrorFormat.exceptionsFile =>
99
+ -- Produce an entry in the exceptions file: with error code and "line" in front of the number.
100
+ s! "{ errctx.path} : line { errctx.lineNumber} : { errctx.error.errorCode} : { error_message} "
101
+ | ErrorFormat.humanReadable =>
102
+ -- Print for humans: clickable file name and omit the error code
103
+ s! "error: { errctx.path} :{ errctx.lineNumber} : { error_message} "
76
104
77
105
/-- Try parsing an `ErrorContext` from a string: return `some` if successful, `none` otherwise. -/
78
106
def parse?_errorContext (line : String) : Option ErrorContext := Id.run do
@@ -111,10 +139,11 @@ def parseStyleExceptions (lines : Array String) : Array ErrorContext := Id.run d
111
139
-- We treat all lines starting with "--" as a comment and ignore them.
112
140
Array.filterMap (parse?_errorContext ·) (lines.filter (fun line ↦ !line.startsWith "--" ))
113
141
114
- /-- Print information about all errors encountered to standard output. -/
115
- def formatErrors (errors : Array ErrorContext) : IO Unit := do
142
+ /-- Print information about all errors encountered to standard output.
143
+ `style` specifies if we print errors for humand or github consumption. -/
144
+ def formatErrors (errors : Array ErrorContext) (style : ErrorFormat) : IO Unit := do
116
145
for e in errors do
117
- IO.println (outputMessage e)
146
+ IO.println (outputMessage e style )
118
147
119
148
/-- Core logic of a text based linter: given a collection of lines,
120
149
return an array of all style errors with line numbers. -/
@@ -150,22 +179,24 @@ end
150
179
151
180
/-- Read a file, apply all text-based linters and print formatted errors.
152
181
Return `true` if there were new errors (and `false` otherwise).
153
- `sizeLimit` is any pre-existing limit on this file's size. -/
154
- def lintFile (path : FilePath) (sizeLimit : Option ℕ) : IO Bool := do
182
+ `sizeLimit` is any pre-existing limit on this file's size.
183
+ `style` specifies if errors should be formatted for github or human consumption. -/
184
+ def lintFile (path : FilePath) (sizeLimit : Option ℕ) (style : ErrorFormat) : IO Bool := do
155
185
let lines ← IO.FS.lines path
156
186
-- We don't need to run any checks on imports-only files.
157
187
-- NB. The Python script used to still run a few linters; this is in fact not necessary.
158
188
if isImportsOnlyFile lines then
159
189
return false
160
190
if let some (StyleError.fileTooLong n limit) := checkFileLength lines sizeLimit then
161
191
let arr := Array.mkArray1 (ErrorContext.mk (StyleError.fileTooLong n limit) 1 path)
162
- formatErrors arr
192
+ formatErrors arr style
163
193
return true
164
194
return false
165
195
166
196
/-- Lint all files referenced in a given import-only file.
167
- Return the number of files which had new style errors. -/
168
- def lintAllFiles (path : FilePath) : IO UInt32 := do
197
+ Return the number of files which had new style errors.
198
+ `style` specifies if errors should be formatted for github or human consumption. -/
199
+ def lintAllFiles (path : FilePath) (style : ErrorFormat) : IO UInt32 := do
169
200
-- Read all module names from the file at `path`.
170
201
let allModules ← IO.FS.lines path
171
202
-- Read the style exceptions file.
@@ -181,14 +212,36 @@ def lintAllFiles (path : FilePath) : IO UInt32 := do
181
212
let size_limits := (style_exceptions.filter (·.path == path)).filterMap (fun errctx ↦
182
213
match errctx.error with
183
214
| StyleError.fileTooLong _ limit => some limit)
184
- if ← lintFile path (size_limits.get? 0 ) then
215
+ if ← lintFile path (size_limits.get? 0 ) style then
185
216
number_error_files := number_error_files + 1
186
217
return number_error_files
187
218
188
- /-- The entry point to the `lake exe lint_style` command. -/
189
- def main : IO UInt32 := do
190
- let mut number_error_files := 0
219
+ open Cli in
220
+ /-- Implementation of the `lint_style` command line program. -/
221
+ def lintStyleCli (args : Cli.Parsed) : IO UInt32 := do
222
+ let errorStyle := match (args.hasFlag "github" , args.hasFlag "update" ) with
223
+ | (true , _) => ErrorFormat.github
224
+ | (false , true ) => ErrorFormat.exceptionsFile
225
+ | (false , false ) => ErrorFormat.humanReadable
226
+ let mut number_error_files : UInt32 := 0
191
227
for s in ["Archive.lean" , "Counterexamples.lean" , "Mathlib.lean" ] do
192
- let n ← lintAllFiles (mkFilePath [s])
228
+ let n ← lintAllFiles (mkFilePath [s]) errorStyle
193
229
number_error_files := number_error_files + n
194
- return number_error_files
230
+ return number_error_files
231
+
232
+ open Cli in
233
+ /-- Setting up command line options and help text for `lake exe lint_style`. -/
234
+ -- so far, no help options or so: perhaps that is fine?
235
+ def lint_style : Cmd := `[Cli|
236
+ lint_style VIA lintStyleCli; ["0.0.1" ]
237
+ "Run text-based style linters on every Lean file in Mathlib/, Archive/ and Counterexamples/.
238
+ Print errors about any unexpected style errors to standard output."
239
+
240
+ FLAGS:
241
+ github; "Print errors in a format suitable for github problem matchers\n \
242
+ otherwise, produce human-readable output"
243
+ update; "Print errors solely for the style exceptions file"
244
+ ]
245
+
246
+ /-- The entry point to the `lake exe lint_style` command. -/
247
+ def main (args : List String) : IO UInt32 := do lint_style.validate args
0 commit comments