Skip to content

Commit 79143bc

Browse files
authored
fix: Improve trimming of Swift function names (#72335)
Some Swift for Windows function names were trimmed to incorrect tokens in the UI, resulting in hard-to-read stack traces. For instance, some functions were shown as `"throws"` and closures were shown as `"#1"`. This change fixes the former issue by using the correct function name. Closures are now displayed as `"closure in <function name>"`, and initialization expressions are displayed as `"initializer expression of <value>"`, improving the user experience when browsing stack traces. ### Legal Boilerplate Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.
1 parent 81dc21b commit 79143bc

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

src/sentry/stacktraces/functions.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,19 +228,40 @@ def process_generics(value, start):
228228
#
229229
# ["unsigned", "int", "whatever"] -> whatever
230230
# ["@objc", "whatever", "->", "int"] -> whatever
231-
try:
232-
func_token = tokens[tokens.index("⟿") - 1]
233-
except ValueError:
234-
if tokens:
231+
if function.find("initialization expression of") != -1:
232+
# Swift initializer expression.
233+
last_of_index = len(tokens) - 1 - tokens[::-1].index("of")
234+
func_token = tokens[last_of_index + 1]
235+
if func_token == "static":
236+
# Static initializer expression, the object is the next token.
237+
func_token = tokens[last_of_index + 2]
238+
if func_token.startswith(":"):
239+
# Trim the colon from the function name.
240+
func_token = func_token[1:]
241+
func_token = "initializer expression of " + func_token
242+
elif tokens:
243+
try:
244+
last_arrow_index = len(tokens) - 1 - tokens[::-1].index("⟿")
245+
func_token = tokens[last_arrow_index - 1]
246+
if func_token == "throws":
247+
# Throwing Swift function, the function name is the previous token.
248+
func_token = tokens[last_arrow_index - 2]
249+
except (ValueError, IndexError):
250+
# No arrow, use the last token.
251+
last_arrow_index = -1
235252
func_token = tokens[-1]
236-
else:
237-
func_token = None
253+
else:
254+
func_token = None
238255

239256
if func_token:
240257
if func_token.startswith("@") and platform in ("cocoa", "swift"):
241258
# Found a Swift attribute instead of a function name, must be an
242259
# anonymous function
243260
func_token = ("thunk for " if is_thunk else "") + "closure"
261+
elif "closure" in tokens and func_token != "closure":
262+
# Found a closure.
263+
func_token = "closure in " + func_token
264+
244265
function = (
245266
func_token.replace("⟨", "<")
246267
.replace("◯", "()")

tests/sentry/grouping/snapshots/test_variants/test_event_hash_variant/newstyle@2019_04_05/in_app_in_ui.pysnap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ creator: sentry
44
source: tests/sentry/grouping/test_variants.py
55
---
66
app:
7-
hash: "9b037f38798ea127aac3e8b8e1e1024a"
7+
hash: "61c031e5e187212561f67b5567f8ae47"
88
component:
99
app*
1010
exception*
@@ -122,7 +122,7 @@ app:
122122
filename*
123123
"mediaslideshow+extensions.swift"
124124
function* (isolated function)
125-
"MediaSlideshow.toSources"
125+
"closure in MediaSlideshow.toSources"
126126
frame*
127127
filename*
128128
"mediaslideshow+extensions.swift"
@@ -147,7 +147,7 @@ app:
147147
"EXC_BREAKPOINT"
148148
--------------------------------------------------------------------------
149149
system:
150-
hash: "fe8b15fe322c91ede2fc48363fe43587"
150+
hash: "7c47e696e27052f4849cb07e99cf649e"
151151
component:
152152
system*
153153
exception*
@@ -265,7 +265,7 @@ system:
265265
filename*
266266
"mediaslideshow+extensions.swift"
267267
function* (isolated function)
268-
"MediaSlideshow.toSources"
268+
"closure in MediaSlideshow.toSources"
269269
frame*
270270
filename*
271271
"mediaslideshow+extensions.swift"

tests/sentry/stacktraces/test_functions.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,30 @@
103103
],
104104
["pthread_cond_timedwait@@GLIBC_2.3.2", "pthread_cond_timedwait"],
105105
["glob64@GLIBC_2.2", "glob64"],
106+
[
107+
"static Namespace.ThrowingFunction() throws -> Namespace.ExitValue?",
108+
"Namespace.ThrowingFunction",
109+
],
110+
[
111+
"closure #1 @Swift.MainActor () -> () in static Foo.CallFunction(args: [Swift.String]) -> ()",
112+
"closure in Foo.CallFunction",
113+
],
114+
[
115+
"closure #1 () -> () in Bar.PostTask(() -> ()) -> ()",
116+
"closure in Bar.PostTask",
117+
],
118+
[
119+
"closure #1 @Sendable () -> Swift.String in variable initialization expression of static Namespace.Class.var : Namespace.Parent",
120+
"closure in initializer expression of Namespace.Class.var",
121+
],
122+
[
123+
"variable initialization expression of static Namespace.Class.var : Namespace.Parent",
124+
"initializer expression of Namespace.Class.var",
125+
],
126+
[
127+
"closure #1 () -> () in variable initialization expression of static (extension in SpaceCreation):Namespace.Class.var : Namespace.Parent",
128+
"closure in initializer expression of Namespace.Class.var",
129+
],
106130
],
107131
)
108132
def test_trim_native_function_name(input, output):
@@ -142,14 +166,13 @@ def test_trim_csharp_function_name(input, output):
142166
"partial apply for thunk for @escaping @callee_guaranteed (@guaranteed SomeType, @guaranteed [String : SomeType2], @guaranteed SomeType3) -> ()",
143167
"thunk for closure",
144168
],
145-
[ # "closure ... in ..." functions are converted to containing
146-
# function. We might want to change this in the future
169+
[
147170
"closure #1 (T1) in foo(bar: T2)",
148-
"foo",
171+
"closure in foo",
149172
],
150173
[
151174
"partial apply for closure #1 () in closure #2 (T1) in f1(_: T2, arg: T3)",
152-
"f1",
175+
"closure in f1",
153176
],
154177
],
155178
)

0 commit comments

Comments
 (0)