Skip to content

Commit bbc9b6a

Browse files
committed
Add switch, generic parameter and function scopes.
1 parent fcaecc7 commit bbc9b6a

13 files changed

+466
-117
lines changed

Sources/SwiftLexicalLookup/IdentifiableSyntax.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ import SwiftSyntax
2525
}
2626
}
2727

28+
@_spi(Experimental) extension FunctionParameterSyntax: IdentifiableSyntax {
29+
@_spi(Experimental) public var identifier: TokenSyntax {
30+
secondName ?? firstName
31+
}
32+
}
33+
2834
@_spi(Experimental) extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
2935
@_spi(Experimental) public var identifier: TokenSyntax {
3036
name
@@ -47,3 +53,15 @@ import SwiftSyntax
4753
name
4854
}
4955
}
56+
57+
@_spi(Experimental) extension GenericParameterSyntax: IdentifiableSyntax {
58+
@_spi(Experimental) public var identifier: TokenSyntax {
59+
name
60+
}
61+
}
62+
63+
@_spi(Experimental) extension PrimaryAssociatedTypeSyntax: IdentifiableSyntax {
64+
@_spi(Experimental) public var identifier: TokenSyntax {
65+
name
66+
}
67+
}

Sources/SwiftLexicalLookup/LookupName.swift

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,6 @@ import SwiftSyntax
110110
case declaration(NamedDeclSyntax)
111111
/// Name introduced implicitly by certain syntax nodes.
112112
case implicit(ImplicitDecl)
113-
/// Explicit `self` keyword.
114-
case `self`(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
115-
/// Explicit `Self` keyword.
116-
case `Self`(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
117113

118114
/// Syntax associated with this name.
119115
@_spi(Experimental) public var syntax: SyntaxProtocol {
@@ -124,8 +120,6 @@ import SwiftSyntax
124120
return syntax
125121
case .implicit(let implicitName):
126122
return implicitName.syntax
127-
case .self(let syntax, _), .Self(let syntax, _):
128-
return syntax
129123
}
130124
}
131125

@@ -138,35 +132,28 @@ import SwiftSyntax
138132
return Identifier(syntax.name)
139133
case .implicit(let kind):
140134
return kind.identifier
141-
case .self:
142-
return Identifier("self")
143-
case .Self:
144-
return Identifier("Self")
145135
}
146136
}
147137

148138
/// Point, after which the name is available in scope.
149139
/// If set to `nil`, the name is available at any point in scope.
150140
var accessibleAfter: AbsolutePosition? {
151141
switch self {
152-
case .identifier(_, let absolutePosition),
153-
.self(_, let absolutePosition),
154-
.Self(_, let absolutePosition):
142+
case .identifier(_, let absolutePosition):
155143
return absolutePosition
156144
default:
157145
return nil
158146
}
159147
}
160148

