Skip to content

Commit 10fe6e7

Browse files
dulmarodmeta-codesync[bot]
authored andcommitted
[infer][swift] demangle function-local class names in retain-cycle labels
Summary: The retain-cycle message demangler (`Llair2TextualUtils.demangle_swift_class_name`) bailed on classes declared local to a function: their mangled name carries a local-discriminator suffix `L_C` instead of the plain `C` of a top-level class, which wasn't in the nominal-instance whitelist, so the raw mangled string leaked into the label. Strip the leading `L<disc>` discriminator before the suffix check so a local class renders its plain name. Flips the `rc_local_class_demangling` fixture from `object of type class T5Hello...L_C` to `object of type class Node`. Recovers the last literal name token only (`Node`); resolving the Swift mangling substitution to the full `LocalNode`, and the nested `C0C` family, are follow-ups. _Authored by Astro 🪐 (Claude Code agent)._ Reviewed By: davidpichardie Differential Revision: D107674132 fbshipit-source-id: 33b060145c8b3a24a643388adff4143243114173
1 parent 38c3e65 commit 10fe6e7

2 files changed

Lines changed: 23 additions & 2 deletions

File tree

infer/src/llvm/Llair2TextualUtils.ml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,25 @@ let parse_mangled_identifier_core mangled =
3939
parse 0 "" 0 )
4040

4141

42+
(** A function-local or private nominal type carries a *local discriminator* [L<disc>] (where
43+
[<disc>] is the anonymous [_] or a positional number) right before its kind char, so e.g. a
44+
class declared inside a function mangles with suffix [L_C] rather than [C]. Strip that leading
45+
discriminator so the bare kind char is left for the nominal-instance suffix check. [L] is the
46+
Swift ABI's local-discriminator introducer specifically, so this never strips a
47+
metadata/function marker (those are [M]/[W]/[X]/[T]-led). *)
48+
let strip_local_discriminator suffix =
49+
match String.chop_prefix suffix ~prefix:"L" with
50+
| None ->
51+
suffix
52+
| Some rest ->
53+
let rest = String.chop_prefix_if_exists rest ~prefix:"_" in
54+
let first_nondigit =
55+
String.lfindi rest ~f:(fun _ c -> not (Char.is_digit c))
56+
|> Option.value ~default:(String.length rest)
57+
in
58+
String.drop_prefix rest first_nondigit
59+
60+
4261
(** Generalized heuristic to extract the plain class name from a Swift mangled name. It finds the
4362
*last* length-prefixed string in the mangling. *)
4463
let demangle_swift_class_name (mangled : string) : string =
@@ -59,7 +78,9 @@ let demangle_swift_class_name (mangled : string) : string =
5978
(* Extract whatever is left in the string after our parsed name *)
6079
let suffix = String.sub mangled ~pos:last_end ~len:(String.length mangled - last_end) in
6180
let clean_suffix =
62-
String.chop_suffix_if_exists suffix ~suffix:"*" |> String.chop_suffix_if_exists ~suffix:"D"
81+
String.chop_suffix_if_exists suffix ~suffix:"*"
82+
|> String.chop_suffix_if_exists ~suffix:"D"
83+
|> strip_local_discriminator
6384
in
6485
(* In the Swift ABI, after the length-prefixed identifier, the mangler appends a type kind.
6586
If the string represents metadata, functions, or coroutines, it will have a complex

infer/tests/codetoanalyze/swift/pulse/issues.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ codetoanalyze/swift/pulse/rc_framework_url_session_overloads.swift, startBad, 1,
3838
codetoanalyze/swift/pulse/rc_framework_url_session_overloads.swift, startBad, 1, RETAIN_CYCLE, no_bucket, ERROR, [assignment of self->task part of the trace starts here,parameter `self` of startBad,assigned,assignment of Swift closure part of the trace starts here,in call to `NSURLSession.downloadTaskWithURL:completionHandler:` (modelled),assigned,assignment of object of type class URLSessionDownloadTaskCycleHolder part of the trace starts here,variable `var6` accessed here,assigned,retain cycle here]
3939
codetoanalyze/swift/pulse/rc_inline_concrete_delegate.swift, BarViewController.barView.get, 0, RETAIN_CYCLE, no_bucket, ERROR, [assignment of self->barView part of the trace starts here,parameter `self` of BarViewController.barView.get,assigned,assignment of self part of the trace starts here,when calling `Hello.BarViewController._3E5CC6EA0AF57922CE73F632956A8E47` here,in call to `BarView.init`,in call to `swift_allocObject` (modelled),return from call to `BarView.init`,assigned,retain cycle here]
4040
codetoanalyze/swift/pulse/rc_intentional_self_retain.swift, fire_bad, 1, RETAIN_CYCLE, no_bucket, ERROR, [assignment of self->leakedSelf part of the trace starts here,parameter `self` of fire_bad,assigned,retain cycle here]
41-
codetoanalyze/swift/pulse/rc_local_class_demangling.swift, wireBad, 1, RETAIN_CYCLE, no_bucket, ERROR, [assignment of object of type class T5Hello19makeLocalClassCycleyyF0C4NodeL_C part of the trace starts here,parameter `self` of wireBad,assigned,retain cycle here]
41+
codetoanalyze/swift/pulse/rc_local_class_demangling.swift, wireBad, 1, RETAIN_CYCLE, no_bucket, ERROR, [assignment of object of type class Node part of the trace starts here,parameter `self` of wireBad,assigned,retain cycle here]
4242
codetoanalyze/swift/pulse/rc_objc_inheritance.swift, configureCell_bad, 7, RETAIN_CYCLE, no_bucket, ERROR, [assignment of self->embedded part of the trace starts here,parameter `self` of configureCell_bad,assigned,assignment of createWidgetView().hostCell part of the trace starts here,in call to `createWidgetView`,in call to `init`,in call to `__objc_alloc_from_swift` (modelled),in call to `init`,parameter `var1` of init,in call to `NSObject.init` (modelled),returned,return from call to `init`,returned,return from call to `init`,returned,return from call to `createWidgetView`,when calling `WidgetDisplayView.setup` here,parameter `self` of WidgetDisplayView.setup,when calling `WidgetDisplayView.hostCell.set` here,parameter `self` of WidgetDisplayView.hostCell.set,assigned,retain cycle here]
4343
codetoanalyze/swift/pulse/rc_unknown_value.swift, MainController.setup, 7, RETAIN_CYCLE, no_bucket, ERROR, [assignment of self part of the trace starts here,when calling `InnerWrapper.init` here,in call to `swift_allocObject` (modelled),when calling `init` here,parameter `self` of init,assigned,assignment of self->wrapper part of the trace starts here,parameter `self` of MainController.setup,assigned,retain cycle here]
4444
codetoanalyze/swift/pulse/rc_unknown_value2.swift, test_retain_cycle_bad2, 12, RETAIN_CYCLE, no_bucket, ERROR, [assignment of object of type class State part of the trace starts here,in call to `swift_allocObject` (modelled),assigned,assignment of dynamically allocated object part of the trace starts here,when calling `DeviceAppManagerDelegateImpl.init` here,in call to `swift_allocObject` (modelled),when calling `init` here,parameter `self` of init,assigned,assignment of State.init().delegate->field_0 part of the trace starts here,in call to `State.init`,in call to `swift_allocObject` (modelled),return from call to `State.init`,assigned,retain cycle here]

0 commit comments

Comments
 (0)