File: DeclarationFragments%2BSimplify.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (191 lines) | stat: -rw-r--r-- 9,025 bytes parent folder | download
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 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 Swift project authors
*/

import Foundation

fileprivate extension SymbolGraph.Symbol.DeclarationFragments.Fragment {
    init(textFragment text: String) {
        self.spelling = text
        self.kind = .text
        self.preciseIdentifier = nil
    }
}

fileprivate extension UnifiedSymbolGraph.Symbol {
    func declarationFragments(selector: UnifiedSymbolGraph.Selector) -> [SymbolGraph.Symbol.DeclarationFragments.Fragment]? {
        return (self.mixins[selector]?[SymbolGraph.Symbol.DeclarationFragments.mixinKey] as? SymbolGraph.Symbol.DeclarationFragments)?.declarationFragments
    }

    func functionSignature(selector: UnifiedSymbolGraph.Selector) -> SymbolGraph.Symbol.FunctionSignature? {
        return self.mixins[selector]?[SymbolGraph.Symbol.FunctionSignature.mixinKey] as? SymbolGraph.Symbol.FunctionSignature
    }
}

private func overloadFragments(
    declarationFragments: [SymbolGraph.Symbol.DeclarationFragments.Fragment]?,
    subHeading: [SymbolGraph.Symbol.DeclarationFragments.Fragment]?,
    navigator: [SymbolGraph.Symbol.DeclarationFragments.Fragment]?,
    functionSignature: SymbolGraph.Symbol.FunctionSignature?
) -> [SymbolGraph.Symbol.DeclarationFragments.Fragment]? {
    guard let sourceFragments = declarationFragments ?? subHeading ?? navigator, !sourceFragments.isEmpty else {
        return nil
    }

    var simplifiedFragments: [SymbolGraph.Symbol.DeclarationFragments.Fragment] = []

    // In Swift, methods have a keyword as their first token; if the declaration follows that
    // pattern then pull that out

    // Sometimes symbols are decorated with attributes or extra keywords in the full declaration.
    // In this case, the sub-heading declaration doesn't include those decorations, so pull that
    // keyword if it exists
    if let firstFragment = subHeading?.first, firstFragment.kind == .keyword {
        simplifiedFragments.append(firstFragment)
    } else if let firstFragment = sourceFragments.first(where: { $0.kind != .attribute && $0.kind != .text }), firstFragment.kind == .keyword {
        // If we only have full declaration fragments, still try to skip a leading attribute if possible
        simplifiedFragments.append(firstFragment)
    }

    // Then, look for the first identifier, which should contain the symbol's name, and add that
    if let firstIdentifier = sourceFragments.first(where: { $0.kind == .identifier }) {
        if !simplifiedFragments.isEmpty {
            simplifiedFragments.append(.init(textFragment: " "))
        }
        simplifiedFragments.append(firstIdentifier)
    }

    // Assumption: All symbols that can be considered "overloads" are written with method
    // syntax, including a list of arguments surrounded by parentheses. In Swift symbol graphs,
    // method parameters are included in the FunctionSignature mixin, so if that's present we
    // use that to parse the data out.

    simplifiedFragments.append(.init(textFragment: "("))

    if let functionSignature = functionSignature {
        for parameter in functionSignature.parameters {
            // Scan through the declaration fragments to see whether this parameter's name is
            // externally-facing or not.
            let fragment: SymbolGraph.Symbol.DeclarationFragments.Fragment
            let parameterName = parameter.externalName ?? parameter.name
            if let paramNameFragment = sourceFragments.first(where: { $0.spelling == parameterName && $0.kind == .externalParameter }) {
                fragment = paramNameFragment
            } else {
                // If not, then insert an underscore for this parameter.
                // FIXME: This is a Swift-centric assumption; change this if/when we support C++ overloads
                fragment = .init(kind: .externalParameter, spelling: "_", preciseIdentifier: nil)
            }
            simplifiedFragments.append(fragment)
            simplifiedFragments.append(.init(textFragment: ":"))
        }
    } else {
        let parameterFragments = sourceFragments.extractFunctionParameters()
        simplifiedFragments.append(contentsOf: parameterFragments)
    }

    if simplifiedFragments.last?.kind == .text, var lastFragment = simplifiedFragments.popLast() {
        lastFragment.spelling += ")"
        simplifiedFragments.append(lastFragment)
    } else {
        simplifiedFragments.append(.init(textFragment: ")"))
    }

    return simplifiedFragments
}