161149
/// Checks if this name was introduced before the syntax used for lookup.
162-
func isAccessible(at origin: AbsolutePosition) -> Bool {
150+
func isAccessible(at lookUpPosition: AbsolutePosition) -> Bool {
163151
guard let accessibleAfter else { return true }
164-
return accessibleAfter <= origin
152+
return accessibleAfter <= lookUpPosition
165153
}
166154

167155
/// Checks if this name refers to the looked up phrase.
168156
func refersTo(_ lookedUpIdentifier: Identifier) -> Bool {
169-
guard let identifier else { return false }
170157
return identifier == lookedUpIdentifier
171158
}
172159

@@ -229,16 +216,10 @@ import SwiftSyntax
229216
accessibleAfter: AbsolutePosition? = nil
230217
) -> [LookupName] {
231218
switch identifiable.identifier.tokenKind {
232-
case .keyword(.self):
233-
return [.self(identifiable, accessibleAfter: accessibleAfter)]
234-
case .keyword(.Self):
235-
return [.Self(identifiable, accessibleAfter: accessibleAfter)]
219+
case .wildcard:
220+
return []
236221
default:
237-
if identifiable.identifier.tokenKind != .wildcard {
238-
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
239-
} else {
240-
return []
241-
}
222+
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
242223
}
243224
}
244225

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftSyntax
14+
15+
/// Scope that introduces generic parameter or
16+
/// primary associated type names and directs
17+
/// futher lookup to it's `WithGenericParametersOrAssociatedTypesScopeSyntax`
18+
/// parent scope's parent scope (i.e. on return, bypasses names
19+
/// introduced by it's parent).
20+
@_spi(Experimental) public protocol GenericParameterOrAssociatedTypeScopeSyntax: ScopeSyntax {}
21+
22+
@_spi(Experimental) extension GenericParameterOrAssociatedTypeScopeSyntax {
23+
/// Returns names matching lookup and bypasses
24+
/// `WithGenericParametersOrAssociatedTypesScopeSyntax` parent scope in futher lookup.
25+
///
26+
/// example:
27+
/// ```swift
28+
/// let a = 23
29+
/// func foo<A>(a: A) {
30+
/// a // <-- start lookup here
31+
/// }
32+
/// ```
33+
/// When starting lookup at the `a` reference,
34+
/// lookup first visits the code block scope associated
35+
/// with the function's body. Then, it's forwarded to the
36+
/// function declaration scope and then to generic parameter
37+
/// scope (`WithGenericParametersOrAssociatedTypesScopeSyntax`).
38+
/// Then, to ensure there is no infinite cycle,
39+
/// this method passes lookup to function scope's parent scope
40+
/// (in this case: file scope).
41+
@_spi(Experimental) public func lookup(
42+
identifier: Identifier?,
43+
at lookUpPosition: AbsolutePosition,
44+
with config: LookupConfig
45+
) -> [LookupResult] {
46+
return defaultLookupImplementation(
47+
identifier: identifier,
48+
at: lookUpPosition,
49+
with: config,
50+
propagateToParent: false
51+
)
52+
+ lookupBypassingParentResults(
53+
identifier: identifier,
54+
at: lookUpPosition,
55+
with: config
56+
)
57+
}
58+
59+
/// Bypasses names introduced by `WithGenericParametersOrAssociatedTypesScopeSyntax` parent scope.
60+
///
61+
/// example:
62+
/// ```swift
63+
/// let a = 23
64+
/// func foo<A>(a: A) {
65+
/// a // <-- start lookup here
66+
/// }
67+
/// ```
68+
/// When starting lookup at the `a` reference,
69+
/// lookup first visits the code block scope associated
70+
/// with the function's body. Then, it's forwarded to the
71+
/// function declaration scope and then to generic parameter
72+
/// scope (`WithGenericParametersOrAssociatedTypesScopeSyntax`).
73+
/// Then, to ensure there is no infinite cycle,
74+
/// we use this method instead of the standard `lookupInParent`
75+
/// to pass lookup to the function scope's parent scope (in this case: file scope)
76+
/// and effectively bypass names already looked up before.
77+
private func lookupBypassingParentResults(
78+
identifier: Identifier?,
79+
at lookUpPosition: AbsolutePosition,
80+
with config: LookupConfig
81+
) -> [LookupResult] {
82+
guard let parentScope else { return [] }
83+
84+
if let parentScope = Syntax(parentScope).asProtocol(SyntaxProtocol.self)
85+
as? WithGenericParametersOrAssociatedTypesScopeSyntax
86+
{
87+
return parentScope.lookupInParent(identifier: identifier, at: lookUpPosition, with: config)
88+
} else {
89+
return lookupInParent(identifier: identifier, at: lookUpPosition, with: config)
90+
}
91+
}
92+
}

Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift renamed to Sources/SwiftLexicalLookup/Scopes/IntroducingToSequentialParentScopeSyntax.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ protocol IntroducingToSequentialParentScopeSyntax: ScopeSyntax {
1919
/// Returns results matching lookup that should be
2020
/// interleaved with results of the sequential parent.
2121
func lookupFromSequentialParent(
22-
for identifier: Identifier?,
23-
at origin: AbsolutePosition,
22+
identifier: Identifier?,
23+
at lookUpPosition: AbsolutePosition,
2424
with config: LookupConfig
2525
) -> [LookupResult]
2626
}

