Skip to content

Commit b9219a0

Browse files
authored
Merge pull request #2952 from MAJKFL/swift-lexical-lookup-rfc
[RFC][SwiftLexicalLookup] Make unqualified name lookup entry-point public
2 parents 347879f + a7be716 commit b9219a0

15 files changed

+185
-238
lines changed

Release Notes/602.md

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
- Pull Request: https://github.com/swiftlang/swift-syntax/pull/2981
88
- Migration steps: None required. The new `category` property has optional type, and there is a default implementation that returns `nil`. Types that conform to `DiagnosticMessage` can choose to implement this property and provide a category when appropriate.
99

10+
- `SwiftLexicalLookup` - A new Swift unqualified lookup library
11+
- Description: The library provides a new Swift unqualified lookup implementation detached from the compiler and accessible to outside clients. The query is stateless and can be directly run on swift-syntax syntax tree, with any syntax node functioning as an entry point. It produces an enum-based data structure as a result that partitions collected names based on the lexical scope of introduction.
12+
- Pull Request: https://github.com/swiftlang/swift-syntax/pull/2952
13+
- Notes: The library follows the behavior of the compiler implementation with some minor differences, such as a different way of handling dollar identifiers `$x` and generic parameters inside extensions. Furthermore, in the future, once the compiler adopts `SwiftLexicalLookup` and it becomes the canonical implementation, results produced by the query will be guaranteed to be correct.
14+
1015
## API Behavior Changes
1116

1217
## Deprecations

Sources/SwiftLexicalLookup/IdentifiableSyntax.swift

+16-16
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,50 @@
1313
import SwiftSyntax
1414

