Mount's clj-kondo hook for defstate causes false-positive errors like Expected: deref, received: nil. on every @state usage when the :stop function has a known return type (e.g. explicitly returns nil).
Disclaimer: I used AI to investigate this problem and suggest a fix. The problem is real, the fix as proposed works in my case for the warnings that I encounter. But perhaps there are better ways of addressing this: I do not claim to fully understand all the issues involved.
Minimal reproduction
;; src/repro.clj
(ns repro
(:require [mount.core :refer [defstate]]))
(defn- start! [] {:bus "hello"})
(defn- stop! [_state] nil)
(defstate state
:start (start!)
:stop (stop! @state))
(defn get-bus []
(:bus @state))
$ clj-kondo --lint src/repro.clj
src/repro.clj:12:10: error: Expected: deref, received: nil.
Root cause
The hook in resources/clj-kondo.exports/mount/mount/hooks/defstate.clj transforms:
(defstate state :start (start!) :stop (stop! @state))
Into:
(def state (do :start (start!) :stop (stop! @state)))
clj-kondo infers state's type from the last expression in the do block, which is (stop! @state). When stop! has a known return type (e.g. explicitly returns nil), clj-kondo infers state has type nil and flags every @state as an invalid deref.
The error does not appear when stop! has an unknown return type (e.g. ends with a call to an external function), because clj-kondo doesn't flag deref on unknown types. This makes the bug appear intermittently depending on how stop! is written.
Suggested fix
Here is the patched :else branch of the hook (edited after further testing locally):
:else
{:node (api/list-node
(cond-> [(api/token-node 'def) n]
docs (conj docs)
true (conj (api/list-node
[(api/token-node 'atom)
(api/list-node
(list*
(api/token-node 'do)
args))]))))}
Mount's clj-kondo hook for
defstatecauses false-positive errors likeExpected: deref, received: nil.on every@stateusage when the:stopfunction has a known return type (e.g. explicitly returnsnil).Disclaimer: I used AI to investigate this problem and suggest a fix. The problem is real, the fix as proposed works in my case for the warnings that I encounter. But perhaps there are better ways of addressing this: I do not claim to fully understand all the issues involved.
Minimal reproduction
Root cause
The hook in
resources/clj-kondo.exports/mount/mount/hooks/defstate.cljtransforms:Into:
clj-kondo infers
state's type from the last expression in thedoblock, which is(stop! @state). Whenstop!has a known return type (e.g. explicitly returnsnil), clj-kondo infersstatehas typeniland flags every@stateas an invalid deref.The error does not appear when
stop!has an unknown return type (e.g. ends with a call to an external function), because clj-kondo doesn't flag deref on unknown types. This makes the bug appear intermittently depending on howstop!is written.Suggested fix
Here is the patched
:elsebranch of the hook (edited after further testing locally):