Skip to content

Commit 1f86e43

Browse files
committed
feat: rewrite the trailing whitespace linter in Lean (#16334)
Co-authored-by: Michael Rothgang <[email protected]>
1 parent ff67e33 commit 1f86e43

File tree

2 files changed

+21
-20
lines changed

2 files changed

+21
-20
lines changed

Mathlib/Tactic/Linter/TextBased.lean

+20-6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ inductive StyleError where
5656
| adaptationNote
5757
/-- A line ends with windows line endings (\r\n) instead of unix ones (\n). -/
5858
| windowsLineEnding
59+
/-- A line contains trailing whitespace. -/
60+
| trailingWhitespace
5961
deriving BEq
6062

6163
/-- How to format style errors -/
@@ -76,13 +78,15 @@ def StyleError.errorMessage (err : StyleError) : String := match err with
7678
"Found the string \"Adaptation note:\", please use the #adaptation_note command instead"
7779
| windowsLineEnding => "This line ends with a windows line ending (\r\n): please use Unix line\
7880
endings (\n) instead"
81+
| trailingWhitespace => "This line ends with some whitespace: please remove this"
7982

8083
/-- The error code for a given style error. Keep this in sync with `parse?_errorContext` below! -/
8184
-- FUTURE: we're matching the old codes in `lint-style.py` for compatibility;
8285
-- in principle, we could also print something more readable.
8386
def StyleError.errorCode (err : StyleError) : String := match err with
8487
| StyleError.adaptationNote => "ERR_ADN"
8588
| StyleError.windowsLineEnding => "ERR_WIN"
89+
| StyleError.trailingWhitespace => "ERR_TWS"
8690

8791
/-- Context for a style error: the actual error, the line number in the file we're reading
8892
and the path to the file. -/
@@ -160,6 +164,7 @@ def parse?_errorContext (line : String) : Option ErrorContext := Id.run do
160164
-- Use default values for parameters which are ignored for comparing style exceptions.
161165
-- NB: keep this in sync with `compare` above!
162166
| "ERR_ADN" => some (StyleError.adaptationNote)
167+
| "ERR_TWS" => some (StyleError.trailingWhitespace)
163168
| "ERR_WIN" => some (StyleError.windowsLineEnding)
164169
| _ => none
165170
match String.toNat? lineNumber with
@@ -196,14 +201,24 @@ section
196201
/-- Lint on any occurrences of the string "Adaptation note:" or variants thereof. -/
197202
def adaptationNoteLinter : TextbasedLinter := fun lines ↦ Id.run do
198203
let mut errors := Array.mkEmpty 0
199-
let mut lineNumber := 1
200-
for line in lines do
204+
for (line, idx) in lines.zipWithIndex do
201205
-- We make this shorter to catch "Adaptation note", "adaptation note" and a missing colon.
202206
if line.containsSubstr "daptation note" then
203-
errors := errors.push (StyleError.adaptationNote, lineNumber)
204-
lineNumber := lineNumber + 1
207+
errors := errors.push (StyleError.adaptationNote, idx + 1)
205208
return (errors, none)
206209

210+
211+
/-- Lint a collection of input strings if one of them contains trailing whitespace. -/
212+
def trailingWhitespaceLinter : TextbasedLinter := fun lines ↦ Id.run do
213+
let mut errors := Array.mkEmpty 0
214+
let mut fixedLines := lines
215+
for (line, idx) in lines.zipWithIndex do
216+
if line.back == ' ' then
217+
errors := errors.push (StyleError.trailingWhitespace, idx + 1)
218+
fixedLines := fixedLines.set! idx line.trimRight
219+
return (errors, if errors.size > 0 then some fixedLines else none)
220+
221+
207222
/-- Whether a collection of lines consists *only* of imports, blank lines and single-line comments.
208223
In practice, this means it's an imports-only file and exempt from almost all linting. -/
209224
def isImportsOnlyFile (lines : Array String) : Bool :=
@@ -215,7 +230,7 @@ end
215230

216231
/-- All text-based linters registered in this file. -/
217232
def allLinters : Array TextbasedLinter := #[
218-
adaptationNoteLinter
233+
adaptationNoteLinter, trailingWhitespaceLinter
219234
]
220235

221236

@@ -257,7 +272,6 @@ def lintFile (path : FilePath) (exceptions : Array ErrorContext) :
257272
(allOutput.flatten.filter (fun e ↦ (e.find?_comparable exceptions).isNone))
258273
return (errors, if changes_made then some changed else none)
259274

260-
261275
/-- Lint a collection of modules for style violations.
262276
Print formatted errors for all unexpected style violations to standard output;
263277
correct automatically fixable style errors if configured so.

scripts/lint-style.py

+1-14
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
ERR_IBY = 11 # isolated by
4040
ERR_IWH = 22 # isolated where
4141
ERR_SEM = 13 # the substring " ;"
42-
ERR_TWS = 15 # trailing whitespace
4342
ERR_CLN = 16 # line starts with a colon
4443
ERR_IND = 17 # second line not correctly indented
4544
ERR_ARR = 18 # space after "←"
@@ -107,15 +106,6 @@ def annotate_strings(enumerate_lines):
107106
continue
108107
yield line_nr, line, *rem, False
109108

110-
def line_endings_check(lines, path):
111-
errors = []
112-
newlines = []
113-
for line_nr, line in lines:
114-
if line.endswith(" \n"):
115-
errors += [(ERR_TWS, line_nr, path)]
116-
line = line.rstrip() + "\n"
117-
newlines.append((line_nr, line))
118-
return errors, newlines
119109

120110
def four_spaces_in_second_line(lines, path):
121111
# TODO: also fix the space for all lines before ":=", right now we only fix the line after
@@ -248,8 +238,6 @@ def format_errors(errors):
248238
output_message(path, line_nr, "ERR_IWH", "Line is an isolated where")
249239
if errno == ERR_SEM:
250240
output_message(path, line_nr, "ERR_SEM", "Line contains a space before a semicolon")
251-
if errno == ERR_TWS:
252-
output_message(path, line_nr, "ERR_TWS", "Trailing whitespace detected on line")
253241
if errno == ERR_CLN:
254242
output_message(path, line_nr, "ERR_CLN", "Put : and := before line breaks, not after")
255243
if errno == ERR_IND:
@@ -267,8 +255,7 @@ def lint(path, fix=False):
267255
lines = f.readlines()
268256
enum_lines = enumerate(lines, 1)
269257
newlines = enum_lines
270-
for error_check in [line_endings_check,
271-
four_spaces_in_second_line,
258+
for error_check in [four_spaces_in_second_line,
272259
isolated_by_dot_semicolon_check,
273260
left_arrow_check,
274261
nonterminal_simp_check]:

0 commit comments

Comments
 (0)