Sources/SwiftLexicalLookup/ScopeImplementations.swift renamed to Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,15 @@ import SwiftSyntax
9797
/// - for `memberBlock` - a, b, c, d, e, f
9898
/// - for `codeBlock` - a
9999
@_spi(Experimental) public func lookup(
100-
for identifier: Identifier?,
101-
at origin: AbsolutePosition,
100+
identifier: Identifier?,
101+
at lookUpPosition: AbsolutePosition,
102102
with config: LookupConfig
103103
) -> [LookupResult] {
104104
switch config.fileScopeHandling {
105105
case .memberBlock:
106106
let names = introducedNames(using: .memberBlock)
107107
.filter { lookupName in
108-
checkName(identifier, refersTo: lookupName, at: origin)
108+
checkName(identifier, refersTo: lookupName, at: lookUpPosition)
109109
}
110110

111111
return names.isEmpty ? [] : [.fromFileScope(self, withNames: names)]
@@ -119,22 +119,19 @@ import SwiftSyntax
119119

120120
if encounteredNonDeclaration {
121121
sequentialItems.append(codeBlockItem)
122+
} else if item.is(DeclSyntax.self) {
123+
let foundNames = LookupName.getNames(from: item)
124+
members.append(contentsOf: foundNames.filter { checkName(identifier, refersTo: $0, at: lookUpPosition) })
122125
} else {
123-
if item.is(DeclSyntax.self) {
124-
let foundNames = LookupName.getNames(from: item)
125-
126-
members.append(contentsOf: foundNames.filter { checkName(identifier, refersTo: $0, at: origin) })
127-
} else {
128-
encounteredNonDeclaration = true
129-
sequentialItems.append(codeBlockItem)
130-
}
126+
encounteredNonDeclaration = true
127+
sequentialItems.append(codeBlockItem)
131128
}
132129
}
133130

134131
let sequentialNames = sequentialLookup(
135132
in: sequentialItems,
136-
for: identifier,
137-
at: origin,
133+
identifier: identifier,
134+
at: lookUpPosition,
138135
with: config,
139136
createResultsForThisScopeWith: { .fromFileScope(self, withNames: $0) }
140137
)
@@ -154,14 +151,14 @@ import SwiftSyntax
154151
}
155152

156153
@_spi(Experimental) public func lookup(
157-
for identifier: Identifier?,
158-
at origin: AbsolutePosition,
154+
identifier: Identifier?,
155+
at lookUpPosition: AbsolutePosition,
159156
with config: LookupConfig
160157
) -> [LookupResult] {
161158
sequentialLookup(
162159
in: statements,
163-
for: identifier,
164-
at: origin,
160+
identifier: identifier,
161+
at: lookUpPosition,
165162
with: config,
166163
createResultsForThisScopeWith: { .fromScope(self, withNames: $0) }
167164
)
@@ -274,14 +271,14 @@ import SwiftSyntax
274271
/// }
275272
/// ```
276273
@_spi(Experimental) public func lookup(
277-
for identifier: Identifier?,
278-
at origin: AbsolutePosition,
274+
identifier: Identifier?,
275+
at lookUpPosition: AbsolutePosition,
279276
with config: LookupConfig
280277
) -> [LookupResult] {
281-
if let elseBody, elseBody.position <= origin, elseBody.endPosition >= origin {
282-
return lookupInParent(for: identifier, at: origin, with: config)
278+
if let elseBody, elseBody.range.contains(lookUpPosition) {
279+
return lookupInParent(identifier: identifier, at: lookUpPosition, with: config)
283280
} else {
284-
return defaultLookupImplementation(for: identifier, at: origin, with: config)
281+
return defaultLookupImplementation(identifier: identifier, at: lookUpPosition, with: config)
285282
}
286283
}
287284
}
@@ -319,25 +316,24 @@ import SwiftSyntax
319316
/// // a is visible here
320317
/// ```
321318
func lookupFromSequentialParent(
322-
for identifier: Identifier?,
323-
at origin: AbsolutePosition,
319+
identifier: Identifier?,
320+
at lookUpPosition: AbsolutePosition,
324321
with config: LookupConfig
325322
) -> [LookupResult] {
326-
guard body.position > origin || body.endPosition < origin
327-
else { return [] }
323+
guard !body.range.contains(lookUpPosition) else { return [] }
328324

329325
let names = namesIntroducedToSequentialParent.filter { introducedName in
330-
checkName(identifier, refersTo: introducedName, at: origin)
326+
checkName(identifier, refersTo: introducedName, at: lookUpPosition)
331327
}
332328

333329
return names.isEmpty ? [] : [.fromScope(self, withNames: names)]
334330
}
335331
}
336332

