* Try to resolve an ObjC caller's selector reference to a Swift `@objc` * implementation. * * Strategy: derive candidate Swift base names from the selector via * `swiftBaseNamesForObjcSelector`. For each, look up Swift methods named * that and verify with a source-window check that the declarat
( ref: UnresolvedRef, context: ResolutionContext )
| 211 | * happens to share the name but isn't bridged). |
| 212 | */ |
| 213 | function resolveObjcCallToSwift( |
| 214 | ref: UnresolvedRef, |
| 215 | context: ResolutionContext |
| 216 | ): ResolvedRef | null { |
| 217 | // ObjC call sites get receiver-prefixed when the receiver isn't self/super |
| 218 | // (see tree-sitter.ts message_expression handling): `[obj foo:bar:]` |
| 219 | // becomes `obj.foo:bar:`. Strip the receiver prefix to recover the raw |
| 220 | // selector for the bridge math. |
| 221 | const rawSelector = ref.referenceName.includes('.') |
| 222 | ? ref.referenceName.slice(ref.referenceName.lastIndexOf('.') + 1) |
| 223 | : ref.referenceName; |
| 224 | |
| 225 | // Bridge math only applies to selector-shape names (contain `:`). |
| 226 | if (!rawSelector.includes(':')) return null; |
| 227 | |
| 228 | const candidates = swiftBaseNamesForObjcSelector(rawSelector); |
| 229 | for (const candidate of candidates) { |
| 230 | const matches = context |
| 231 | .getNodesByName(candidate) |
| 232 | .filter((n) => n.language === 'swift' && (n.kind === 'method' || n.kind === 'function')); |
| 233 | for (const match of matches) { |
| 234 | const window = declarationSourceWindow(match, context); |
| 235 | if (isObjcExposed(window)) { |
| 236 | return { |
| 237 | original: ref, |
| 238 | targetNodeId: match.id, |
| 239 | confidence: 0.6, |
| 240 | resolvedBy: 'framework', |
| 241 | }; |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | return null; |
| 246 | } |
| 247 | |
| 248 | export const swiftObjcBridgeResolver: FrameworkResolver = { |
| 249 | name: 'swift-objc-bridge', |
no test coverage detected