Skip to content

Commit b5ff3f9

Browse files
authored
feat(native): port Objective-C extractor to Rust (#1106)
* feat(native): port Objective-C extractor to Rust Mirrors `src/extractors/objc.ts` in `crates/codegraph-core/src/extractors/objc.rs`. Adds the `tree-sitter-objc` dependency, wires `LanguageKind::ObjC` (`.m`) in the Rust `parser_registry` and `file_collector`, adds `.m` to `NATIVE_SUPPORTED_EXTENSIONS` on the JS side, and registers `OBJC_AST_TYPES` / `OBJC_AST_CONFIG` so the native and WASM engines extract identical `ast_nodes` for Objective-C source. Handles class interfaces / implementations (with `: Superclass`), protocols, instance and class method declarations/definitions with multi-part selectors assembled from leading identifiers and `method_parameter` children, C-level function declarations/definitions, `#import`/`@import` imports, and message expression call extraction. * fix(extractors): address ObjC review feedback - Use `module` field (not `path`) for `@import` in Rust to mirror the JS extractor and match the tree-sitter-objc `module_import` grammar field. - Drop the unreachable `implementation_definition` branch from `collect_class_members` — it is only invoked from `handle_class_interface` and `class_interface` nodes do not contain `implementation_definition` children. - Qualify category methods with `(Category)` in the JS extractor so its output matches Rust for `@interface Foo (Cat)` / `@implementation Foo (Cat)` when the grammar emits `class_interface`/`class_implementation` rather than dedicated `category_interface` nodes. Two categories can declare same-named methods, so the qualified parent disambiguates the symbols. - Document the `.m` extension collision with MATLAB/Octave in the file collector since `.m` files are unconditionally routed to the ObjC parser. * fix: correct expected count after merge dropped two entries (#1106) The native-drop-classification test asserts the count of WASM-only languages. The Clojure PR (#1097) removed src/c.clj and decremented the count from 11 to 10. This PR removes src/k.m (now natively supported via ObjC). After the merge both entries are removed, so the assertion needs to be 9, not 10. * fix(extractors): align JS ObjC engine with native for @import and calls Three engine-parity gaps Greptile flagged on the WASM side that broke the "identical output" goal: - @import statements: tree-sitter-objc v3 emits `module_import` not `import_declaration`, so the JS dispatch arm never matched and every `@import Foundation;` was silently dropped. Accept both node types. - C-style calls (printf, CGContextFillRect, …): the grammar lacks a `function` field on `call_expression`, so the named-field lookup always misses. Rust falls back to the first identifier child; JS did not, so every C call was dropped. Add the same fallback. - Message expressions: the grammar tags each keyword identifier with the `method` field rather than exposing a `selector` field, so the JS selector lookup misfired for multi-keyword selectors. Assemble the selector from `method` children with `:` joining, matching `build_message_selector` in the Rust extractor. Also expose `fieldNameForChild` on the `TreeSitterNode` type and add three JS extractor tests covering the new parity behaviour. * fix: bump EXPECTED_LEN after merging Solidity LanguageKind (#1106) * fix(extractors): align JS ObjC engine with native for v3 grammar (#1106) The v3 tree-sitter-objc grammar emits flat 'identifier' + 'method_parameter' children directly under method nodes (no 'keyword_selector' wrapper) and nests property names under 'struct_declaration > struct_declarator > [pointer_declarator >] identifier' rather than exposing a 'name' field. The JS extractor was still following the old grammar shape, which silently dropped multi-keyword method definitions, never populated parameter children, and omitted all '@Property' members from class children. - buildSelector: assemble selector from flat 'identifier'+'method_parameter' children directly under the method node, matching build_selector in crates/codegraph-core/src/extractors/objc.rs. - extractMethodParams: iterate 'method_parameter' children directly and use the last 'identifier' child as the parameter name (mirrors extract_method_params in the Rust extractor). - collectClassMembers: extract '@Property' names via a deep identifier walk under 'struct_declaration > struct_declarator' (mirrors extract_property_name in the Rust extractor). Added three regression tests covering keyword method definitions with parameters, and pointer + non-pointer '@Property' member names. * fix(extractors): handle parameterized_arguments for ObjC protocol adoption (#1106) The JS extractor was looking up 'protocol_qualifiers' which doesn't exist in tree-sitter-objc v3 (the grammar wraps adopted protocols in 'parameterized_arguments' instead). As a result every '@interface Foo : NSObject <Bar, Baz>' silently dropped its 'implements' relations on the JS side, while the Rust extractor correctly extracted them. Mirror handle_class_interface in crates/codegraph-core/src/extractors/objc.rs (parameterized_arguments + the type_name > type_identifier nesting) and add a regression test.
1 parent 06b6536 commit b5ff3f9

13 files changed

Lines changed: 1061 additions & 53 deletions

File tree

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/codegraph-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ tree-sitter-dart = "0.0.4"
3636
tree-sitter-zig = "1"
3737
tree-sitter-haskell = "0.23"
3838
tree-sitter-ocaml = "0.24"
39+
tree-sitter-objc = "3"
3940
tree-sitter-gleam = "1"
4041
tree-sitter-julia = "0.23"
4142
tree-sitter-clojure-orchard = "0.2"

crates/codegraph-core/src/extractors/helpers.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,19 @@ pub const OCAML_AST_CONFIG: LangAstConfig = LangAstConfig {
374374
string_prefixes: &[],
375375
};
376376

377+
/// Objective-C string literals use the `@"..."` prefix. The shared
378+
/// `build_string_node` strips a leading `@` before applying prefixes, so we
379+
/// don't need to list it explicitly here.
380+
pub const OBJC_AST_CONFIG: LangAstConfig = LangAstConfig {
381+
new_types: &[],
382+
throw_types: &["throw_statement"],
383+
await_types: &[],
384+
string_types: &["string_literal"],
385+
regex_types: &[],
386+
quote_chars: &['"'],
387+
string_prefixes: &[],
388+
};
389+
377390
pub const GLEAM_AST_CONFIG: LangAstConfig = LangAstConfig {
378391
new_types: &[],
379392
throw_types: &[],

crates/codegraph-core/src/extractors/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod javascript;
1818
pub mod julia;
1919
pub mod kotlin;
2020
pub mod lua;
21+
pub mod objc;
2122
pub mod ocaml;
2223
pub mod php;
2324
pub mod python;
@@ -137,6 +138,9 @@ pub fn extract_symbols_with_opts(
137138
LanguageKind::Ocaml | LanguageKind::OcamlInterface => {
138139
ocaml::OcamlExtractor.extract_with_opts(tree, source, file_path, include_ast_nodes)
139140
}
141+
LanguageKind::ObjC => {
142+
objc::ObjCExtractor.extract_with_opts(tree, source, file_path, include_ast_nodes)
143+
}
140144
LanguageKind::Gleam => {
141145
gleam::GleamExtractor.extract_with_opts(tree, source, file_path, include_ast_nodes)
142146
}

0 commit comments

Comments
 (0)