Clj-kondo can be configured in three ways:
config.edn
file in the .clj-kondo
directory (see project setup)--config
file argument from the command line--config
EDN argument from the command line (see examples below)The command line argument overrides a config.edn
.
Look at the default configuration for all available options.
$ clj-kondo --lint corpus --config '{:output {:format :json}}' | jq '.findings[0]'
{
"type": "invalid-arity",
"filename": "corpus/nested_namespaced_maps.clj",
"row": 9,
"col": 1,
"level": "error",
"message": "wrong number of args (2) passed to nested-namespaced-maps/test-fn"
}
Printing in EDN format is also supported.
$ clj-kondo --lint "$(clj -Spath)" --config '{:output {:include-files ["^clojure/test"]}}'
clojure/test.clj:496:6: warning: redundant let
clojure/test/tap.clj:86:5: warning: redundant do
linting took 3289ms, errors: 0, warnings: 2
$ clj-kondo --lint "$(clj -Spath)" --config '{:output {:include-files ["^clojure/test"] :exclude-files ["tap"]}}'
clojure/test.clj:496:6: warning: redundant let
linting took 3226ms, errors: 0, warnings: 1
$ clj-kondo --lint "$(clj -Spath)" --config '{:output {:progress true}}'
.................................................................................................................
cljs/tools/reader.cljs:527:9: warning: redundant do
(rest of the output omitted)
$ echo '(select-keys [:a])' | clj-kondo --lint -
<stdin>:1:1: error: wrong number of args (1) passed to clojure.core/select-keys
linting took 10ms, errors: 1, warnings: 0
$ echo '(select-keys [:a])' | clj-kondo --lint - --config '{:linters {:invalid-arity {:level :off}}}'
linting took 10ms, errors: 0, warnings: 0
You can accomplish this by using ^:replace
metadata, which will override
instead of merge with other configurations:
$ clj-kondo --lint corpus --config '^:replace {:linters {:redundant-let {:level :info}}}'
corpus/redundant_let.clj:4:3: info: redundant let
corpus/redundant_let.clj:8:3: info: redundant let
corpus/redundant_let.clj:12:3: info: redundant let
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]}}}
In the following code the my-defn
macro is defined, but clj-kondo doesn't know how to interpret it:
(ns foo)
(defmacro my-defn [name args & body]
`(defn ~name ~args
(do (println "hello!")
~@body)))
(my-defn foo [x])
Hence (foo 1 2 3)
will not lead to an invalid arity error. However, the syntax
of my-defn
is a subset of clojure.core/defn
, so for detecting arity errors
we might have just linted it like that. That is what the following configuration accomplishes:
{:lint-as {foo/my-defn clojure.core/defn}}
In the following code, the namespaces foo.specs
and bar.specs
are only loaded for the side effect of registering specs, so we don't like clj-kondo reporting those namespaces as required but unused.
(ns foo (:require [foo.specs] [bar.specs]))
(defn my-fn [x] x)
That can be done using this config:
{:linters {:unused-namespace {:exclude [foo.specs bar.specs]}}}
A regex is also supported:
{:linters {:unused-namespace {:exclude [".*\\.specs$"]}}}
This will exclude all namespaces ending with .specs
.
In the following code streams
is a macro that assigns a special meaning to the symbol where
, so it should not be reported as an unresolved symbol:
(ns foo
(:require [riemann.streams [streams]]))
(def email (mailer {:host "mail.relay"
:from "riemann@example.com"}))
(streams
(where (and (= (:service event) “my-service”)
(= (:level event) “ERROR”))
,,,))
This is the config for it:
{:linters
{:unresolved-symbol
{:exclude [(riemann.streams/streams [where])]}}}
To exclude all symbols in calls to riemann.streams/streams
write :exclude [(riemann.streams/streams)]
, without the vector.
To exclude a symbol from being reported as unresolved globally in your project, e.g. foo
, you can use :exclude [foo]
.
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))
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"]
,,,)
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$"]}}}}}
These are some example configurations used in real projects. Feel free to create a PR with yours too.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close