Skip to content

Commit 5af6d91

Browse files
committed
Simlify config handling. Add documentation.
1 parent 5519ccb commit 5af6d91

File tree

10 files changed

+163
-107
lines changed

10 files changed

+163
-107
lines changed

Sources/SwiftLexicalLookup/Configurations/FileScopeHandlingConfig.swift renamed to Sources/SwiftLexicalLookup/Configurations/FileScopeNameIntroductionStrategy.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import SwiftSyntax
1414

1515
/// Specifies how names should be introduced at the file scope.
16-
@_spi(Experimental) public enum FileScopeNameIntroductionStrategy: LookupConfig {
16+
@_spi(Experimental) public enum FileScopeHandlingConfig {
1717
/// Default behavior. Names introduced sequentially like in member block
1818
/// scope up to the first non-declaration after and including which,
1919
/// the declarations are treated like in code block scope.

Sources/SwiftLexicalLookup/Configurations/LookupConfig.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212

1313
import Foundation
1414

15-
/// Used to customize lookup behavior.
16-
@_spi(Experimental) public protocol LookupConfig {}
15+
@_spi(Experimental) public struct LookupConfig {
16+
/// Specifies behaviour of file scope.
17+
/// `memberBlockUpToLastDecl` by default.
18+
public var fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl
1719

18-
extension LookupConfig {
19-
var identifier: ObjectIdentifier {
20-
ObjectIdentifier(Self.self)
20+
public init(fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl) {
21+
self.fileScopeHandling = fileScopeHandling
2122
}
2223
}

Sources/SwiftLexicalLookup/Configurations/LookupConfigDictionary.swift

Lines changed: 0 additions & 37 deletions
This file was deleted.

Sources/SwiftLexicalLookup/IdentifiableSyntax.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
3333

3434
extension ClosureCaptureSyntax: IdentifiableSyntax {
3535
@_spi(Experimental) public var identifier: TokenSyntax {
36+
/* Doesn't work with closures like:
37+
_ = { [y=1+2] in
38+
print(y)
39+
}
40+
*/
3641
expression.as(DeclReferenceExprSyntax.self)!.baseName
3742
}
3843
}

Sources/SwiftLexicalLookup/LookupName.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ import SwiftSyntax
4444
/// If set to `nil`, the name is available at any point in scope.
4545
var accessibleAfter: AbsolutePosition? {
4646
switch self {
47-
case .identifier(_, let absolutePosition):
48-
absolutePosition
49-
case .declaration(_, let absolutePosition):
47+
case .identifier(_, let absolutePosition), .declaration(_, let absolutePosition):
5048
absolutePosition
5149
}
5250
}

Sources/SwiftLexicalLookup/LookupResult.swift

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,23 @@ import SwiftSyntax
1616
@_spi(Experimental) public enum LookupResult {
1717
/// Scope and the names that matched lookup.
1818
case fromScope(ScopeSyntax, withNames: [LookupName])
19-
/// File scope, names that matched lookup and name introduction
20-
/// strategy used for the lookup.
21-
case fromFileScope(
22-
SourceFileSyntax,
23-
withNames: [LookupName],
24-
nameIntroductionStrategy: FileScopeNameIntroductionStrategy
25-
)
19+
/// File scope and names that matched lookup.
20+
case fromFileScope(SourceFileSyntax, withNames: [LookupName])
2621

2722
/// Associated scope.
2823
@_spi(Experimental) public var scope: ScopeSyntax? {
2924
switch self {
3025
case .fromScope(let scopeSyntax, _):
3126
scopeSyntax
32-
case .fromFileScope(let fileScopeSyntax, withNames: _, nameIntroductionStrategy: _):
27+
case .fromFileScope(let fileScopeSyntax, _):
3328
fileScopeSyntax
3429
}
3530
}
3631

3732
/// Names that matched lookup.
3833
@_spi(Experimental) public var names: [LookupName] {
3934
switch self {
40-
case .fromScope(_, let names):
41-
names
42-
case .fromFileScope(_, withNames: let names, nameIntroductionStrategy: _):
35+
case .fromScope(_, let names), .fromFileScope(_, let names):
4336
names
4437
}
4538
}

Sources/SwiftLexicalLookup/ScopeImplementations.swift

Lines changed: 113 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,34 @@ extension SyntaxProtocol {
2424
}
2525

2626
@_spi(Experimental) extension SourceFileSyntax: ScopeSyntax {
27+
/// All names introduced in the file scope
28+
/// according to the default strategy: `memberBlockUpToLastDecl`.
2729
public var introducedNames: [LookupName] {
2830
introducedNames(using: .memberBlockUpToLastDecl)
2931
}
3032

31-
public func introducedNames(using nameIntroductionStrategy: FileScopeNameIntroductionStrategy) -> [LookupName] {
32-
switch nameIntroductionStrategy {
33+
/// All names introduced in the file scope
34+
/// using the provided configuration.
35+
///
36+
/// Example usage:
37+
/// ```swift
38+
/// class a {}
39+
/// class b {
40+
/// // <--
41+
/// }
42+
/// let c = 0
43+
/// class d {}
44+
/// if true {}
45+
/// class e {}
46+
/// let f = 0
47+
/// ```
48+
/// During lookup, according to different configurations,
49+
/// names available at the marked place are:
50+
/// - for `fileScopeNameIntroductionStrategy` - a, b, c, d
51+
/// - for `memberBlock` - a, b, c, d, e, f
52+
/// - for `codeBlock` - a
53+
public func introducedNames(using fileScopeHandling: FileScopeHandlingConfig) -> [LookupName] {
54+
switch fileScopeHandling {
3355
case .memberBlockUpToLastDecl:
3456
var encounteredNonDeclaration = false
3557

@@ -58,23 +80,43 @@ extension SyntaxProtocol {
5880
}
5981
}
6082

83+
/// Returns names matching lookup using provided file
84+
/// scope handling configuration (by default: `memberBlockUpToLastDecl`).
85+
///
86+
/// Example usage:
87+
/// ```swift
88+
/// class a {}
89+
/// class b {
90+
/// // <--
91+
/// }
92+
/// let c = 0
93+
/// class d {}
94+
/// if true {}
95+
/// class e {}
96+
/// let f = 0
97+
/// ```
98+
/// According to different configurations,
99+
/// names available at the marked place are:
100+
/// - for `fileScopeNameIntroductionStrategy` - a, b, c, d
101+
/// - for `memberBlock` - a, b, c, d, e, f
102+
/// - for `codeBlock` - a
61103
public func lookup(
62104
for name: String?,
63105
at syntax: SyntaxProtocol,
64-
with configDict: LookupConfigDictionary
106+
with config: LookupConfig
65107
) -> [LookupResult] {
66-
let nameIntroductionStrategy = configDict[FileScopeNameIntroductionStrategy.self] ?? .memberBlockUpToLastDecl
67-
68-
let names = introducedNames(using: nameIntroductionStrategy)
108+
let names = introducedNames(using: config.fileScopeHandling)
69109
.filter { introducedName in
70110
introducedName.isAccessible(at: syntax) && (name == nil || introducedName.refersTo(name!))
71111
}
72112

73-
return [.fromFileScope(self, withNames: names, nameIntroductionStrategy: nameIntroductionStrategy)]
113+
return [.fromFileScope(self, withNames: names)]
74114
}
75115
}
76116

77117
@_spi(Experimental) extension CodeBlockSyntax: ScopeSyntax {
118+
/// Names introduced in the code block scope
119+
/// accessible after their declaration.
78120
public var introducedNames: [LookupName] {
79121
statements.flatMap { codeBlockItem in
80122
LookupName.getNames(from: codeBlockItem.item, accessibleAfter: codeBlockItem.endPosition)
@@ -83,12 +125,24 @@ extension SyntaxProtocol {
83125
}
84126

85127
@_spi(Experimental) extension ForStmtSyntax: ScopeSyntax {
128+
/// Names introduced in the `for` body.
86129
public var introducedNames: [LookupName] {
87130
LookupName.getNames(from: pattern)
88131
}
89132
}
90133

91134
@_spi(Experimental) extension ClosureExprSyntax: ScopeSyntax {
135+
/// All names introduced by the closure signature.
136+
/// Could be closure captures or (shorthand) parameters.
137+
///
138+
/// Example:
139+
/// ```swift
140+
/// let x = { [weak self, a] b, _ in
141+
/// // <--
142+
/// }
143+
/// ```
144+
/// During lookup, names available at the marked place are:
145+
/// `self`, a, b.
92146
public var introducedNames: [LookupName] {
93147
let captureNames =
94148
signature?.capture?.children(viewMode: .sourceAccurate).flatMap { child in
@@ -117,6 +171,7 @@ extension SyntaxProtocol {
117171
}
118172

119173
@_spi(Experimental) extension WhileStmtSyntax: ScopeSyntax {
174+
/// Names introduced by the `while` loop by its conditions.
120175
public var introducedNames: [LookupName] {
121176
conditions.flatMap { element in
122177
LookupName.getNames(from: element.condition)
@@ -125,11 +180,28 @@ extension SyntaxProtocol {
125180
}
126181

127182
@_spi(Experimental) extension IfExprSyntax: ScopeSyntax {
183+
/// Parent scope, omitting ancestor `if` statements if part of their `else if` clause.
128184
public var parentScope: ScopeSyntax? {
129185
getParent(for: self.parent, previousIfElse: self.elseKeyword == nil)
130186
}
131187

132-
/// Finds the parent scope, omitting parent `if` statements if part of their `else if` clause.
188+
/// Finds parent scope, omitting ancestor `if` statements if part of their `else if` clause.
189+
///
190+
/// Example:
191+
/// ```swift
192+
/// func foo() {
193+
/// if let a = x {
194+
/// // <--
195+
/// } else if let b {
196+
/// // <--
197+
/// } else if y == 1 {
198+
/// // <--
199+
/// }
200+
/// }
201+
/// ```
202+
/// For each of the marked scopes, resulting parent
203+
/// is the enclosing code block scope associated with
204+
/// the function body.
133205
private func getParent(for syntax: Syntax?, previousIfElse: Bool) -> ScopeSyntax? {
134206
guard let syntax else { return nil }
135207

@@ -144,26 +216,40 @@ extension SyntaxProtocol {
144216
}
145217
}
146218

219+
/// Names introduced by the `if` optional binding conditions.
147220
public var introducedNames: [LookupName] {
148221
conditions.flatMap { element in
149222
LookupName.getNames(from: element.condition, accessibleAfter: element.endPosition)
150223
}
151224
}
152225

226+
/// Returns names matching lookup.
227+
/// Lookup triggered from inside of `else`
228+
/// clause is immediately forwarded to parent scope.
229+
///
230+
/// Example:
231+
/// ```swift
232+
/// if let a = x {
233+
/// // <-- a is visible here
234+
/// } else {
235+
/// // <-- a is not visible here
236+
/// }
237+
/// ```
153238
public func lookup(
154239
for name: String?,
155240
at syntax: SyntaxProtocol,
156-
with configDict: LookupConfigDictionary
241+
with config: LookupConfig
157242
) -> [LookupResult] {
158243
if let elseBody, elseBody.position <= syntax.position, elseBody.endPosition >= syntax.position {
159-
lookupInParent(for: name, at: syntax, with: configDict)
244+
lookupInParent(for: name, at: syntax, with: config)
160245
} else {
161-
defaultLookupImplementation(for: name, at: syntax, with: configDict)
246+
defaultLookupImplementation(for: name, at: syntax, with: config)
162247
}
163248
}
164249
}
165250

166251
@_spi(Experimental) extension MemberBlockSyntax: ScopeSyntax {
252+
/// All names introduced by members of this member scope.
167253
public var introducedNames: [LookupName] {
168254
members.flatMap { member in
169255
LookupName.getNames(from: member.decl)
@@ -172,19 +258,32 @@ extension SyntaxProtocol {
172258
}
173259

174260
@_spi(Experimental) extension GuardStmtSyntax: ScopeSyntax {
261+
/// Guard doesn't introduce any names to its children.
262+
/// It's always empty.
175263
public var introducedNames: [LookupName] {
176264
[]
177265
}
178266

267+
/// Returns names matching lookup.
268+
/// Lookup triggered from inside of `else`
269+
/// clause is immediately forwarded to parent scope.
270+
///
271+
/// Example:
272+
/// ```swift
273+
/// guard let a = x else {
274+
/// return // a is not visible here
275+
/// }
276+
/// // a is visible here
277+
/// ```
179278
public func lookup(
180279
for name: String?,
181280
at syntax: SyntaxProtocol,
182-
with configDict: LookupConfigDictionary
281+
with config: LookupConfig
183282
) -> [LookupResult] {
184283
if body.position <= syntax.position && body.endPosition >= syntax.position {
185-
lookupInParent(for: name, at: self, with: configDict)
284+
lookupInParent(for: name, at: self, with: config)
186285
} else {
187-
defaultLookupImplementation(for: name, at: syntax, with: configDict)
286+
defaultLookupImplementation(for: name, at: syntax, with: config)
188287
}
189288
}
190289
}

0 commit comments

Comments
 (0)