mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
98 lines
3.8 KiB
Swift
98 lines
3.8 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2025 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
package import IndexStoreDB
|
|
import SKLogging
|
|
import SemanticIndex
|
|
@_spi(LinkCompletion) import SwiftDocC
|
|
|
|
extension CheckedIndex {
|
|
/// Find a `SymbolOccurrence` that is considered the primary definition of the symbol with the given `DocCSymbolLink`.
|
|
///
|
|
/// If the `DocCSymbolLink` has an ambiguous definition, the most important role of this function is to deterministically return
|
|
/// the same result every time.
|
|
package func primaryDefinitionOrDeclarationOccurrence(
|
|
ofDocCSymbolLink symbolLink: DocCSymbolLink
|
|
) -> SymbolOccurrence? {
|
|
var components = symbolLink.components
|
|
guard components.count > 0 else {
|
|
return nil
|
|
}
|
|
// Do a lookup to find the top level symbol
|
|
let topLevelSymbol = components.removeLast()
|
|
var topLevelSymbolOccurrences: [SymbolOccurrence] = []
|
|
forEachCanonicalSymbolOccurrence(byName: topLevelSymbol.name) { symbolOccurrence in
|
|
topLevelSymbolOccurrences.append(symbolOccurrence)
|
|
return true // continue
|
|
}
|
|
topLevelSymbolOccurrences = topLevelSymbolOccurrences.filter {
|
|
let symbolInformation = LinkCompletionTools.SymbolInformation(fromSymbolOccurrence: $0)
|
|
return symbolInformation.matches(topLevelSymbol.disambiguation)
|
|
}
|
|
// Search each potential symbol's parents to find an exact match
|
|
let symbolOccurences = topLevelSymbolOccurrences.filter { topLevelSymbolOccurrence in
|
|
var components = components
|
|
var symbolOccurrence = topLevelSymbolOccurrence
|
|
while let nextComponent = components.popLast(), let parentSymbolOccurrence = symbolOccurrence.parent(self) {
|
|
let parentSymbolInformation = LinkCompletionTools.SymbolInformation(
|
|
fromSymbolOccurrence: parentSymbolOccurrence
|
|
)
|
|
guard parentSymbolOccurrence.symbol.name == nextComponent.name,
|
|
parentSymbolInformation.matches(nextComponent.disambiguation)
|
|
else {
|
|
return false
|
|
}
|
|
symbolOccurrence = parentSymbolOccurrence
|
|
}
|
|
// If we have exactly one component left, check to see if it's the module name
|
|
if components.count == 1 {
|
|
let lastComponent = components.removeLast()
|
|
guard lastComponent.name == topLevelSymbolOccurrence.location.moduleName else {
|
|
return false
|
|
}
|
|
}
|
|
guard components.isEmpty else {
|
|
return false
|
|
}
|
|
return true
|
|
}.sorted()
|
|
if symbolOccurences.count > 1 {
|
|
logger.debug("Multiple symbols found for DocC symbol link '\(symbolLink.linkString)'")
|
|
}
|
|
return symbolOccurences.first
|
|
}
|
|
}
|
|
|
|
extension SymbolOccurrence {
|
|
func parent(_ index: CheckedIndex) -> SymbolOccurrence? {
|
|
let allParentRelations =
|
|
relations
|
|
.filter { $0.roles.contains(.childOf) }
|
|
.sorted()
|
|
if allParentRelations.count > 1 {
|
|
logger.debug("Symbol \(symbol.usr) has multiple parent symbols")
|
|
}
|
|
guard let parentRelation = allParentRelations.first else {
|
|
return nil
|
|
}
|
|
if parentRelation.symbol.kind == .extension {
|
|
let allSymbolOccurrences = index.occurrences(relatedToUSR: parentRelation.symbol.usr, roles: .extendedBy)
|
|
.sorted()
|
|
if allSymbolOccurrences.count > 1 {
|
|
logger.debug("Extension \(parentRelation.symbol.usr) extends multiple symbols")
|
|
}
|
|
return allSymbolOccurrences.first
|
|
}
|
|
return index.primaryDefinitionOrDeclarationOccurrence(ofUSR: parentRelation.symbol.usr)
|
|
}
|
|
}
|