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:lastwhich 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.