Skip to content

Commit 940b06e

Browse files
committed
Add proper guard scope and implicit name lookup.
1 parent 3db6f01 commit 940b06e

15 files changed

+821
-169
lines changed

Sources/SwiftLexicalLookup/Configurations/LookupConfig.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import Foundation
1515
@_spi(Experimental) public struct LookupConfig {
1616
/// Specifies behaviour of file scope.
1717
/// `memberBlockUpToLastDecl` by default.
18-
public var fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl
18+
@_spi(Experimental) public var fileScopeHandling: FileScopeHandlingConfig
1919

20-
public init(fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl) {
20+
@_spi(Experimental) public init(
21+
fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl
22+
) {
2123
self.fileScopeHandling = fileScopeHandling
2224
}
2325
}

Sources/SwiftLexicalLookup/IdentifiableSyntax.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,25 @@
1313
import SwiftSyntax
1414

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

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

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

28-
extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
28+
@_spi(Experimental) extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
2929
@_spi(Experimental) public var identifier: TokenSyntax {
3030
name
3131
}
3232
}
3333

34-
extension ClosureCaptureSyntax: IdentifiableSyntax {
34+
@_spi(Experimental) extension ClosureCaptureSyntax: IdentifiableSyntax {
3535
@_spi(Experimental) public var identifier: TokenSyntax {
3636
/* Doesn't work with closures like:
3737
_ = { [y=1+2] in
@@ -41,3 +41,9 @@ extension ClosureCaptureSyntax: IdentifiableSyntax {
4141
expression.as(DeclReferenceExprSyntax.self)!.baseName
4242
}
4343
}
44+
45+
@_spi(Experimental) extension AccessorParametersSyntax: IdentifiableSyntax {
46+
@_spi(Experimental) public var identifier: TokenSyntax {
47+
name
48+
}
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
protocol IntroducingToSequentialParentScopeSyntax: ScopeSyntax {
16+
/// Returns names matching lookup that should be
17+
/// handled by it's parent sequential scope.
18+
func introducesToSequentialParent(
19+
for name: String?,
20+
at syntax: SyntaxProtocol,
21+
with config: LookupConfig,
22+
state: LookupState
23+
) -> [LookupResult]
24+
}

Sources/SwiftLexicalLookup/LookupName.swift

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,60 @@
1212

1313
import SwiftSyntax
1414

15+
@_spi(Experimental) public enum LookupImplicitNameKind {
16+
/// `self` keyword representing object instance.
17+
case `self`(SyntaxProtocol)
18+
/// `Self` keyword representing object type.
19+
case `Self`(DeclSyntaxProtocol)
20+
/// `self` captured by a closure.
21+
case error(CatchClauseSyntax)
22+
/// `newValue` available by default inside `set` and `willSet`.
23+
case newValue(AccessorDeclSyntax)
24+
/// `oldValue` available by default inside `didSet`.
25+
case oldValue(AccessorDeclSyntax)
26+
27+
/// Syntax associated with this name.
28+
@_spi(Experimental) public var syntax: SyntaxProtocol {
29+
switch self {
30+
case .self(let syntax):
31+
syntax
32+
case .Self(let syntax):
33+
syntax
34+
case .error(let syntax):
35+
syntax
36+
case .newValue(let syntax):
37+
syntax
38+
case .oldValue(let syntax):
39+
syntax
40+
}
41+
}
42+
43+
/// Used for name comparison.
44+
var name: String {
45+
switch self {
46+
case .self:
47+
"self"
48+
case .Self:
49+
"Self"
50+
case .error:
51+
"error"
52+
case .newValue:
53+
"newValue"
54+
case .oldValue:
55+
"oldValue"
56+
}
57+
}
58+
}
59+
1560
@_spi(Experimental) public enum LookupName {
1661
/// Identifier associated with the name.
17-
/// Could be an identifier of a variable, function or closure parameter and more
62+
/// Could be an identifier of a variable, function or closure parameter and more.
1863
case identifier(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
1964
/// Declaration associated with the name.
20-
/// Could be class, struct, actor, protocol, function and more
65+
/// Could be class, struct, actor, protocol, function and more.
2166
case declaration(NamedDeclSyntax, accessibleAfter: AbsolutePosition?)
67+
/// Name introduced implicitly certain syntax nodes.
68+
case implicit(LookupImplicitNameKind)
2269

2370
/// Syntax associated with this name.
2471
@_spi(Experimental) public var syntax: SyntaxProtocol {
@@ -27,6 +74,8 @@ import SwiftSyntax
2774
syntax
2875
case .declaration(let syntax, _):
2976
syntax
77+
case .implicit(let implicitName):
78+
implicitName.syntax
3079
}
3180
}
3281

@@ -37,6 +86,8 @@ import SwiftSyntax
3786
Identifier(syntax.identifier)
3887
case .declaration(let syntax, _):
3988
Identifier(syntax.name)
89+
default:
90+
nil
4091
}
4192
}
4293

@@ -46,6 +97,18 @@ import SwiftSyntax
4697
switch self {
4798
case .identifier(_, let absolutePosition), .declaration(_, let absolutePosition):
4899
absolutePosition
100+
default:
101+
nil
102+
}
103+
}
104+
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
49112
}
50113
}
51114

@@ -57,12 +120,15 @@ import SwiftSyntax
57120

58121
/// Checks if this name refers to the looked up phrase.
59122
func refersTo(_ lookedUpName: String) -> Bool {
60-
guard let name = identifier?.name else { return false }
123+
guard let name else { return false }
61124
return name == lookedUpName
62125
}
63126

64-
/// Extracts names introduced by the given `from` structure.
65-
static func getNames(from syntax: SyntaxProtocol, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] {
127+
/// Extracts names introduced by the given `syntax` structure.
128+
static func getNames(
129+
from syntax: SyntaxProtocol,
130+
accessibleAfter: AbsolutePosition? = nil
131+
) -> [LookupName] {
66132
switch Syntax(syntax).as(SyntaxEnum.self) {
67133
case .variableDecl(let variableDecl):
68134
variableDecl.bindings.flatMap { binding in
@@ -90,10 +156,6 @@ import SwiftSyntax
90156
functionCallExpr.arguments.flatMap { argument in
91157
getNames(from: argument.expression, accessibleAfter: accessibleAfter)
92158
}
93-
case .guardStmt(let guardStmt):
94-
guardStmt.conditions.flatMap { cond in
95-
getNames(from: cond.condition, accessibleAfter: cond.endPosition)
96-
}
97159
default:
98160
if let namedDecl = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? NamedDeclSyntax {
99161
handle(namedDecl: namedDecl, accessibleAfter: accessibleAfter)
@@ -106,17 +168,26 @@ import SwiftSyntax
106168
}
107169

108170
/// Extracts name introduced by `IdentifiableSyntax` node.
109-
private static func handle(identifiable: IdentifiableSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName]
110-
{
111-
if identifiable.identifier.text != "_" {
171+
private static func handle(
172+
identifiable: IdentifiableSyntax,
173+
accessibleAfter: AbsolutePosition? = nil
174+
) -> [LookupName] {
175+
if let closureCapture = identifiable as? ClosureCaptureSyntax,
176+
closureCapture.identifier.tokenKind == .keyword(.self)
177+
{
178+
return [.implicit(.self(closureCapture))] // Handle `self` closure capture.
179+
} else if identifiable.identifier.tokenKind != .wildcard {
112180
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
113181
} else {
114182
return []
115183
}
116184
}
117185

118186
/// Extracts name introduced by `NamedDeclSyntax` node.
119-
private static func handle(namedDecl: NamedDeclSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] {
187+
private static func handle(
188+
namedDecl: NamedDeclSyntax,
189+
accessibleAfter: AbsolutePosition? = nil
190+
) -> [LookupName] {
120191
[.declaration(namedDecl, accessibleAfter: accessibleAfter)]
121192
}
122193
}

Sources/SwiftLexicalLookup/LookupResult.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import SwiftSyntax
1414

15-
/// Represents resul
15+
/// Represents result from a specific scope.
1616
@_spi(Experimental) public enum LookupResult {
1717
/// Scope and the names that matched lookup.
1818
case fromScope(ScopeSyntax, withNames: [LookupName])
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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 Foundation
14+
15+
@_spi(Experimental) public struct LookupState {
16+
/// Specifies scopes that introduce names to their parent and
17+
/// should be skipped during lookup in sequential scopes.
18+
var skipSequentialIntroductionFrom: IntroducingToSequentialParentScopeSyntax?
19+
20+
@_spi(Experimental) public init() {}
21+
}

0 commit comments

Comments
 (0)