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
}
}
|