Skip to content

Commit

Permalink
[#51] Implement simple find usages
Browse files Browse the repository at this point in the history
This functionality is limited only to functions in loaded namespaces,
but it still pretty handy.

One notable problem that we should ideally solve is that `fdeps`
doesn't track function dependencies in lambdas.
  • Loading branch information
bbatsov committed May 1, 2019
1 parent ae1e82a commit 5b96f22
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
36 changes: 36 additions & 0 deletions src/orchard/xref.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
(ns orchard.xref
"Utilities for finding function dependencies and
usages."
{:added "0.5.0"}
(:require
[orchard.query :as q]))

(defn- as-val [thing]
(cond
(var? thing) (deref thing)
(symbol? thing) (deref (find-var thing))
(fn? thing) thing))

(defn fdeps
"Returns a set with all the functions invoked by `val`.
`val` must be a function value, a var or a symbol."
[val]
(let [val (as-val val)]
(set (some->> val class .getDeclaredFields
(keep (fn [^java.lang.reflect.Field f]
(or (and (identical? clojure.lang.Var (.getType f))
(java.lang.reflect.Modifier/isPublic (.getModifiers f))
(java.lang.reflect.Modifier/isStatic (.getModifiers f))
(-> f .getName (.startsWith "const__"))
(.get f val))
nil)))))))

(defn xref
"Find all functions that refer `var`.
It can be either a var or a symbol that can resolved to a var."
[var]
(let [var (if (var? var) var (find-var var))
all-vars (q/vars {:ns-query {:project? true} :private? true})
all-vals (map deref all-vars)
deps-map (zipmap all-vars (map fdeps all-vals))]
(map first (filter (fn [[k v]] (contains? v var)) deps-map))))
29 changes: 29 additions & 0 deletions test/orchard/xref_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
(ns orchard.xref-test
(:require
[clojure.test :refer :all]
[orchard.xref :as xref]))

(defn- dummy-fn [x]
(map #(* % 2) (filter even? (range 1 10))))

(deftest fdeps-test
(testing "with a fn value"
(is (= (xref/fdeps dummy-fn)
#{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range})))
(testing "with a var"
(is (= (xref/fdeps #'dummy-fn)
#{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range})))
(testing "with a symbol"
(is (= (xref/fdeps 'orchard.xref-test/dummy-fn)
#{#'clojure.core/map #'clojure.core/filter
#'clojure.core/even? #'clojure.core/range}))))

(deftest xref-test
(testing "with a var"
(is (= (xref/xref #'dummy-fn) '()))
(is (contains? (into #{} (xref/xref #'map)) #'orchard.xref-test/dummy-fn)))
(testing "with a symbol"
(is (= (xref/xref 'orchard.xref-test/dummy-fn) '()))
(is (contains? (into #{} (xref/xref #'map)) #'orchard.xref-test/dummy-fn))))

0 comments on commit 5b96f22

Please sign in to comment.