Skip to content

Commit 3450029

Browse files
committed
Move name accessibility evaluation to LookupName. Simplify name extraction in LookupName.
1 parent b9c4f51 commit 3450029

File tree

5 files changed

+117
-127
lines changed

5 files changed

+117
-127
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
/// Syntax node that can be refered to with an identifier.
16+
public protocol IdentifiableSyntax: SyntaxProtocol {
17+
var identifier: TokenSyntax { get }
18+
}
19+
20+
extension IdentifierPatternSyntax: IdentifiableSyntax {}
21+
22+
extension ClosureParameterSyntax: IdentifiableSyntax {
23+
@_spi(Experimental) public var identifier: SwiftSyntax.TokenSyntax {
24+
secondName ?? firstName
25+
}
26+
}
27+
28+
extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
29+
@_spi(Experimental) public var identifier: SwiftSyntax.TokenSyntax {
30+
name
31+
}
32+
}

Sources/SwiftLexicalLookup/LookupName.swift

Lines changed: 45 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,46 @@ import SwiftSyntax
1515
@_spi(Experimental) public enum LookupName {
1616
/// Identifier associated with the name.
1717
/// Could be an identifier of a variable, function or closure parameter and more
18-
case identifier(String, SyntaxProtocol)
18+
case identifier(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
1919
/// Declaration associated with the name.
2020
/// Could be class, struct, actor, protocol, function and more
21-
case declaration(String, DeclSyntaxProtocol)
21+
case declaration(NamedDeclSyntax, accessibleAfter: AbsolutePosition?)
2222

2323
/// Syntax associated with this name.
2424
@_spi(Experimental) public var syntax: SyntaxProtocol {
2525
switch self {
26-
case .identifier(_, let syntax):
26+
case .identifier(let syntax, _):
2727
syntax
28-
case .declaration(_, let syntax):
28+
case .declaration(let syntax, _):
2929
syntax
3030
}
3131
}
3232

3333
/// Introduced name.
3434
@_spi(Experimental) public var name: String {
3535
switch self {
36-
case .identifier(let name, _):
37-
name
38-
case .declaration(let name, _):
39-
name
36+
case .identifier(let syntax, _):
37+
syntax.identifier.text
38+
case .declaration(let syntax, _):
39+
syntax.name.text
40+
}
41+
}
42+
43+
/// Point, after which the name is available in scope.
44+
/// If set to `nil`, the name is available at any point in scope.
45+
var accessibleAfter: AbsolutePosition? {
46+
switch self {
47+
case .identifier(_, let absolutePosition):
48+
absolutePosition
49+
case .declaration(_, let absolutePosition):
50+
absolutePosition
4051
}
4152
}
4253

4354
/// Checks if this name was introduced before the syntax used for lookup.
44-
func isBefore(_ lookedUpSyntax: SyntaxProtocol) -> Bool {
45-
syntax.position < lookedUpSyntax.position
55+
func isAccessible(at lookedUpSyntax: SyntaxProtocol) -> Bool {
56+
guard let accessibleAfter else { return true }
57+
return accessibleAfter <= lookedUpSyntax.position
4658
}
4759

4860
/// Checks if this name refers to the looked up phrase.
@@ -51,91 +63,50 @@ import SwiftSyntax
5163
}
5264

5365
/// Extracts names introduced by the given `from` structure.
54-
static func getNames(from syntax: SyntaxProtocol) -> [LookupName] {
66+
static func getNames(from syntax: SyntaxProtocol, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] {
5567
switch Syntax(syntax).as(SyntaxEnum.self) {
5668
case .variableDecl(let variableDecl):
5769
variableDecl.bindings.flatMap { binding in
58-
getNames(from: binding.pattern)
70+
getNames(from: binding.pattern, accessibleAfter: accessibleAfter)
5971
}
6072
case .tuplePattern(let tuplePattern):
6173
tuplePattern.elements.flatMap { tupleElement in
62-
getNames(from: tupleElement.pattern)
74+
getNames(from: tupleElement.pattern, accessibleAfter: accessibleAfter)
6375
}
6476
case .valueBindingPattern(let valueBindingPattern):
65-
getNames(from: valueBindingPattern.pattern)
77+
getNames(from: valueBindingPattern.pattern, accessibleAfter: accessibleAfter)
6678
case .expressionPattern(let expressionPattern):
67-
getNames(from: expressionPattern.expression)
79+
getNames(from: expressionPattern.expression, accessibleAfter: accessibleAfter)
6880
case .sequenceExpr(let sequenceExpr):
6981
sequenceExpr.elements.flatMap { expression in
70-
getNames(from: expression)
82+
getNames(from: expression, accessibleAfter: accessibleAfter)
7183
}
7284
case .patternExpr(let patternExpr):
73-
getNames(from: patternExpr.pattern)
85+
getNames(from: patternExpr.pattern, accessibleAfter: accessibleAfter)
7486
case .optionalBindingCondition(let optionalBinding):
75-
getNames(from: optionalBinding.pattern)
76-
case .identifierPattern(let identifierPattern):
77-
handle(identifierPattern: identifierPattern)
78-
case .closureShorthandParameter(let closureShorthandParameter):
79-
handle(closureShorthandParameter: closureShorthandParameter)
80-
case .closureParameter(let closureParameter):
81-
handle(closureParameter: closureParameter)
82-
case .functionDecl(let functionDecl):
83-
handle(functionDecl: functionDecl)
84-
case .classDecl(let classDecl):
85-
handle(classDecl: classDecl)
86-
case .structDecl(let structDecl):
87-
handle(structDecl: structDecl)
88-
case .actorDecl(let actorDecl):
89-
handle(actorDecl: actorDecl)
90-
case .protocolDecl(let protocolDecl):
91-
handle(protocolDecl: protocolDecl)
87+
getNames(from: optionalBinding.pattern, accessibleAfter: accessibleAfter)
9288
default:
93-
[]
89+
if let namedDecl = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? NamedDeclSyntax {
90+
handle(namedDecl: namedDecl, accessibleAfter: accessibleAfter)
91+
} else if let identifiable = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? IdentifiableSyntax {
92+
handle(identifiable: identifiable, accessibleAfter: accessibleAfter)
93+
} else {
94+
[]
95+
}
9496
}
9597
}
9698

97-
/// Extracts name introduced by `identifierPattern`.
98-
private static func handle(identifierPattern: IdentifierPatternSyntax) -> [LookupName] {
99-
[.identifier(identifierPattern.identifier.text, identifierPattern)]
100-
}
101-
102-
/// Extracts name introduced by `closureParameter`.
103-
private static func handle(closureParameter: ClosureParameterSyntax) -> [LookupName] {
104-
[.identifier(closureParameter.secondName?.text ?? closureParameter.firstName.text, closureParameter)]
105-
}
106-
107-
/// Extracts name introduced by `closureShorthandParameter`.
108-
private static func handle(closureShorthandParameter: ClosureShorthandParameterSyntax) -> [LookupName] {
109-
let name = closureShorthandParameter.name.text
110-
if name != "_" {
111-
return [.identifier(name, closureShorthandParameter)]
99+
/// Extracts name introduced by `IdentifiableSyntax` node.
100+
private static func handle(identifiable: IdentifiableSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] {
101+
if identifiable.identifier.text != "_" {
102+
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
112103
} else {
113104
return []
114105
}
115106
}
116-
117-
/// Extracts name introduced by `functionDecl`.
118-
private static func handle(functionDecl: FunctionDeclSyntax) -> [LookupName] {
119-
[.declaration(functionDecl.name.text, functionDecl)]
120-
}
121-
122-
/// Extracts name introduced by `classDecl`.
123-
private static func handle(classDecl: ClassDeclSyntax) -> [LookupName] {
124-
[.declaration(classDecl.name.text, classDecl)]
125-
}
126-
127-
/// Extracts name introduced by `structDecl`.
128-
private static func handle(structDecl: StructDeclSyntax) -> [LookupName] {
129-
[.declaration(structDecl.name.text, structDecl)]
130-
}
131-
132-
/// Extracts name introduced by `actorDecl`.
133-
private static func handle(actorDecl: ActorDeclSyntax) -> [LookupName] {
134-
[.declaration(actorDecl.name.text, actorDecl)]
135-
}
136-
137-
/// Extracts name introduced by `protocolDecl`.
138-
private static func handle(protocolDecl: ProtocolDeclSyntax) -> [LookupName] {
139-
[.declaration(protocolDecl.name.text, protocolDecl)]
107+
108+
/// Extracts name introduced by `NamedDeclSyntax` node.
109+
private static func handle(namedDecl: NamedDeclSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] {
110+
[.declaration(namedDecl, accessibleAfter: accessibleAfter)]
140111
}
141112
}

Sources/SwiftLexicalLookup/ScopeImplementations.swift

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,61 +15,32 @@ import SwiftSyntax
1515
extension SyntaxProtocol {
1616
/// Parent scope of this syntax node, or scope introduced by this syntax node.
1717
var scope: ScopeSyntax? {
18-
switch Syntax(self).as(SyntaxEnum.self) {
19-
case .sourceFile(let sourceFile):
20-
sourceFile
21-
case .codeBlock(let codeBlock):
22-
codeBlock
23-
case .forStmt(let forStmt):
24-
forStmt
25-
case .closureExpr(let closureExpr):
26-
closureExpr
27-
case .whileStmt(let whileStmt):
28-
whileStmt
29-
case .ifExpr(let ifExpr):
30-
ifExpr
31-
case .memberBlock(let memberBlock):
32-
memberBlock
33-
default:
18+
if let scopeSyntax = Syntax(self).asProtocol(SyntaxProtocol.self) as? ScopeSyntax {
19+
scopeSyntax
20+
} else {
3421
self.parent?.scope
3522
}
3623
}
3724
}
3825

3926
extension SourceFileSyntax: ScopeSyntax {
40-
var parentScope: ScopeSyntax? {
41-
nil
42-
}
43-
4427
var introducedNames: [LookupName] {
4528
[]
4629
}
47-
48-
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
49-
[]
50-
}
5130
}
5231

5332
extension CodeBlockSyntax: ScopeSyntax {
5433
var introducedNames: [LookupName] {
5534
statements.flatMap { codeBlockItem in
56-
LookupName.getNames(from: codeBlockItem.item)
35+
LookupName.getNames(from: codeBlockItem.item, accessibleAfter: codeBlockItem.item.endPosition)
5736
}
5837
}
59-
60-
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
61-
defaultLookupImplementation(for: name, at: syntax, positionSensitive: true)
62-
}
6338
}
6439

6540
extension ForStmtSyntax: ScopeSyntax {
6641
var introducedNames: [LookupName] {
6742
LookupName.getNames(from: pattern)
6843
}
69-
70-
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
71-
defaultLookupImplementation(for: name, at: syntax)
72-
}
7344
}
7445

7546
extension ClosureExprSyntax: ScopeSyntax {
@@ -84,10 +55,6 @@ extension ClosureExprSyntax: ScopeSyntax {
8455
}
8556
} ?? []
8657
}
87-
88-
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
89-
defaultLookupImplementation(for: name, at: syntax)
90-
}
9158
}
9259

