Skip to content

Commit 0ef6a21

Browse files
committed
Add identifier based name lookup.
1 parent c5988ed commit 0ef6a21

File tree

9 files changed

+129
-59
lines changed

9 files changed

+129
-59
lines changed

Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protocol IntroducingToSequentialParentScopeSyntax: ScopeSyntax {
1616
/// Returns names matching lookup that should be
1717
/// handled by it's parent sequential scope.
1818
func introducesToSequentialParent(
19-
for name: String?,
19+
for identifier: Identifier?,
2020
at syntax: SyntaxProtocol,
2121
with config: LookupConfig,
2222
state: LookupState

Sources/SwiftLexicalLookup/LookupName.swift

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ import SwiftSyntax
4040
}
4141
}
4242

43-
/// Used for name comparison.
44-
var name: String {
43+
/// Name associated with implicit name kind.
44+
private var name: String {
4545
switch self {
4646
case .self:
4747
"self"
@@ -55,6 +55,11 @@ import SwiftSyntax
5555
"oldValue"
5656
}
5757
}
58+
59+
/// Identifier used for name comparison.
60+
var identifier: Identifier {
61+
Identifier(name)
62+
}
5863
}
5964

6065
@_spi(Experimental) public enum LookupName {
@@ -79,15 +84,15 @@ import SwiftSyntax
7984
}
8085
}
8186

82-
/// Introduced name.
87+
/// Identifier used for name comparison.
8388
@_spi(Experimental) public var identifier: Identifier? {
8489
switch self {
8590
case .identifier(let syntax, _):
8691
Identifier(syntax.identifier)
8792
case .declaration(let syntax, _):
8893
Identifier(syntax.name)
89-
default:
90-
nil
94+
case .implicit(let kind):
95+
kind.identifier
9196
}
9297
}
9398

@@ -102,26 +107,16 @@ import SwiftSyntax
102107
}
103108
}
104109

105-
/// Used for name comparison.
106-
var name: String? {
107-
switch self {
108-
case .identifier, .declaration:
109-
identifier?.name
110-
case .implicit(let implicitName):
111-
implicitName.name
112-
}
113-
}
114-
115110
/// Checks if this name was introduced before the syntax used for lookup.
116111
func isAccessible(at lookedUpSyntax: SyntaxProtocol) -> Bool {
117112
guard let accessibleAfter else { return true }
118113
return accessibleAfter <= lookedUpSyntax.position
119114
}
120115

