good. morning
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.
I hate it when I forget to bookmark stuff like that
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
e.g. this is one of them https://github.com/benedekfazekas/morpheus
morning ☀️
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)})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 😅
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?
I mean, [fn] being a call.
not today unfortunately
or maybe
it does actually
hah cool!
Great! I'm suddenly spoilt for choice 😄
yes, it works since the above code doesn't care about function calls, just references
I was confused since clj-kondo doesn't report invalid arity calls for [foo ..] but that's a completely different issue
morning
Good morning
Yes, that's no problem here 🙂
References is great!
Morning
Morning 👋
Good morning
@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 :)
Glad to be of help 😅
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))This. I'm looking for functions that don't have :x and that aren't descendants of functions that have :x .
and what is :x to make the example more comprehensible?
> 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?
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.
Make sense? 🙂
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 🙂
right, makes sense (and yes, this is sad to hear ;))
yeh 🙂 But we get to keep our Clojure... For now 😬
you should migrate to #C03U8L2NXNC;)
hehe, do what you want. back to the blog post...
I'm sure my boss would be thrilled if I proposed that 😛
I can't do what I want, unfortunately! Hardly anybody thinks the migration is a good idea, but oh well
it's a living
I hope you can come up with a more fun use case for the blog post 😅
https://blog.michielborkent.nl/clj-kondo-call-graph-metadata.html
I didn't come up with a more fun example since I wanted to keep it real-world slack ;)
https://clojurians.slack.com/archives/C8NUSGWG6/p1781088996618669
brilliant 👏 👏 👏 👏 👏
And I think you're right, I think this be heavily referenced!
good mroning
Good morning!
@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/?
Good morning!
Link added!
🎉 More routes into the community! 🎉