Skip to content

feat: add Objective-C language support#165

Merged
colbymchenry merged 1 commit into
colbymchenry:mainfrom
0x1306a94:feat/objc-language-support
May 26, 2026
Merged

feat: add Objective-C language support#165
colbymchenry merged 1 commit into
colbymchenry:mainfrom
0x1306a94:feat/objc-language-support

Conversation

@0x1306a94
Copy link
Copy Markdown
Contributor

@0x1306a94 0x1306a94 commented May 18, 2026

Summary

Adds partial Objective-C support to the tree-sitter extraction pipeline so iOS/macOS codebases can be indexed with the same graph queries as other languages.

Wiring

  • Register objc in LANGUAGES, WASM_GRAMMAR_FILES (tree-sitter-objc.wasm from tree-sitter-wasms, ABI 14), and EXTRACTORS
  • Map .m / .mmobjc; classify .h as Objective-C when content matches @interface / @implementation / @protocol / @synthesize (after the existing C++ .h heuristic)
  • File discovery uses zero-config isSourceFile + .gitignore — no separate include globs needed

Extraction (objc.ts)

  • Classes@interface emits one class node; @implementation reuses it via visitNode (avoids duplicate class/method nodes)
  • Protocols@protocolprotocol nodes
  • Methodsmethod_definition only; full multi-part selectors (doThing:with:) via resolveName
  • Properties@property via extractPropertyName (walks struct_declarator / pointer_declarator)
  • Functions & imports — C-style function_definition and #import (preproc_include)
  • Inheritanceextends for superclass; implements for angle-bracket protocols on @interface
  • Callscall_expression and [receiver message] (message_expression) call edges

Core hooks

  • resolveName / extractPropertyName on LanguageExtractor (used by ObjC; generic fallbacks unchanged)
  • extractInheritance early-return branch for class_interface
  • Import resolver try-order: .h, .m, .mm

Docs & eval corpus

  • README: Partial support row (not full — documents .mm ObjC++ limits)
  • CHANGELOG [Unreleased] entry with known limitations
  • Agent-eval corpus: Masonry (small), FMDB (medium), SDWebImage (large)

Benchmark (local dev build)

Repo Files Nodes verify-extraction Notes
Masonry 267 1,418 PASS A/B: 6 tool calls / 1 Read vs 18 / 15 Reads
FMDB ~80 PASS
SDWebImage 274 3,710 PASS

Known limitations

  • Chained/nested message sends (e.g. [[Class alloc] init]) only record the innermost method name
  • No instantiates edge for [Class alloc] patterns
  • @protocol Foo <Bar> refinement lists not yet wired to implements edges
  • Heavy C++ in .mm files may parse incompletely under the ObjC grammar

Test plan

  • npm run build
  • npx vitest run __tests__/extraction.test.ts -t "Objective-C" (7 tests)
  • node scripts/add-lang/verify-extraction.mjs on sample + Masonry / FMDB / SDWebImage

@0x1306a94 0x1306a94 force-pushed the feat/objc-language-support branch 2 times, most recently from 74ca84e to ed563b2 Compare May 22, 2026 02:44
@0x1306a94 0x1306a94 changed the title feat(objc): add Objective-C extraction for .m, .mm, and ObjC .h feat(extraction): add Objective-C indexing for .m, .mm, and ObjC headers May 22, 2026
@0x1306a94 0x1306a94 changed the title feat(extraction): add Objective-C indexing for .m, .mm, and ObjC headers feat: add Objective-C language support May 22, 2026
@0x1306a94 0x1306a94 force-pushed the feat/objc-language-support branch 4 times, most recently from a80d5b0 to f26998e Compare May 24, 2026 23:57
Wire tree-sitter-objc for .m/.mm/ObjC headers: classes, protocols,
methods, @Property, #import edges, inheritance, and message-send calls.
@colbymchenry colbymchenry force-pushed the feat/objc-language-support branch from f26998e to af197eb Compare May 26, 2026 05:31
@colbymchenry colbymchenry merged commit 61153f9 into colbymchenry:main May 26, 2026
colbymchenry added a commit that referenced this pull request May 26, 2026
)