121116
/// Checks if this name refers to the looked up phrase.
122-
func refersTo(_ lookedUpName: String) -> Bool {
123-
guard let name else { return false }
124-
return name == lookedUpName
117+
func refersTo(_ lookedUpIdentifier: Identifier) -> Bool {
118+
guard let identifier else { return false }
119+
return identifier == lookedUpIdentifier
125120
}
126121

127122
/// Extracts names introduced by the given `syntax` structure.

Sources/SwiftLexicalLookup/ScopeImplementations.swift

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ import SwiftSyntax
101101
/// - for `memberBlock` - a, b, c, d, e, f
102102
/// - for `codeBlock` - a
103103
@_spi(Experimental) public func lookup(
104-
for name: String?,
104+
for identifier: Identifier?,
105105
at syntax: SyntaxProtocol,
106106
with config: LookupConfig,
107107
state: LookupState
@@ -110,7 +110,7 @@ import SwiftSyntax
110110
case .codeBlock:
111111
return sequentialLookup(
112112
in: statements,
113-
for: name,
113+
for: identifier,
114114
at: syntax,
115115
with: config,
116116
state: state,
@@ -119,7 +119,7 @@ import SwiftSyntax
119119
case .memberBlock:
120120
let names = introducedNames(using: .memberBlock)
121121
.filter { lookupName in
122-
does(name: name, referTo: lookupName, at: syntax)
122+
does(identifier: identifier, referTo: lookupName, at: syntax)
123123
}
124124

125125
return names.isEmpty ? [] : [.fromFileScope(self, withNames: names)]
@@ -137,7 +137,7 @@ import SwiftSyntax
137137
if item.is(DeclSyntax.self) || item.is(VariableDeclSyntax.self) {
138138
let foundNames = LookupName.getNames(from: item)
139139

140-
members.append(contentsOf: foundNames.filter { does(name: name, referTo: $0, at: syntax) })
140+
members.append(contentsOf: foundNames.filter { does(identifier: identifier, referTo: $0, at: syntax) })
141141
} else {
142142
encounteredNonDeclaration = true
143143
sequentialItems.append(codeBlockItem)
@@ -147,7 +147,7 @@ import SwiftSyntax
147147

148148
let sequentialNames = sequentialLookup(
149149
in: sequentialItems,
150-
for: name,
150+
for: identifier,
151151
at: syntax,
152152
with: config,
153153
state: state,
@@ -169,14 +169,14 @@ import SwiftSyntax
169169
}
170170

171171
@_spi(Experimental) public func lookup(
172-
for name: String?,
172+
for identifier: Identifier?,
173173
at syntax: SyntaxProtocol,
174174
with config: LookupConfig,
175175
state: LookupState
176176
) -> [LookupResult] {
177177
sequentialLookup(
178178
in: statements,
179-
for: name,
179+
for: identifier,
180180
at: syntax,
181181
with: config,
182182
state: state,
@@ -291,15 +291,15 @@ import SwiftSyntax
291291
/// }
292292
/// ```
293293
@_spi(Experimental) public func lookup(
294-
for name: String?,
294+
for identifier: Identifier?,
295295
at syntax: SyntaxProtocol,
296296
with config: LookupConfig,
297297
state: LookupState
298298
) -> [LookupResult] {
299299
if let elseBody, elseBody.position <= syntax.position, elseBody.endPosition >= syntax.position {
300-
lookupInParent(for: name, at: syntax, with: config, state: state)
300+
lookupInParent(for: identifier, at: syntax, with: config, state: state)
301301
} else {
302-
defaultLookupImplementation(for: name, at: syntax, with: config, state: state)
302+
defaultLookupImplementation(for: identifier, at: syntax, with: config, state: state)
303303
}
304304
}
305305
}
@@ -315,15 +315,15 @@ import SwiftSyntax
315315

316316
@_spi(Experimental) extension GuardStmtSyntax: IntroducingToSequentialParentScopeSyntax {
317317
@_spi(Experimental) public func introducesToSequentialParent(
318-
for name: String?,
318+
for identifier: Identifier?,
319319
at syntax: SwiftSyntax.SyntaxProtocol,
320320
with config: LookupConfig,
321321
state: LookupState
322322
) -> [LookupResult] {
323323
let names = conditions.flatMap { element in
324324
LookupName.getNames(from: element.condition, accessibleAfter: element.endPosition)
325325
}.filter { introducedName in
326-
does(name: name, referTo: introducedName, at: syntax)
326+
does(identifier: identifier, referTo: introducedName, at: syntax)
327327
}
328328

329329
return names.isEmpty ? [] : [.fromScope(self, withNames: names)]
@@ -345,17 +345,17 @@ import SwiftSyntax
345345
/// // a is visible here
346346
/// ```
347347
@_spi(Experimental) public func lookup(
348-
for name: String?,
348+
for identifier: Identifier?,
349349
at syntax: SyntaxProtocol,
350350
with config: LookupConfig,
351351
state: LookupState
352352
) -> [LookupResult] {
353353
if body.position <= syntax.position && body.endPosition >= syntax.position {
354354
var newState = state
355355
newState.skipSequentialIntroductionFrom = self
356-
return lookupInParent(for: name, at: syntax, with: config, state: newState)
356+
return lookupInParent(for: identifier, at: syntax, with: config, state: newState)
357357
} else {
358-
return defaultLookupImplementation(for: name, at: syntax, with: config, state: state)
358+
return defaultLookupImplementation(for: identifier, at: syntax, with: config, state: state)
359359
}
360360
}
361361
}

Sources/SwiftLexicalLookup/ScopeSyntax.swift

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ extension SyntaxProtocol {
1616
/// Returns all names that `for` refers to at this syntax node.
1717
/// Optional configuration can be passed as `config` to customize the lookup behavior.
1818
///
19-
/// - Returns: An array of `LookupResult` for `name` at this syntax node,
20-
/// ordered by visibility. If `name` is set to `nil`, returns all available names ordered by visibility.
19+
/// - Returns: An array of `LookupResult` for `identifier` at this syntax node,
20+
/// ordered by visibility. If `identifier` is set to `nil`, returns all available names ordered by visibility.
2121
/// The order is from the innermost to the outermost scope,
2222
/// and within each result, names are ordered by their introduction
2323
/// in the source code.
@@ -44,10 +44,10 @@ extension SyntaxProtocol {
4444
/// in this exact order. The constant declaration within the function body is omitted
4545
/// due to the ordering rules that prioritize visibility within the function body.
4646
@_spi(Experimental) public func lookup(
47-
for name: String?,
47+
for identifier: Identifier?,
4848
with config: LookupConfig = LookupConfig()
4949
) -> [LookupResult] {
50-
scope?.lookup(for: name, at: self, with: config, state: LookupState()) ?? []
50+
scope?.lookup(for: identifier, at: self, with: config, state: LookupState()) ?? []
5151
}
5252
}
5353

@@ -59,7 +59,7 @@ extension SyntaxProtocol {
5959
/// Finds all declarations `name` refers to. `syntax` specifies the node lookup was triggered with.
6060
/// If `name` set to `nil`, returns all available names at the given node.
6161
func lookup(
62-
for name: String?,
62+
for identifier: Identifier?,
6363
at syntax: SyntaxProtocol,
6464
with config: LookupConfig,
6565
state: LookupState
@@ -75,48 +75,48 @@ extension SyntaxProtocol {
7575
/// refers to and is accessible at given syntax node then passes lookup to the parent.
7676
/// If `name` set to `nil`, returns all available names at the given node.
7777
@_spi(Experimental) public func lookup(
78-
for name: String?,
78+
for identifier: Identifier?,
7979
at syntax: SyntaxProtocol,
8080
with config: LookupConfig,
8181
state: LookupState
8282
) -> [LookupResult] {
83-
defaultLookupImplementation(for: name, at: syntax, with: config, state: state)
83+
defaultLookupImplementation(for: identifier, at: syntax, with: config, state: state)
8484
}
8585

8686
/// Returns `LookupResult` of all names introduced in this scope that `name`
8787
/// refers to and is accessible at given syntax node then passes lookup to the parent.
8888
/// If `name` set to `nil`, returns all available names at the given node.
8989
func defaultLookupImplementation(
90-
for name: String?,
90+
for identifier: Identifier?,
9191
at syntax: SyntaxProtocol,
9292
with config: LookupConfig,
9393
state: LookupState
9494
) -> [LookupResult] {
9595
let filteredNames =
9696
introducedNames
9797
.filter { introducedName in
98-
does(name: name, referTo: introducedName, at: syntax)
98+
does(identifier: identifier, referTo: introducedName, at: syntax)
9999
}
100100

101101
if filteredNames.isEmpty {
102-
return lookupInParent(for: name, at: syntax, with: config, state: state)
102+
return lookupInParent(for: identifier, at: syntax, with: config, state: state)
103103
} else {
104104
return [.fromScope(self, withNames: filteredNames)]
105-
+ lookupInParent(for: name, at: syntax, with: config, state: state)
105+
+ lookupInParent(for: identifier, at: syntax, with: config, state: state)
106106
}
107107
}
108108

109109
/// Looks up in parent scope.
110110
func lookupInParent(
111-
for name: String?,
111+
for identifier: Identifier?,
112112
at syntax: SyntaxProtocol,
113113
with config: LookupConfig,
114114
state: LookupState
115115
) -> [LookupResult] {
116-
parentScope?.lookup(for: name, at: syntax, with: config, state: state) ?? []
116+
parentScope?.lookup(for: identifier, at: syntax, with: config, state: state) ?? []
117117
}
118118

119-
func does(name: String?, referTo introducedName: LookupName, at syntax: SyntaxProtocol) -> Bool {
120-
introducedName.isAccessible(at: syntax) && (name == nil || introducedName.refersTo(name!))
119+
func does(identifier: Identifier?, referTo introducedName: LookupName, at syntax: SyntaxProtocol) -> Bool {
120+
introducedName.isAccessible(at: syntax) && (identifier == nil || introducedName.refersTo(identifier!))
121121
}
122122
}

Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protocol SequentialScopeSyntax: ScopeSyntax {
2121
/// scopes that match the lookup.
2222
func sequentialLookup(
2323
in codeBlockItems: any Collection<CodeBlockItemSyntax>,
24-
for name: String?,
24+
for identifier: Identifier?,
2525
at syntax: SyntaxProtocol,
2626
with config: LookupConfig,
2727
state: LookupState,
@@ -32,7 +32,7 @@ protocol SequentialScopeSyntax: ScopeSyntax {
3232
extension SequentialScopeSyntax {
3333
func sequentialLookup(
3434
in codeBlockItems: any Collection<CodeBlockItemSyntax>,
35-
for name: String?,
35+
for identifier: Identifier?,
3636
at syntax: SyntaxProtocol,
3737
with config: LookupConfig,
3838
state: LookupState,
@@ -61,7 +61,7 @@ extension SequentialScopeSyntax {
6161
// Add names introduced by the encountered scope.
6262
result.append(
6363
contentsOf: introducingToParentScope.introducesToSequentialParent(
64-
for: name,
64+
for: identifier,
6565
at: syntax,
6666
with: config,
6767
state: state
@@ -75,7 +75,7 @@ extension SequentialScopeSyntax {
7575
from: codeBlockItem.item,
7676
accessibleAfter: codeBlockItem.endPosition
7777
).filter { introducedName in
78-
does(name: name, referTo: introducedName, at: syntax)
78+
does(identifier: identifier, referTo: introducedName, at: syntax)
7979
}
8080
)
8181
}
@@ -87,6 +87,6 @@ extension SequentialScopeSyntax {
8787
currentChunk = []
8888
}
8989

90-
return (result.isEmpty ? [] : result.reversed()) + lookupInParent(for: name, at: syntax, with: config, state: state)
90+
return (result.isEmpty ? [] : result.reversed()) + lookupInParent(for: identifier, at: syntax, with: config, state: state)
9191
}
9292
}

Sources/SwiftSyntax/Identifier.swift

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
public struct Identifier: Equatable, Hashable, Sendable {
1515
/// The sanitized `text` of a token.
1616
public var name: String {
17-
String(syntaxText: raw.name)
17+
string?.name ?? String(syntaxText: raw!.name)
1818
}
1919

2020
@_spi(RawSyntax)
21-
public let raw: RawIdentifier
21+
public let raw: RawIdentifier?
22+
public let string: StringIdentifier?
2223

23-
private let arena: SyntaxArenaRef
24+
private let arena: SyntaxArenaRef?
2425

2526
public init?(_ token: TokenSyntax) {
2627
guard case .identifier = token.tokenKind else {
@@ -29,6 +30,24 @@ public struct Identifier: Equatable, Hashable, Sendable {
2930

3031
self.raw = RawIdentifier(token.tokenView)
3132
self.arena = token.tokenView.raw.arenaReference
33+
self.string = nil
34+
}
35+
36+
public init(_ string: String) {
37+
self.string = StringIdentifier(string)
38+
self.raw = nil
39+
self.arena = nil
40+
}
41+
42+
public static func == (lhs: Self, rhs: Self) -> Bool {
43+
if let lhsRaw = lhs.raw,
44+
let rhsRaw = rhs.raw,
45+
let lhsArena = lhs.arena,
46+
let rhsArena = rhs.arena {
47+
return lhsRaw == rhsRaw && lhsArena == rhsArena
48+
} else {
49+
return lhs.name == rhs.name
50+
}
3251
}
3352
}
3453

@@ -48,3 +67,18 @@ public struct RawIdentifier: Equatable, Hashable, Sendable {
4867
}
4968
}
5069
}
70+
71+
public struct StringIdentifier: Equatable, Hashable, Sendable {
72+
public let name: String
73+
74+
fileprivate init(_ string: String) {
75+
let backtick = "`"
76+
if string.count > 2 && string.hasPrefix(backtick) && string.hasSuffix(backtick) {
77+
let startIndex = string.index(after: string.startIndex)
78+
let endIndex = string.index(before: string.endIndex)
79+
self.name = String(string[startIndex..<endIndex])
80+
} else {
81+
self.name = string
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)