Skip to content

Commit b47d442

Browse files
authored
Merge pull request #2877 from MAJKFL/add-dollar-identifier-where-clause-handling
[SwiftLexicalLookup] Add dollar identifier, better extension handling and more `ASTScope` related fixes
2 parents 27e74a2 + 2cd693c commit b47d442

14 files changed

+403
-476
lines changed

Sources/SwiftLexicalLookup/CMakeLists.txt

+1-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ add_swift_syntax_library(SwiftLexicalLookup
1111
LookupName.swift
1212
LookupResult.swift
1313
SimpleLookupQueries.swift
14-
15-
Configurations/FileScopeHandlingConfig.swift
16-
Configurations/LookupConfig.swift
14+
LookupConfig.swift
1715

1816
Scopes/CanInterleaveResultsLaterScopeSyntax.swift
1917
Scopes/FunctionScopeSyntax.swift

Sources/SwiftLexicalLookup/Configurations/FileScopeHandlingConfig.swift

-23
This file was deleted.

Sources/SwiftLexicalLookup/Configurations/LookupConfig.swift renamed to Sources/SwiftLexicalLookup/LookupConfig.swift

+1-30
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
@_spi(Experimental) public struct LookupConfig {
14-
/// Specifies behavior of file scope.
15-
@_spi(Experimental) public var fileScopeHandling: FileScopeHandlingConfig
1614
/// Specifies whether lookup should finish in the closest sequential scope.
1715
///
1816
/// ### Example
@@ -33,41 +31,14 @@
3331
/// If `finishInSequentialScope` would be set to `false`, the only name
3432
/// returned by lookup would be the `a` declaration from inside function body.
3533
@_spi(Experimental) public var finishInSequentialScope: Bool
36-
/// Specifies whether to include results generated in file and member block scopes.
37-
///
38-
/// ### Example
39-
/// ```swift
40-
/// class X {
41-
/// let a = 42
42-
///
43-
/// func (a: Int) {
44-
/// let a = 123
45-
///
46-
/// a // <-- lookup here
47-
/// }
48-
/// }
49-
/// ```
50-
/// When looking up at the specified position with `includeMembers`
51-
/// set to `true`, lookup will return declaration from inside function body,
52-
/// function parameter and the `a` declaration from `class X` member block.
53-
/// If `includeMembers` would be set to `false`, the latter name would be omitted.
54-
@_spi(Experimental) public var includeMembers: Bool
5534

5635
/// Creates a new lookup configuration.
5736
///
58-
/// - `fileScopeHandling` - specifies behavior of file scope.
59-
/// `memberBlockUpToLastDecl` by default.
6037
/// - `finishInSequentialScope` - specifies whether lookup should finish
6138
/// in the closest sequential scope. `false` by default.
62-
/// - `includeMembers` - specifies whether to include results generated
63-
/// in file and member block scopes. `true` by default.
6439
@_spi(Experimental) public init(
65-
fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl,
66-
finishInSequentialScope: Bool = false,
67-
includeMembers: Bool = true
40+
finishInSequentialScope: Bool = false
6841
) {
69-
self.fileScopeHandling = fileScopeHandling
7042
self.finishInSequentialScope = finishInSequentialScope
71-
self.includeMembers = includeMembers
7243
}
7344
}

Sources/SwiftLexicalLookup/LookupName.swift

+60-39
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import SwiftSyntax
4646
}
4747

