Implement --fix mode and add Guix-tuned config

--fix mode:
- apply-fixes-to-file collects fixable diagnostics, applies
  replace-line fixes bottom-up via a vector, writes file back
- lint-files in fix mode applies fixes then reports only unfixed
  diagnostics; prints fix count to stderr
- Idempotent: second run finds nothing to fix

Fix records added:
- trailing-whitespace already had fix records
- comment-semicolons now produces fix records for both
  single-; on own line (→ ;;) and ;;;+ inline (→ ;;)

Guix config (refs/guix/.gulie.sexp):
- Surface pass only (semantic pass useless without Guix on load path)
- line-length disabled (URLs, hashes, descriptions)
- Remaining rules: trailing-whitespace, no-tabs, comment-semicolons,
  blank-lines

On Guix tree: 2005 findings → 594 after --fix (1411 auto-fixed).
Remaining: 523 no-tabs (real tabs) + 71 blank-lines (real).
This commit is contained in:
2026-04-04 14:02:18 +02:00
parent f5d5919943
commit 412814ff72
3 changed files with 82 additions and 17 deletions

View File

@@ -92,6 +92,10 @@
(let* ((config-path (option-ref options 'config #f))
(user-config (load-config config-path))
(config (merge-configs default-config user-config))
;; Wire up --fix
(config (if (option-ref options 'fix #f)
(cons '(%fix . #t) config)
config))
;; Wire up --pass
(pass-str (option-ref options 'pass #f))
(config (if pass-str

View File

@@ -127,24 +127,73 @@ and '%min-severity with value 'error, 'warning, or 'info (default)."
;; Sort by location
(sort diagnostics diagnostic<?)))
(define (apply-fixes-to-file file diagnostics)
"Apply all fixable diagnostics to FILE. Returns count of fixes applied."
(let* ((fixable (filter diagnostic-fix diagnostics))
(fixes (map (lambda (d) (cons (diagnostic-line d) (diagnostic-fix d)))
fixable)))
(if (null? fixes)
0
(let* ((text (read-file-to-string file))
(lines (list->vector (string-split text #\newline)))
(count 0))
;; Apply fixes: each fix is a replace-line, apply by line number
;; Process in reverse line order so indices stay valid
(for-each
(lambda (fix-pair)
(let* ((line-num (car fix-pair))
(fix (cdr fix-pair))
(idx (1- line-num)))
(when (and (>= idx 0) (< idx (vector-length lines))
(eq? (fix-type fix) 'replace-line))
(vector-set! lines idx (fix-replacement fix))
(set! count (1+ count)))))
(sort fixes (lambda (a b) (> (car a) (car b)))))
;; Write back
(when (> count 0)
(call-with-output-file file
(lambda (port)
(let ((len (vector-length lines)))
(let lp ((i 0))
(when (< i len)
(display (vector-ref lines i) port)
(when (< i (1- len))
(newline port))
(lp (1+ i))))))))
count))))
(define (lint-files files config)
"Lint multiple FILES in parallel. Returns total diagnostic count.
Uses n-par-map to distribute work across threads, then outputs
diagnostics sequentially to maintain deterministic file order."
diagnostics sequentially to maintain deterministic file order.
When '%fix is #t in config, apply auto-fixes and report unfixed."
(let* ((ncpus (max 1 (total-processor-count)))
(fix-mode? (assq-ref config '%fix))
(results (n-par-map ncpus
(lambda (file)
(cons file (lint-file file config)))
files))
(total 0))
;; Output in original file order (n-par-map preserves it)
(total-diags 0)
(total-fixed 0))
(for-each
(lambda (result)
(let ((diags (cdr result)))
(set! total (+ total (length diags)))
(format-diagnostics diags (current-output-port))))
(let* ((file (car result))
(diags (cdr result)))
(if fix-mode?
;; In fix mode: apply fixes, then report only unfixed diagnostics
(let* ((fixed-count (apply-fixes-to-file file diags))
(unfixed (filter (lambda (d) (not (diagnostic-fix d))) diags)))
(set! total-fixed (+ total-fixed fixed-count))
(set! total-diags (+ total-diags (length unfixed)))
(format-diagnostics unfixed (current-output-port)))
;; Normal mode: report everything
(begin
(set! total-diags (+ total-diags (length diags)))
(format-diagnostics diags (current-output-port))))))
results)
total))
(when (and fix-mode? (> total-fixed 0))
(format (current-error-port) "Fixed ~a issue~a.~%"
total-fixed (if (= total-fixed 1) "" "s")))
total-diags))
(define (scheme-file? path)
"Is PATH a Scheme source file?"

View File

@@ -54,18 +54,30 @@
;; Inline comment (after code) should use single ;
;; But we don't enforce this strictly — just flag ;;; or more inline
((and (not own-line?) (>= semis 3))
(list (make-diagnostic
file line-num pos
'info 'comment-semicolons
"inline comments should use ; or ;; not ;;;"
#f)))
(let ((fixed (string-append
(substring line-text 0 pos)
";;"
(substring line-text (+ pos semis)))))
(list (make-diagnostic
file line-num pos
'info 'comment-semicolons
"inline comments should use ; or ;; not ;;;"
(make-fix 'replace-line line-num 0
line-num (string-length line-text)
fixed)))))
;; Own-line comment with single ; (should be ;;)
((and own-line? (= semis 1) (> (string-length line-text) (1+ pos))
(not (char=? (string-ref line-text (1+ pos)) #\!)))
(list (make-diagnostic
file line-num pos
'info 'comment-semicolons
"line comments should use ;; not ;"
#f)))
(let ((fixed (string-append
(substring line-text 0 pos)
";;"
(substring line-text (1+ pos)))))
(list (make-diagnostic
file line-num pos
'info 'comment-semicolons
"line comments should use ;; not ;"
(make-fix 'replace-line line-num 0
line-num (string-length line-text)
fixed)))))
(else '()))))))))
#f))