Skip to content

Commit 2f0b28d

Browse files
authored
Merge pull request swiftlang#2748 from MAJKFL/main
[SwiftLexicalLookup][GSoC] Add proper guard scope and implicit name lookup
2 parents 2e2d036 + fcaecc7 commit 2f0b28d

13 files changed

+984
-150
lines changed

Sources/SwiftLexicalLookup/IdentifiableSyntax.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,9 @@ import SwiftSyntax
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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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 all names introduced to parent.
17+
var namesIntroducedToSequentialParent: [LookupName] { get }
18+
19+
/// Returns results matching lookup that should be
20+
/// interleaved with results of the sequential parent.
21+
func lookupFromSequentialParent(
22+
for identifier: Identifier?,
23+
at origin: AbsolutePosition,
24+
with config: LookupConfig
25+
) -> [LookupResult]
26+
}

Sources/SwiftLexicalLookup/LookupName.swift

Lines changed: 129 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,108 @@
1212

1313
import SwiftSyntax
1414

15+
/// An entity that is implicitly declared based on the syntactic structure of the program.
16+
@_spi(Experimental) public enum ImplicitDecl {
17+
/// `self` keyword representing object instance.
18+
/// Could be associated with type declaration, extension,
19+
/// or closure captures.
20+
case `self`(DeclSyntaxProtocol)
21+
/// `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`
25+
/// block that does not specify a catch pattern.
26+
case error(CatchClauseSyntax)
27+
/// `newValue` available by default inside `set` and `willSet`.
28+
case newValue(AccessorDeclSyntax)
29+
/// `oldValue` available by default inside `didSet`.
30+
case oldValue(AccessorDeclSyntax)
31+
32+
/// Syntax associated with this name.
33+
@_spi(Experimental) public var syntax: SyntaxProtocol {
34+
switch self {
35+
case .self(let syntax):
36+
return syntax
37+
case .Self(let syntax):
38+
return syntax
39+
case .error(let syntax):
40+
return syntax
41+
case .newValue(let syntax):
42+
return syntax
43+
case .oldValue(let syntax):
44+
return syntax
45+
}
46+
}
47+
48+
/// The name of the implicit declaration.
49+
private var name: String {
50+
switch self {
51+
case .self:
52+
return "self"
53+
case .Self:
54+
return "Self"
55+
case .error:
56+
return "error"
57+
case .newValue:
58+
return "newValue"
59+
case .oldValue:
60+
return "oldValue"
61+
}
62+
}
63+
64+
/// Identifier used for name comparison.
65+
///
66+
/// Note that `self` and `Self` are treated as identifiers for name lookup purposes
67+
/// and that a variable named `self` can shadow the `self` keyword. For example.
68+
/// ```swift
69+
/// class Foo {
70+
/// func test() {
71+
/// let `Self` = "abc"
72+
/// print(Self.self)
73+
///
74+
/// let `self` = "def"
75+
/// print(self)
76+
/// }
77+
/// }
78+
///
79+
/// Foo().test()
80+
/// ```
81+
/// prints:
82+
/// ```
83+
/// abc
84+
/// def
85+
/// ```
86+
/// `self` and `Self` identifers override implicit `self` and `Self` introduced by
87+
/// the `Foo` class declaration.
88+
var identifier: Identifier {
89+
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")
100+
}
101+
}
102+
}
103+
15104
@_spi(Experimental) public enum LookupName {
16105
/// Identifier associated with the name.
17106
/// Could be an identifier of a variable, function or closure parameter and more.
18107
case identifier(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
19108
/// Declaration associated with the name.
20109
/// Could be class, struct, actor, protocol, function and more.
21110
case declaration(NamedDeclSyntax)
111+
/// Name introduced implicitly by certain syntax nodes.
112+
case implicit(ImplicitDecl)
113+
/// Explicit `self` keyword.
114+
case `self`(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
115+
/// Explicit `Self` keyword.
116+
case `Self`(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
22117

23118
/// Syntax associated with this name.
24119
@_spi(Experimental) public var syntax: SyntaxProtocol {
@@ -27,40 +122,52 @@ import SwiftSyntax
27122
return syntax
28123
case .declaration(let syntax):
29124
return syntax
125+
case .implicit(let implicitName):
126+
return implicitName.syntax
127+
case .self(let syntax, _), .Self(let syntax, _):
128+
return syntax
30129
}
31130
}
32131

33-
/// Introduced name.
132+
/// Identifier used for name comparison.
34133
@_spi(Experimental) public var identifier: Identifier? {
35134
switch self {
36135
case .identifier(let syntax, _):
37136
return Identifier(syntax.identifier)
38137
case .declaration(let syntax):
39138
return Identifier(syntax.name)
139+
case .implicit(let kind):
140+
return kind.identifier
141+
case .self:
142+
return Identifier("self")
143+
case .Self:
144+
return Identifier("Self")
40145
}
41146
}
42147

43148
/// Point, after which the name is available in scope.
44149
/// If set to `nil`, the name is available at any point in scope.
45150
var accessibleAfter: AbsolutePosition? {
46151
switch self {
47-
case .identifier(_, let absolutePosition):
152+
case .identifier(_, let absolutePosition),
153+
.self(_, let absolutePosition),
154+
.Self(_, let absolutePosition):
48155
return absolutePosition
49156
default:
50157
return nil
51158
}
52159
}
53160

54161
/// Checks if this name was introduced before the syntax used for lookup.
55-
func isAccessible(at lookedUpSyntax: SyntaxProtocol) -> Bool {
162+
func isAccessible(at origin: AbsolutePosition) -> Bool {
56163
guard let accessibleAfter else { return true }
57-
return accessibleAfter <= lookedUpSyntax.position
164+
return accessibleAfter <= origin
58165
}
59166

60167
/// Checks if this name refers to the looked up phrase.
61-
func refersTo(_ lookedUpName: String) -> Bool {
62-
guard let name = identifier?.name else { return false }
63-
return name == lookedUpName
168+
func refersTo(_ lookedUpIdentifier: Identifier) -> Bool {
169+
guard let identifier else { return false }
170+
return identifier == lookedUpIdentifier
64171
}
65172

66173
/// Extracts names introduced by the given `syntax` structure.
@@ -105,10 +212,6 @@ import SwiftSyntax
105212
return functionCallExpr.arguments.flatMap { argument in
106213
getNames(from: argument.expression, accessibleAfter: accessibleAfter)
107214
}
108-
case .guardStmt(let guardStmt):
109-
return guardStmt.conditions.flatMap { cond in
110-
getNames(from: cond.condition, accessibleAfter: cond.endPosition)
111-
}
112215
default:
113216
if let namedDecl = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? NamedDeclSyntax {
114217
return handle(namedDecl: namedDecl, accessibleAfter: accessibleAfter)
@@ -121,12 +224,21 @@ import SwiftSyntax
121224
}
122225

123226
/// Extracts name introduced by `IdentifiableSyntax` node.
124-
private static func handle(identifiable: IdentifiableSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName]
125-
{
126-
if identifiable.identifier.tokenKind != .wildcard {
127-
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
128-
} else {
129-
return []
227+
private static func handle(
228+
identifiable: IdentifiableSyntax,
229+
accessibleAfter: AbsolutePosition? = nil
230+
) -> [LookupName] {
231+
switch identifiable.identifier.tokenKind {
232+
case .keyword(.self):
233+
return [.self(identifiable, accessibleAfter: accessibleAfter)]
234+
case .keyword(.Self):
235+
return [.Self(identifiable, accessibleAfter: accessibleAfter)]
236+
default:
237+
if identifiable.identifier.tokenKind != .wildcard {
238+
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
239+
} else {
240+
return []
241+
}
130242
}
131243
}
132244

0 commit comments

Comments
 (0)