@@ -34,7 +34,7 @@ protocol Scope {
34
34
var sourceSyntax : SyntaxProtocol { get }
35
35
36
36
/// Returns the declaration `name` refers to at a particular syntax node location.
37
- func getDeclarationFor ( name: String , at syntax: SyntaxProtocol ) -> Syntax ?
37
+ func getDeclarationsFor ( name: String , at syntax: SyntaxProtocol ) -> [ Syntax ]
38
38
}
39
39
40
40
extension Scope {
@@ -46,43 +46,33 @@ extension Scope {
46
46
return syntax? . scope
47
47
}
48
48
}
49
-
49
+
50
50
// MARK: - lookupLabeledStmts
51
-
51
+
52
52
/// Given syntax node position, returns all available labeled statements.
53
53
func lookupLabeledStmts( at syntax: SyntaxProtocol ) -> [ LabeledStmtSyntax ] {
54
- return lookupLabeledStmtsHelper ( at: syntax. parent)
55
- }
56
-
57
- /// Helper method to recursively collect labeled statements from the syntax node's parents.
58
- private func lookupLabeledStmtsHelper( at syntax: Syntax ? ) -> [ LabeledStmtSyntax ] {
59
- guard let syntax, !syntax. is ( MemberBlockSyntax . self) else { return [ ] }
60
- if let labeledStmtSyntax = syntax. as ( LabeledStmtSyntax . self) {
61
- return [ labeledStmtSyntax] + lookupLabeledStmtsHelper( at: labeledStmtSyntax. parent)
62
- } else {
63
- return lookupLabeledStmtsHelper ( at: syntax. parent)
64
- }
54
+ return walkParentTreeUpToFunctionBoundary ( at: syntax. parent, collect: LabeledStmtSyntax . self)
65
55
}
66
-
56
+
67
57
// MARK: - lookupFallthroughSourceAndDest
68
-
58
+
69
59
/// Given syntax node position, returns the current switch case and it's fallthrough destination.
70
60
func lookupFallthroughSourceAndDestination( at syntax: SyntaxProtocol ) -> ( SwitchCaseSyntax ? , SwitchCaseSyntax ? ) {
71
- guard let originalSwitchCase = syntax . ancestorOrSelf ( mapping : { $0 . as ( SwitchCaseSyntax . self) } ) else {
61
+ guard let originalSwitchCase = walkParentTreeUpToFunctionBoundary ( at : Syntax ( syntax ) , collect : SwitchCaseSyntax . self) else {
72
62
return ( nil , nil )
73
63
}
74
-
64
+
75
65
let nextSwitchCase = lookupNextSwitchCase ( at: originalSwitchCase)
76
-
66
+
77
67
return ( originalSwitchCase, nextSwitchCase)
78
68
}
79
-
69
+
80
70
/// Given a switch case, returns the case that follows according to the parent.
81
71
private func lookupNextSwitchCase( at switchCaseSyntax: SwitchCaseSyntax ) -> SwitchCaseSyntax ? {
82
72
guard let switchCaseListSyntax = switchCaseSyntax. parent? . as ( SwitchCaseListSyntax . self) else { return nil }
83
-
73
+
84
74
var visitedOriginalCase = false
85
-
75
+
86
76
for child in switchCaseListSyntax. children ( viewMode: . sourceAccurate) {
87
77
if let thisCase = child. as ( SwitchCaseSyntax . self) {
88
78
if thisCase. id == switchCaseSyntax. id {
@@ -92,21 +82,21 @@ extension Scope {
92
82
}
93
83
}
94
84
}
95
-
85
+
96
86
return nil
97
87
}
98
-
88
+
99
89
// MARK: - lookupCatchNode
100
-
90
+
101
91
/// Given syntax node position, returns the closest ancestor catch node.
102
92
func lookupCatchNode( at syntax: Syntax ) -> Syntax ? {
103
93
return lookupCatchNodeHelper ( at: syntax, traversedCatchClause: false )
104
94
}
105
-
95
+
106
96
/// Given syntax node location, finds where an error could be caught. If set to `true`, `traverseCatchClause`lookup will skip the next do statement.
107
97
private func lookupCatchNodeHelper( at syntax: Syntax ? , traversedCatchClause: Bool ) -> Syntax ? {
108
98
guard let syntax else { return nil }
109
-
99
+
110
100
switch syntax. as ( SyntaxEnum . self) {
111
101
case . doStmt:
112
102
if traversedCatchClause {
@@ -122,14 +112,47 @@ extension Scope {
122
112
} else {
123
113
return lookupCatchNodeHelper ( at: syntax. parent, traversedCatchClause: traversedCatchClause)
124
114
}
125
- case . functionDecl( let functionDecl) :
126
- if functionDecl. signature. effectSpecifiers? . throwsClause != nil {
127
- return syntax
128
- } else {
129
- return lookupCatchNodeHelper ( at: syntax. parent, traversedCatchClause: traversedCatchClause)
130
- }
115
+ case . functionDecl, . accessorDecl, . initializerDecl:
116
+ return syntax
131
117
default :
132
118
return lookupCatchNodeHelper ( at: syntax. parent, traversedCatchClause: traversedCatchClause)
133
119
}
134
120
}
121
+
122
+ /// Callect the first syntax node matching the collection type up to a function boundary.
123
+ func walkParentTreeUpToFunctionBoundary< T: SyntaxProtocol > ( at syntax: Syntax ? ,
124
+ collect: T . Type ) -> T ? {
125
+ walkParentTreeUpToFunctionBoundary ( at: syntax, collect: collect, stopWithFirstMatch: true ) . first
126
+ }
127
+
128
+ /// Callect syntax nodes matching the collection type up to a function boundary.
129
+ func walkParentTreeUpToFunctionBoundary< T: SyntaxProtocol > ( at syntax: Syntax ? ,
130
+ collect: T . Type ,
131
+ stopWithFirstMatch: Bool = false ) -> [ T ] {
132
+ walkParentTree ( upTo: [ MemberBlockSyntax . self,
133
+ FunctionDeclSyntax . self,
134
+ InitializerDeclSyntax . self,
135
+ ClosureExprSyntax . self] ,
136
+ at: syntax,
137
+ collect: collect,
138
+ stopWithFirstMatch: stopWithFirstMatch
139
+ )
140
+ }
141
+
142
+ /// Callect syntax nodes matching the collection type up until encountering one of the specified syntax nodes.
143
+ func walkParentTree< T: SyntaxProtocol > ( upTo stopAt: [ SyntaxProtocol . Type ] ,
144
+ at syntax: Syntax ? ,
145
+ collect: T . Type ,
146
+ stopWithFirstMatch: Bool = false ) -> [ T ] {
147
+ guard let syntax, !stopAt. contains ( where: { syntax. is ( $0) } ) else { return [ ] }
148
+ if let matchedSyntax = syntax. as ( T . self) {
149
+ if stopWithFirstMatch {
150
+ return [ matchedSyntax]
151
+ } else {
152
+ return [ matchedSyntax] + walkParentTree( upTo: stopAt, at: syntax. parent, collect: collect, stopWithFirstMatch: stopWithFirstMatch)
153
+ }
154
+ } else {
155
+ return walkParentTree ( upTo: stopAt, at: syntax. parent, collect: collect, stopWithFirstMatch: stopWithFirstMatch)
156
+ }
157
+ }
135
158
}
0 commit comments