11/*
22 This source file is part of the Swift.org open source project
33
4- Copyright (c) 2023-2024 Apple Inc. and the Swift project authors
4+ Copyright (c) 2023-2025 Apple Inc. and the Swift project authors
55 Licensed under Apache License v2.0 with Runtime Library Exception
66
77 See https://swift.org/LICENSE.txt for license information
@@ -20,11 +20,11 @@ extension PathHierarchy {
2020 /// - onlyFindSymbols: Whether or not only symbol matches should be found.
2121 /// - Returns: Returns the unique identifier for the found match or raises an error if no match can be found.
2222 /// - Throws: Raises a ``PathHierarchy/Error`` if no match can be found.
23- func find( path rawPath: String , parent: ResolvedIdentifier ? = nil , onlyFindSymbols: Bool ) throws -> ResolvedIdentifier {
23+ func find( path rawPath: String , parent: ResolvedIdentifier ? = nil , onlyFindSymbols: Bool ) throws ( Error ) -> ResolvedIdentifier {
2424 return try findNode ( path: rawPath, parentID: parent, onlyFindSymbols: onlyFindSymbols) . identifier
2525 }
2626
27- private func findNode( path rawPath: String , parentID: ResolvedIdentifier ? , onlyFindSymbols: Bool ) throws -> Node {
27+ private func findNode( path rawPath: String , parentID: ResolvedIdentifier ? , onlyFindSymbols: Bool ) throws ( Error ) -> Node {
2828 // The search for a documentation element can be though of as 3 steps:
2929 // - First, parse the path into structured path components.
3030 // - Second, find nodes that match the beginning of the path as starting points for the search
@@ -79,15 +79,15 @@ extension PathHierarchy {
7979 }
8080
8181 // A function to avoid repeating the
82- func searchForNodeInModules( ) throws -> Node {
82+ func searchForNodeInModules( ) throws ( Error ) -> Node {
8383 // Note: This captures `parentID`, `remaining`, and `rawPathForError`.
8484 if let moduleMatch = modules. first ( where: { $0. matches ( firstComponent) } ) {
8585 return try searchForNode ( descendingFrom: moduleMatch, pathComponents: remaining. dropFirst ( ) , onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPath)
8686 }
8787 if modules. count == 1 {
8888 do {
8989 return try searchForNode ( descendingFrom: modules. first!, pathComponents: remaining, onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPath)
90- } catch let error as PathHierarchy . Error {
90+ } catch {
9191 switch error {
9292 case . notFound:
9393 // Ignore this error and raise an error about not finding the module instead.
@@ -129,7 +129,7 @@ extension PathHierarchy {
129129 }
130130
131131 // A recursive function to traverse up the path hierarchy searching for the matching node
132- func searchForNodeUpTheHierarchy( from startingPoint: Node ? , path: ArraySlice < PathComponent > ) throws -> Node {
132+ func searchForNodeUpTheHierarchy( from startingPoint: Node ? , path: ArraySlice < PathComponent > ) throws ( Error ) -> Node {
133133 guard let possibleStartingPoint = startingPoint else {
134134 // If the search has reached the top of the hierarchy, check the modules as a base case to break the recursion.
135135 do {
@@ -147,7 +147,7 @@ extension PathHierarchy {
147147 let firstComponent = path. first!
148148
149149 // Keep track of the inner most error and raise that if no node is found.
150- var innerMostError : ( any Swift . Error ) ?
150+ var innerMostError : Error ?
151151
152152 // If the starting point's children match this component, descend the path hierarchy from there.
153153 if possibleStartingPoint. anyChildMatches ( firstComponent) {
@@ -211,7 +211,7 @@ extension PathHierarchy {
211211 pathComponents: ArraySlice < PathComponent > ,
212212 onlyFindSymbols: Bool ,
213213 rawPathForError: String
214- ) throws -> Node {
214+ ) throws ( Error ) -> Node {
215215 // All code paths through this function wants to perform extra verification on the return value before returning it to the caller.
216216 // To accomplish that, the core implementation happens in `_innerImplementation`, which is called once, right below its definition.
217217
@@ -220,7 +220,7 @@ extension PathHierarchy {
220220 pathComponents: ArraySlice < PathComponent > ,
221221 onlyFindSymbols: Bool ,
222222 rawPathForError: String
223- ) throws -> Node {
223+ ) throws ( Error ) -> Node {
224224 var node = startingPoint
225225 var remaining = pathComponents [ ... ]
226226
@@ -234,21 +234,13 @@ extension PathHierarchy {
234234 while true {
235235 let ( children, pathComponent) = try findChildContainer ( node: & node, remaining: remaining, rawPathForError: rawPathForError)
236236
237+ let child : PathHierarchy . Node ?
237238 do {
238- guard let child = try children. find ( pathComponent. disambiguation) else {
239- // The search has ended with a node that doesn't have a child matching the next path component.
240- throw makePartialResultError ( node: node, remaining: remaining, rawPathForError: rawPathForError)
241- }
242- node = child
243- remaining = remaining. dropFirst ( )
244- if remaining. isEmpty {
245- // If all path components are consumed, then the match is found.
246- return child
247- }
248- } catch DisambiguationContainer . Error . lookupCollision( let collisions) {
249- func handleWrappedCollision( ) throws -> Node {
250- let match = try handleCollision ( node: node, remaining: remaining, collisions: collisions, onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPathForError)
251- return match
239+ child = try children. find ( pathComponent. disambiguation)
240+ } catch {
241+ let collisions = error. collisions
242+ func handleWrappedCollision( ) throws ( Error) -> Node {
243+ try handleCollision ( node: node, remaining: remaining, collisions: collisions, onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPathForError)
252244 }
253245
254246 // When there's a collision, use the remaining path components to try and narrow down the possible collisions.
@@ -314,6 +306,17 @@ extension PathHierarchy {
314306 // Couldn't resolve the collision by look ahead.
315307 return try handleWrappedCollision ( )
316308 }
309+
310+ guard let child else {
311+ // The search has ended with a node that doesn't have a child matching the next path component.
312+ throw makePartialResultError ( node: node, remaining: remaining, rawPathForError: rawPathForError)
313+ }
314+ node = child
315+ remaining = remaining. dropFirst ( )
316+ if remaining. isEmpty {
317+ // If all path components are consumed, then the match is found.
318+ return child
319+ }
317320 }
318321 }
319322
@@ -336,7 +339,7 @@ extension PathHierarchy {
336339 collisions: [ ( node: PathHierarchy . Node , disambiguation: String ) ] ,
337340 onlyFindSymbols: Bool ,
338341 rawPathForError: String
339- ) throws -> Node {
342+ ) throws ( Error ) -> Node {
340343 if let favoredMatch = collisions. singleMatch ( { !$0. node. isDisfavoredInLinkCollisions } ) {
341344 return favoredMatch. node
342345 }
@@ -421,7 +424,7 @@ extension PathHierarchy {
421424 node: inout Node ,
422425 remaining: ArraySlice < PathComponent > ,
423426 rawPathForError: String
424- ) throws -> ( DisambiguationContainer , PathComponent ) {
427+ ) throws ( Error ) -> ( DisambiguationContainer , PathComponent ) {
425428 var pathComponent = remaining. first!
426429 if let match = node. children [ pathComponent. full] {
427430 // The path component parsing may treat dash separated words as disambiguation information.
@@ -439,12 +442,10 @@ extension PathHierarchy {
439442// MARK: Disambiguation Container
440443
441444extension PathHierarchy . DisambiguationContainer {
442- /// Errors finding values in the disambiguation tree
443- enum Error : Swift . Error {
444- /// Multiple matches found.
445- ///
446- /// Includes a list of values paired with their missing disambiguation suffixes.
447- case lookupCollision( [ ( node: PathHierarchy . Node , disambiguation: String ) ] )
445+ /// Multiple matches found.
446+ struct LookupCollisionError : Swift . Error {
447+ /// A list of values paired with their missing disambiguation suffixes.
448+ let collisions : [ ( node: PathHierarchy . Node , disambiguation: String ) ]
448449 }
449450
450451 /// Attempts to find the only element in the disambiguation container without using any disambiguation information.
@@ -464,7 +465,7 @@ extension PathHierarchy.DisambiguationContainer {
464465 /// - No match is found; indicated by a `nil` return value.
465466 /// - Exactly one match is found; indicated by a non-nil return value.
466467 /// - More than one match is found; indicated by a raised error listing the matches and their missing disambiguation.
467- func find( _ disambiguation: PathHierarchy . PathComponent . Disambiguation ? ) throws -> PathHierarchy . Node ? {
468+ func find( _ disambiguation: PathHierarchy . PathComponent . Disambiguation ? ) throws ( LookupCollisionError ) -> PathHierarchy . Node ? {
468469 if disambiguation == nil , let match = singleMatch ( ) {
469470 return match
470471 }
@@ -478,13 +479,13 @@ extension PathHierarchy.DisambiguationContainer {
478479 let matches = storage. filter ( { $0. kind == kind } )
479480 guard matches. count <= 1 else {
480481 // Suggest not only hash disambiguation, but also type signature disambiguation.
481- throw Error . lookupCollision ( Self . disambiguatedValues ( for: matches) . map { ( $0. value, $0. disambiguation. makeSuffix ( ) ) } )
482+ throw LookupCollisionError ( collisions : Self . disambiguatedValues ( for: matches) . map { ( $0. value, $0. disambiguation. makeSuffix ( ) ) } )
482483 }
483484 return matches. first? . node
484485 case ( nil , let hash? ) :
485486 let matches = storage. filter ( { $0. hash == hash } )
486487 guard matches. count <= 1 else {
487- throw Error . lookupCollision ( matches. map { ( $0. node, " - " + $0. kind!) } ) // An element wouldn't match if it didn't have kind disambiguation.
488+ throw LookupCollisionError ( collisions : matches. map { ( $0. node, " - " + $0. kind!) } ) // An element wouldn't match if it didn't have kind disambiguation.
488489 }
489490 return matches. first? . node
490491 case ( nil , nil ) :
@@ -498,13 +499,13 @@ extension PathHierarchy.DisambiguationContainer {
498499 case ( let parameterTypes? , nil ) :
499500 let matches = storage. filter ( { typesMatch ( provided: parameterTypes, actual: $0. parameterTypes) } )
500501 guard matches. count <= 1 else {
501- throw Error . lookupCollision ( matches. map { ( $0. node, " -> " + formattedTypes( $0. parameterTypes) !) } ) // An element wouldn't match if it didn't have parameter type disambiguation.
502+ throw LookupCollisionError ( collisions : matches. map { ( $0. node, " -> " + formattedTypes( $0. parameterTypes) !) } ) // An element wouldn't match if it didn't have parameter type disambiguation.
502503 }
503504 return matches. first? . node
504505 case ( nil , let returnTypes? ) :
505506 let matches = storage. filter ( { typesMatch ( provided: returnTypes, actual: $0. returnTypes) } )
506507 guard matches. count <= 1 else {
507- throw Error . lookupCollision ( matches. map { ( $0. node, " - " + formattedTypes( $0. returnTypes) !) } ) // An element wouldn't match if it didn't have return type disambiguation.
508+ throw LookupCollisionError ( collisions : matches. map { ( $0. node, " - " + formattedTypes( $0. returnTypes) !) } ) // An element wouldn't match if it didn't have return type disambiguation.
508509 }
509510 return matches. first? . node
510511 case ( nil , nil ) :
@@ -515,7 +516,7 @@ extension PathHierarchy.DisambiguationContainer {
515516 }
516517
517518 // Disambiguate by a mix of kinds and USRs
518- throw Error . lookupCollision ( self . disambiguatedValues ( ) . map { ( $0. value, $0. disambiguation. makeSuffix ( ) ) } )
519+ throw LookupCollisionError ( collisions : self . disambiguatedValues ( ) . map { ( $0. value, $0. disambiguation. makeSuffix ( ) ) } )
519520 }
520521}
521522
0 commit comments