Skip to content

Commit 70765d7

Browse files
[stacktrace] Flag Clojure functions as duplicate
1 parent a283d63 commit 70765d7

File tree

3 files changed

+48
-49
lines changed

3 files changed

+48
-49
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: 43 additions & 49 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"
@@ -118,18 +115,16 @@
118115
(testing "for project"
119116
(is (seq (filter (comp :project :flags) frames4))))
120117

121-
(testing "for duplicate frames"
122-
;; Index frames. For all frames flagged as :dup, the frame above it in
123-
;; the stack (index i - 1) should be substantially the same source info.
124-
(let [ixd1 (zipmap (iterate inc 0) frames1)
125-
ixd2 (zipmap (iterate inc 0) frames2)
126-
dup? #(or (= (:name %1) (:name %2))
127-
(and (= (:file %1) (:file %2))
128-
(= (:line %1) (:line %2))))]
129-
(is (every? (fn [[i v]] (dup? v (get ixd1 (dec ^long i))))
130-
(filter (comp :dup :flags val) ixd1)))
131-
(is (every? (fn [[i v]] (dup? v (get ixd2 (dec ^long i))))
132-
(filter (comp :dup :flags val) ixd2)))))))
118+
(testing "flags duplicate fns even if method names differ"
119+
(is+ [{:fn "foo", :flags (matchers/mismatch (matchers/embeds #{:dup}))}
120+
{:fn "foo", :flags (matchers/embeds #{:dup})}
121+
{:fn "eval12444", :flags (matchers/mismatch (matchers/embeds #{:dup}))}
122+
{:fn "eval12444", :flags (matchers/embeds #{:dup})}]
123+
(#'sut/analyze-stacktrace-data
124+
'[[orchard.stacktrace_test$foo invokeStatic "NO_SOURCE_FILE" 376]
125+
[orchard.stacktrace_test$foo invoke "NO_SOURCE_FILE" 375]
126+
[orchard.stacktrace_test$eval12444 invokeStatic "NO_SOURCE_FILE" 378]
127+
[orchard.stacktrace_test$eval12444 invoke "NO_SOURCE_FILE" 378]])))))
133128

134129
(deftest exception-causes-test
135130
(testing "Exception cause unrolling"
@@ -142,8 +137,8 @@
142137

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

148143
(testing "extract-location with location-data already present"
149144
(is (= {:class "clojure.lang.Compiler$CompilerException"
@@ -188,8 +183,7 @@
188183

189184
(deftest test-analyze-throwable
190185
(testing "shape of analyzed throwable"
191-
(is (match?
192-
[;; first cause
186+
(is+ [;; first cause
193187
{:class "clojure.lang.ExceptionInfo"
194188
:message "BOOM-1"
195189
:data "{:boom \"1\"}"
@@ -253,16 +247,16 @@
253247
[clojure.lang.Compiler$InvokeExpr eval "Compiler.java" 3705]]
254248
:cause "BOOM-3"
255249
:data {:boom "3"}
256-
:stacktrace-type :throwable}))))
250+
:stacktrace-type :throwable})))
257251

258252
(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]))))))
253+
(is+ [{:phase :macro-syntax-check}
254+
{:phase nil}]
255+
(catch-and-analyze (eval '(let [1])))))
262256

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

267261
(deftest tooling-frame-name?
268262
(are [frame-name] (true? (#'sut/tooling-frame-name? frame-name))

0 commit comments

Comments
 (0)