STATUS: WORK IN PROGRESS. THE CONFIGURATION FORMAT IS STILL IN FLUX, BREAKING CHANGES WILL HAPPEN.
Clj-kondo provides rudimentary type checking using a few basic rules and type
annotations. The linter for this is called :type-mismatch
in the
configuration. Clj-kondo knows the types of a selection of
clojure
functions and is therefore able to warn about mismatches like:
$ clj-kondo --lint - <<< '(first (map inc))'
<stdin>:1:8: warning: Expected: seqable collection, received: transducer.
or
$ clj-kondo --lint - <<< '(inc (subs "foo" 1 2))'
<stdin>:1:6: warning: Expected: number, received: string.
The second way clj-kondo is informed about types is by inspecting type hints:
$ clj-kondo --lint - <<< '(defn foo [^String x] x) (foo 1)'
<stdin>:1:31: warning: Expected: string or nil, received: positive integer.
This provides a way for users to inform clj-kondo and get some type checking for free. But type hints were never designed with type checking in mind. Therefore clj-kondo lets users bring in their own type annotations in the configuration:
{:linters
{:type-mismatch
{:level :warning
:namespaces {foo {foo {:arities {1 {:args [:string]
:ret :string}}}}}}}}
$ clj-kondo --lint - <<< '(ns bar (:require [foo :refer [foo]])) (foo 1)'
<stdin>:1:45: warning: Expected: string, received: positive integer.
$ clj-kondo --lint - <<< '(ns bar (:require [foo :refer [foo]])) (inc (foo "foo"))
<stdin>:1:45: warning: Expected: number, received: string.'
The configuration is organized per namespace. Annotations are provided per arity.
{:linters
{:type-mismatch
{:level :warning
:namespaces {foo {foo-1 {:arities {1 {:args [:string]
:ret :string}
2 {:args [:string :int]
:ret :map}
:varargs {:args [:string :int {:op :rest
:spec :int}]
:ret :int}}}
foo-2 ...}
bar {bar-1 ...}}}}}
Available types and relations can be found here.
Special operators:
-
{:op :rest, :spec :int}
. This can be used to match remaining arguments in vararg signatures. This operation also supports:last
which can be used if the last vararg has a different type than the others (like inclojure.core/apply
). The spec may be wrapped in a vector, to match pair-wise arguments:{:args [{:op :rest, :spec [:any :any]}]
. Trying to match this spec with an uneven number of arguments will fail. -
{:op :keys, :req {:a :string} :opt {:b :int}}
. This can be used to match map literals and check for required and optional keys.
- Provide a PR for missing clojure annotations.
- Report false positives.
- Try out the configuration and report feedback.