This page contains an overview of all available linters and their corresponding configuration. For general configurations options, go here.
Table of Contents
- Linters
- Aliased namespace symbol
- Aliased namespace var usage
- Case
- Case duplicate test
- Clj-kondo config
- Cond-else
- Condition always true
- Conflicting-alias
- Consistent-alias
- Datalog syntax
- Deprecated var
- Deprecated namespace
- Deps.edn
- Bb.edn
- Discouraged var
- Discouraged namespace
- Discouraged tag
- Docstring blank
- Docstring no summary
- Docstring leading trailing whitespace
- Duplicate map key
- Duplicate require
- Duplicate set key
- Duplicate field name
- Dynamic vars
- Quoted case test constant
- Equals false
- Equals true
- File
- Format
- Def + fn instead of defn
- Inline def
- Invalid arity
- Conflicting arity
- Reduce without initial value
- Loop without recur
- Line length
- Keyword in binding vector
- Main without gen-class
- Minus one
- Misplaced docstring
- Missing body in when
- Missing clause in try
- Missing docstring
- Missing else branch
- Missing map value
- Missing test assertion
- Namespace name mismatch
- Non-arg vec return type hint
- Not empty?
- Plus one
- Private call
- Protocol method varargs
- Redefined var
- Var same name except case
- Redundant do
- Redundant fn wrapper
- Redundant call
- Redundant let
- Refer
- Refer all
- Single key in
- Single logical operand
- Single operand comparison
- Shadowed var
- Syntax
- Type mismatch
- Unbound destructuring default
- Uninitialized var
- Unused alias
- Unused binding
- Unused value
- Used underscored bindings
- Unknown :require option
- Unreachable code
- Unused import
- Unresolved namespace
- Unresolved symbol
- Unresolved var
- Unsorted imports
- Unsorted required namespaces
- Unused namespace
- Unused private var
- Unused referred var
- Use
- Warn on reflection
Keyword: :aliased-namespace-symbol
.
Description: warn when the namespace of a qualified symbol has a defined alias.
Default level: :off
.
Example trigger:
(ns foo
(:require [clojure.string :as str]))
(clojure.string/join ", " (range 10))
Example message: An alias is defined for clojure.string: str
.
Config: to suppress the above warning:
{:linters {:aliased-namespace-symbol {:exclude [clojure.string]}}}
Keyword: :aliased-namespace-var-usage
.
Description: warn when a var from a namespace that was used with :as-alias
is used.
Default level: :warning
.
Example trigger:
(ns foo
(:require [clojure.data.xml :as-alias xml]))
(xml/parse-str "<foo/>")
Example message: Namespace only aliased but wasn't loaded: clojure.data.xml
Keyword: :case-duplicate-test
.
Description: identify duplicate case test constants.
Default level: :error
.
Example trigger: (case x :a 1 :b 2 :a 3)
Example message: Duplicate case test constant: :a
.
Keyword: :case-quoted-test
Description: Warn on quoted test constants in case
, a common mistake when
users don't yet understand that test constants in case are not evaluated.
Default level: :warning
Example trigger:
(case 'x
'x 1)
Example message: Case test is compile time constant and should not be quoted.
Keyword: :case-symbol-test
Description: Warn on symbol test constants in case
. Sometimes this is
intentional, but often users expect the symbol to be evaluated. To avoid this
confusion, enable this opt-in linter. Another reason to enable it might this
extra corner case in
CLJS-2209. To opt out after
enabling this linter, you can prepend the case
expression with
#_{:clj-kondo/ignore [:case-symbol-test]}
.
Default level: :off
Example trigger:
(let [x 1]
(case x
x 1))
Example message: Case test symbol is compile time constant and is never evaluated.
Keyword: :clj-kondo-config
Description: warn on common errors in .clj-kondo/config
files
Default level: :warning
Example trigger:
.clj-kondo/config.edn
:
{:linters {:foo 1}}
Example message: Unexpected linter name: :foo
.
Keyword: :cond-else
.
Description: warn on cond
with a different constant for the else branch than :else
.
Default level: :warning
.
Example trigger: (cond (odd? (rand-int 10)) :foo :default :bar)
.
Example message: use :else as the catch-all test expression in cond
.
Keyword: :condition-always-true
.
Description: warn on a condition that evaluates to an always truthy constant,
like when passing a function instead of calling it. This linter intentionally
doesn't check for literally true
values of vars since this is often a dev/production setting.
Default level: :off
(will be :warning
in a future release).
Example trigger: (if odd? :odd :even)
.
Example message: Condition always true
.
Keyword: :conflicting-alias
.
Description: warn on conflicting alias.
Default level: :error
.
Example trigger:
(require '[clojure.string :as s]
'[clojure.spec.alpha :as s])
Example message: Conflicting alias for clojure.spec.alpha
.
Keyword: :consistent-alias
Description: Sometimes it's desirable to have a consistent alias for certain
namespaces in a project. E.g. in the below code it could be desirable if every
alias for old.api
was old-api
:
Default level: :warning
.
Example trigger:
(ns foo (:require [new.api :as api]))
(ns bar (:require [old.api :as old-api]))
(ns baz (:require [old.api :as api]))
Config:
The consistent alias linter needs pre-configured aliases for namespaces that should have a consistent alias. This configuration:
{:linters {:consistent-alias {:aliases {old.api old-api}}}}
will produce this warning:
Inconsistent alias. Expected old-api instead of api.
Keyword: :datalog-syntax
.
Description: warn on invalid datalog syntax. This linter is implemented using io.lambdaforge/datalog-parser. Also see this blog post.
Default level: :error
.
Example trigger:
(ns user (:require [datahike.api :refer [q]]))
(q '[:find ?a :where [?b :foo _]] 42)
Example message: Query for unknown vars: [?a]
.
Keyword: :deprecated-var
.
Description: warn on usage of var that is deprecated.
Default level: :warning
.
Example trigger: (def ^:deprecated x) x
Example warning: #'user/x is deprecated
.
Config:
Say you have the following function:
(ns app.foo)
(defn foo {:deprecated "1.9.0"} [])
and you still want to be able to call it without getting a warning, for example in function in the same namespace which is also deprecated:
(defn bar {:deprecated "1.9.0"} []
(foo))
or in test code:
(ns app.foo-test
(:require
[app.foo :refer [foo]]
[clojure.test :refer [deftest is]]))
(deftest foo-test [] (is (nil? (foo))))
To achieve this, use this config:
{:linters
{:deprecated-var
{:exclude
{app.foo/foo
{:defs [app.foo/bar]
:namespaces [app.foo-test]}}}}}
A regex is also permitted, e.g. to exclude all test namespaces:
{:linters {:deprecated-var {:exclude {app.foo/foo {:namespaces [".*-test$"]}}}}}
Keyword: :deprecated-namespace
.
Description: warn on usage of namespace that is deprecated.
Default level: :warning
.
Example trigger:
(ns foo {:deprecated true})
(def x 1)
(ns bar (:require [foo]))
Example warning: Namespace foo is deprecated.
.
Config:
To exclude warnings about specific namespaces, use:
{:linters {:deprecated-namespace {:exclude [the-deprecated.namespace]}}}
Keyword: :deps.edn
Description: warn on common errors in deps.edn
and bb.edn
files.
Default level: :warning
Example trigger:
deps.edn
:
{:deps {foo/bar "2020.10.11"}}
Example message:
Expected map, found: java.lang.String
Keyword: :bb.edn-undefined-task
Description: warn on taks undefined task dependencies in bb.edn
files.
Default level: :error
Example trigger:
bb.edn
:
{:tasks {run {:depends [compile]}}}
Example message:
Depending on undefined task: compile
Keyword: :bb.edn-cyclic-task-dependency
Description: warn on cyclic dependencies bb.edn
files.
Default level: :error
Example trigger:
bb.edn
:
{:tasks {a {:depends [b]
:task (println "a")}
b {:depends [a]}}}
Example message:
Cyclic task dependency: a -> b -> a
Keyword: :bb.edn-unexpected-key
Description: warn on unexpected keys in bb.edn
Default level: :warning
Example trigger:
bb.edn
:
{:requires [[babashka.fs :as fs]]}
Example message:
Global :requires belong in the :tasks map.
Keyword: :bb.edn-task-missing-docstring
Description: warn on missing docstring for map tasks.
Default level: :off
Example trigger:
bb.edn
:
{:tasks {a {:task (call-fn}]}
Example message:
Docstring missing for task: a
Keyword: :discouraged-var
Description: warn on the usage of a var that is discouraged to be used.
Default level: :warning
Config:
{:linters {:discouraged-var {clojure.core/read-string {:message "Use edn/read-string instead of read-string"}}}}
The matching namespace symbol may be given a group name using a regex
pattern. The warning can be made undone on the namespace level (e.g. via
:config-in-ns
or ns metadata) by providing :level
on the var level:
{:linters {:discouraged-var {clojure.core/read-string {:level :off}}}}
Example trigger:
With the configuration above:
(read-string "(+ 1 2 3)")
Example message:
Use edn/read-string instead of read-string
Keyword: :discouraged-namespace
Description: warn on the require or usage of a namespace that is discouraged to be used.
Default level: :warning
Config:
{:linters {:discouraged-namespace {clojure.java.jdbc {:message "Use next.jdbc instead of clojure.java.jdbc"}}}}
The matching namespace symbol may be given a group name using a regex pattern.
{:ns-groups [{:pattern "clojure\\.java\\.jdbc.*"
:name jdbc-legacy}]
:linters {:discouraged-namespace {jdbc-legacy {:message "Use next.jdbc instead of clojure.java.jdbc"}}}}
Add :discouraged-namespace
linter into :config-in-ns
to specify that specific namespaces are discouraged to be used in some namespace of ns-group.
{:config-in-ns {app.jdbc {:linters {:discouraged-namespace {clojure.java.jdbc {:message "Use next.jdbc instead of clojure.java.jdbc"}}}}}}
Example trigger:
With the configuration above:
(require '[clojure.java.jdbc :as j])
Keyword: :discouraged-tag
Description: warn on the usage of a tagged literal that is discouraged to be used.
Default level: :warning
Config:
{:linters {:discouraged-tag {inst {:message "Prefer #java-time/instant" }}}}
Example trigger:
Given the above configuration:
{:date #inst "2020"}
Example message:
Prefer #java-time/instant
Keyword: :docstring-blank
.
Description: warn on blank docstring.
Default level: :warning
.
Example trigger: (defn foo "" [a b] 1)
Example message: Docstring should not be blank.
.
Keyword: :docstring-no-summary
.
Description: warn when first line of docstring is not a complete sentence. This linter is based on the community style guide.
Explanation by Bozhidar Batsov:
The idea is simple - each docstring should start with a one-line sentence. This minimizes the work tools have to do to extract some meaningful summary of what a var does (and as a bonus - it plays great with the Emacs minibuffer, that happens to have a height of 1 line).
Default level: :off
.
Example trigger: (defn foo "not a sentence" [a b] 1)
Example message: First line of the docstring should be a capitalized sentence ending with punctuation.
Keyword: :docstring-leading-trailing-whitespace
.
Description: warn when docstring has leading or trailing whitespace
Default level: :off
.
Example trigger: (defn foo "Has trailing whitespace.\n" [a b] 1)
Example message: Docstring should not have leading or trailing whitespace.
Keyword: :duplicate-map-key
.
Description: warn on duplicate key in map.
Default level: :error
.
Example trigger: {:a 1 :a 2}
Example message: duplicate key :a
.
Keyword: :duplicate-require
.
Description: warns on namespace that has been required more than once within a namespace.
Example trigger:
(ns foo
(:require [clojure.string :as str]
[clojure.string :as str]))
Example message: duplicate require of clojure.string
Keyword: :duplicate-set-key
.
Description: similar to :duplicate-map-key
but for sets.
Example trigger: #{:a :a}
Example message: duplicate set element :a
.
Keyword: :duplicate-field-name
.
Description: identify duplicate fields in deftype/defrecord fields definition.
Default level: :error
.
Example trigger: (deftype T [x y z y])
Example message: Duplicate field name: y
.
Keyword: :dynamic-var-not-earmuffed
Description: warn when dynamic var doesn't have an earmuffed name.
Default level: :off
.
Example trigger: (def ^:dynamic foo)
Example message: "Var is declared dynamic but name is not earmuffed: foo"
Keyword: :earmuffed-var-not-dynamic
Description: warn when var with earmuffed name isn't declared dynamic.
Default level: :warning
.
Example trigger: (def *foo*)
Example message: "Var has earmuffed name but is not declared dynamic: *foo*"
Keyword: :quoted-case-test-constant
.
Description: warn when encountering quoted test case constants.
Default level: :warning
.
Example trigger: (case x 'a 1 :b 2)
Example message: Case test is compile time constant and should not be quoted.
Keyword: :equals-false
Description: warn on usage of (= false x)
or (= x false)
rather than (false? x)
Default level: :off
Example trigger: (fn [x] (= false x))
Example message: Prefer (false? x) over (= false x)
.
Keyword: :equals-true
Description: warn on usage of (= true x)
or (= x true)
rather than (true? x)
Default level: :off
Example trigger: (fn [x] (= true x))
Example message: Prefer (true? x) over (= true x)
.
Keyword: :file
.
Description: warn on error while reading file.
Default level: :error
.
Example trigger: clj-kondo --lint foo.clje
.
Example message: file does not exist
.
Keyword: :format
.
Description: warn on unexpected amount of arguments in format
.
Default level: :error
.
Example trigger: (format "%s" 1 2)
.
Example message: Format string expects 1 arguments instead of 2.
.
Keyword: :def-fn
.
Description: tells about closures defined with the combination of
def
and fn
with optional let
in-between. In almost all cases
defn
can be used instead which has the benefit of adding :arglists
metadata to vars.
The practice of using defn
instead of def
+ fn
has the following benefits:
:argslists*
metadata on the var- more readable stacktraces
Default level: :off
.
Example triggers:
(def f (fn [] nil))
which can be written as:(defn f [] nil)
).(def f (let [y 1] (fn [x] (+ x y))))
which can be written as(let [y 1] (defn f [x] (+ x y)))
.
Example messages:
Use defn instead of def + fn
Config:
{:linters {:def-fn {:level :warning}}}
More info:
See issue.
Keyword: :inline-def
.
Description: warn on non-toplevel usage of def
(and defn
, etc.).
Default level: :warning
.
Example trigger: (defn foo [] (def x 1))
.
Example message: inline def
.
Keyword: :invalid-arity
.
Description: warn when a function (or macro) is called with an invalid amount of arguments.
Default level: :error
.
Example trigger: (inc)
.
Example message: clojure.core/inc is called with 0 args but expects 1
.
Config:
Some macros rewrite their arguments and therefore can cause false positive arity errors. Imagine the following silly macro:
(ns silly-macros)
(defmacro with-map [m [fn & args]]
`(~fn ~m ~@args))
which you can call like:
(silly-macros/with-map {:a 1 :d 2} (select-keys [:a :b :c])) ;;=> {:a 1}
Normally a call to this macro will give an invalid arity error for (select-keys [:a :b :c])
, but not when you use the following configuration:
{:linters {:invalid-arity {:skip-args [silly-macros/with-map]}}}
Keyword: :conflicting-fn-arity
.
Description: warn when an overloaded function defines multiple argument vectors with the same arity.
Default level: :error
.
Example trigger: (fn ([x] x) ([y]) x)
.
Example message: More than one function overload with arity 2.
.
Keyword: :reduce-without-init
.
Description: warn when reduce is called without an explicit initial value. Read this article why this can be problematic.
Default level: :off
.
Example trigger: (reduce max [])
.
Example message: Reduce called without explicit initial value.
Config: to suppress the above warning:
{:linters {:reduce-without-init {:exclude [clojure.core/max cljs.core/max]}}}
Keyword: :loop-without-recur
.
Description: warn when loop does not contain recur.
Default level: :warning
.
Example trigger: (loop [])
.
Example message: Loop without recur.
Keyword: :line-length
.
Description: warn when lines are longer than a configured length.
Default level: :warning
.
Default line length: :max-line-length
is nil
by default, which disables line length linting.
Config:
The line length linter needs to know how long you are prepared to allow your lines to be. This configuration:
{:linters {:line-length {:max-line-length 120}}}
will produce this warning:
Line is longer than 120 characters.
Config:
To exclude lines with URLs use: :exclude-urls true
{:linters {:line-length {:max-line-length 120
:exclude-urls true}}}
To exclude lines that matches a pattern via re-find
, use: :exclude-pattern ";; :ll/ok"
:
{:linters {:line-length {:max-line-length 120
:exclude-pattern ";; :ll/ok"}}}
Keyword: :keyword-binding
Description: warn when a keyword is used in a :keys
binding vector
Default level: :off
.
Example trigger: (let [{:keys [:a]} {:a 1}] a)
.
Example message: Keyword binding should be a symbol: :a
Keyword: :main-without-gen-class
.
Description: warn when -main function is present without corresponding :gen-class
.
Default level: :off
.
Example trigger: (ns foo) (defn -main [& _args])
.
Example message: Main function without gen-class.
Keyword: :minus-one
Description: warn on usages of -
that can be replaced with dec
.
Default level: :off
Example trigger:
(def x 1)
(- x 1)
Example message: Prefer (dec x) over (- x 1)
Also see :plus-one
.
Keyword: :misplaced-docstring
.
Description: warn when docstring appears after argument vector instead of before.
Default level: :warning
.
Example trigger: (defn foo [] "cool fn" 1)
.
Example message: Misplaced docstring.
Keyword: :missing-body-in-when
.
Description: warn when when
is called only with a condition.
Default level: :warning
.
Example trigger: (when true)
.
Example message: Missing body in when
.
Keyword: :missing-clause-in-try
.
Description: warn when try
expression misses catch
or finally
clause.
Default level: :warning
.
Example trigger: (try 1)
.
Example message: Missing catch or finally in try.
Keyword: :missing-docstring
.
Description: warn when public var misses docstring.
Default level: :off
.
Example trigger: (defn foo [] 1)
.
Example message: Missing docstring.
Keyword: :missing-else-branch
.
Description: warns about missing else branch in if
expression.
Default level: :warning
.
Example trigger: (if :foo :bar)
.
Example message: Missing else branch..
Keyword: :missing-map-value
.
Description: warn on key with uneven amount of elements, i.e. one of the keys misses a value.
Default level: :error
.
Example trigger: {:a 1 :b}
Example message: missing value for key :b
.
Keyword: :missing-test-assertion
.
Description: warn on deftest
expression without test assertion.
Default level: :warning
.
Example trigger:
(require '[clojure.test :as test])
(test/deftest foo (pos? 1))
Example message: missing test assertion
.
Keyword: :namespace-name-mismatch
.
Description: warn when the namespace in the ns
form does not
correspond with the file name of the file.
Default level: :error
.
Example trigger: a file named foo.clj
containing a namespace (ns bar)
.
Example message: Namespace name does not match file name: bar
Example trigger: a folder/file containing dashes instead of underscores, example: example-namespace/foo.clj
containing a namespace (ns example-namespace.foo)
.
Example message: Namespace name does not match file name: example-namespace.foo
Keyword: :non-arg-vec-return-type-hint
.
Description: warn when a return type in defn
is not placed on the argument vector (CLJ only).
Default level: :warning
.
Example trigger: (defn ^String foo [] "cool fn")
.
Example message: Prefer placing return type hint on arg vector: String
Read this issue for more background information on this linter.
Keyword: :not-empty?
Description: warn on (not (empty? ...))
idiom. According to the docstring of empty?
seq
is prefered.
Default level: :warning
.
Example trigger: (not (empty? []))
Example message: use the idiom (seq x) rather than (not (empty? x))
.
Keyword: :plus-one
Description: warn on usages of +
that can be replaced with inc
.
Default level: :off
Example trigger:
(def x 1)
(+ x 1)
Example message: Prefer (inc x) over (+ 1 x)
Also see :minus-one
.
Keyword: :private-call
.
Description: warn when private var is used. The name of this linter should be renamed to "private usage" since it will warn on usage of private vars and not only inside calls.
Default level: :error
.
Example trigger:
(ns foo) (defn- f [])
(ns bar (:require [foo]))
(foo/f)
Example message: #'foo/f is private
.
To suppress the above message, refer to foo/f
using the var #'foo/f
or write:
#_{:clj-kondo/ignore [:private-call]}
(foo/f)
Keyword: :protocol-method-varargs
.
Description: warn on definition of varargs protocol method.
Default level: :error
.
Example trigger: (defprotocol Foo (foo [x & xs]))
Example message: Protocol methods do not support varargs
.
Keyword: :redefined-var
.
Description: warn on redefined var.
Default level: :warning
.
Example trigger: (def x 1) (def x 2)
Example message: redefined var #'user/x
.
Keyword: :var-same-name-except-case
.
Description: warn on vars that share the same name with different case (only in Clojure mode) as these could cause clashing class file names on case insensitive filesystems.
Default level: :warning
.
Example trigger: (defmacro One [] 1) (defn one [] 1)
Example message: warning: Var name one differs only in case from: One
.
Keyword: :redundant-do
.
Description: warn on usage of do that is redundant. The warning usually arises because of an explicit or implicit do as the direct parent s-expression.
Default level: :warning
.
Example trigger: (defn foo [] (do 1))
.
Example message: redundant do
.
Keyword: :redundant-fn-wrapper
Description: warn on redundant function wrapper.
Default level: :off
.
Example trigger: #(inc %)
.
Example message: Redundant fn wrapper
.
Keyword: :redundant-call
Description: warn on redundant calls. The warning arises when a single argument is passed to a function or macro that that returns its arguments.
Default level: :off
.
clojure.core
and cljs.core
functions and macros that trigger this lint:
->
,->>
cond->
,cond->>
some->
,some->>
comp
,partial
merge
Config:
{:linters {:redundant-call {:exclude #{clojure.core/->}
:include #{clojure.core/conj!}}}}
Use :exclude
to suppress warnings for the built-in list. Use :include
to
warn on additional vars.
Example trigger: (-> 1)
.
Example message: Single arg use of -> always returns the arg itself
.
Keyword: :redundant-let
.
Description: warn on usage of let that is redundant. The warning usually arises because directly nested lets.
Default level: :warning
.
Example trigger: (let [x 1] (let [y 2] (+ x y)))
.
Example message: Redundant let expression.
Keyword: :refer
Description: warns when :refer
is used. This can be used when one wants to
enforce usage of aliases.
Default level: :off
.
Example trigger: (ns foo (:require [clojure.set :refer [union]]))
.
Example warning: require with :refer
.
Config: to suppress the above warning:
{:linters {:refer {:exclude [clojure.set]}}}
Keyword: :refer-all
Description: warns when :refer :all
is used.
Default level: :warning
.
Example trigger: (ns foo (:require [clojure.set :refer :all]))
.
Example message: use alias or :refer
.
Config: to suppress the above warning:
{:linters {:refer-all {:exclude [clojure.set]}}}
Keyword: :single-key-in
.
Description: warn on associative path function with a single value path.
Default level: :off
.
Example trigger: (get-in {:a 1} [:a])
.
Example message: get-in with single key.
Keyword: :single-logical-operand
.
Description: warn on single operand logical operators with always the same value.
Default level: :warning
.
Example trigger: (and 1)
.
Example message: Single arg use of and always returns the arg itself.
Keyword: :single-operand-comparison
.
Description: warn on comparison with only one argument.
Default level: :warning
.
Example trigger: (< 1)
.
Example message: Single operand use of clojure.core/< is always true.
Keyword: :shadowed-var
.
Description: warn on var that is shadowed by local.
Default level: :off
.
Example trigger: (def x 1) (let [x 2] x)
.
Example message: Shadowed var: user/x.
Config:
{:linters {:shadowed-var {:level :warning
:exclude [ns]
:suggest {name nom}}}}
(fn [name] name)
^--- Shadowed var: clojure.core/name. Suggestion: nom
Use :exclude
to suppress warnings for specific binding names. Use :include
to warn only for specific names.
To avoid shadowing core vars you can also use :refer-clojure
+ :exclude
in
the ns
form.
Keyword: :syntax
.
Description: warn on invalid syntax.
Default level: :warning
.
Example trigger: [)
.
Example messages:
Mismatched bracket: found an opening [ and a closing ) on line 1
Mismatched bracket: found an opening [ on line 1 and a closing )
Keyword: :type-mismatch
.
Description: warn on type mismatches, e.g. passing a keyword where a number is expected.
Default level: :error
.
Example trigger: (inc :foo)
Example message: Expected: number, received: keyword.
Config:
You can add or override type annotations. See types.md.
Keyword: :unbound-destructuring-default
.
Description: warn on binding in :or
which does not occur in destructuring.
Default level: :warning
.
Example trigger: (let [{:keys [:i] :or {i 2 j 3}} {}] i)
Example message: j is not bound in this destructuring form
.
Keyword: :uninitialized-var
Description: warn on var without initial value
Default level: :warning
Example trigger: (def x)
Example message: Uninitialized var
Keyword: :unused-alias
.
Description: warn on unused alias introduced in ns form.
Default level: :off
.
Example trigger: (ns foo (:require [foo :as-alias bar]))
Example message: Unused alias: bar
.
Keyword: :unused-binding
.
Description: warn on unused binding.
Default level: :warning
.
Example trigger: (let [x 1] (prn :foo))
Example message: unused binding x
.
Config:
To exclude unused bindings from being reported, start their names with
underscores: _x
or add regex patterns to :exclude-patterns []
.
To exclude warnings about key-destructured function arguments, use:
{:linters {:unused-binding {:exclude-destructured-keys-in-fn-args true}}}
This will disable warnings for the following example:
(defn f [{:keys [:a :b :c]} d])
To disable warnings about :as
bindings (which can be useful for
documentation), use:
{:linters {:unused-binding {:exclude-destructured-as true}}}
This will disable the warning in:
(defn f [{:keys [a b c] :as g}] a b c)
(defn g [[a :as b]] a)
To exclude warnings about defmulti dispatch function arguments, use:
{:linters {:unused-binding {:exclude-defmulti-args true}}}
This will disable the warning in:
(defmulti f (fn [a b] a))
To exclude bindings named "this" use:
{:linters {:unused-binding {:exclude-patterns ["^this"]}}}
Patterns are matched via re-find
.
Keyword: :unused-value
Description: warn on unused value: constants, unrealized lazy values, pure functions and transient ops (assoc!
, conj!
etc).
Default level: :warning
.
Example triggers:
(do 1 2)
(do (map inc [1 2 3]) 2)
(do (assoc {} :foo :bar) 2)
(do (assoc! (transient {}) :foo :bar) 2)
Example message: Unused value: 1
.
Keyword: :used-underscored-binding
.
Description: warn when a underscored (ie marked as unused) binding is used.
Default level: :off
.
Example trigger: (let [_x 0] _x)
.
Example message: Using binding marked as unused: _x
These warnings can be enabled by setting the level to :warning
or
:error
in your config.
{:linters {:used-underscored-binding {:level :warning}}}
To suppress the above warning:
{:linters {:used-underscored-binding {:level :warning
:exclude [_x]}}}
A regex is also supported:
{:linters {:used-underscored-binding {:level :warning
:exclude ["^_x.*$"]}}}
This will exclude all bindings starting with _x
.
Keyword: :unknown-require-option
Description: warn on unknown :require
option pairs.
Default level: :warning
.
Example trigger: (ns foo (:require [bar :s b]))
.
Example message: Unknown :require option: :s
.
Config: use :exclude [:s]
to suppress the above warning.
Keyword: :unreachable-code
.
Description: warn on unreachable code.
Default level: :warning
.
Example trigger: (cond :else 1 (odd? 1) 2)
.
Example message: unreachable code
.
Keyword: :unused-import
.
Description: warn on unused import.
Default level: :warning
.
Example trigger: (ns foo (:import [java.util UUID]))
.
Example message: Unused import UUID.
Keyword: :unresolved-namespace
.
Default level: :error
.
Example trigger: foo.bar/baz
.
Example message: Unresolved namespace foo.bar. Are you missing a require?
Config: use :exclude [foo.bar]
to suppress the above warning.
You can report duplicate warnings using:
{:linters {:unresolved-namespace {:report-duplicates true}}}
Keyword: :unresolved-symbol
.
Default level: :error
.
Example trigger: x
.
Example message: Unresolved symbol: x
.
Config:
In the following code, match?
is a test assert expression brought in by matcher-combinators.test
.
We don't want it to be reported as an unresolved symbol.
(ns foo
(:require [clojure.test :refer [deftest is]]
[matcher-combinators.test]))
(deftest my-test
(is (match? [1 odd?] [1 3])))
The necessary config:
{:linters
{:unresolved-symbol
{:exclude [(clojure.test/is [match?])]}}}
If you want to exclude unresolved symbols from being reported:
- for all symbols under calls to
clojure.test/is
, omit the vector of symbols::exclude [(clojure.test/is)]
- for symbol
match?
globally for your project, specify only the vector of symbols::exclude [match?]
Sometimes vars are introduced by executing macros, e.g. when using HugSQL's def-db-fns
. You can suppress warnings about these vars by using declare
. Example:
(ns hugsql-example
(:require [hugsql.core :as hugsql]))
(declare select-things)
;; this will define a var #'select-things:
(hugsql/def-db-fns "select_things.sql")
(defn get-my-things [conn params]
(select-things conn params))
If the amount of symbols introduced by HugSQL becomes too unwieldy, consider
introducing a separate namespace in which HugSQL generates the vars:
foo.db.hugsql
. You can then refer to this namespace from foo.db
with
(require '[foo.db.hugsql :as sql]) (sql/insert! ...)
and clj-kondo will not
complain about this.
Furthermore, the :lint-as
option can help treating certain macros like
built-in ones. This is in clj-kondo's own config:
:lint-as {me.raynes.conch/programs clojure.core/declare
me.raynes.conch/let-programs clojure.core/let}
and helps preventing false positive unresolved symbols in this code:
(ns foo (:require [me.raynes.conch :refer [programs let-programs]]))
(programs rm mkdir echo mv)
(let-programs [clj-kondo "./clj-kondo"]
,,,)
You can report duplicate warnings using:
{:linters {:unresolved-symbol {:report-duplicates true}}}
Since v2023.04.??+ (to be released) you can use :exclude-patterns
to suppress symbols by regex patterns (as strings, processed via re-find
):
(ns scratch)
(defmacro match
{:clj-kondo/config
'{:linters {:unresolved-symbol {:exclude-patterns ["^\\?"]}}}}
[& _xs])
(match {:foo ?foo} {:foo :bar}
[?foo x])
In the above example, only x
is reported as an unresolved symbol, while
symbols starting with a question mark are not reported.
Keyword: :unresolved-var
.
Description: warns on unresolved var from other namespace.
Default level: :warning
.
Example trigger: (require '[clojure.set :as set]) (set/onion)
.
Example message: Unresolved var: set/onion
.
Config:
Given this example:
(ns foo)
(defmacro gen-vars [& names]) (gen-vars x y z)
(ns bar (:require foo))
foo/x
(foo/y)
you can exclude warnings for all unresolved vars from namespace foo
using:
{:linters {:unresolved-var {:exclude [foo]}}}
or exclude a selection of unresolved vars using qualified symbols:
{:linters {:unresolved-var {:exclude [foo/x]}}}
You can report duplicate warnings using:
{:linters {:unresolved-var {:report-duplicates true}}}
Keyword: :unsorted-imports
.
Description: warns on non-alphabetically sorted imports in ns
and require
forms.
Default level: :off
.
Example trigger: (ns foo (:import [foo A] [bar B]))
.
Example message: Unsorted import: [bar B]
.
Keyword: :unsorted-required-namespaces
.
Description: warns on non-alphabetically sorted libspecs in ns
and require
forms.
Default level: :off
.
Example trigger: (ns foo (:require b a))
.
Example message: Unsorted namespace: a
.
Keyword: :unused-namespace
.
Description: warns on required but unused namespace.
Default level: :warning
.
Example trigger: (ns foo (:require [bar :as b]))
.
Example message: namespace bar is required but never used
.
Config:
Given this example:
(ns foo (:require [foo.specs :as specs]))
you will get a warning about foo.specs
being unused.
To suppress this, you can either leave out the alias specs
if it isn't used
anywhere in the namespace or use this config:
{:linters {:unused-namespace {:exclude [foo.specs]}}}
A regex is also supported:
{:linters {:unused-namespace {:exclude [".*\\.specs$"]}}}
This will exclude all namespaces ending with .specs
.
Namespaces without :as
or :refer
are assumed to be loaded for side effects,
e.g. for clojure.spec or defining a protocol or multi-method, so the following
will not trigger a warning:
(ns foo (:require [foo.specs]))
If you'd like to have namespaces without :as
or :refer
trigger
warnings, you can enable this by setting the :simple-libspec
option
{:linters {:unused-namespace {:simple-libspec true}}}
Keyword: :unused-private-var
.
Description: warns on unused private vars.
Default level: :warning
.
Example trigger: (ns foo) (defn- f [])
Example message: Unused private var foo/f
Config:
To suppress the above warning:
{:linters {:unused-private-var {:exclude [foo/f]}}}
When defining a private var with defonce just for side effects, you can start the name with an underscore:
(defonce ^:private _dude (launch-missiles))
Keyword: :unused-referred-var
.
Description: warns about unused referred vars.
Default level: :warning
.
Example trigger: (ns foo (:require [clojure.set :refer [union]]))
.
Example message: #'clojure.set/union is referred but never used
.
Config:
Imagine you want to have taoensso.timbre/debug
available in all of your
namespaces. Even when you don't use it, you don't want to get a warning about
it. That can be done as follows:
{:linters {:unused-referred-var {:exclude {taoensso.timbre [debug]}}}}
Keyword: :use
.
Description: warns about :use
or use
.
Default level: :warning
.
Example trigger: (ns foo (:use [clojure.set]))
.
Example message: use :require with alias or :refer
.
Config:
This linter is closely tied to Refer All. Namespaces configured to
suppress the :refer-all
warning will also suppress the :use
warning.
Keyword: :warn-on-reflection
Description: warns about not setting *warn-on-reflection*
to true in Clojure
namespaces. Defaults to only warning when doing interop.
Default level: :off
Example trigger: (.length "hello")
Example message: Var *warn-on-reflection* is not set in this namespace.
Config:
:warn-on-reflection {:level :off
:warn-only-on-interop true}
The value of :warn-only-on-interop
can be set to false
to always warn in
Clojure namespaces.
Keyword: :underscore-in-namespace
Description: warns about the usage of the _
character in the declaration of namespaces (as opposed to -
).
Default level: :warning
Example trigger: (ns special_files)
Example message: Avoid underscore in namespace name: special_files