9360
extension WhileStmtSyntax: ScopeSyntax {
@@ -96,10 +63,6 @@ extension WhileStmtSyntax: ScopeSyntax {
9663
LookupName.getNames(from: element.condition)
9764
}
9865
}
99-
100-
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
101-
defaultLookupImplementation(for: name, at: syntax)
102-
}
10366
}
10467

10568
extension IfExprSyntax: ScopeSyntax {
@@ -124,15 +87,15 @@ extension IfExprSyntax: ScopeSyntax {
12487

12588
var introducedNames: [LookupName] {
12689
conditions.flatMap { element in
127-
LookupName.getNames(from: element.condition)
90+
LookupName.getNames(from: element.condition, accessibleAfter: element.condition.endPosition)
12891
}
12992
}
13093

13194
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
13295
if let elseBody, elseBody.position <= syntax.position, elseBody.endPosition >= syntax.position {
13396
parentScope?.lookup(for: name, at: syntax) ?? []
13497
} else {
135-
defaultLookupImplementation(for: name, at: syntax, positionSensitive: true)
98+
defaultLookupImplementation(for: name, at: syntax)
13699
}
137100
}
138101
}
@@ -143,8 +106,4 @@ extension MemberBlockSyntax: ScopeSyntax {
143106
LookupName.getNames(from: member.decl)
144107
}
145108
}
146-
147-
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
148-
defaultLookupImplementation(for: name, at: syntax)
149-
}
150109
}