4848
/// The name of the implicit declaration.
49-
private var name: String {
49+
private var name: StaticString {
5050
switch self {
5151
case .self:
5252
return "self"
@@ -86,17 +86,38 @@ import SwiftSyntax
8686
/// `self` and `Self` identifers override implicit `self` and `Self` introduced by
8787
/// the `Foo` class declaration.
8888
var identifier: Identifier {
89+
Identifier(name)
90+
}
91+
92+
/// Position of this implicit name.
93+
@_spi(Experimental) public var position: AbsolutePosition {
8994
switch self {
90-
case .self:
91-
return Identifier("self")
92-
case .Self:
93-
return Identifier("Self")
94-
case .error:
95-
return Identifier("error")
96-
case .newValue:
97-
return Identifier("newValue")
98-
case .oldValue:
99-
return Identifier("oldValue")
95+
case .self(let declSyntax):
96+
switch Syntax(declSyntax).as(SyntaxEnum.self) {
97+
case .functionDecl(let functionDecl):
98+
return functionDecl.name.positionAfterSkippingLeadingTrivia
99+
case .initializerDecl(let initializerDecl):
100+
return initializerDecl.initKeyword.positionAfterSkippingLeadingTrivia
101+
case .subscriptDecl(let subscriptDecl):
102+
return subscriptDecl.accessorBlock?.positionAfterSkippingLeadingTrivia
103+
?? subscriptDecl.endPositionBeforeTrailingTrivia
104+
case .variableDecl(let variableDecl):
105+
return variableDecl.bindings.first?.accessorBlock?.positionAfterSkippingLeadingTrivia
106+
?? variableDecl.endPosition
107+
default:
108+
return declSyntax.positionAfterSkippingLeadingTrivia
109+
}
110+
case .Self(let declSyntax):
111+
switch Syntax(declSyntax).as(SyntaxEnum.self) {
112+
case .protocolDecl(let protocolDecl):
113+
return protocolDecl.name.positionAfterSkippingLeadingTrivia
114+
default:
115+
return declSyntax.positionAfterSkippingLeadingTrivia
116+
}
117+
case .error(let catchClause):
118+
return catchClause.catchItems.positionAfterSkippingLeadingTrivia
119+
default:
120+
return syntax.positionAfterSkippingLeadingTrivia
100121
}
101122
}
102123
}
@@ -110,6 +131,8 @@ import SwiftSyntax
110131
case declaration(NamedDeclSyntax)
111132
/// Name introduced implicitly by certain syntax nodes.
112133
case implicit(ImplicitDecl)
134+
/// Dollar identifier introduced by a closure without parameters.
135+
case dollarIdentifier(ClosureExprSyntax, strRepresentation: String)
113136

114137
/// Syntax associated with this name.
115138
@_spi(Experimental) public var syntax: SyntaxProtocol {
@@ -120,6 +143,8 @@ import SwiftSyntax
120143
return syntax
121144
case .implicit(let implicitName):
122145
return implicitName.syntax
146+
case .dollarIdentifier(let closureExpr, _):
147+
return closureExpr
123148
}
124149
}
125150

@@ -132,6 +157,8 @@ import SwiftSyntax
132157
return Identifier(syntax.name)
133158
case .implicit(let kind):
134159
return kind.identifier
160+
case .dollarIdentifier(_, strRepresentation: _):
161+
return nil
135162
}
136163
}
137164

@@ -149,34 +176,9 @@ import SwiftSyntax
149176
case .declaration(let syntax):
150177
return syntax.name.positionAfterSkippingLeadingTrivia
151178
case .implicit(let implicitName):
152-
switch implicitName {
153-
case .self(let declSyntax):
154-
switch Syntax(declSyntax).as(SyntaxEnum.self) {
155-
case .functionDecl(let functionDecl):
156-
return functionDecl.name.positionAfterSkippingLeadingTrivia
157-
case .initializerDecl(let initializerDecl):
158-
return initializerDecl.initKeyword.positionAfterSkippingLeadingTrivia
159-
case .subscriptDecl(let subscriptDecl):
160-
return subscriptDecl.accessorBlock?.positionAfterSkippingLeadingTrivia
161-
?? subscriptDecl.endPositionBeforeTrailingTrivia
162-
case .variableDecl(let variableDecl):
163-
return variableDecl.bindings.first?.accessorBlock?.positionAfterSkippingLeadingTrivia
164-
?? variableDecl.endPosition
165-
default:
166-
return declSyntax.positionAfterSkippingLeadingTrivia
167-
}
168-
case .Self(let declSyntax):
169-
switch Syntax(declSyntax).as(SyntaxEnum.self) {
170-
case .protocolDecl(let protocolDecl):
171-
return protocolDecl.name.positionAfterSkippingLeadingTrivia
172-
default:
173-
return declSyntax.positionAfterSkippingLeadingTrivia
174-
}
175-
case .error(let catchClause):
176-
return catchClause.body.positionAfterSkippingLeadingTrivia
177-
default:
178-
return implicitName.syntax.positionAfterSkippingLeadingTrivia
179-
}
179+
return implicitName.position
180+
case .dollarIdentifier(let closureExpr, _):
181+
return closureExpr.positionAfterSkippingLeadingTrivia
180182
}
181183
}
182184

