我們先回顧昨天已經拿到了所有 ID
目前的資料太過於零散且包含許多雜訊。
於是,一個 visitor
無法解決的問題。
那就~在來一層抽象(visitor
)解決對吧!!?
fileprivate final class IdentifierVisitor: SyntaxVisitor {
lazy var ids: [IdentifierExprSyntax] = []
override final func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
return .skipChildren
}
override final func visit(_ node: IdentifierExprSyntax) -> SyntaxVisitorContinueKind {
ids.append(node)
return .skipChildren
}
}
fileprivate final class DeclVisitor: SyntaxVisitor {
lazy var idVisitor: IdentifierVisitor = IdentifierVisitor(viewMode: .sourceAccurate)
private lazy var subVisitors: [DeclVisitor] = []
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
append(node.members)
return .skipChildren
}
public final func customWalk<SyntaxType>(_ node: SyntaxType) where SyntaxType: SyntaxProtocol {
super.walk(node)
self.idVisitor.walk(node)
}
private final func append<Syntax: SyntaxProtocol>(_ syntax: Syntax) {
let visitor = DeclVisitor(viewMode: .sourceAccurate)
self.subVisitors.append(visitor)
visitor.customWalk(syntax)
}
var all: [DeclVisitor] {
return [self] + subVisitors.flatMap(\.all)
}
var ids: [String] {
return self.idVisitor.ids.map {
$0.withoutTrivia().description
}
}
}
private func parse(_ code: String) -> [DeclVisitor] {
let ast = Parser.parse(source: code)
let visitor = DeclVisitor(viewMode: .sourceAccurate)
visitor.customWalk(ast)
return visitor.all
}
import Foundation
import SwiftSyntax
import SwiftParser
import XCTest
final class DeclTests: XCTestCase {
func testCode() {
let code = """
import Foundation
let global = Out()
class Out {
func test() {
print(global)
}
}
func test() {
class In {
let a = 1
func test(a: String) {
let inner = In()
print(self.a, a, 123)
func test2() {
escape {
nonescape {
self.test(a: "leak")
print(inner)
}
}
}
}
}
}
func escape(block: @escaping () -> Void) {}
func nonescape(block: () -> Void) {}
"""
let expect = [
/// global
[
// let global = Out()
"Out",
],
/// Out
[
/// print(global)
"print", "global",
],
/// In
[
/// let inner = In()
"In",
/// print(self.a, a, 123)
"print", "self", "a",
/// escape {
"escape",
/// nonescape {
"nonescape",
/// self.test(a: "leak")
"self",
/// print(inner)
"print", "inner",
]
]
let result = parse(code).map(\.ids)
XCTAssertEqual(result, expect)
}
}
最終於獲得三份 ID
,並且依照 global
, class In
, class Out
分別蒐集而來。