Sources/SwiftLexicalLookup/ScopeSyntax.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,22 @@ extension ScopeSyntax {
5959
var parentScope: ScopeSyntax? {
6060
self.parent?.scope
6161
}
62+
63+
/// Returns all names introduced in this scope that `name` refers to and
64+
/// is accessible at given syntax node then passes lookup to the parent.
65+
func lookup(for name: String, at syntax: SyntaxProtocol) -> [LookupName] {
66+
defaultLookupImplementation(for: name, at: syntax)
67+
}
6268

63-
/// Returns all names introduced in this scope that `name` refers to and then
64-
/// passes lookup to the parent. Optionally, if `positionSensitive` is set to `true`,
65-
/// the method filters names that were introduced in this scope after `syntax`.
69+
/// Returns all names introduced in this scope that `name` refers to and
70+
/// is accessible at given syntax node then passes lookup to the parent.
6671
func defaultLookupImplementation(
6772
for name: String,
68-
at syntax: SyntaxProtocol,
69-
positionSensitive: Bool = false
73+
at syntax: SyntaxProtocol
7074
) -> [LookupName] {
7175
introducedNames
7276
.filter { introducedName in
73-
(!positionSensitive || introducedName.isBefore(syntax)) && introducedName.refersTo(name)
77+
introducedName.isAccessible(at: syntax) && introducedName.refersTo(name)
7478
} + (parentScope?.lookup(for: name, at: syntax) ?? [])
7579
}
7680
}

Tests/SwiftLexicalLookupTest/NameLookupTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,28 @@ final class testNameLookup: XCTestCase {
217217
])
218218
)
219219
}
220+
221+
func testLookupInDeclaration() {
222+
assertLexicalNameLookup(
223+
source: """
224+
class foo {
225+
let 1️⃣a = 2️⃣a
226+
227+
func foo() {
228+
let 3️⃣a = 4️⃣a
229+
230+
if let 5️⃣a = 6️⃣a {
231+
let (a, b) = 8️⃣a
232+
}
233+
}
234+
235+
let 9️⃣a = 0️⃣a
236+
}
237+
""",
238+
references: ["2️⃣": ["1️⃣", "9️⃣"], "0️⃣": ["1️⃣", "9️⃣"], "4️⃣": ["1️⃣", "9️⃣"], "6️⃣": ["3️⃣", "1️⃣", "9️⃣"], "8️⃣": ["5️⃣", "3️⃣", "1️⃣", "9️⃣"]],
239+
expectedResultTypes: .all(
240+
IdentifierPatternSyntax.self
241+
)
242+
)
243+
}
220244
}

0 commit comments

Comments
 (0)