Skip to content

Instantly share code, notes, and snippets.

@borkdude
Forked from roman01la/analyze.clj
Created October 25, 2021 16:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save borkdude/a0b25f70e5fb9c5168d00495b74ce62c to your computer and use it in GitHub Desktop.
Save borkdude/a0b25f70e5fb9c5168d00495b74ce62c to your computer and use it in GitHub Desktop.
analyzing unused and undefined re-frame subscriptions via clj-kondo
(ns analyze.core
(:require [clj-kondo.core :as clj-kondo]
[clojure.set :as set]))
;; checks re-frame's :<- syntax
;; to mark dependency subscriptions as used
(def analyze-reg-sub
"(require '[clj-kondo.hooks-api :as api])
(fn [{node :node}]
(let [[_ kw & children] (:children node)
reg-sub-kw (api/reg-keyword! kw 're-frame.core/reg-sub)
reg-dep-kw! #(-> % :children first (api/reg-keyword! 're-frame.core/subscribe))
sub-kws (map #(if (api/vector-node? %) (reg-dep-kw! %) %)
(butlast children))]
{:node (api/list-node
`(do ~reg-sub-kw ~@sub-kws ~(last children)))}))")
;; requires subscribtion name (keyword)
;; to be statically defined at the call site
;; i.e. `(subscribe [:my/sub])`
(def analyze-subscribe
"(require '[clj-kondo.hooks-api :as api])
(fn [{node :node}]
(let [[_ query] (:children node)
[kw & children] (:children query)]
{:node (api/list-node
`(do ~(api/reg-keyword! kw 're-frame.core/subscribe)
~@children))}))")
(def out
(clj-kondo/run!
{:lint ["src"]
:config {:output {:analysis {:keywords true}}
:hooks {:__dangerously-allow-string-hooks__ true
:analyze-call {'re-frame.core/reg-sub analyze-reg-sub
're-frame.core/subscribe analyze-subscribe}}}}))
(defn get-keywords-usages [var-name]
(->> (:analysis out)
:keywords
(filter #(= var-name (:def %)))
(map #(assoc % :kw (if (:ns %)
(keyword (str (:ns %)) (:name %))
(keyword (:name %)))))))
(defn find-unused-keywords [defs usages]
(let [defs-set (into #{} (map :kw) defs)
usages-set (into #{} (map :kw) usages)
unused (set/difference defs-set usages-set)]
(filter (comp unused :kw) defs)))
(defn find-usage-of-undefined-keywords [defs usages]
(let [defs-set (into #{} (map :kw) defs)
usages-set (into #{} (map :kw) usages)
undefined (set/difference usages-set defs-set)]
(filter (comp undefined :kw) defs)))
(defn fmt-message [warning-type {:keys [kw filename row]}]
(-> (case warning-type
:unused-subscription "Unused subscription")
(str " " kw " at line " row " in " filename)))
(let [subs [(get-keywords-usages 're-frame.core/reg-sub)
(get-keywords-usages 're-frame.core/subscribe)]
unused-subs (apply find-unused-keywords subs)
undefined-subs (apply find-usage-of-undefined-keywords subs)]
(doseq [sub unused-subs]
(println (fmt-message :unused-subscription sub))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment