clojure-europe

simongray 2026-06-10T06:35:29.764749Z

good. morning

reefersleep 2026-06-10T07:29:50.750759Z

Yes, thank you 😄 I was thinking that maybe someone had already built something like this on top of clj-kondo. Or used a separate static analysis tool. I remember seeing one where you could pull data about your code base using datalog.

reefersleep 2026-06-10T07:34:15.161499Z

I hate it when I forget to bookmark stuff like that

borkdude 2026-06-10T07:35:31.106429Z

there's a few tools here https://github.com/clojure/tools.deps.alpha/wiki/Tools#deps-management built on kondo for seeing which function calls another function also kondo provides inspecting metadata of functions. that's the two things you need

borkdude 2026-06-10T07:36:20.312919Z

e.g. this is one of them https://github.com/benedekfazekas/morpheus

nnecklace 2026-06-10T07:40:31.131689Z

morning ☀️

borkdude 2026-06-10T07:43:24.261859Z

Here's a small bb script that might work:

#!/usr/bin/env bb
(require '[babashka.pods :as pods]
         '[clojure.set :as set])

(pods/load-pod 'clj-kondo/clj-kondo "2025.06.05")
(require '[pod.borkdude.clj-kondo :as clj-kondo])

(def analysis
  (-> (clj-kondo/run! {:lint ["src"]
                       :config {:analysis {:var-definitions {:meta [:x]}
                                           :var-usages true}}})
      :analysis))

(defn fq [ns name] (symbol (str ns) (str name)))

(def defs (:var-definitions analysis))
(def all-fns (set (map #(fq (:ns %) (:name %)) defs)))
(def with-x (set (keep #(when (-> % :meta :x) (fq (:ns %) (:name %))) defs)))

;; caller -> callees, project vars only
(def graph
  (reduce (fn [g {:keys [from from-var to name]}]
            (let [callee (fq to name)]
              (if (and from-var (all-fns callee))
                (update g (fq from from-var) (fnil conj #{}) callee)
                g)))
          {}
          (:var-usages analysis)))

(defn reachable [starts]
  (loop [seen #{} todo (vec starts)]
    (if-let [n (peek todo)]
      (if (seen n)
        (recur seen (pop todo))
        (recur (conj seen n) (into (pop todo) (graph n))))
      seen)))

(def descendants (set/difference (reachable with-x) with-x))

(prn {:with-x with-x
      :descendants-of-x descendants
      :without-x (set/difference all-fns with-x descendants)})

reefersleep 2026-06-10T07:48:59.499349Z

Uhm wow, thanks so much! 🙌 While you were so helpfully doing my work for me 😄 I was frustrated that I couldn't find the thing I was talking about, and finally managed to do so. It's https://github.com/jpmonettas/clindex , which " scans a Clojure[Script] project together with all its dependencies and generates a datascript database with facts about them.". Sounds pretty cool for general code base analysis. Leaving it here so that others can benefit and I can find it again 😅

😍 1
reefersleep 2026-06-10T07:50:23.706989Z

Haven't read thoroughly through your code, but; in the case of a CLJS code base that uses reagent, [my-component args] usually constitutes a call to a function my-component which returns hiccup. Does the above account for that?

reefersleep 2026-06-10T07:51:15.937689Z

I mean, [fn] being a call.

borkdude 2026-06-10T07:51:37.902399Z

not today unfortunately

borkdude 2026-06-10T07:51:45.893209Z

or maybe

borkdude 2026-06-10T07:51:48.509379Z

it does actually

reefersleep 2026-06-10T07:51:51.840589Z

hah cool!

reefersleep 2026-06-10T07:52:13.673189Z

Great! I'm suddenly spoilt for choice 😄

borkdude 2026-06-10T07:52:34.260469Z

yes, it works since the above code doesn't care about function calls, just references

borkdude 2026-06-10T07:55:23.681509Z

I was confused since clj-kondo doesn't report invalid arity calls for [foo ..] but that's a completely different issue

2026-06-10T08:03:05.931749Z

morning

mdiin 2026-06-10T08:18:07.825759Z

Good morning

reefersleep 2026-06-10T08:36:09.960259Z

Yes, that's no problem here 🙂

reefersleep 2026-06-10T08:36:16.982929Z

References is great!

2026-06-10T09:03:26.025589Z

Morning

teodorlu 2026-06-10T09:08:16.444179Z

Morning 👋

thomas 2026-06-10T09:30:19.909469Z

Good morning

borkdude 2026-06-10T09:50:09.651999Z

@reefersleep I'm writing a blog post about it now since this usage of clj-kondo comes up fairly often so I can hopefully just point people to that. Thanks for the trigger :)

💪 1
reefersleep 2026-06-10T09:50:28.235599Z

Glad to be of help 😅

borkdude 2026-06-10T09:52:17.178469Z

Let's see if I understand you correctly though. > Say I'd like an overview of which fns in my Clojurescript app that has x > metadata, and which don't, and which are children (or grandchildren and so > forth) of fns that for have x metadata. Do you mean this or the reverse?

(defn grandchild [] 
  :leaf)

(defn child []
  (grandchild))

(defn ^:x grandparent []
  (child))

reefersleep 2026-06-10T09:53:31.801369Z

This. I'm looking for functions that don't have :x and that aren't descendants of functions that have :x .

👍 1
borkdude 2026-06-10T09:54:23.605689Z

and what is :x to make the example more comprehensible?

borkdude 2026-06-10T09:57:08.451299Z

> I'm looking for functions that don't have :x and that aren't descendants of functions that have :x . I assume it's not common that functions have :x metadata so won't most functions match with not having x and not being descendants of x?

reefersleep 2026-06-10T09:59:17.879909Z

I don't know if my actual use case will spark joy. We're migrating our CLJS code base to Typescript. The planning people dearly want a more precise overview of which visual components we haven't planned in Jira to migrate yet. I'm going to go through our CLJS, top to bottom, and assign metadata to currently Jira-planned functions to point to those Jira issues. Then, once I've exhausted currently known Jira issues, I'll create an overview of the functions that are not yet Jira planned, so that we can create Jira issues for them. I figured I'd do that via static analysis, just find all functions and remove those that have the correct metadata. And in order to avoid having to manually go through long call chains, I'd like to remove those fns that are already part of a call chain of a close-to-the-top-level-of-the-call-chain function that has the correct metadata.

reefersleep 2026-06-10T09:59:23.674949Z

Make sense? 🙂

reefersleep 2026-06-10T10:01:04.572559Z

If everyone's done their work well so far, I should end up adding :x manually to most call chains. If they've missed a lot of stuff, it could swing the other way. Impossible to say 🙂

borkdude 2026-06-10T10:01:20.293519Z

right, makes sense (and yes, this is sad to hear ;))

reefersleep 2026-06-10T10:01:39.593589Z

yeh 🙂 But we get to keep our Clojure... For now 😬

borkdude 2026-06-10T10:02:04.950669Z

you should migrate to #C03U8L2NXNC;)

borkdude 2026-06-10T10:02:41.069579Z

hehe, do what you want. back to the blog post...

reefersleep 2026-06-10T10:02:59.250669Z

I'm sure my boss would be thrilled if I proposed that 😛

reefersleep 2026-06-10T10:03:21.805389Z

I can't do what I want, unfortunately! Hardly anybody thinks the migration is a good idea, but oh well

reefersleep 2026-06-10T10:03:29.050899Z

it's a living

reefersleep 2026-06-10T10:04:37.539479Z

I hope you can come up with a more fun use case for the blog post 😅

borkdude 2026-06-10T10:56:06.034359Z

I didn't come up with a more fun example since I wanted to keep it real-world slack ;)

reefersleep 2026-06-10T11:01:43.687179Z

brilliant 👏 👏 👏 👏 👏

reefersleep 2026-06-10T11:02:12.631289Z

And I think you're right, I think this be heavily referenced!

imre 2026-06-10T12:47:17.688989Z

good mroning

neumann 2026-06-10T17:17:54.260769Z

Good morning!

neumann 2026-06-10T17:20:47.304899Z

@borkdude Nice move turning this discussion into a blog article! I think that's a great way to free knowledge from Slack and make it more available to others. I'll put it in the Deref. Also, when you mention Slack, could you link to https://clojurians.net/?

lread 2026-06-10T18:45:52.309749Z

Good morning!

borkdude 2026-06-10T21:12:49.108799Z

Link added!

1
neumann 2026-06-10T21:58:41.360079Z

🎉 More routes into the community! 🎉