File: TypeDeclResolver.swift

package info (click to toggle)
swiftlang 6.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 2,791,532 kB
  • sloc: cpp: 9,901,743; ansic: 2,201,431; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (118 lines) | stat: -rw-r--r-- 4,109 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax

/// Resolves type declarations from Swift syntax nodes
class TypeDeclResolver {
  typealias TypeDecl = NamedDeclSyntax & DeclGroupSyntax & DeclSyntaxProtocol
  /// A representation of a qualified name of a type declaration
  ///
  /// `Outer.Inner` type declaration is represented as ["Outer", "Inner"]
  typealias QualifiedName = [String]
  private var typeDeclByQualifiedName: [QualifiedName: TypeDecl] = [:]

  enum Error: Swift.Error {
    case typeNotFound(QualifiedName)
  }

  private class TypeDeclCollector: SyntaxVisitor {
    let resolver: TypeDeclResolver
    var scope: [TypeDecl] = []
    var rootTypeDecls: [TypeDecl] = []

    init(resolver: TypeDeclResolver) {
      self.resolver = resolver
      super.init(viewMode: .all)
    }

    func visitNominalDecl(_ node: TypeDecl) -> SyntaxVisitorContinueKind {
      let name = node.name.text
      let qualifiedName = scope.map(\.name.text) + [name]
      resolver.typeDeclByQualifiedName[qualifiedName] = node
      scope.append(node)
      return .visitChildren
    }

    func visitPostNominalDecl() {
      let type = scope.removeLast()
      if scope.isEmpty {
        rootTypeDecls.append(type)
      }
    }

    override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
      return visitNominalDecl(node)
    }
    override func visitPost(_ node: StructDeclSyntax) {
      visitPostNominalDecl()
    }
    override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
      return visitNominalDecl(node)
    }
    override func visitPost(_ node: EnumDeclSyntax) {
      visitPostNominalDecl()
    }
  }

  /// Collects type declarations from a parsed Swift source file
  func collect(from schema: SourceFileSyntax) {
    let collector = TypeDeclCollector(resolver: self)
    collector.walk(schema)
  }

  /// Builds the type name scope for a given type usage
  private func buildScope(type: IdentifierTypeSyntax) -> QualifiedName {
    var innerToOuter: [String] = []
    var context: SyntaxProtocol = type
    while let parent = context.parent {
      if let parent = parent.asProtocol(NamedDeclSyntax.self), parent.isProtocol(DeclGroupSyntax.self) {
        innerToOuter.append(parent.name.text)
      }
      context = parent
    }
    return innerToOuter.reversed()
  }

  /// Looks up a qualified name of a type declaration by its unqualified type usage
  /// Returns the qualified name hierarchy of the type declaration
  /// If the type declaration is not found, returns the unqualified name
  private func tryQualify(type: IdentifierTypeSyntax) -> QualifiedName {
    let name = type.name.text
    let scope = buildScope(type: type)
    /// Search for the type declaration from the innermost scope to the outermost scope
    for i in (0...scope.count).reversed() {
      let qualifiedName = Array(scope[0..<i] + [name])
      if typeDeclByQualifiedName[qualifiedName] != nil {
        return qualifiedName
      }
    }
    return [name]
  }

  /// Looks up a type declaration by its unqualified type usage
  func lookupType(for type: IdentifierTypeSyntax) throws -> TypeDecl {
    let qualifiedName = tryQualify(type: type)
    guard let typeDecl = typeDeclByQualifiedName[qualifiedName] else {
      throw Error.typeNotFound(qualifiedName)
    }
    return typeDecl
  }

  /// Looks up a type declaration by its fully qualified name
  func lookupType(fullyQualified: QualifiedName) throws -> TypeDecl {
    guard let typeDecl = typeDeclByQualifiedName[fullyQualified] else {
      throw Error.typeNotFound(fullyQualified)
    }
    return typeDecl
  }
}