1515
/// Syntax node that can be refered to with an identifier.
16-
@_spi(Experimental) public protocol IdentifiableSyntax: SyntaxProtocol {
16+
protocol IdentifiableSyntax: SyntaxProtocol {
1717
var identifier: TokenSyntax { get }
1818
}
1919

20-
@_spi(Experimental) extension IdentifierPatternSyntax: IdentifiableSyntax {}
20+
extension IdentifierPatternSyntax: IdentifiableSyntax {}
2121

22-
@_spi(Experimental) extension ClosureParameterSyntax: IdentifiableSyntax {
23-
@_spi(Experimental) public var identifier: TokenSyntax {
22+
extension ClosureParameterSyntax: IdentifiableSyntax {
23+
var identifier: TokenSyntax {
2424
secondName ?? firstName
2525
}
2626
}
2727

28-
@_spi(Experimental) extension FunctionParameterSyntax: IdentifiableSyntax {
29-
@_spi(Experimental) public var identifier: TokenSyntax {
28+
extension FunctionParameterSyntax: IdentifiableSyntax {
29+
var identifier: TokenSyntax {
3030
secondName ?? firstName
3131
}
3232
}
3333

34-
@_spi(Experimental) extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
35-
@_spi(Experimental) public var identifier: TokenSyntax {
34+
extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
35+
var identifier: TokenSyntax {
3636
name
3737
}
3838
}
3939

40-
@_spi(Experimental) extension ClosureCaptureSyntax: IdentifiableSyntax {
41-
@_spi(Experimental) public var identifier: TokenSyntax {
40+
extension ClosureCaptureSyntax: IdentifiableSyntax {
41+
var identifier: TokenSyntax {
4242
name
4343
}
4444
}
4545

46-
@_spi(Experimental) extension AccessorParametersSyntax: IdentifiableSyntax {
47-
@_spi(Experimental) public var identifier: TokenSyntax {
46+
extension AccessorParametersSyntax: IdentifiableSyntax {
47+
var identifier: TokenSyntax {
4848
name
4949
}
5050
}
5151

52-
@_spi(Experimental) extension GenericParameterSyntax: IdentifiableSyntax {
53-
@_spi(Experimental) public var identifier: TokenSyntax {
52+
extension GenericParameterSyntax: IdentifiableSyntax {
53+
var identifier: TokenSyntax {
5454
name
5555
}
5656
}
5757

58-
@_spi(Experimental) extension PrimaryAssociatedTypeSyntax: IdentifiableSyntax {
59-
@_spi(Experimental) public var identifier: TokenSyntax {
58+
extension PrimaryAssociatedTypeSyntax: IdentifiableSyntax {
59+
var identifier: TokenSyntax {
6060
name
6161
}
6262
}

Sources/SwiftLexicalLookup/LookupConfig.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import SwiftIfConfig
1414

15-
@_spi(Experimental) public struct LookupConfig {
15+
public struct LookupConfig {
1616
/// Specifies whether lookup should finish in the closest sequential scope.
1717
///
1818
/// ### Example
@@ -32,14 +32,14 @@ import SwiftIfConfig
3232
/// function parameter and the `a` declaration from `class X` member block.
3333
/// If `finishInSequentialScope` would be set to `false`, the only name
3434
/// returned by lookup would be the `a` declaration from inside function body.
35-
@_spi(Experimental) public var finishInSequentialScope: Bool
36-
@_spi(Experimental) public var configuredRegions: ConfiguredRegions?
35+
public var finishInSequentialScope: Bool
36+
public var configuredRegions: ConfiguredRegions?
3737

3838
/// Creates a new lookup configuration.
3939
///
4040
/// - `finishInSequentialScope` - specifies whether lookup should finish
4141
/// in the closest sequential scope. `false` by default.
42-
@_spi(Experimental) public init(
42+
public init(
4343
finishInSequentialScope: Bool = false,
4444
configuredRegions: ConfiguredRegions? = nil
4545
) {

Sources/SwiftLexicalLookup/LookupName.swift

+30-42
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@
1313
import SwiftSyntax
1414

1515
/// An entity that is implicitly declared based on the syntactic structure of the program.
16-
@_spi(Experimental) public enum ImplicitDecl {
16+
public enum ImplicitDecl {
1717
/// `self` keyword representing object instance.
18-
/// Could be associated with type declaration, extension,
19-
/// or closure captures. Introduced at function edge.
20-
case `self`(DeclSyntaxProtocol)
18+
/// Introduced at member boundary.
19+
/// Associated syntax node could be: `FunctionDeclSyntax`,
20+
/// `AccessorDeclSyntax`, `SubscriptDeclSyntax`,
21+
/// `DeinitializerDeclSyntax`, or `InitializerDeclSyntax`.
22+
case `self`(DeclSyntax)
2123
/// `Self` keyword representing object type.
22-
/// Could be associated with type declaration or extension.
23-
case `Self`(DeclSyntaxProtocol)
24-
/// `error` value caught by a `catch`
24+
/// Associated syntax node could be: `ExtensionDeclSyntax`,
25+
/// or `ProtocolDeclSyntax`.
26+
case `Self`(DeclSyntax)
27+
/// `error` available by default inside `catch`
2528
/// block that does not specify a catch pattern.
2629
case error(CatchClauseSyntax)
2730
/// `newValue` available by default inside `set` and `willSet`.
@@ -30,7 +33,7 @@ import SwiftSyntax
3033
case oldValue(AccessorDeclSyntax)
3134

3235
/// Syntax associated with this name.
33-
@_spi(Experimental) public var syntax: SyntaxProtocol {
36+
public var syntax: SyntaxProtocol {
3437
switch self {
3538
case .self(let syntax):
3639
return syntax
@@ -46,7 +49,7 @@ import SwiftSyntax
4649
}
4750

4851
/// The name of the implicit declaration.
49-
private var name: StaticString {
52+
public var name: StaticString {
5053
switch self {
5154
case .self:
5255
return "self"
@@ -85,12 +88,12 @@ import SwiftSyntax
8588
/// ```
8689
/// `self` and `Self` identifers override implicit `self` and `Self` introduced by
8790
/// the `Foo` class declaration.
88-
var identifier: Identifier {
91+
public var identifier: Identifier {
8992
Identifier(canonicalName: name)
9093
}
9194

9295
/// Position of this implicit name.
93-
@_spi(Experimental) public var position: AbsolutePosition {
96+
public var position: AbsolutePosition {
9497
switch self {
9598
case .self(let declSyntax):
9699
switch Syntax(declSyntax).as(SyntaxEnum.self) {
@@ -128,17 +131,15 @@ import SwiftSyntax
128131
}
129132
}
130133

131-
@_spi(Experimental) public enum LookupName {
134+
public enum LookupName {
132135
/// Identifier associated with the name.
133136
/// Could be an identifier of a variable, function or closure parameter and more.
134-
case identifier(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
137+
case identifier(Syntax, accessibleAfter: AbsolutePosition?)
135138
/// Declaration associated with the name.
136139
/// Could be class, struct, actor, protocol, function and more.
137-
case declaration(NamedDeclSyntax)
140+
case declaration(Syntax)
138141
/// Name introduced implicitly by certain syntax nodes.
139142
case implicit(ImplicitDecl)
140-
/// Dollar identifier introduced by a closure without parameters.
141-
case dollarIdentifier(ClosureExprSyntax, strRepresentation: String)
142143
/// Represents equivalent names grouped together.
143144
/// - Important: The array should be non-empty.
144145
///
@@ -154,32 +155,28 @@ import SwiftSyntax
154155
case equivalentNames([LookupName])
155156

156157
/// Syntax associated with this name.
157-
@_spi(Experimental) public var syntax: SyntaxProtocol {
158+
public var syntax: SyntaxProtocol {
158159
switch self {
159160
case .identifier(let syntax, _):
160161
return syntax
161162
case .declaration(let syntax):
162163
return syntax
163164
case .implicit(let implicitName):
164165
return implicitName.syntax
165-
case .dollarIdentifier(let closureExpr, _):
166-
return closureExpr
167166
case .equivalentNames(let names):
168167
return names.first!.syntax
169168
}
170169
}
171170

172171
/// Identifier used for name comparison.
173-
@_spi(Experimental) public var identifier: Identifier? {
172+
public var identifier: Identifier {
174173
switch self {
175174
case .identifier(let syntax, _):
176-
return Identifier(syntax.identifier)
175+
return Identifier((syntax.asProtocol(SyntaxProtocol.self) as! IdentifiableSyntax).identifier)!
177176
case .declaration(let syntax):
178-
return Identifier(syntax.name)
177+
return Identifier((syntax.asProtocol(SyntaxProtocol.self) as! NamedDeclSyntax).name)!
179178
case .implicit(let kind):
180179
return kind.identifier
181-
case .dollarIdentifier(_, strRepresentation: _):
182-
return nil
183180
case .equivalentNames(let names):
184181
return names.first!.identifier
185182
}
@@ -192,16 +189,15 @@ import SwiftSyntax
192189
/// Such cases are function parameters (as they can
193190
/// contain two identifiers) and function declarations (where name
194191
/// is precided by access modifiers and `func` keyword).
195-
@_spi(Experimental) public var position: AbsolutePosition {
192+
public var position: AbsolutePosition {
196193
switch self {
197194
case .identifier(let syntax, _):
198-
return syntax.identifier.positionAfterSkippingLeadingTrivia
195+
return (syntax.asProtocol(SyntaxProtocol.self) as! IdentifiableSyntax).identifier
196+
.positionAfterSkippingLeadingTrivia
199197
case .declaration(let syntax):
200-
return syntax.name.positionAfterSkippingLeadingTrivia
198+
return (syntax.asProtocol(SyntaxProtocol.self) as! NamedDeclSyntax).name.positionAfterSkippingLeadingTrivia
201199
case .implicit(let implicitName):
202200
return implicitName.position
203-
case .dollarIdentifier(let closureExpr, _):
204-
return closureExpr.positionAfterSkippingLeadingTrivia
205201
case .equivalentNames(let names):
206202
return names.first!.position
207203
}
@@ -226,13 +222,7 @@ import SwiftSyntax
226222

227223
func refersTo(_ otherIdentifier: Identifier?) -> Bool {
228224
guard let otherIdentifier else { return true }
229-
230-
switch self {
231-
case .dollarIdentifier(_, let strRepresentation):
232-
return strRepresentation == otherIdentifier.name
233-
default:
234-
return identifier == otherIdentifier
235-
}
225+
return identifier == otherIdentifier
236226
}
237227

238228
/// Extracts names introduced by the given `syntax` structure.
@@ -305,22 +295,22 @@ import SwiftSyntax
305295
return []
306296
}
307297

308-
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
298+
return [.identifier(Syntax(identifiable), accessibleAfter: accessibleAfter)]
309299
}
310300

311301
/// Extracts name introduced by `NamedDeclSyntax` node.
312302
private static func handle(
313303
namedDecl: NamedDeclSyntax,
314304
accessibleAfter: AbsolutePosition? = nil
315305
) -> [LookupName] {
316-
[.declaration(namedDecl)]
306+
[.declaration(Syntax(namedDecl))]
317307
}
318308

319309
/// Debug description of this lookup name.
320-
@_spi(Experimental) public var debugDescription: String {
310+
public var debugDescription: String {
321311
let sourceLocationConverter = SourceLocationConverter(fileName: "", tree: syntax.root)
322312
let location = sourceLocationConverter.location(for: position)
323-
let strName = (identifier?.name ?? "NO-NAME") + " at: \(location.line):\(location.column)"
313+
let strName = identifier.name + " at: \(location.line):\(location.column)"
324314

325315
switch self {
326316
case .identifier:
@@ -336,8 +326,6 @@ import SwiftSyntax
336326
return "declaration: \(strName)"
337327
case .implicit:
338328
return "implicit: \(strName)"
339-
case .dollarIdentifier(_, strRepresentation: let str):
340-
return "dollarIdentifier: \(str)"
341329
case .equivalentNames(let names):
342330
return "Composite name: [ \(names.map(\.debugDescription).joined(separator: ", ")) ]"
343331
}

0 commit comments

Comments
 (0)