Skip to content

Commit 898389c

Browse files
[stacktrace] Flag Clojure functions as duplicate
1 parent a283d63 commit 898389c

File tree

3 files changed

+50
-37
lines changed

3 files changed

+50
-37
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## master (unreleased)
44

5+
- [#353](https://github.com/clojure-emacs/orchard/pull/353): Stacktrace: flag Clojure functions as duplicate.
6+
57
## 0.36.0 (2025-06-29)
68

79
- [#346](https://github.com/clojure-emacs/orchard/pull/346): Inspector: only show those datafied collection items that have unique datafy represantation.

src/orchard/stacktrace.clj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@
143143
(partition 2 1)
144144
(map (fn [[frame parent]]
145145
(if (or (= (:name frame) (:name parent))
146+
;; Deduplicate demunged function names
147+
(and (:fn frame)
148+
(= (:fn frame) (:fn parent)))
146149
(and (= (:file frame) (:file parent))
147150
(= (:line frame) (:line parent))))
148151
(flag-frame parent :dup)

test/orchard/stacktrace_test.clj

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
[clojure.string :as str]
55
[clojure.test :refer [are deftest is testing]]
66
[matcher-combinators.matchers :as matchers]
7-
[orchard.stacktrace :as sut]))
8-
9-
(require 'matcher-combinators.test) ;; for `match?`
7+
[orchard.stacktrace :as sut]
8+
[orchard.test.util :refer [is+]]))
109

1110
;; # Utils
1211

@@ -61,16 +60,16 @@
6160

6261
(deftest spec-assert-stacktrace-test
6362
(testing "Spec assert components"
64-
(is (match? [{:stacktrace some?
65-
:message some?
66-
:spec {:spec some?
67-
:value string?
68-
:problems [{:in some?
69-
:val some?
70-
:predicate some?
71-
:spec some?
72-
:at some?}]}}]
73-
spec-causes))))
63+
(is+ [{:stacktrace some?
64+
:message some?
65+
:spec {:spec some?
66+
:value string?
67+
:problems [{:in some?
68+
:val some?
69+
:predicate some?
70+
:spec some?
71+
:at some?}]}}]
72+
spec-causes)))
7473