337-
@_spi(Experimental) extension ActorDeclSyntax: TypeScopeSyntax {}
338-
@_spi(Experimental) extension ClassDeclSyntax: TypeScopeSyntax {}
339-
@_spi(Experimental) extension StructDeclSyntax: TypeScopeSyntax {}
340-
@_spi(Experimental) extension EnumDeclSyntax: TypeScopeSyntax {}
333+
@_spi(Experimental) extension ActorDeclSyntax: TypeScopeSyntax, WithGenericParametersOrAssociatedTypesScopeSyntax {}
334+
@_spi(Experimental) extension ClassDeclSyntax: TypeScopeSyntax, WithGenericParametersOrAssociatedTypesScopeSyntax {}
335+
@_spi(Experimental) extension StructDeclSyntax: TypeScopeSyntax, WithGenericParametersOrAssociatedTypesScopeSyntax {}
336+
@_spi(Experimental) extension EnumDeclSyntax: TypeScopeSyntax, WithGenericParametersOrAssociatedTypesScopeSyntax {}
341337
@_spi(Experimental) extension ExtensionDeclSyntax: TypeScopeSyntax {}
342338

343339
@_spi(Experimental) extension AccessorDeclSyntax: ScopeSyntax {
@@ -360,7 +356,50 @@ import SwiftSyntax
360356

361357
@_spi(Experimental) extension CatchClauseSyntax: ScopeSyntax {
362358
/// Implicit `error` when there are no catch items.
363-
public var introducedNames: [LookupName] {
359+
@_spi(Experimental) public var introducedNames: [LookupName] {
364360
return catchItems.isEmpty ? [.implicit(.error(self))] : []
365361
}
366362
}
363+
364+
@_spi(Experimental) extension SwitchCaseSyntax: ScopeSyntax {
365+
/// Names introduced within `case` items.
366+
@_spi(Experimental) public var introducedNames: [LookupName] {
367+
label.as(SwitchCaseLabelSyntax.self)?.caseItems.flatMap { child in
368+
LookupName.getNames(from: child.pattern)
369+
} ?? []
370+
}
371+
}
372+
373+
@_spi(Experimental) extension GenericParameterClauseSyntax: GenericParameterOrAssociatedTypeScopeSyntax {
374+
/// Generic parameter names introduced by this clause.
375+
@_spi(Experimental) public var introducedNames: [LookupName] {
376+
parameters.children(viewMode: .sourceAccurate).flatMap { child in
377+
LookupName.getNames(from: child, accessibleAfter: child.endPosition)
378+
}
379+
}
380+
}
381+
382+
@_spi(Experimental) extension PrimaryAssociatedTypeClauseSyntax: GenericParameterOrAssociatedTypeScopeSyntax {
383+
/// Primary associated type names introduced by this clause.
384+
@_spi(Experimental) public var introducedNames: [LookupName] {
385+
primaryAssociatedTypes.children(viewMode: .sourceAccurate).flatMap { child in
386+
LookupName.getNames(from: child, accessibleAfter: child.endPosition)
387+
}
388+
}
389+
}
390+
391+
@_spi(Experimental) extension ProtocolDeclSyntax: WithGenericParametersOrAssociatedTypesScopeSyntax {
392+
/// Protocol declarations don't introduce names by themselves.
393+
@_spi(Experimental) public var introducedNames: [LookupName] {
394+
[]
395+
}
396+
}
397+
398+
@_spi(Experimental) extension FunctionDeclSyntax: WithGenericParametersOrAssociatedTypesScopeSyntax {
399+
/// Function parameters introduced by this function's signature.
400+
@_spi(Experimental) public var introducedNames: [LookupName] {
401+
signature.parameterClause.parameters.flatMap { parameter in
402+
LookupName.getNames(from: parameter)
403+
}
404+
}
405+
}

0 commit comments

Comments
 (0)