@@ -197,6 +199,17 @@ import SwiftSyntax
197199
return accessibleAfter <= lookUpPosition
198200
}
199201

202+
func refersTo(_ otherIdentifier: Identifier?) -> Bool {
203+
guard let otherIdentifier else { return true }
204+
205+
switch self {
206+
case .dollarIdentifier(_, let strRepresentation):
207+
return strRepresentation == otherIdentifier.name
208+
default:
209+
return identifier == otherIdentifier
210+
}
211+
}
212+
200213
/// Extracts names introduced by the given `syntax` structure.
201214
///
202215
/// When e.g. looking up a variable declaration like `let a = a`,
@@ -221,6 +234,12 @@ import SwiftSyntax
221234
return tuplePattern.elements.flatMap { tupleElement in
222235
getNames(from: tupleElement.pattern, accessibleAfter: accessibleAfter)
223236
}
237+
case .tupleExpr(let tupleExpr):
238+
return tupleExpr.elements.flatMap { tupleElement in
239+
getNames(from: tupleElement, accessibleAfter: accessibleAfter)
240+
}
241+
case .labeledExpr(let labeledExpr):
242+
return getNames(from: labeledExpr.expression, accessibleAfter: accessibleAfter)
224243
case .valueBindingPattern(let valueBindingPattern):
225244
return getNames(from: valueBindingPattern.pattern, accessibleAfter: accessibleAfter)
226245
case .expressionPattern(let expressionPattern):
@@ -288,6 +307,8 @@ import SwiftSyntax
288307
return "declaration: \(strName)"
289308
case .implicit:
290309
return "implicit: \(strName)"
310+
case .dollarIdentifier(_, strRepresentation: let str):
311+
return "dollarIdentifier: \(str)"
291312
}
292313
}
293314
}

Sources/SwiftLexicalLookup/LookupResult.swift

+49-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,36 @@ import SwiftSyntax
2020
case fromFileScope(SourceFileSyntax, withNames: [LookupName])
2121
/// Indicates where to perform member lookup.
2222
case lookInMembers(LookInMembersScopeSyntax)
23+
/// Indicates to lookup generic parameters of extended type.
24+
///
25+
/// ### Example
26+
/// ```swift
27+
/// extension Foo {
28+
/// func bar() {
29+
/// let a = A() // <-- lookup here
30+
/// }
31+
/// }
32+
/// ```
33+
/// For a lookup started at the marked position, `lookInGenericParametersOfExtendedType`
34+
/// will be included as one of the results prompting the client
35+
/// to lookup the generic parameters of of the extended `Foo` type.
36+
case lookInGenericParametersOfExtendedType(ExtensionDeclSyntax)
37+
/// Indicates this closure expression could introduce dollar identifiers.
38+
///
39+
/// ### Example
40+
/// ```swift
41+
/// func foo() {
42+
/// let a = {
43+
/// $0 // <-- lookup here
44+
/// }
45+
/// }
46+
/// ```
47+
/// When looking up for any identifier at the indicated position,
48+
/// the result will include `mightIntroduceDollarIdentifiers`
49+
/// result kind. If it's performed for a dollar identifier, `LookupName.dollarIdentifier`
50+
/// with the appropriate identifier will be used in the
51+
/// result associated with the closure expression inside `a`.
52+
case mightIntroduceDollarIdentifiers(ClosureExprSyntax)
2353