7574
(deftest stacktrace-frames-test
7675
(testing "File types"
@@ -79,26 +78,24 @@
7978
(is (= #{:clj :java} (set (map :type frames2)))))
8079

8180
(testing "Full file mappings"
82-
(is (match?
83-
(matchers/seq-of {:file-url #".+!/clojure/core.clj$"})
84-
(filter #(= "clojure.core" (:ns %)) frames1)))
85-
(is (match?
86-
(matchers/seq-of {:file-url #"^file:/.+"})
87-
(filter #(some->> (:ns %) (re-find #"^orchard")) frames1))))
81+
(is+ (matchers/seq-of {:file-url #".+!/clojure/core.clj$"})
82+
(filter #(= "clojure.core" (:ns %)) frames1))
83+
(is+ (matchers/seq-of {:file-url #"^file:/.+"})
84+
(filter #(some->> (:ns %) (re-find #"^orchard")) frames1)))
8885

8986
(testing "Clojure ns, fn, and var"
9087
;; All Clojure frames should have non-nil :ns :fn and :var attributes.
91-
(is (match? (matchers/seq-of {:ns some?, :fn some?, :var some?})
92-
(filter #(= :clj (:type %)) frames1)))
93-
(is (match? (matchers/seq-of {:ns some?, :fn some?, :var some?})
94-
(filter #(= :clj (:type %)) frames2))))
88+
(is+ (matchers/seq-of {:ns some?, :fn some?, :var some?})
89+
(filter #(= :clj (:type %)) frames1))
90+
(is+ (matchers/seq-of {:ns some?, :fn some?, :var some?})
91+
(filter #(= :clj (:type %)) frames2)))
9592

9693
(testing "Clojure name demunging"
9794
;; Clojure fn names should be free of munging characters.
98-
(is (match? (matchers/seq-of {:fn (matchers/mismatch #"[_$]|(--\d+)")})
99-
(filter :fn frames1)))
100-
(is (match? (matchers/seq-of {:fn (matchers/mismatch #"[_$]|(--\d+)")})
101-
(filter :fn frames2)))))
95+
(is+ (matchers/seq-of {:fn (matchers/mismatch #"[_$]|(--\d+)")})
96+
(filter :fn frames1))
97+
(is+ (matchers/seq-of {:fn (matchers/mismatch #"[_$]|(--\d+)")})
98+
(filter :fn frames2))))
10299

103100
(deftest stacktrace-frame-flags-test
104101
(testing "Flags"
@@ -142,8 +139,8 @@
142139

143140
(deftest compilation-errors-test
144141
(testing "first cause of compiler exception looks like this"
145-
(is (match? #"Syntax error compiling at \(.*orchard/stacktrace_test\.clj:"
146-
(:message (first causes3)))))
142+
(is+ {:message #"Syntax error compiling at \(.*orchard/stacktrace_test\.clj:"}
143+
(first causes3)))
147144

148145
(testing "extract-location with location-data already present"
149146
(is (= {:class "clojure.lang.Compiler$CompilerException"
@@ -188,8 +185,7 @@
188185

189186
(deftest test-analyze-throwable
190187
(testing "shape of analyzed throwable"
191-
(is (match?
192-
[;; first cause
188+
(is+ [;; first cause
193189
{:class "clojure.lang.ExceptionInfo"
194190
:message "BOOM-1"
195191
:data "{:boom \"1\"}"
@@ -253,16 +249,16 @@
253249
[clojure.lang.Compiler$InvokeExpr eval "Compiler.java" 3705]]
254250
:cause "BOOM-3"
255251
:data {:boom "3"}
256-
:stacktrace-type :throwable}))))
252+
:stacktrace-type :throwable})))
257253

258254
(testing "Includes a `:phase` for the causes that include it"
259-
(is (match? [{:phase :macro-syntax-check}
260-
{:phase nil}]
261-
(catch-and-analyze (eval '(let [1]))))))
255+
(is+ [{:phase :macro-syntax-check}
256+
{:phase nil}]
257+
(catch-and-analyze (eval '(let [1])))))
262258

263259
(testing "Does not include `:phase` for vanilla runtime exceptions"
264-
(is (match? [{:phase nil}]
265-
(catch-and-analyze (throw (ex-info "" {})))))))
260+
(is+ [{:phase nil}]
261+
(catch-and-analyze (throw (ex-info "" {}))))))
266262

267263
(deftest tooling-frame-name?
268264
(are [frame-name] (true? (#'sut/tooling-frame-name? frame-name))
@@ -331,3 +327,15 @@
331327
{:name "x"}]
332328
(#'sut/flag-tooling (conj frames {:name "x"})))
333329
"The j.u.concurrent frames don't get the flag if they're not at the bottom")))
330+
331+
(deftest flag-duplicates
332+
(testing "flags same fns even if method names differ"
333+
(is+ [{:fn "foo", :flags (matchers/mismatch (matchers/embeds #{:dup}))}
334+
{:fn "foo", :flags (matchers/embeds #{:dup})}
335+
{:fn "eval12444", :flags (matchers/mismatch (matchers/embeds #{:dup}))}
336+
{:fn "eval12444", :flags (matchers/embeds #{:dup})}]
337+
(#'sut/analyze-stacktrace-data
338+
'[[orchard.stacktrace_test$foo invokeStatic "NO_SOURCE_FILE" 376]
339+
[orchard.stacktrace_test$foo invoke "NO_SOURCE_FILE" 375]
340+
[orchard.stacktrace_test$eval12444 invokeStatic "NO_SOURCE_FILE" 378]
341+
[orchard.stacktrace_test$eval12444 invoke "NO_SOURCE_FILE" 378]]))))

0 commit comments

Comments
 (0)