{%hackmd @themes/orangeheart %}
# LeakDetect
---
## Leak
----
> `Retain Cycle`
---
## [ARC](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html)
----

```swift=
john?.apartment = unit4A
unit4A?.tennant = john
```
----

```swift=
john = nil
unit4A = nil
```
----

```diff=
class Apartment {
- var tennant: Person?
+ weak var tennant: Person?
```
---
## Implict Strong Capture
```swift=
{
print(self)
}
```
----
## Implict Strong Capture
```swift=
{ [self]
print(self)
}
```
----
### Capture List
> [vc1, vc2 = homeVC, weak this = self]
----
### [weak self]
```swift=
// in some func
{ [weak self] in
print(self)
}
```
----
## Case
```swift=
let obj = Foo()
obj.callback = {
print(obj)
}
```
---
## Implict Strong Capture
> Object Method
----
### Curry Function
```swift=
class Foo {
func hello() {
print("bar")
}
}
func hello(_ `self`: Foo) -> () -> () {
return { [self] in
print("bar")
}
}
let foo = Foo()
let _hello = hello(foo) // let _hello = foo.hello
_hello() // bar
```
----
### Case 1
```swift=
let callback = self.hello
```
----
### Case 2
```swift=
rx
.subscribe(onNext: self.hello)
.disposed(by: bag)
```
---
## Implict Strong Capture
> parent closure auto capture for child closure
----
## Case
```swift=
{
{ [weak self] in
}
}
```
----
## Case
```swift=
/// parent closure
{ [self] /// parent closure 自動 capture self
/// child closure
{ [weak self] in
}
}
```
---
## 實作
* SwiftSyntax
* SourceKit
---
## SwiftSyntax
> [AST](https://swift-ast-explorer.com/)
----
### 目標
* 找到所有 `closure` -> `ClosureExprSyntax`
* 找到所有特定變數 -> `TokenSyntax`
----
## SyntaxVisitor
```swift=
import SwiftSyntax
class YourVisitor: SyntaxVisitor {
func visit(_ node: TokenSyntax)
-> SyntaxVisitorContinueKind {
.skipChildren
}
func visit(_ node: ClosureExprSyntax)
-> SyntaxVisitorContinueKind {
.skipChildren
}
}
```
---
## ClosureExprSyntax
```swift=
public struct ClosureExprSyntax: ExprSyntaxProtocol, SyntaxHashable {
enum Cursor: Int {
case leftBrace
case signature
case statements
case rightBrace
}
}
```
----
## ClosureExprSyntax
```graphviz
digraph ClosureExprSyntax {
rankdir = TB;
"ClosureExprSyntax" -> "leftBrace" -> "{"
"ClosureExprSyntax" -> "signature" -> "ClosureSignatureSyntax"
"ClosureExprSyntax" -> "statements" -> "CodeBlockItemListSyntax"
"ClosureExprSyntax" -> "rightBrace" -> "}"
}
```
----
## ClosureExprSyntax
```swift=
{ // leftBrace
[self] (i: Int) in // signature
code0 // statements[0]
code1 // statements[1]
code2 // statements[2]
} // rightBrace
```
----
## ClosureExprSyntax
```graphviz
digraph ClosureExprSyntax {
rankdir = TB;
"ClosureExprSyntax" -> "leftBrace" -> "{"
"ClosureExprSyntax" -> "signature" -> "ClosureSignatureSyntax"
"ClosureExprSyntax" -> "statements" -> "CodeBlockItemListSyntax"
"ClosureExprSyntax" -> "rightBrace" -> "}"
"CodeBlockItemListSyntax" -> "DispatchQueue.main.async {}" -> "..." -> "ClosureExprSyntax1"
"CodeBlockItemListSyntax" -> "let a: Int = {1}()" -> "... " -> "ClosureExprSyntax2"
"CodeBlockItemListSyntax" -> "Code3" -> "... "
}
```
---
## TokenSyntax
----
## Token
```swift=
a.b.c
// ^ ^ ^
a
// ^
a.b.c()
// ^ ^ ^
a()
// ^
```
----
### MemberAccessExprSyntax
```swift=
public struct MemberAccessExprSyntax: ExprSyntaxProtocol, SyntaxHashable {
enum Cursor: Int {
case base
case dot
case name
case declNameArguments
}
}
```
----
### a.b.c
```graphviz
digraph ClosureExprSyntax {
rankdir = TB;
"member1" -> "base 1" -> "member2"
"member1" -> "."
"member1" -> "name 1" -> "Token c" -> "c"
"member2" -> "base 2" -> "Token a" -> "a"
"member2" -> ". "
"member2" -> "name 2" -> "Token b" -> "b"
}
```
---
## SourceKit
----
### SourceKit

----
### SourceKitten
----
### Cursor

----
### Cursor Info


----
### SourceKitResponse
```lldb=
▿ SourceKitResponse
▿ raw : 14 elements
▿ 0 : 2 elements
- key : "key.typeusr"
- value : "$sSiD"
▿ 1 : 2 elements
- key : "key.line"
- value : 18
▿ 2 : 2 elements
- key : "key.fully_annotated_decl"
- value : "<decl.var.local><syntaxtype.keyword>let</syntaxtype.keyword> <decl.name>a</decl.name>: <decl.var.type><ref.struct usr=\"s:Si\">Int</ref.struct></decl.var.type></decl.var.local>"
▿ 3 : 2 elements
- key : "key.annotated_decl"
- value : "<Declaration>let a: <Type usr=\"s:Si\">Int</Type></Declaration>"
▿ 4 : 2 elements
- key : "key.modulename"
- value : "ViewController"
▿ 5 : 2 elements
- key : "key.typename"
- value : "Int"
▿ 6 : 2 elements
- key : "key.name"
- value : "a"
▿ 7 : 2 elements
- key : "key.kind"
- value : "source.lang.swift.ref.var.local"
▿ 8 : 2 elements
- key : "key.length"
- value : 1
▿ 9 : 2 elements
- key : "key.decl_lang"
- value : "source.lang.swift"
▿ 10 : 2 elements
- key : "key.column"
- value : 13
▿ 11 : 2 elements
- key : "key.offset"
- value : 351
▿ 12 : 2 elements
- key : "key.usr"
- value : "s:14ViewControllerAAC11viewDidLoadyyF1aL_Sivp"
▿ 13 : 2 elements
- key : "key.filepath"
- value : "/Users/yume/Desktop/AAAAAAA/AAAAAAA/ViewController.swift"
```
----
## kind
```lldb=
▿ 7 : 2 elements
- key : "key.kind"
- value : "source.lang.swift.ref.var.local"
```
----
## Type
```lldb=
▿ 0 : 2 elements
- key : "key.typeusr"
- value : "$sSiD"
▿ 5 : 2 elements
- key : "key.typename"
- value : "Int"
```

----
## USR

----
## Offset
```lldb=
▿ 11 : 2 elements
- key : "key.offset"
- value : 351
```

---
## Find Implict Strong Capture
----
### 特定目標
* 找到所有 `closure`
* 找到該 `closure` 所有特定變數
* 變數在該 `closure` 內
* 變數不能抓取超過該 `closure`
----
### 變數在該 `closure` 內
```swift=
{
let obj = self
// ^
}
```
----
### 變數不能抓取超過該 `closure`
```swift=
{
{
self
// x
}
class Foo {
func bar() {
self
// x
}
}
}
```
----
### Final Impl
```swift=
if closure.`{`.offset > token.from.offset {
report(token)
}
```
----

---
# Demo
{"metaMigratedAt":"2023-06-17T08:13:19.138Z","metaMigratedFrom":"YAML","title":"LeakDetect","breaks":true,"slideOptions":"{\"theme\":\"league\",\"transition\":\"fade\"}","description":"Retain Cycle","contributors":"[{\"id\":\"6883ab5f-8423-424e-bfcb-d0002f96698f\",\"add\":10310,\"del\":3195}]"}