2454
/// Associated scope.
2555
@_spi(Experimental) public var scope: ScopeSyntax {
@@ -30,6 +60,10 @@ import SwiftSyntax
3060
return fileScopeSyntax
3161
case .lookInMembers(let lookInMemb):
3262
return lookInMemb
63+
case .lookInGenericParametersOfExtendedType(let extensionDecl):
64+
return extensionDecl
65+
case .mightIntroduceDollarIdentifiers(let closureExpr):
66+
return closureExpr
3367
}
3468
}
3569

@@ -38,7 +72,9 @@ import SwiftSyntax
3872
switch self {
3973
case .fromScope(_, let names), .fromFileScope(_, let names):
4074
return names
41-
case .lookInMembers(_):
75+
case .lookInMembers(_),
76+
.lookInGenericParametersOfExtendedType(_),
77+
.mightIntroduceDollarIdentifiers(_):
4278
return []
4379
}
4480
}
@@ -53,6 +89,14 @@ import SwiftSyntax
5389
}
5490
}
5591

92+
/// Returns result specific for the particular `scope` kind with provided `names`
93+
/// as an array with one element. If names are empty, returns an empty array.
94+
static func getResultArray(for scope: ScopeSyntax, withNames names: [LookupName]) -> [LookupResult] {
95+
guard !names.isEmpty else { return [] }
96+
97+
return [getResult(for: scope, withNames: names)]
98+
}
99+
56100
/// Debug description of this lookup name.
57101
@_spi(Experimental) public var debugDescription: String {
58102
var description =
@@ -87,6 +131,10 @@ import SwiftSyntax
87131
return "fromFileScope"
88132
case .lookInMembers:
89133
return "lookInMembers"
134+
case .lookInGenericParametersOfExtendedType(_):
135+
return "lookInGenericParametersOfExtendedType"
136+
case .mightIntroduceDollarIdentifiers(_):
137+
return "mightIntroduceDollarIdentifiers"
90138
}
91139
}
92140
}

Sources/SwiftLexicalLookup/Scopes/FunctionScopeSyntax.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212

1313
import SwiftSyntax
1414

15-
protocol FunctionScopeSyntax: DeclSyntaxProtocol, WithGenericParametersScopeSyntax {
15+
@_spi(Experimental) public protocol FunctionScopeSyntax: DeclSyntaxProtocol, WithGenericParametersScopeSyntax {
1616
var signature: FunctionSignatureSyntax { get }
17+
var body: CodeBlockSyntax? { get }
1718
}
1819

1920
extension FunctionScopeSyntax {
2021
/// Function parameters introduced by this function's signature.
21-
@_spi(Experimental) public var introducedNames: [LookupName] {
22+
@_spi(Experimental) public var defaultIntroducedNames: [LookupName] {
2223
signature.parameterClause.parameters.flatMap { parameter in
2324
LookupName.getNames(from: parameter)
2425
} + (parentScope?.is(MemberBlockSyntax.self) ?? false ? [.implicit(.self(self))] : [])
@@ -33,7 +34,7 @@ extension FunctionScopeSyntax {
3334
) -> [LookupResult] {
3435
var thisScopeResults: [LookupResult] = []
3536

36-
if !signature.range.contains(lookUpPosition) {
37+
if body?.range.contains(lookUpPosition) ?? false {
3738
thisScopeResults = defaultLookupImplementation(
3839
identifier,
3940
at: position,

0 commit comments

Comments
 (0)