Rules are organized by category. Each rule shows its severity and default preset status.
Severity levels: error, warning, info
Presets: Rules belong to one or more built-in presets.
:default— low-noise rules suitable for existing codebases:strict— extends:defaultwith opinionated best-practice rules; good for new projects and AI-assisted coding:all— every rule, including metrics and style preferences
The Default column below reflects :default preset membership. Rules marked as "strict" are off in :default but enabled in :strict.
Suppressing violations: Any rule can be suppressed using #+mallet (declaim (mallet:suppress-next :rule-name)). See the README for details.
Correctness — Objectively wrong code
| Rule | Description | Severity | Default | Options |
|---|---|---|---|---|
:wrong-otherwise |
ecase/etypecase with otherwise/t clause |
error | on | |
:mixed-optional-and-key |
Mixing &optional and &key parameters |
error | on | |
:asdf-if-feature-keyword |
Feature expressions must use keywords in defsystem | warning | on | |
:asdf-secondary-system-name |
Secondary systems must use primary/suffix name |
warning | on | |
:coalton-missing-to-boolean |
Coalton lisp Boolean form missing to-boolean |
warning | off |
Suspicious — Likely wrong or dangerous patterns
| Rule | Description | Severity | Default | Options |
|---|---|---|---|---|
:no-eval |
Runtime use of cl:eval |
warning | on | |
:runtime-intern |
Runtime use of symbol-interning functions | warning | strict | |
:runtime-unintern |
Runtime use of cl:unintern |
warning | strict | |
:asdf-operate-in-perform |
Calling asdf:operate inside :perform |
warning | on |
Practice — Commonly accepted best practices
| Rule | Description | Severity | Default | Options |
|---|---|---|---|---|
:no-package-use |
Use of :use in defpackage |
warning | strict | |
:no-ignore-errors |
Use of cl:ignore-errors |
warning | on | |
:no-allow-other-keys |
Use of &allow-other-keys in lambda lists |
warning | strict | |
:double-colon-access |
Accessing internal symbols via :: |
warning | strict | |
:error-without-custom-condition |
Calling error without a custom condition type |
warning | strict | |
:asdf-component-strings |
ASDF components should use strings | warning | on | |
:asdf-reader-conditional |
#+/#- reader conditionals in defsystem |
info | strict | |
:bare-float-literal |
Float literals should have explicit type markers | info | strict | |
:coalton-missing-declare |
Coalton function define missing declare type signature |
warning | off |
Cleanliness — Dead code and unused definitions
| Rule | Description | Severity | Default | Fix | Options |
|---|---|---|---|---|---|
:unused-variables |
Variables that are never used | warning | on | ||
:unused-local-functions |
Local functions that are never called | warning | on | ||
:unused-local-nicknames |
Local nicknames that are never used | warning | on | yes | |
:unused-imported-symbols |
Imported symbols that are never used | warning | on | yes | |
:unused-loop-variables |
Loop variables that are never used | info | strict | ||
:stale-suppression |
Suppression directive has no effect | warning | on |
Style — Idiomatic patterns and naming
| Rule | Description | Severity | Default | Options |
|---|---|---|---|---|
:one-package-per-file |
Files should define their own package | info | off | |
:missing-else |
Use when/unless instead of if without else |
warning | on | |
:progn-in-conditional |
Use cond/when/unless instead of bare progn in if/and/or |
info | strict | |
:missing-otherwise |
case/typecase without otherwise clause |
warning | strict | |
:defpackage-interned-symbol |
Use uninterned symbols in package definitions | info | strict | |
:needless-let* |
Use let when bindings are independent |
warning | on | |
:special-variable-naming |
Special variables should be named *foo* |
info | off | |
:constant-naming |
Constants should be named +foo+ |
info | off | |
:asdf-redundant-package-prefix |
Redundant package prefixes in .asd files |
info | strict | |
:missing-docstring |
Top-level definitions missing docstrings | info | off | |
:missing-package-docstring |
Package definitions missing docstrings | info | off | |
:missing-variable-docstring |
Variable definitions missing docstrings | info | off | |
:missing-struct-docstring |
Struct definitions missing docstrings | info | off | |
:redundant-progn |
progn with a single body form is redundant |
warning | strict |
Format — Whitespace and file formatting
| Rule | Description | Severity | Default | Fix | Options |
|---|---|---|---|---|---|
:trailing-whitespace |
Lines with trailing whitespace | warning | on | yes | |
:no-tabs |
Tab characters in source | warning | on | ||
:missing-final-newline |
Files must end with a newline | warning | on | yes | |
:closing-paren-on-own-line |
Closing parens on their own line | warning | strict | ||
:line-length |
Lines exceeding maximum length | info | off | :max (100) |
|
:consecutive-blank-lines |
Excessive consecutive blank lines | info | off | yes | :max (2) |
Metrics — Code quality measurements
| Rule | Description | Severity | Default | Options |
|---|---|---|---|---|
:function-length |
Function exceeds maximum line count | info | off | :max (50) |
:cyclomatic-complexity |
Function has high cyclomatic complexity | info | off | :max (20), :variant (standard) |
:comment-ratio |
Function has too many comments | info | off | :max (0.5), :min-lines (5), :include-docstrings (nil) |
Rules that catch objectively wrong code — constructs that defeat language semantics or cause runtime errors.
ecase and etypecase should not have otherwise or t clauses.
;; Bad: defeats exhaustiveness checking
(ecase type
(:a 1)
(:b 2)
(otherwise 0))
;; Good
(ecase type
(:a 1)
(:b 2))Severity: error | Default: enabled
Don't mix &optional and &key in lambda lists.
;; Bad: ambiguous parameter binding
(defun foo (x &optional y &key z)
...)
;; Good: use only one
(defun foo (x &key y z)
...)Severity: error | Default: enabled
Feature expressions in :if-feature and (:feature ...) dependency forms must use keywords. Plain symbols are evaluated as variables at read time and will likely cause errors. Applies only to .asd files. Handles compound feature expressions like (:and ...), (:or ...), and (:not ...).
;; Bad: plain symbols instead of keywords
(defsystem "my-system"
:depends-on ((:feature unix "cl-cffi-gtk"))
:components ((:file "main" :if-feature sbcl)
(:file "other" :if-feature (:and unix linux))))
;; Good: keywords
(defsystem "my-system"
:depends-on ((:feature :unix "cl-cffi-gtk"))
:components ((:file "main" :if-feature :sbcl)
(:file "other" :if-feature (:and :unix :linux))))Severity: warning | Default: enabled
Secondary system names in a .asd file must follow the primary/suffix convention. In a file named foo.asd, systems other than "foo" must be named "foo/something". Arbitrary names like "foo-tests" or "bar" are not allowed by ASDF and may cause issues with system resolution.
;; Bad: in my-system.asd
(defsystem "my-system"
:depends-on ("alexandria"))
(defsystem "my-system-tests" ; should be "my-system/tests"
:depends-on ("my-system"))
(defsystem "other-project" ; unrelated name
:depends-on ("alexandria"))
;; Good: follows primary/suffix convention
(defsystem "my-system"
:depends-on ("alexandria"))
(defsystem "my-system/tests"
:depends-on ("my-system"))Severity: warning | Default: enabled
(lisp Boolean (...) body) forms inside coalton-toplevel must use to-boolean to convert CL generalized booleans to Coalton True/False. Without it, CL predicates return nil instead of False, causing incorrect runtime behavior.
(coalton-toplevel
;; Bad: CL predicate returns nil, not False
(declare even? (Integer -> Boolean))
(define (even? n)
(lisp Boolean (n)
(cl:evenp n)))
;; Good: to-boolean converts nil/T to False/True
(declare even? (Integer -> Boolean))
(define (even? n)
(lisp Boolean (n)
(to-boolean (cl:evenp n)))))Detection: Recursively searches all nesting levels inside coalton-toplevel for (lisp Boolean (...) body) patterns. Recognizes both to-boolean and package-qualified variants like coalton-library/classes:to-boolean.
Severity: warning | Default: disabled (available via --all or --enable coalton-missing-to-boolean)
Rules that detect likely wrong or dangerous patterns — code that probably indicates a bug or a security risk.
Deprecated alias:
:eval-usage(use:no-eval)
Avoid using cl:eval at runtime. Runtime evaluation of arbitrary code is a common source of security vulnerabilities (code injection) and makes programs hard to reason about. This rule detects direct calls as well as indirect invocation via funcall and apply.
;; Bad
(eval user-input)
(funcall #'eval expr)
(apply #'eval forms)
;; Good: use compile-time macros or explicit dispatch instead
(case action
(:add (+ x y))
(:sub (- x y)))Exclusions:
defmacrobodies are skipped (macro expansion code is not runtime)eval-whenbodies are only checked when:executeis in the situation list
Severity: warning | Default: enabled
Avoid using symbol-interning functions at runtime. Runtime interning causes symbol table side effects, makes code harder to reason about, and is often a sign that a macro or compile-time mechanism should be used instead.
Detected functions:
cl:intern,cl:uninternuiop:intern*alexandria:symbolicate,alexandria:format-symbol,alexandria:make-keyword
;; Bad
(intern name :my-package)
(alexandria:symbolicate prefix "-" suffix)
;; Good: use compile-time macros or static symbols
(defmacro def-accessor (name)
`(defun ,(alexandria:symbolicate name '-get) () ...))Exclusions:
defmacrobodies are skipped (macro expansion code is not runtime)eval-whenbodies are only checked when:executeis in the situation list
Severity: warning | Default: disabled
Avoid calling cl:unintern at runtime. unintern mutates the live package structure and can break symbol identity across packages. This is a focused companion to :runtime-intern that targets only unintern.
;; Bad
(unintern sym :my-package)
(funcall #'unintern sym)
;; Good: redesign to avoid runtime package mutationExclusions:
defmacrobodies are skipped (macro expansion code is not runtime)
Severity: warning | Default: disabled
Do not call asdf:operate or related functions inside :perform method bodies. These calls can cause infinite loops or unexpected build behavior. Use symbol-call instead. Applies only to .asd files.
Detected functions: operate, oos, load-system, test-system, clear-system, require-system, make, compile-system (when qualified with asdf: or an ASDF sub-package prefix).
;; Bad: direct asdf calls in :perform
(defsystem "my-system"
:perform (test-op (o c)
(asdf:load-system "my-system/tests")
(asdf:test-system "my-system/tests")))
;; Good: use symbol-call
(defsystem "my-system"
:perform (test-op (o c)
(symbol-call :asdf :load-system "my-system/tests")
(symbol-call :asdf :test-system "my-system/tests")))Severity: warning | Default: enabled
Rules that enforce commonly accepted best practices — not wrong, but widely discouraged patterns.
Avoid using :use in cl:defpackage or uiop:define-package. The :use option imports all exported symbols from the named package, which makes it hard to tell which symbols are actually used and risks symbol conflicts when the used package exports new symbols.
The packages #:cl, #:common-lisp, #:coalton, and #:coalton-prelude are exempt.
;; Bad
(defpackage #:my-package
(:use #:cl #:alexandria) ; imports all of alexandria's exports
(:export #:my-function))
;; Good: import only what you need
(defpackage #:my-package
(:use #:cl)
(:import-from #:alexandria #:when-let #:if-let)
(:export #:my-function))Severity: warning | Default: disabled (:strict and above)
Deprecated alias:
:ignore-errors-usage(use:no-ignore-errors)
Avoid using cl:ignore-errors at runtime. ignore-errors silently swallows all errors and returns nil, making it very difficult to diagnose problems. Use handler-case with specific condition types instead.
;; Bad: silently swallows all errors
(ignore-errors (parse-config path))
;; Good: handle specific conditions explicitly
(handler-case (parse-config path)
(file-error (e) (format t "Cannot read config: ~A" e))
(parse-error (e) (format t "Invalid config: ~A" e)))Exclusions:
defmacrobodies are skipped (macro expansion code is not runtime)
Severity: warning | Default: enabled
Deprecated alias:
:allow-other-keys(use:no-allow-other-keys)
Avoid &allow-other-keys in lambda lists. It disables keyword argument checking, hiding typos and invalid arguments that would otherwise be caught at call sites.
;; Bad: silently accepts any keyword
(defun connect (host &key port timeout &allow-other-keys)
...)
;; Good: explicit parameters
(defun connect (host &key port timeout)
...)Severity: warning | Default: enabled
Avoid accessing internal symbols via :: package qualifier. Using :: bypasses package encapsulation, coupling code to package internals and making it fragile against refactoring.
;; Bad
(my-lib::internal-function arg)
;; Good: use only exported symbols
(my-lib:public-function arg)Severity: warning | Default: disabled (:strict and above)
Avoid calling error with a string literal or a built-in CL condition type. These prevent callers from handling errors specifically with handler-case. Define and signal a custom condition type instead.
;; Bad: callers can only catch simple-error or cl:error
(error "connection failed: ~A" host)
(error 'cl:simple-error :format-control "failed: ~A" :format-arguments (list host))
;; Good: define a custom condition type
(define-condition connection-error (error)
((host :initarg :host :reader connection-error-host)))
(error 'connection-error :host host)Severity: warning | Default: disabled
Avoid #+/#- reader conditionals inside defsystem bodies in .asd files. Reader conditionals are processed at read time and cannot be controlled by ASDF, making them less portable. Use ASDF's built-in :if-feature (component option) and (:feature ...) (dependency modifier) instead.
;; Bad: reader conditionals in defsystem
(defsystem "my-system"
:components ((:file "main")
#+sbcl (:file "sbcl-support")
#-windows (:file "unix-support")))
;; Good: use :if-feature
(defsystem "my-system"
:components ((:file "main")
(:file "sbcl-support" :if-feature :sbcl)
(:file "unix-support" :if-feature (:not :windows))))Exclusions:
- Reader conditionals inside
:performbodies are not flagged (they are legitimate runtime CL code) - Reader conditionals outside
defsystemforms are ignored - Comments, string literals, and character literals are not scanned
Severity: info | Default: disabled
ASDF systems, components, and dependencies should use strings not symbols. Applies only to .asd files.
;; Bad
(defsystem #:my-system
:depends-on (#:alexandria))
;; Good
(defsystem "my-system"
:depends-on ("alexandria"))Severity: warning | Default: enabled
Float literals should have explicit type markers (f, d, s, l). Without a marker, the type depends on *read-default-float-format*, which can vary.
;; Bad: depends on *read-default-float-format*
(defvar *threshold* 0.5)
;; Good: explicit double-float
(defvar *threshold* 0.5d0)Severity: info | Default: disabled
Every function define inside coalton-toplevel should have a preceding declare type signature. Missing declarations can cause the monomorphism restriction and lack documentation of intent.
(coalton-toplevel
;; Bad: no type declaration
(define (add-one x) (+ x 1))
;; Good: declare before define
(declare add-one (Integer -> Integer))
(define (add-one x) (+ x 1)))What is not flagged:
- Value defines:
(define pi 314) define-type,define-instance,define-structforms- Lambda-value defines:
(define foo (fn (x) x))
Detection: Each coalton-toplevel block is checked independently. A declare must appear before the corresponding define within the same block.
Severity: warning | Default: disabled (available via --all or --enable coalton-missing-declare)
Rules that detect dead code and unused definitions.
Variables should be used or explicitly ignored with (declare (ignore ...)) or underscore prefix.
;; Bad
(defun add (x y)
(+ x 1)) ; y is unused
;; Good
(defun add (x _y)
(+ x 1))Severity: warning | Default: enabled
Local functions defined in flet or labels should be used.
;; Bad
(flet ((helper (x) (* x 2))
(unused (x) (+ x 1)))
(helper 10)) ; unused is never called
;; Good
(flet ((helper (x) (* x 2)))
(helper 10))Severity: warning | Default: enabled
Local nicknames defined in defpackage should be used in the package.
;; Bad: unused nickname
(defpackage #:foo
(:use #:cl)
(:local-nicknames
(#:unused #:alexandria))) ; never used
;; Good: all nicknames used
(defpackage #:foo
(:use #:cl)
(:local-nicknames
(#:a #:alexandria)))Severity: warning | Default: enabled
Imported symbols should be used or re-exported.
;; Bad: imported but never used
(defpackage #:foo
(:use #:cl)
(:import-from #:alexandria
#:flatten
#:hash-table-keys)) ; never used
;; Good
(defpackage #:foo
(:use #:cl)
(:import-from #:alexandria #:flatten)
(:export #:flatten)) ; re-exportedSeverity: warning | Default: enabled
Loop variables should be used within the loop body.
;; Bad
(loop for x in list
for y in other-list ; y is unused
collect x)
;; Good: explicitly ignore with underscore
(loop for x in list
for _y in other-list
collect x)Severity: info | Default: disabled
A suppression directive (inline comment or #+mallet form) has no effect because no matching violation was found at the suppressed location.
This rule fires when:
- A
; mallet:suppress rule-namecomment is present but the named rule produces no violation on that form. - A
; mallet:disable/; mallet:enableregion contains no violations for the listed rules.
Enable this rule to catch outdated suppression comments left behind after code changes.
;; Bad: no violation here, suppression is stale
(let ((x (foo)) ; mallet:suppress needless-let*
(y (bar)))
(list x y))
;; Good: violation exists, suppression is meaningful
(let* ((x (foo)) ; mallet:suppress needless-let*
(y (bar)))
(list x y))Severity: warning | Default: enabled
Rules for idiomatic patterns and naming conventions.
Each .lisp file should start with defpackage or uiop:define-package, defining its own package. Files that begin with in-package without a preceding defpackage in the same file are flagged.
;; Bad: in-package without a preceding defpackage
(in-package #:my-app/utils)
(defun helper () ...)
;; Good: file defines its own package
(defpackage #:my-app/utils
(:use #:cl)
(:export #:helper))
(in-package #:my-app/utils)
(defun helper () ...)Severity: info | Default: disabled (available via --all or --enable one-package-per-file)
Deprecated alias:
:if-without-else(use:missing-else)
Use when or unless instead of if without else.
;; Bad
(if condition
(do-something))
;; Good
(when condition
(do-something))Severity: warning | Default: enabled
Use cond instead of if with bare progn. Use when instead of and with bare progn as the last argument. Use unless instead of or with bare progn as the last argument.
;; Bad: if with bare progn
(if condition
(progn
(do-one)
(do-two)))
;; Good
(cond
(condition
(do-one)
(do-two)))
;; Bad: and with bare progn as last argument
(and condition
(progn
(do-one)
(do-two)))
;; Good
(when condition
(do-one)
(do-two))
;; Bad: or with bare progn as last argument
(or condition
(progn
(do-one)
(do-two)))
;; Good
(unless condition
(do-one)
(do-two))Severity: info | Default: disabled
case and typecase should have an otherwise clause.
;; Bad
(case type
(:a 1)
(:b 2))
;; Good
(case type
(:a 1)
(:b 2)
(otherwise nil))Severity: warning | Default: disabled
Deprecated alias:
:interned-package-symbol(use:defpackage-interned-symbol)
Use uninterned symbols (#:symbol) in package definitions instead of keywords or bare symbols.
;; Bad: keywords
(defpackage :myapp
(:use :cl))
;; Good: uninterned symbols
(defpackage #:myapp
(:use #:cl))Severity: info | Default: disabled
Use let instead of let* when bindings don't depend on each other (including single-binding let*).
;; Bad: bindings are independent
(let* ((x (foo))
(y (bar)))
(list x y))
;; Good
(let ((x (foo))
(y (bar)))
(list x y))Severity: warning | Default: enabled
Special variables should be named *foo*. Applies to defvar, defparameter, and sb-ext:defglobal.
;; Bad
(defvar config nil)
(sb-ext:defglobal global-state nil)
;; Good
(defvar *config* nil)
(sb-ext:defglobal *global-state* nil)Note: Variables named with +plus+ convention are also accepted (since defglobal is sometimes used for constants).
Severity: info | Default: disabled
Constants should be named +foo+.
;; Bad
(defconstant pi 3.14159)
;; Good
(defconstant +pi+ 3.14159)Severity: info | Default: disabled
Package prefixes asdf:, cl:, common-lisp:, and uiop: are redundant in .asd files. These files run in the asdf-user package which already uses asdf, cl, and uiop, so qualifying symbols with those package names is unnecessary. Sub-package prefixes like uiop/filesystem: are also flagged.
;; Bad: redundant package prefixes
(asdf:defsystem "my-system"
:depends-on ("alexandria")
:description (cl:format nil "tests"))
;; Good: no prefixes needed
(defsystem "my-system"
:depends-on ("alexandria")
:description (format nil "tests"))Severity: info | Default: disabled
Top-level definitions should have docstrings. Applies to defun, defmacro, defgeneric, and defclass forms. defmethod is exempt because methods inherit documentation from the generic function.
;; Bad: no docstring
(defun add (x y)
(+ x y))
(defclass point ()
((x :initarg :x)
(y :initarg :y)))
;; Good: docstrings present
(defun add (x y)
"Return the sum of X and Y."
(+ x y))
(defclass point ()
((x :initarg :x)
(y :initarg :y))
(:documentation "A two-dimensional point."))Severity: info | Default: disabled
Package definitions should have a :documentation string. Applies to defpackage and define-package forms.
;; Bad: no :documentation option
(defpackage #:my-lib
(:use #:cl)
(:export #:connect))
;; Good: documentation present
(defpackage #:my-lib
(:use #:cl)
(:export #:connect)
(:documentation "My library for connections."))Severity: info | Default: disabled
Variable definitions should have docstrings. Applies to defvar and defparameter forms. defvar without an initial value is exempt (cannot place a docstring there).
;; Bad: no docstring
(defvar *connection-timeout* 30)
(defparameter *max-retries* 5)
;; Good: docstrings present
(defvar *connection-timeout* 30
"Seconds before a connection attempt times out.")
(defparameter *max-retries* 5
"Number of times to retry a failed connection.")Severity: info | Default: disabled
Struct definitions should have docstrings. Checks both the body string position (like defun) and the (:documentation ...) option in name-and-options. Applies to defstruct forms.
;; Bad: no docstring
(defstruct point x y)
;; Good: body docstring
(defstruct point
"A two-dimensional point."
x y)
;; Also good: :documentation in options
(defstruct (point (:constructor make-point) (:documentation "A two-dimensional point."))
x y)Severity: info | Default: disabled
progn with a single body form is redundant — the progn wrapper can be removed.
;; Bad: progn with one form
(progn
(do-something))
;; Good: no wrapper needed
(do-something)Exclusions:
prognwrapping a single,@splice(unquote-splicing) is allowed, since the splice may expand to multiple forms
Severity: warning | Default: disabled (:strict and above)
Rules for whitespace and file formatting. These follow strong community consensus (Emacs/SLIME standards).
Lines should not have trailing whitespace.
Auto-fixable: --fix removes trailing whitespace.
Severity: warning | Default: enabled
Use spaces instead of tab characters.
Severity: warning | Default: enabled
Deprecated alias:
:final-newline(use:missing-final-newline)
Files must end with a newline.
Auto-fixable: --fix appends a newline.
Severity: warning | Default: enabled
Closing parentheses should follow the last expression on the same line, not appear alone on their own line. This is the idiomatic Common Lisp style, unlike Algol-family languages.
;; Bad
(defun foo ()
(bar)
)
;; Good
(defun foo ()
(bar))Exception: Closing parens after a line ending with a comment are allowed, since appending them to the comment line would break the comment.
;; OK: previous line ends with a comment
(defvar *alist*
'((a . 1)
(b . 2) ; last entry
))Severity: warning | Default: disabled (:strict and above)
Lines should not exceed maximum length.
Options: :max (default: 100)
(:enable :line-length :max 120)Severity: info | Default: disabled
Limit consecutive blank lines.
Options: :max (default: 2)
Auto-fixable: --fix removes excess blank lines.
(:enable :consecutive-blank-lines :max 1)Severity: info | Default: disabled
Code quality metrics that measure complexity and size. These are informational and don't indicate bugs or errors.
Functions should not exceed maximum line count.
Options: :max (default: 50)
Counting: Only code lines — excludes comments, blank lines, docstrings, and disabled reader conditionals (#+nil, #+(or), #-(and)).
Nested flet/labels functions are counted separately.
Severity: info | Default: disabled
Functions should not exceed maximum cyclomatic complexity.
Options:
:max(default: 20) - Maximum allowed complexity:variant(default::standard) -:standardor:modified
Complexity calculation (:standard variant):
- Base: 1 per function
- Conditionals: +1 for each
if,when,unless - COND: +1 per clause (excluding final
t/otherwise) - CASE/TYPECASE: +1 per clause (excluding final
otherwise/t)ecase,etypecase,ccase,ctypecase: all clauses count
- Logical operators: +1 for each
andoror - DO/DO*: +1 each (end-test condition)
- LOOP: +1 per conditional keyword (
when,unless,if,while,until) - Exception handling: +1 per handler clause
- Third-party: Alexandria (
if-let,when-let, etc.), Trivia (match, etc.),string-case
:modified variant: Same, except CASE/TYPECASE and case-like macros count as +1 total instead of per-clause.
Nested flet/labels functions are counted separately.
Severity: info | Default: disabled
Functions should not have too many comments relative to code. Useful for catching AI-generated code padded with excessive inline commentary.
Options:
:max(default: 0.5) - Maximum comment ratio (0.0–1.0):min-lines(default: 5) - Minimum qualifying lines before the rule applies:include-docstrings(default:nil) - Count docstring lines as comments
Ratio formula: comment-lines / (comment-lines + code-lines)
Nested flet/labels functions are counted separately.
Notes:
- Functions with fewer qualifying lines (comment + code) than
:min-linesare skipped. Docstring lines are not counted toward the threshold unless:include-docstrings tis set.
Severity: info | Default: disabled