Skip to content

Commit b9a29c5

Browse files
committed
Introduce basic support for resolving nominal types within single module
Implement rudimentary name lookup and extension binding for nominal type names so we can resolve Swift type names within a module.
1 parent 11e285d commit b9a29c5

File tree

2 files changed

+415
-0
lines changed

2 files changed

+415
-0
lines changed
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
//
10+
// SPDX-License-Identifier: Apache-2.0
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
import SwiftSyntax
15+
16+
/// Perform nominal type resolution, including the binding of extensions to
17+
/// their extended nominal types and mapping type names to their full names.
18+
@_spi(Testing)
19+
public class NominalTypeResolution {
20+
/// A syntax node for a nominal type declaration.
21+
@_spi(Testing)
22+
public typealias NominalTypeDeclSyntaxNode = any DeclGroupSyntax & NamedDeclSyntax
23+
24+
/// Mapping from the syntax identifier for a given type declaration node,
25+
/// such as StructDeclSyntax, to the set of extensions of this particular
26+
/// type.
27+
private var extensionsByType: [SyntaxIdentifier: [ExtensionDeclSyntax]] = [:]
28+
29+
/// Mapping from extension declarations to the type declaration that they
30+
/// extend.
31+
private var resolvedExtensions: [ExtensionDeclSyntax: NominalTypeDeclSyntaxNode] = [:]
32+
33+
/// Extensions that have been encountered but not yet resolved to
34+
private var unresolvedExtensions: [ExtensionDeclSyntax] = []
35+
36+
/// Mapping from qualified nominal type names to their syntax nodes.
37+
private var topLevelNominalTypes: [String: NominalTypeDeclSyntaxNode] = [:]
38+
39+
@_spi(Testing) public init() { }
40+
}
41+
42+
// MARK: Nominal type name resolution.
43+
extension NominalTypeResolution {
44+
/// Compute the fully-qualified name of the given nominal type node.
45+
///
46+
/// This produces the name that can be resolved back to the nominal type
47+
/// via resolveNominalType(_:).
48+
@_spi(Testing)
49+
public func fullyQualifiedName(of node: NominalTypeDeclSyntaxNode) -> String? {
50+
let nameComponents = fullyQualifiedNameComponents(of: node)
51+
return nameComponents.isEmpty ? nil : nameComponents.joined(separator: ".")
52+
}
53+
54+
private func fullyQualifiedNameComponents(of node: NominalTypeDeclSyntaxNode) -> [String] {
55+
var nameComponents: [String] = []
56+
57+
var currentNode = Syntax(node)
58+
while true {
59+
// If it's a nominal type, add its name.
60+
if let nominal = currentNode.asProtocol(SyntaxProtocol.self) as? NominalTypeDeclSyntaxNode,
61+
let nominalName = nominal.name.identifier?.name {
62+
nameComponents.append(nominalName)
63+
}
64+
65+
// If it's an extension, add the full name of the extended type.
66+
if let extensionDecl = currentNode.as(ExtensionDeclSyntax.self),
67+
let extendedNominal = extendedType(of: extensionDecl) {
68+
let extendedNominalNameComponents = fullyQualifiedNameComponents(of: extendedNominal)
69+
return extendedNominalNameComponents + nameComponents.reversed()
70+
}
71+
72+
guard let parent = currentNode.parent else {
73+
break
74+
75+
}
76+
currentNode = parent
77+
}
78+
79+
return nameComponents.reversed()
80+
}
81+
82+
/// Resolve a nominal type name to its syntax node, or nil if it cannot be
83+
/// resolved for any reason.
84+
@_spi(Testing)
85+
public func resolveNominalType(_ name: String) -> NominalTypeDeclSyntaxNode? {
86+
let components = name.split(separator: ".")
87+
return resolveNominalType(components)
88+
}
89+
90+
/// Resolve a nominal type name to its syntax node, or nil if it cannot be
91+
/// resolved for any reason.
92+
private func resolveNominalType(_ nameComponents: some Sequence<some StringProtocol>) -> NominalTypeDeclSyntaxNode? {
93+
// Resolve the name components in order.
94+
var currentNode: NominalTypeDeclSyntaxNode? = nil
95+
for nameComponentStr in nameComponents {
96+
let nameComponent = String(nameComponentStr)
97+
98+
var nextNode: NominalTypeDeclSyntaxNode? = nil
99+
if let currentNode {
100+
nextNode = lookupNominalType(nameComponent, in: currentNode)
101+
} else {
102+
nextNode = topLevelNominalTypes[nameComponent]
103+
}
104+
105+
// If we couldn't resolve the next name, we're done.
106+
guard let nextNode else {
107+
return nil
108+
}
109+
110+
currentNode = nextNode
111+
}
112+
113+
return currentNode
114+
}
115+
116+
/// Look for a nominal type with the given name within this declaration group,
117+
/// which could be a nominal type declaration or extension thereof.
118+
private func lookupNominalType(
119+
_ name: String,
120+
inDeclGroup parentNode: some DeclGroupSyntax
121+
) -> NominalTypeDeclSyntaxNode? {
122+
for member in parentNode.memberBlock.members {
123+
let memberDecl = member.decl.asProtocol(DeclSyntaxProtocol.self)
124+
125+
// If we have a member with the given name that is a nominal type
126+
// declaration, we found what we're looking for.
127+
if let namedMemberDecl = memberDecl.asProtocol(NamedDeclSyntax.self),
128+
namedMemberDecl.name.identifier?.name == name,
129+
let nominalTypeDecl = memberDecl as? NominalTypeDeclSyntaxNode
130+
{
131+
return nominalTypeDecl
132+
}
133+
}
134+
135+
return nil
136+
}
137+
138+
/// Lookup nominal type name within a given nominal type.
139+
private func lookupNominalType(
140+
_ name: String,
141+
in parentNode: NominalTypeDeclSyntaxNode
142+
) -> NominalTypeDeclSyntaxNode? {
143+
// Look in the parent node itself.
144+
if let found = lookupNominalType(name, inDeclGroup: parentNode) {
145+
return found
146+
}
147+
148+
// Look in known extensions of the parent node.
149+
if let extensions = extensionsByType[parentNode.id] {
150+
for extensionDecl in extensions {
151+
if let found = lookupNominalType(name, inDeclGroup: extensionDecl) {
152+
return found
153+
}
154+
}
155+
}
156+
157+
return nil
158+
}
159+
}
160+
161+
// MARK: Binding extensions
162+
extension NominalTypeResolution {
163+
/// Look up the nominal type declaration to which this extension is bound.
164+
@_spi(Testing)
165+
public func extendedType(of extensionDecl: ExtensionDeclSyntax) -> NominalTypeDeclSyntaxNode? {
166+
return resolvedExtensions[extensionDecl]
167+
}
168+
169+
/// Bind all of the unresolved extensions to their nominal types.
170+
///
171+
/// Returns the list of extensions that could not be resolved.
172+
@_spi(Testing)
173+
public func bindExtensions() -> [ExtensionDeclSyntax] {
174+
while !unresolvedExtensions.isEmpty {
175+
// Try to resolve all of the unresolved extensions.
176+
let numExtensionsBefore = unresolvedExtensions.count
177+
unresolvedExtensions.removeAll { extensionDecl in
178+
// Try to resolve the type referenced by this extension declaration. If
179+
// it fails, we'll try again later.
180+
let nestedTypeNameComponents = extensionDecl.nestedTypeName
181+
guard let resolvedType = resolveNominalType(nestedTypeNameComponents) else {
182+
return false
183+
}
184+
185+
// We have successfully resolved the extended type. Record it and
186+
// remove the extension from the list of unresolved extensions.
187+
extensionsByType[resolvedType.id, default: []].append(extensionDecl)
188+
resolvedExtensions[extensionDecl] = resolvedType
189+
190+
return true
191+
}
192+
193+
// If we didn't resolve anything, we're done.
194+
if numExtensionsBefore == unresolvedExtensions.count {
195+
break
196+
}
197+
198+
assert(numExtensionsBefore > unresolvedExtensions.count)
199+
}
200+
201+
// Any unresolved extensions at this point are fundamentally unresolvable.
202+
return unresolvedExtensions
203+
}
204+
}
205+
206+
extension ExtensionDeclSyntax {
207+
/// Produce the nested type name for the given
208+
fileprivate var nestedTypeName: [String] {
209+
var nameComponents: [String] = []
210+
var extendedType = extendedType
211+
while true {
212+
switch extendedType.as(TypeSyntaxEnum.self) {
213+
case .attributedType(let attributedType):
214+
extendedType = attributedType.baseType
215+
continue
216+
217+
case .identifierType(let identifierType):
218+
guard let identifier = identifierType.name.identifier else {
219+
return []
220+
}
221+
222+
nameComponents.append(identifier.name)
223+
return nameComponents.reversed()
224+
225+
case .memberType(let memberType):
226+
guard let identifier = memberType.name.identifier else {
227+
return []
228+
}
229+
230+
nameComponents.append(identifier.name)
231+
extendedType = memberType.baseType
232+
continue
233+
234+
// Structural types implemented as nominal types.
235+
case .arrayType:
236+
return ["Array"]
237+
238+
case .dictionaryType:
239+
return ["Dictionary"]
240+
241+
case .implicitlyUnwrappedOptionalType, .optionalType:
242+
return [ "Optional" ]
243+
244+
// Types that never involve nominals.
245+
246+
case .classRestrictionType, .compositionType, .functionType, .metatypeType,
247+
.missingType, .namedOpaqueReturnType, .packElementType,
248+
.packExpansionType, .someOrAnyType, .suppressedType, .tupleType:
249+
return []
250+
}
251+
}
252+
}
253+
}
254+
255+
// MARK: Adding source files to the resolution.
256+
extension NominalTypeResolution {
257+
/// Add the given source file.
258+
@_spi(Testing)
259+
public func addSourceFile(_ sourceFile: SourceFileSyntax) {
260+
let visitor = NominalAndExtensionFinder(typeResolution: self)
261+
visitor.walk(sourceFile)
262+
}
263+
264+
private class NominalAndExtensionFinder: SyntaxVisitor {
265+
var typeResolution: NominalTypeResolution
266+
var nestingDepth = 0
267+
268+
init(typeResolution: NominalTypeResolution) {
269+
self.typeResolution = typeResolution
270+
super.init(viewMode: .sourceAccurate)
271+
}
272+
273+
// Entering nominal type declarations.
274+
275+
func visitNominal(_ node: NominalTypeDeclSyntaxNode) {
276+
if nestingDepth == 0 {
277+
typeResolution.topLevelNominalTypes[node.name.text] = node
278+
}
279+
280+
nestingDepth += 1
281+
}
282+
283+
override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
284+
visitNominal(node)
285+
return .visitChildren
286+
}
287+
288+
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
289+
visitNominal(node)
290+
return .visitChildren
291+
}
292+
293+
override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
294+
visitNominal(node)
295+
return .visitChildren
296+
}
297+
298+
override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
299+
visitNominal(node)
300+
return .visitChildren
301+
}
302+
303+
override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
304+
visitNominal(node)
305+
return .visitChildren
306+
}
307+
308+
// Exiting nominal type declarations.
309+
func visitPostNominal(_ node: NominalTypeDeclSyntaxNode) {
310+
assert(nestingDepth > 0)
311+
nestingDepth -= 1
312+
}
313+
314+
override func visitPost(_ node: ActorDeclSyntax) {
315+
visitPostNominal(node)
316+
}
317+
318+
override func visitPost(_ node: ClassDeclSyntax) {
319+
visitPostNominal(node)
320+
}
321+
322+
override func visitPost(_ node: EnumDeclSyntax) {
323+
visitPostNominal(node)
324+
}
325+
326+
override func visitPost(_ node: ProtocolDeclSyntax) {
327+
visitPostNominal(node)
328+
}
329+
330+
override func visitPost(_ node: StructDeclSyntax) {
331+
visitPostNominal(node)
332+
}
333+
334+
// Extension handling
335+
override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
336+
// Note that the extension is unresolved. We'll bind it later.
337+
typeResolution.unresolvedExtensions.append(node)
338+
nestingDepth += 1
339+
return .visitChildren
340+
}
341+
342+
override func visitPost(_ node: ExtensionDeclSyntax) {
343+
nestingDepth -= 1
344+
}
345+
346+
// Avoid stepping into functions.
347+
348+
override func visit(_ node: CodeBlockSyntax) -> SyntaxVisitorContinueKind {
349+
return .skipChildren
350+
}
351+
}
352+
}

0 commit comments

Comments
 (0)