internal extension SymbolGraph.Symbol {
    func overloadSubheadingFragments() -> [DeclarationFragments.Fragment]? {
        return overloadFragments(
            declarationFragments: self.declarationFragments,
            subHeading: self.names.subHeading,
            navigator: self.names.navigator,
            functionSignature: self.functionSignature)
    }
}

internal extension UnifiedSymbolGraph.Symbol {
    func overloadSubheadingFragments() -> [UnifiedSymbolGraph.Selector: [SymbolGraph.Symbol.DeclarationFragments.Fragment]] {
        var fragmentsMap: [UnifiedSymbolGraph.Selector: [SymbolGraph.Symbol.DeclarationFragments.Fragment]] = [:]

        for selector in self.allSelectors {
            if let fragments = overloadFragments(
                declarationFragments: self.declarationFragments(selector: selector),
                subHeading: self.names[selector]?.subHeading,
                navigator: self.names[selector]?.navigator,
                functionSignature: self.functionSignature(selector: selector)
            ) {
                fragmentsMap[selector] = fragments
            }
        }

        return fragmentsMap
    }
}

internal extension [SymbolGraph.Symbol.DeclarationFragments.Fragment] {
    func extractFunctionParameters() -> [SymbolGraph.Symbol.DeclarationFragments.Fragment] {
        var parameterFragments = [SymbolGraph.Symbol.DeclarationFragments.Fragment]()

        // A parameter can be named one of three ways:
        // 1. Only an external name
        // 2. External name followed by internal
        // 3. Only an internal name (can happen in Swift when subscripts have anonymous parameters)

        // To further complicate matters, function types in Swift are rendered with their parameter
        // names as proper internal/external parameter fragments. While we could try to scan through
        // for arguments flanked by parentheses, we should instead rely on the symbol graph to have
        // included the FunctionSignature mixin so that this method isn't called. This function will
        // fail to distinguish the parameter names and simply render them all inline.

        // If there are no parameter fragments in this declaration, assume no parameters and bail
        guard var currentIndex = self.firstIndex(where: \.isParameter) else {
            return []
        }
        // Assumption: Method/function declarations end their parameters list with a close
        // parenthesis, or (in Swift) an arrow. If we find neither of these, scan until the end of
        // the list.
        let endOfArguments = self.lastIndex(where: { $0.spelling.contains("->") })
            ?? self.lastIndex(where: { $0.spelling.contains(")") })
            ?? self.endIndex

        while currentIndex < endOfArguments {
            let currentFragment = self[currentIndex]
            if currentFragment.isParameter {
                if currentFragment.kind == .externalParameter {
                    parameterFragments.append(currentFragment)

                    // In Swift, parameters with distinct internal and external names are
                    // rendered with the external name first, followed by a space, then the
                    // internal name. If the next two fragments match that pattern, skip forward
                    // so we don't accidentally insert an extra underscore parameter.
                    if currentIndex + 2 < endOfArguments,
                       self[currentIndex + 1].spelling == " ",
                       self[currentIndex + 2].kind == .internalParameter 
                    {
                        currentIndex += 2
                    }
                } else {
                    // FIXME: This is a Swift-centric assumption; change this if/when we support C++ overloads
                    parameterFragments.append(.init(kind: .externalParameter, spelling: "_", preciseIdentifier: nil))
                }
                parameterFragments.append(.init(textFragment: ":"))
            }

            currentIndex += 1
        }

        return parameterFragments
    }
}

fileprivate extension SymbolGraph.Symbol.DeclarationFragments.Fragment {
    var isParameter: Bool {
        return self.kind == .internalParameter || self.kind == .externalParameter
    }
}