Skip to content

Commit 3db6f01

Browse files
authored
Merge pull request #2719 from MAJKFL/main
[SwiftLexicalLookup][GSoC] Add initial name lookup functionality
2 parents 44a94e5 + caae677 commit 3db6f01

11 files changed

+1351
-26
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
/// Specifies how names should be introduced at the file scope.
16+
@_spi(Experimental) public enum FileScopeHandlingConfig {
17+
/// Default behavior. Names introduced sequentially like in member block
18+
/// scope up to the first non-declaration after and including which,
19+
/// the declarations are treated like in code block scope.
20+
case memberBlockUpToLastDecl
21+
/// File scope behaves like member block scope.
22+
case memberBlock
23+
/// File scope behaves like code block scope.
24+
case codeBlock
25+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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 LookupConfig {
16+
/// Specifies behaviour of file scope.
17+
/// `memberBlockUpToLastDecl` by default.
18+
public var fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl
19+
20+
public init(fileScopeHandling: FileScopeHandlingConfig = .memberBlockUpToLastDecl) {
21+
self.fileScopeHandling = fileScopeHandling
22+
}
23+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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: TokenSyntax {
24+
secondName ?? firstName
25+
}
26+
}
27+
28+
extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
29+
@_spi(Experimental) public var identifier: TokenSyntax {
30+
name
31+
}
32+
}
33+
34+
extension ClosureCaptureSyntax: IdentifiableSyntax {
35+
@_spi(Experimental) public var identifier: TokenSyntax {
36+
/* Doesn't work with closures like:
37+
_ = { [y=1+2] in
38+
print(y)
39+
}
40+
*/
41+
expression.as(DeclReferenceExprSyntax.self)!.baseName
42+
}
43+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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+
@_spi(Experimental) public enum LookupName {
16+
/// Identifier associated with the name.
17+
/// Could be an identifier of a variable, function or closure parameter and more
18+
case identifier(IdentifiableSyntax, accessibleAfter: AbsolutePosition?)
19+
/// Declaration associated with the name.
20+
/// Could be class, struct, actor, protocol, function and more
21+
case declaration(NamedDeclSyntax, accessibleAfter: AbsolutePosition?)
22+
23+
/// Syntax associated with this name.
24+
@_spi(Experimental) public var syntax: SyntaxProtocol {
25+
switch self {
26+
case .identifier(let syntax, _):
27+
syntax
28+
case .declaration(let syntax, _):
29+
syntax
30+
}
31+
}
32+
33+
/// Introduced name.
34+
@_spi(Experimental) public var identifier: Identifier? {
35+
switch self {
36+
case .identifier(let syntax, _):
37+
Identifier(syntax.identifier)
38+
case .declaration(let syntax, _):
39+
Identifier(syntax.name)
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), .declaration(_, let absolutePosition):
48+
absolutePosition
49+
}
50+
}
51+
52+
/// Checks if this name was introduced before the syntax used for lookup.
53+
func isAccessible(at lookedUpSyntax: SyntaxProtocol) -> Bool {
54+
guard let accessibleAfter else { return true }
55+
return accessibleAfter <= lookedUpSyntax.position
56+
}
57+
58+
/// Checks if this name refers to the looked up phrase.
59+
func refersTo(_ lookedUpName: String) -> Bool {
60+
guard let name = identifier?.name else { return false }
61+
return name == lookedUpName
62+
}
63+
64+
/// Extracts names introduced by the given `from` structure.
65+
static func getNames(from syntax: SyntaxProtocol, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] {
66+
switch Syntax(syntax).as(SyntaxEnum.self) {
67+
case .variableDecl(let variableDecl):
68+
variableDecl.bindings.flatMap { binding in
69+
getNames(from: binding.pattern, accessibleAfter: accessibleAfter)
70+
}
71+
case .tuplePattern(let tuplePattern):
72+
tuplePattern.elements.flatMap { tupleElement in
73+
getNames(from: tupleElement.pattern, accessibleAfter: accessibleAfter)
74+
}
75+
case .valueBindingPattern(let valueBindingPattern):
76+
getNames(from: valueBindingPattern.pattern, accessibleAfter: accessibleAfter)
77+
case .expressionPattern(let expressionPattern):
78+
getNames(from: expressionPattern.expression, accessibleAfter: accessibleAfter)
79+
case .sequenceExpr(let sequenceExpr):
80+
sequenceExpr.elements.flatMap { expression in
81+
getNames(from: expression, accessibleAfter: accessibleAfter)
82+
}
83+
case .patternExpr(let patternExpr):
84+
getNames(from: patternExpr.pattern, accessibleAfter: accessibleAfter)
85+
case .optionalBindingCondition(let optionalBinding):
86+
getNames(from: optionalBinding.pattern, accessibleAfter: accessibleAfter)
87+
case .matchingPatternCondition(let matchingPatternCondition):
88+
getNames(from: matchingPatternCondition.pattern, accessibleAfter: accessibleAfter)
89+
case .functionCallExpr(let functionCallExpr):
90+
functionCallExpr.arguments.flatMap { argument in
91+
getNames(from: argument.expression, accessibleAfter: accessibleAfter)
92+
}
93+
case .guardStmt(let guardStmt):
94+
guardStmt.conditions.flatMap { cond in
95+
getNames(from: cond.condition, accessibleAfter: cond.endPosition)
96+
}
97+
default:
98+
if let namedDecl = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? NamedDeclSyntax {
99+
handle(namedDecl: namedDecl, accessibleAfter: accessibleAfter)
100+
} else if let identifiable = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? IdentifiableSyntax {
101+
handle(identifiable: identifiable, accessibleAfter: accessibleAfter)
102+
} else {
103+
[]
104+
}
105+
}
106+
}
107+
108+
/// Extracts name introduced by `IdentifiableSyntax` node.
109+
private static func handle(identifiable: IdentifiableSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName]
110+
{
111+
if identifiable.identifier.text != "_" {
112+
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
113+
} else {
114+
return []
115+
}
116+
}
117+
118+
/// Extracts name introduced by `NamedDeclSyntax` node.
119+
private static func handle(namedDecl: NamedDeclSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] {
120+
[.declaration(namedDecl, accessibleAfter: accessibleAfter)]
121+
}
122+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
/// Represents resul
16+
@_spi(Experimental) public enum LookupResult {
17+
/// Scope and the names that matched lookup.
18+
case fromScope(ScopeSyntax, withNames: [LookupName])
19+
/// File scope and names that matched lookup.
20+
case fromFileScope(SourceFileSyntax, withNames: [LookupName])
21+
22+
/// Associated scope.
23+
@_spi(Experimental) public var scope: ScopeSyntax? {
24+
switch self {
25+
case .fromScope(let scopeSyntax, _):
26+
scopeSyntax
27+
case .fromFileScope(let fileScopeSyntax, _):
28+
fileScopeSyntax
29+
}
30+
}
31+
32+
/// Names that matched lookup.
33+
@_spi(Experimental) public var names: [LookupName] {
34+
switch self {
35+
case .fromScope(_, let names), .fromFileScope(_, let names):
36+
names
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)