From 8a12c723e22b9ad336a62c19603b2133d5ec4351 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Mon, 24 Apr 2023 15:16:50 -0600 Subject: [PATCH] add a "references" relationship kind and referenceLocation mixin rdar://108470705 --- .../Relationship/ReferenceLocation.swift | 39 +++++++++++++++++++ .../Relationship/Relationship.swift | 2 + .../Relationship/RelationshipKind.swift | 11 ++++++ .../Relationship/ReferenceLocationTests.swift | 36 +++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 Sources/SymbolKit/SymbolGraph/Relationship/ReferenceLocation.swift create mode 100644 Tests/SymbolKitTests/SymbolGraph/Relationship/ReferenceLocationTests.swift diff --git a/Sources/SymbolKit/SymbolGraph/Relationship/ReferenceLocation.swift b/Sources/SymbolKit/SymbolGraph/Relationship/ReferenceLocation.swift new file mode 100644 index 0000000..41462c8 --- /dev/null +++ b/Sources/SymbolKit/SymbolGraph/Relationship/ReferenceLocation.swift @@ -0,0 +1,39 @@ +/* + This source file is part of the Swift.org open source project + + Copyright (c) 2023 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 + +extension SymbolGraph.Relationship { + /// A mixin for `references` relationships that indicates the source location of the reference. + public struct ReferenceLocation: Mixin, Codable, Equatable { + public static var mixinKey = "referenceLocation" + + /// The source locations where the reference occurs. + public var range: SymbolGraph.LineList.SourceRange + + /// The URI of the source file where the reference occurs. + public var uri: String + + /// The file URL of the source file where the reference occurs. + @available(macOS 10.11, *) + public var url: URL? { + // The URI string provided in the symbol graph file may be an invalid URL (rdar://69242070) + // + // Using `URL.init(dataRepresentation:relativeTo:)` here handles URI strings with unescaped + // characters without trying to escape or otherwise process the URI string in SymbolKit. + return URL(dataRepresentation: Data(uri.utf8), relativeTo: nil) + } + + public init(range: SymbolGraph.LineList.SourceRange, uri: String) { + self.range = range + self.uri = uri + } + } +} diff --git a/Sources/SymbolKit/SymbolGraph/Relationship/Relationship.swift b/Sources/SymbolKit/SymbolGraph/Relationship/Relationship.swift index 3164b03..d15cb2d 100644 --- a/Sources/SymbolKit/SymbolGraph/Relationship/Relationship.swift +++ b/Sources/SymbolKit/SymbolGraph/Relationship/Relationship.swift @@ -116,10 +116,12 @@ extension SymbolGraph.Relationship { // Mixins static let swiftConstraints = Swift.GenericConstraints.relationshipCodingInfo static let sourceOrigin = SourceOrigin.relationshipCodingInfo + static let referenceLocation = ReferenceLocation.relationshipCodingInfo static let mixinKeys: [String: RelationshipMixinCodingInfo] = [ CodingKeys.swiftConstraints.codingKey.stringValue: Self.swiftConstraints, CodingKeys.sourceOrigin.codingKey.stringValue: Self.sourceOrigin, + CodingKeys.referenceLocation.codingKey.stringValue: Self.referenceLocation, ] static func == (lhs: SymbolGraph.Relationship.CodingKeys, rhs: SymbolGraph.Relationship.CodingKeys) -> Bool { diff --git a/Sources/SymbolKit/SymbolGraph/Relationship/RelationshipKind.swift b/Sources/SymbolKit/SymbolGraph/Relationship/RelationshipKind.swift index cd6b413..e0b9c91 100644 --- a/Sources/SymbolKit/SymbolGraph/Relationship/RelationshipKind.swift +++ b/Sources/SymbolKit/SymbolGraph/Relationship/RelationshipKind.swift @@ -107,5 +107,16 @@ extension SymbolGraph.Relationship { by an extension block symbol `A`. */ public static let extensionTo = Kind(rawValue: "extensionTo") + + /** + A symbol `A` references a symbol `B` in its implementation. + + This relationship can be used to describe implementation details of functions or + properties, by noting which symbols are used in its implementation. + + The implied inverse of this relationship is that the symbol `B` is referenced by the + symbol `A`. + */ + public static let references = Kind(rawValue: "references") } } diff --git a/Tests/SymbolKitTests/SymbolGraph/Relationship/ReferenceLocationTests.swift b/Tests/SymbolKitTests/SymbolGraph/Relationship/ReferenceLocationTests.swift new file mode 100644 index 0000000..cea3781 --- /dev/null +++ b/Tests/SymbolKitTests/SymbolGraph/Relationship/ReferenceLocationTests.swift @@ -0,0 +1,36 @@ +/* + This source file is part of the Swift.org open source project + + Copyright (c) 2023 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 XCTest +@testable import SymbolKit + +class ReferenceLocationTests: XCTestCase { + typealias ReferenceLocation = SymbolGraph.Relationship.ReferenceLocation + + func testRoundTrip() throws { + let referenceLocation = ReferenceLocation( + range: .init( + start: .init(line: 14, character: 0), + end: .init(line: 14, character: 6)), + uri: "file://file.swift" + ) + var source = SymbolGraph.Relationship(source: "source", target: "target", kind: .references, targetFallback: nil) + source[mixin: ReferenceLocation.self] = referenceLocation + + let encoder = JSONEncoder() + let decoder = JSONDecoder() + + let encodedRelationship = try encoder.encode(source) + let decoded = try decoder.decode(SymbolGraph.Relationship.self, from: encodedRelationship) + + XCTAssertEqual(source, decoded) + XCTAssertEqual(decoded[mixin: ReferenceLocation.self], referenceLocation) + } +}