Covers #165: tree-sitter-objc extractor for .m / .mm / content-sniffed
.h, with full multi-part selectors, @protocol nodes, @Property, message
expression call edges, extends/implements edges. Validated on
AFNetworking / RestKit / Texture. Disclosed limitations match the
README's 'Partial support' note (categories produce duplicate class
nodes per category file; .mm ObjC++ parses incompletely under the ObjC
grammar; mixed Swift/ObjC bridging out of scope, tracked separately).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request May 26, 2026
…thod definition

#165 extracted multi-part ObjC selectors correctly on the *definition*
side (`GET:parameters:headers:progress:success:failure:` as a method
node name) but the call-site handler in tree-sitter.ts only built the
first selector keyword, so calls never resolved to those definitions.
Verified on AFNetworking: 0 → 84 call edges targeting multi-keyword
methods after the fix.

The grammar emits one `method` field child per keyword on
message_expression nodes; collecting them all and joining with `:`
reconstructs the full selector (matching what extractObjcMethodName
does on the definition side).

Regression test in extraction.test.ts covers
`[d setObject:@"v" forKey:@"k"]` → `d.setObject:forKey:`,
the 3-keyword form, and the self/super-skip case
(`touchesBegan:withEvent:`).

This is a prerequisite for Swift↔ObjC bridging (the bridge rides the
same call-edge path), but stands on its own as a #165 followup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request May 26, 2026
…ging (#430)

Implements the design from `docs/design/mixed-ios-and-react-native-bridging.md`.
Closes the cross-language flow gap so `trace` / `callers` / `callees` / `impact` connect end-to-end across language boundaries in real iOS, React Native, and Expo codebases.

## Bridges shipped

| Boundary | Mechanism | Real-codebase validation |
|---|---|---|
| **Swift ↔ Objective-C** | Resolver applying Apple's @objc auto-bridging name math + Cocoa preposition prefixes | Charts (S, 269) · realm-swift (M, 369) · wikipedia-ios (L, 1734) |
| **React Native legacy bridge** | Resolver parsing `RCT_EXPORT_MODULE` / `RCT_EXPORT_METHOD` / `RCT_REMAP_METHOD` (ObjC) + `@ReactMethod` (Java/Kotlin) | AsyncStorage (S, ~60) · react-native-svg (M, ~700) · react-native-firebase (L, ~1100) |
| **React Native TurboModules** | Resolver treating `Native<X>.ts` spec interface as ground truth | via RNSvg + RNFirebase subsets |
| **Native → JS events** | Synthesizer matching native `sendEventWithName:`/`emit(...)` to JS `addListener('e', handler)` keyed by literal event name; falls back to enclosing constant/variable for wrapper-API parameter handlers | RNGeolocation (S) · RNFirebase (L) |
| **Expo Modules** | Framework extract synthesizes `method` nodes from Swift/Kotlin `Module { Name("X"); Function("y") { ... } }` DSL | expo-haptics (S, 14) · expo-camera (M, 72) · ExpoSweep (L, 332, 7 packages) |
| **Fabric + legacy Paper view components** | Extract `component` + `property` nodes from Codegen `codegenNativeComponent<Props>('Name', ...)` specs AND legacy `RCT_EXPORT_VIEW_PROPERTY` / `@ReactProp` macros, then synthesize component → native class by name+suffix convention | react-native-segmented-control (S, legacy) · react-native-screens (M, Codegen) · react-native-skia (L, hybrid monorepo) |

## Bug fixes surfaced along the way

- `tree-sitter.ts` message_expression — multi-keyword ObjC call sites now reconstruct `a:b:` selectors so they resolve to multi-part method definitions (gap discovered post-#165; 0 → 84 call edges to `GET:parameters:...` style methods on AFNetworking).
- `src/index.ts` resolver lifecycle — `indexAll()` now re-initializes the resolver after extraction so framework `detect()` sees the populated index. Pre-existing latent bug that affected UIKit and SwiftUI resolvers too.
- `src/extraction/index.ts` `buildDetectionContext` — added `listDirectories` so framework detect() can probe monorepo subpackages uniformly (fix needed for react-native-skia detection).

## Regression check on 5 control repos

| Repo | Result |
|---|---|
| Express (small JS) | ✅ unchanged — 266 routes, express framework detected |
| Excalidraw (medium TS/React) | ✅ 9284 nodes (CLAUDE.md baseline ~9290); canonical `trace(mutateElement, renderStaticScene)` returns the flow |
| Django realworld (Python) | ✅ django framework detected, 16 routes |
| Spring petclinic (Java) | ✅ spring framework detected, 17 routes |
| Texture (pure ObjC, large) | ✅ exactly matches #165 baseline: 4702 methods, 894 classes, 808/808 file coverage, 913 multi-keyword selectors, 55 protocols, 1036 properties |

## Tests

928 passing (+87 net new bridge tests across the 5 channels); 2 pre-existing skips. The mcp-staleness-banner / watcher parallel flakiness is unchanged by this work (different test fails each run, all pass in isolation; pre-existing on main).

## Documentation

- README: new 'Mixed iOS / React Native / Expo bridging' section with the per-boundary table and validation-corpus links.
- CHANGELOG `[Unreleased]`: full entry per bridge with measurements.
- `docs/design/mixed-ios-and-react-native-bridging.md`: the design doc (§8 measurements filled in across §8a-§8g).
- `docs/design/dynamic-dispatch-coverage-playbook.md` §6 coverage matrix: six new rows.
- `.claude/skills/agent-eval/corpus.json`: four new sections covering 15 real GitHub repos for the eval harness.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@0x1306a94 0x1306a94 deleted the feat/objc-language-support branch May 26, 2026 07:19
colbymchenry added a commit that referenced this pull request May 26, 2026
…435)

The 0.9.5 release tag included all of:
- Shared MCP daemon (#411)
- Per-file staleness banner (#403)
- Worktree-borrow detection (#312)
- Watcher inotify-budget fix (#276)
- Objective-C indexing (#165)
- Mixed iOS / React Native / Expo cross-language bridging (#430)

But the [0.9.5] block in CHANGELOG.md only had two Fixed entries (the
fs-based change detection and default-ignore set), because the major
feature entries were still sitting under [Unreleased] when 0.9.5 was
tagged. release.yml extracts release notes from the matching version
block, so the published v0.9.5 release notes are missing the bulk of
what shipped.

Move all the [Unreleased] entries that pre-date 0.9.5's tag (commit
318cda1) into [0.9.5], and reset [Unreleased] to empty. The GitHub
Release notes for v0.9.5 get updated separately via gh release edit.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
KannaKuron added a commit to KannaKuron/codegraph4bevy that referenced this pull request May 26, 2026
…, watcher fixes

Key upstream changes merged:
- feat(extraction): Objective-C language support (colbymchenry#165)
- feat(mcp): share one serve --mcp per project across MCP clients (colbymchenry#411)
- feat(mcp): per-file staleness banner + tunable watcher debounce (colbymchenry#403)
- feat(mcp): detect borrowed git worktree index (colbymchenry#312)
- feat(index): default-ignore dependency/build/cache dirs (colbymchenry#407)
- feat(resolution): mixed iOS / React Native / Expo bridging (colbymchenry#430)
- fix(watcher): exclude ignored dirs before watching (colbymchenry#276)
- fix(sync): filesystem-based change detection (colbymchenry#414)

Conflicts resolved: CLAUDE.md, .cursor/rules, extraction tests (kept split),
MCP files (tools/server-instructions/transport/engine), package.json (kept jieba)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants