diff --git a/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift b/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift index 5ab0f7b6a..b4200ae07 100644 --- a/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift +++ b/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift @@ -2707,7 +2707,7 @@ public class DocumentationContext { - Returns: A ``DocumentationNode`` with the given identifier. - Throws: ``ContextError/notFound(_:)`` if a documentation node with the given identifier was not found. */ - public func entity(with reference: ResolvedTopicReference) throws -> DocumentationNode { + public func entity(with reference: ResolvedTopicReference) throws(ContextError) -> DocumentationNode { if let cached = documentationCache[reference] { return cached } diff --git a/Sources/SwiftDocC/Infrastructure/Link Resolution/ExternalPathHierarchyResolver.swift b/Sources/SwiftDocC/Infrastructure/Link Resolution/ExternalPathHierarchyResolver.swift index 55bb27281..7efffba88 100644 --- a/Sources/SwiftDocC/Infrastructure/Link Resolution/ExternalPathHierarchyResolver.swift +++ b/Sources/SwiftDocC/Infrastructure/Link Resolution/ExternalPathHierarchyResolver.swift @@ -44,12 +44,10 @@ final class ExternalPathHierarchyResolver { } return .success(foundReference) - } catch let error as PathHierarchy.Error { + } catch { return .failure(unresolvedReference, error.makeTopicReferenceResolutionErrorInfo() { collidingNode in self.fullName(of: collidingNode) // If the link was ambiguous, determine the full name of each colliding node to be presented in the link diagnostic. }) - } catch { - fatalError("Only PathHierarchy.Error errors are raised from the symbol link resolution code above.") } } diff --git a/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift b/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift index 27cd2d2a1..de1a3bbbf 100644 --- a/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift +++ b/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift @@ -67,7 +67,7 @@ public class LinkResolver { do { return try localResolver.resolve(unresolvedReference, in: parent, fromSymbolLink: isCurrentlyResolvingSymbolLink) - } catch let error as PathHierarchy.Error { + } catch { // Check if there's a known external resolver for this module. if case .moduleNotFound(_, let remainingPathComponents, _) = error, let resolver = externalResolvers[remainingPathComponents.first!.full] { let result = resolver.resolve(unresolvedReference, fromSymbolLink: isCurrentlyResolvingSymbolLink) @@ -86,8 +86,6 @@ public class LinkResolver { } else { return .failure(unresolvedReference, error.makeTopicReferenceResolutionErrorInfo() { localResolver.fullName(of: $0, in: context) }) } - } catch { - fatalError("Only SymbolPathTree.Error errors are raised from the symbol link resolution code above.") } } diff --git a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy+Find.swift b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy+Find.swift index 0ead6bbe1..a4ea9ad67 100644 --- a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy+Find.swift +++ b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy+Find.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2023-2024 Apple Inc. and the Swift project authors + Copyright (c) 2023-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 @@ -20,11 +20,11 @@ extension PathHierarchy { /// - onlyFindSymbols: Whether or not only symbol matches should be found. /// - Returns: Returns the unique identifier for the found match or raises an error if no match can be found. /// - Throws: Raises a ``PathHierarchy/Error`` if no match can be found. - func find(path rawPath: String, parent: ResolvedIdentifier? = nil, onlyFindSymbols: Bool) throws -> ResolvedIdentifier { + func find(path rawPath: String, parent: ResolvedIdentifier? = nil, onlyFindSymbols: Bool) throws(Error) -> ResolvedIdentifier { return try findNode(path: rawPath, parentID: parent, onlyFindSymbols: onlyFindSymbols).identifier } - private func findNode(path rawPath: String, parentID: ResolvedIdentifier?, onlyFindSymbols: Bool) throws -> Node { + private func findNode(path rawPath: String, parentID: ResolvedIdentifier?, onlyFindSymbols: Bool) throws(Error) -> Node { // The search for a documentation element can be though of as 3 steps: // - First, parse the path into structured path components. // - Second, find nodes that match the beginning of the path as starting points for the search @@ -79,7 +79,7 @@ extension PathHierarchy { } // A function to avoid repeating the - func searchForNodeInModules() throws -> Node { + func searchForNodeInModules() throws(Error) -> Node { // Note: This captures `parentID`, `remaining`, and `rawPathForError`. if let moduleMatch = modules.first(where: { $0.matches(firstComponent) }) { return try searchForNode(descendingFrom: moduleMatch, pathComponents: remaining.dropFirst(), onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPath) @@ -87,7 +87,7 @@ extension PathHierarchy { if modules.count == 1 { do { return try searchForNode(descendingFrom: modules.first!, pathComponents: remaining, onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPath) - } catch let error as PathHierarchy.Error { + } catch { switch error { case .notFound: // Ignore this error and raise an error about not finding the module instead. @@ -129,7 +129,7 @@ extension PathHierarchy { } // A recursive function to traverse up the path hierarchy searching for the matching node - func searchForNodeUpTheHierarchy(from startingPoint: Node?, path: ArraySlice) throws -> Node { + func searchForNodeUpTheHierarchy(from startingPoint: Node?, path: ArraySlice) throws(Error) -> Node { guard let possibleStartingPoint = startingPoint else { // If the search has reached the top of the hierarchy, check the modules as a base case to break the recursion. do { @@ -147,7 +147,7 @@ extension PathHierarchy { let firstComponent = path.first! // Keep track of the inner most error and raise that if no node is found. - var innerMostError: (any Swift.Error)? + var innerMostError: Error? // If the starting point's children match this component, descend the path hierarchy from there. if possibleStartingPoint.anyChildMatches(firstComponent) { @@ -211,7 +211,7 @@ extension PathHierarchy { pathComponents: ArraySlice, onlyFindSymbols: Bool, rawPathForError: String - ) throws -> Node { + ) throws(Error) -> Node { // All code paths through this function wants to perform extra verification on the return value before returning it to the caller. // To accomplish that, the core implementation happens in `_innerImplementation`, which is called once, right below its definition. @@ -220,7 +220,7 @@ extension PathHierarchy { pathComponents: ArraySlice, onlyFindSymbols: Bool, rawPathForError: String - ) throws -> Node { + ) throws(Error) -> Node { var node = startingPoint var remaining = pathComponents[...] @@ -234,21 +234,13 @@ extension PathHierarchy { while true { let (children, pathComponent) = try findChildContainer(node: &node, remaining: remaining, rawPathForError: rawPathForError) + let child: PathHierarchy.Node? do { - guard let child = try children.find(pathComponent.disambiguation) else { - // The search has ended with a node that doesn't have a child matching the next path component. - throw makePartialResultError(node: node, remaining: remaining, rawPathForError: rawPathForError) - } - node = child - remaining = remaining.dropFirst() - if remaining.isEmpty { - // If all path components are consumed, then the match is found. - return child - } - } catch DisambiguationContainer.Error.lookupCollision(let collisions) { - func handleWrappedCollision() throws -> Node { - let match = try handleCollision(node: node, remaining: remaining, collisions: collisions, onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPathForError) - return match + child = try children.find(pathComponent.disambiguation) + } catch { + let collisions = error.collisions + func handleWrappedCollision() throws(Error) -> Node { + try handleCollision(node: node, remaining: remaining, collisions: collisions, onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPathForError) } // When there's a collision, use the remaining path components to try and narrow down the possible collisions. @@ -314,6 +306,17 @@ extension PathHierarchy { // Couldn't resolve the collision by look ahead. return try handleWrappedCollision() } + + guard let child else { + // The search has ended with a node that doesn't have a child matching the next path component. + throw makePartialResultError(node: node, remaining: remaining, rawPathForError: rawPathForError) + } + node = child + remaining = remaining.dropFirst() + if remaining.isEmpty { + // If all path components are consumed, then the match is found. + return child + } } } @@ -336,7 +339,7 @@ extension PathHierarchy { collisions: [(node: PathHierarchy.Node, disambiguation: String)], onlyFindSymbols: Bool, rawPathForError: String - ) throws -> Node { + ) throws(Error) -> Node { if let favoredMatch = collisions.singleMatch({ !$0.node.isDisfavoredInLinkCollisions }) { return favoredMatch.node } @@ -421,7 +424,7 @@ extension PathHierarchy { node: inout Node, remaining: ArraySlice, rawPathForError: String - ) throws -> (DisambiguationContainer, PathComponent) { + ) throws(Error) -> (DisambiguationContainer, PathComponent) { var pathComponent = remaining.first! if let match = node.children[pathComponent.full] { // The path component parsing may treat dash separated words as disambiguation information. @@ -439,12 +442,10 @@ extension PathHierarchy { // MARK: Disambiguation Container extension PathHierarchy.DisambiguationContainer { - /// Errors finding values in the disambiguation tree - enum Error: Swift.Error { - /// Multiple matches found. - /// - /// Includes a list of values paired with their missing disambiguation suffixes. - case lookupCollision([(node: PathHierarchy.Node, disambiguation: String)]) + /// Multiple matches found. + struct LookupCollisionError: Swift.Error { + /// A list of values paired with their missing disambiguation suffixes. + let collisions: [(node: PathHierarchy.Node, disambiguation: String)] } /// Attempts to find the only element in the disambiguation container without using any disambiguation information. @@ -464,7 +465,7 @@ extension PathHierarchy.DisambiguationContainer { /// - No match is found; indicated by a `nil` return value. /// - Exactly one match is found; indicated by a non-nil return value. /// - More than one match is found; indicated by a raised error listing the matches and their missing disambiguation. - func find(_ disambiguation: PathHierarchy.PathComponent.Disambiguation?) throws -> PathHierarchy.Node? { + func find(_ disambiguation: PathHierarchy.PathComponent.Disambiguation?) throws(LookupCollisionError) -> PathHierarchy.Node? { if disambiguation == nil, let match = singleMatch() { return match } @@ -478,13 +479,13 @@ extension PathHierarchy.DisambiguationContainer { let matches = storage.filter({ $0.kind == kind }) guard matches.count <= 1 else { // Suggest not only hash disambiguation, but also type signature disambiguation. - throw Error.lookupCollision(Self.disambiguatedValues(for: matches).map { ($0.value, $0.disambiguation.makeSuffix()) }) + throw LookupCollisionError(collisions: Self.disambiguatedValues(for: matches).map { ($0.value, $0.disambiguation.makeSuffix()) }) } return matches.first?.node case (nil, let hash?): let matches = storage.filter({ $0.hash == hash }) guard matches.count <= 1 else { - throw Error.lookupCollision(matches.map { ($0.node, "-" + $0.kind!) }) // An element wouldn't match if it didn't have kind disambiguation. + throw LookupCollisionError(collisions: matches.map { ($0.node, "-" + $0.kind!) }) // An element wouldn't match if it didn't have kind disambiguation. } return matches.first?.node case (nil, nil): @@ -498,13 +499,13 @@ extension PathHierarchy.DisambiguationContainer { case (let parameterTypes?, nil): let matches = storage.filter({ typesMatch(provided: parameterTypes, actual: $0.parameterTypes) }) guard matches.count <= 1 else { - throw Error.lookupCollision(matches.map { ($0.node, "->" + formattedTypes($0.parameterTypes)!) }) // An element wouldn't match if it didn't have parameter type disambiguation. + throw LookupCollisionError(collisions: matches.map { ($0.node, "->" + formattedTypes($0.parameterTypes)!) }) // An element wouldn't match if it didn't have parameter type disambiguation. } return matches.first?.node case (nil, let returnTypes?): let matches = storage.filter({ typesMatch(provided: returnTypes, actual: $0.returnTypes) }) guard matches.count <= 1 else { - throw Error.lookupCollision(matches.map { ($0.node, "-" + formattedTypes($0.returnTypes)!) }) // An element wouldn't match if it didn't have return type disambiguation. + throw LookupCollisionError(collisions: matches.map { ($0.node, "-" + formattedTypes($0.returnTypes)!) }) // An element wouldn't match if it didn't have return type disambiguation. } return matches.first?.node case (nil, nil): @@ -515,7 +516,7 @@ extension PathHierarchy.DisambiguationContainer { } // Disambiguate by a mix of kinds and USRs - throw Error.lookupCollision(self.disambiguatedValues().map { ($0.value, $0.disambiguation.makeSuffix()) }) + throw LookupCollisionError(collisions: self.disambiguatedValues().map { ($0.value, $0.disambiguation.makeSuffix()) }) } } diff --git a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift index 4dc85a296..597b6af77 100644 --- a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift +++ b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift @@ -228,7 +228,7 @@ final class PathHierarchyBasedLinkResolver { /// - isCurrentlyResolvingSymbolLink: Whether or not the documentation link is a symbol link. /// - context: The documentation context to resolve the link in. /// - Returns: The result of resolving the reference. - func resolve(_ unresolvedReference: UnresolvedTopicReference, in parent: ResolvedTopicReference, fromSymbolLink isCurrentlyResolvingSymbolLink: Bool) throws -> TopicReferenceResolutionResult { + func resolve(_ unresolvedReference: UnresolvedTopicReference, in parent: ResolvedTopicReference, fromSymbolLink isCurrentlyResolvingSymbolLink: Bool) throws(PathHierarchy.Error) -> TopicReferenceResolutionResult { let parentID = resolvedReferenceMap[parent] let found = try pathHierarchy.find(path: Self.path(for: unresolvedReference), parent: parentID, onlyFindSymbols: isCurrentlyResolvingSymbolLink) guard let foundReference = resolvedReferenceMap[found] else { diff --git a/Sources/SwiftDocC/Infrastructure/Symbol Graph/ExtendedTypeFormatTransformation.swift b/Sources/SwiftDocC/Infrastructure/Symbol Graph/ExtendedTypeFormatTransformation.swift index e9d2e42e7..680d8e666 100644 --- a/Sources/SwiftDocC/Infrastructure/Symbol Graph/ExtendedTypeFormatTransformation.swift +++ b/Sources/SwiftDocC/Infrastructure/Symbol Graph/ExtendedTypeFormatTransformation.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-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 @@ -569,7 +569,7 @@ extension ExtendedTypeFormatTransformation { // MARK: Apply Mappings to SymbolGraph private extension SymbolGraph { - mutating func apply(compactMap include: (SymbolGraph.Symbol) throws -> SymbolGraph.Symbol?) rethrows { + mutating func apply(compactMap include: (SymbolGraph.Symbol) throws(Error) -> SymbolGraph.Symbol?) throws(Error) { for (key, symbol) in self.symbols { self.symbols.removeValue(forKey: key) if let newSymbol = try include(symbol) { diff --git a/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift b/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift index a62aac5ba..0b6150d35 100644 --- a/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift +++ b/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift @@ -461,14 +461,14 @@ extension Symbol { /// When building multi-platform documentation symbols might have more than one declaration /// depending on variances in their implementation across platforms (e.g. use `NSPoint` vs `CGPoint` parameter in a method). /// This method finds matching symbols between graphs and merges their declarations in case there are differences. - func mergeDeclaration(mergingDeclaration: SymbolGraph.Symbol.DeclarationFragments, identifier: String, symbolAvailability: SymbolGraph.Symbol.Availability?, alternateSymbols: SymbolGraph.Symbol.AlternateSymbols?, selector: UnifiedSymbolGraph.Selector) throws { + func mergeDeclaration(mergingDeclaration: SymbolGraph.Symbol.DeclarationFragments, identifier: String, symbolAvailability: SymbolGraph.Symbol.Availability?, alternateSymbols: SymbolGraph.Symbol.AlternateSymbols?, selector: UnifiedSymbolGraph.Selector) throws(DocumentationContext.ContextError) { let trait = DocumentationDataVariantsTrait(for: selector) let platformName = selector.platform func merge( _ mergingValue: Value, into variants: inout DocumentationDataVariants<[[PlatformName?] : Value]> - ) throws { + ) throws(DocumentationContext.ContextError) { guard let platformName else { variants[trait]?[[nil]] = mergingValue return diff --git a/Sources/SwiftDocC/Utility/FoundationExtensions/RangeReplaceableCollection+Group.swift b/Sources/SwiftDocC/Utility/FoundationExtensions/RangeReplaceableCollection+Group.swift index 8aaf30ec9..ba2384bf3 100644 --- a/Sources/SwiftDocC/Utility/FoundationExtensions/RangeReplaceableCollection+Group.swift +++ b/Sources/SwiftDocC/Utility/FoundationExtensions/RangeReplaceableCollection+Group.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-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 @@ -15,7 +15,7 @@ extension RangeReplaceableCollection { /// /// - Parameter belongsInGroupWithPrevious: A check whether the given element belongs in the same group as the previous element /// - Returns: An array of subsequences of elements that belong together. - func group(asLongAs belongsInGroupWithPrevious: (_ previous: Element, _ current: Element) throws -> Bool) rethrows -> [SubSequence] { + func group(asLongAs belongsInGroupWithPrevious: (_ previous: Element, _ current: Element) throws(Error) -> Bool) throws(Error) -> [SubSequence] { var result = [SubSequence]() let indexPairs = zip(indices, indices.dropFirst()) diff --git a/Sources/SwiftDocC/Utility/FoundationExtensions/Sequence+FirstMap.swift b/Sources/SwiftDocC/Utility/FoundationExtensions/Sequence+FirstMap.swift index c51d041d8..e9502eec9 100644 --- a/Sources/SwiftDocC/Utility/FoundationExtensions/Sequence+FirstMap.swift +++ b/Sources/SwiftDocC/Utility/FoundationExtensions/Sequence+FirstMap.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-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 @@ -30,7 +30,7 @@ extension Sequence { /// - Parameter predicate: A mapping closure that accepts an element of this sequence as its parameter and returns a transformed value or `nil`. /// - Throws: Any error that's raised by the mapping closure. /// - Returns: The first mapped, non-nil value, or `nil` if the mapping closure returned `nil` for every value in the sequence. - func mapFirst(where predicate: (Element) throws -> T?) rethrows -> T? { + func mapFirst(where predicate: (Element) throws(Error) -> Result?) throws(Error) -> Result? { for element in self { if let result = try predicate(element) { return result diff --git a/Sources/SwiftDocC/Utility/Synchronization.swift b/Sources/SwiftDocC/Utility/Synchronization.swift index 7e243bbe9..80395c176 100644 --- a/Sources/SwiftDocC/Utility/Synchronization.swift +++ b/Sources/SwiftDocC/Utility/Synchronization.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-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 @@ -77,7 +77,7 @@ public class Synchronized { /// - Parameter block: A throwing block of work that optionally returns a value. /// - Returns: Returns the returned value of `block`, if any. @discardableResult - public func sync(_ block: (inout Value) throws -> Result) rethrows -> Result { + public func sync(_ block: (inout Value) throws(Error) -> Result) throws(Error) -> Result { #if os(macOS) || os(iOS) os_unfair_lock_lock(lock) defer { os_unfair_lock_unlock(lock) } @@ -116,7 +116,7 @@ extension Lock { } @discardableResult - package func sync(_ block: () throws -> Result) rethrows -> Result { + package func sync(_ block: () throws(Error) -> Result) throws(Error) -> Result { #if os(macOS) || os(iOS) os_unfair_lock_lock(lock) defer { os_unfair_lock_unlock(lock) }