* Try to resolve a Swift caller's bare reference to an ObjC implementation. * * Strategy: look up the ObjC reverse-bridge map for nodes whose Swift base * name would match. Return the first match (matches the existing * single-target resolution contract).
( ref: UnresolvedRef, context: ResolutionContext )
| 171 | * single-target resolution contract). |
| 172 | */ |
| 173 | function resolveSwiftCallToObjc( |
| 174 | ref: UnresolvedRef, |
| 175 | context: ResolutionContext |
| 176 | ): ResolvedRef | null { |
| 177 | // Swift call sites of `obj.foo(bar:)` reach the resolver as either bare |
| 178 | // name `foo` (tree-sitter-swift) or qualified `obj.foo` — strip prefix. |
| 179 | const rawName = ref.referenceName.includes('.') |
| 180 | ? ref.referenceName.slice(ref.referenceName.lastIndexOf('.') + 1) |
| 181 | : ref.referenceName; |
| 182 | |
| 183 | const map = buildObjcMap(context); |
| 184 | const candidates = map.get(rawName); |
| 185 | if (!candidates || candidates.length === 0) return null; |
| 186 | |
| 187 | // Prefer ObjC methods whose corresponding Swift declaration isn't itself |
| 188 | // present (so we don't wrongly redirect a Swift call to ObjC when a Swift |
| 189 | // method of the same name is the real target — that's the in-language case |
| 190 | // and should already be resolved by the name-matcher). Since this resolver |
| 191 | // runs AFTER exact-match, any matching Swift node would already have won; |
| 192 | // so a candidate reaching us is a legitimate cross-language hit. |
| 193 | const target = candidates[0]; |
| 194 | if (!target) return null; |
| 195 | return { |
| 196 | original: ref, |
| 197 | targetNodeId: target.id, |
| 198 | confidence: 0.6, |
| 199 | resolvedBy: 'framework', |
| 200 | }; |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * Try to resolve an ObjC caller's selector reference to a Swift `@objc` |
no test coverage detected