Skip to content

Add semantic contexts for Swift, Kotlin, PHP, Rust, and Go - #1015

Merged
safishamsi merged 1 commit into
Graphify-Labs:v8from
TheFedaikin:v8
May 28, 2026
Merged

Add semantic contexts for Swift, Kotlin, PHP, Rust, and Go#1015
safishamsi merged 1 commit into
Graphify-Labs:v8from
TheFedaikin:v8

Conversation

@TheFedaikin

Copy link
Copy Markdown
Contributor

Summary

Second wave of the cross-language semantic extractor (after #996). Brings the same vocabulary to Swift,
Kotlin, PHP, Rust, and Go: normalized type-relationship edges and references edges with
parameter_type / return_type / generic_arg / field / attribute contexts.

Storage is unchanged — graph.json simply carries more source-backed edges.

What changed

  • Swift. Pre-scan protocol_declaration vs class/struct/enum/actor declarations to classify each
    inheritance_specifier as inherits (class supertype) or implements (protocol / extension
    conformance). protocol P: Q emits inherits. Method parameter and return_type, plus
    property_declaration annotations, emit references with the standard contexts.
  • Kotlin. Split delegation_specifier into inherits (constructor_invocation) vs implements
    (plain user_type). Function function_value_parameters and post-colon return types emit references;
    property_declaration types emit field.
  • PHP. base_clauseinherits, class_interface_clauseimplements, use Trait
    mixes_in. Typed properties, simple parameters, and post-) return types emit references. Also fixes
    the event-listener property_declaration handler to only short-circuit when it actually matched a
    $listen / $subscribe property — non-listener typed properties were previously dropped.
  • Rust. impl Trait for T emits T implements Trait. Supertraits on trait_item via
    trait_bounds emit inherits. Struct field_declaration types emit references field;
    function_item parameters and return types emit parameter_type / return_type, with generic
    arguments emitted as generic_arg.
  • Go. Struct field_declaration without a field_identifier (the Go embedded-field syntax) emits
    embeds; named fields emit references field. Interface type_elem entries emit embeds (interface
    embedding). method_declaration / function_declaration parameters and result (handling both
    single-type and parameter_list forms) emit parameter_type / return_type.

Fixture changes

  • tests/fixtures/sample.swift: adds BaseProcessor class, Result<T>, a current: Result<DataProcessor> property, and a run(input: DataProcessor) -> Result<DataProcessor> method.
  • tests/fixtures/sample.kt: adds Loggable interface, BaseProcessor open class, Result<T>, a
    DataProcessor : BaseProcessor(), Loggable that overrides log(), a current: Result<DataProcessor>
    property, and a run method.
  • tests/fixtures/sample.php: adds Loggable interface, HasName trait, BaseProcessor, Result,
    and a DataProcessor extends BaseProcessor implements Loggable with use HasName;, a private Result $current; field, and a run(DataProcessor $input): Result method.
  • tests/fixtures/sample.rs: adds Processor / Logger: Processor traits, Result<T>, a
    DataProcessor struct with a Result<DataProcessor> field, an impl Processor for DataProcessor, and
    a build(input: DataProcessor) -> Result<DataProcessor> method.
  • tests/fixtures/sample.go: adds Logger / Reader interfaces, a ReaderLogger that embeds both,
    BaseProcessor, Result, and a DataProcessor that embeds BaseProcessor with a *Result field plus
    a Build(input *DataProcessor) (*Result, error) method.

These fixture additions keep all pre-existing per-language assertions intact. The Swift
test_swift_conformance_edge / test_swift_extension_conformance_edge tests are renamed to
test_swift_protocol_conformance_emits_implements / test_swift_extension_conformance_emits_implements
and now assert implements to match the new split.

Tests

Full pytest suite: 1294 passed, 11 skipped.

New tests:

  • test_swift_protocol_conformance_emits_implements
  • test_swift_extension_conformance_emits_implements
  • test_swift_splits_inherits_and_implements
  • test_swift_parameter_return_generic_and_field_contexts
  • test_kotlin_splits_inherits_and_implements
  • test_kotlin_parameter_return_generic_and_field_contexts
  • test_php_splits_inherits_implements_mixes_in
  • test_php_property_parameter_and_return_contexts
  • test_rust_trait_impl_emits_implements
  • test_rust_supertrait_emits_inherits
  • test_rust_struct_field_emits_field_context
  • test_rust_method_parameter_return_and_generic_contexts
  • test_go_embeds_struct_field
  • test_go_interface_embedding_emits_embeds
  • test_go_struct_named_field_emits_field_context
  • test_go_method_parameter_return_contexts

Compatibility note

Swift inherits edges for protocol conformance are now implements. This matches the Java migration
applied in #996 and the C# split that also landed in PR 1 — protocols / interfaces / traits are
uniformly modeled as implements, class supertyping as inherits. Existing call/contains/method/import
edges and node IDs are unchanged.

Non-goals

  • PR 3 (ObjC, Julia, C/C++, Scala, Dart, PowerShell, Fortran, SystemVerilog) is the planned follow-up.

@safishamsi safishamsi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR — the design is solid and the test coverage is strong across all five languages. One issue before merge: there's a latent crash in the Go method_declaration block where emit_go_method_refs(...) appears to be outside the if name_node: guard. If name_node is None, method_nid will be undefined and you'll get an UnboundLocalError. Tests pass currently only because fixtures always have a name node. Could you fix the indentation and open a fresh PR against v8?

@safishamsi

Copy link
Copy Markdown
Collaborator

Thanks for the PR — the design is solid and the test coverage is strong across all five languages. One issue before merge: there's a latent crash in the Go method_declaration block where emit_go_method_refs(...) appears to be outside the if name_node: guard. If name_node is None, method_nid will be undefined and you'll get an UnboundLocalError. Tests pass currently only because fixtures always have a name node. Could you fix the indentation and open a fresh PR against v8?

@TheFedaikin

Copy link
Copy Markdown
Contributor Author

@safishamsi I redid the test and indent looked right. Re-pushed the changes with a proof. I think it's getting really unwieldy with the current line-count for both manual and AI-driven reviews. Can I do some refactoring of the methods so they are more sustainable in this? I was hesitant of doing this in the middle of adding the functionality to unblock some of the stuff I needed from graphify but I get a formal approval I can be more confident with the time invested so it benefits all of the contributors.

@TheFedaikin
TheFedaikin requested a review from safishamsi May 26, 2026 11:16
@safishamsi

Copy link
Copy Markdown
Collaborator

Fetched the branch and checked line 4719 directly:

```
4716: method_nid = _make_id(stem, method_name) # 20 spaces — inside if name_node:
4717: add_node(method_nid, ...) # 20 spaces
4718: add_edge(file_nid, method_nid, ...) # 20 spaces
4719: emit_go_method_refs(node, method_nid, line) # 16 spaces — still OUTSIDE
```

The bug is still there. The structural test missed it because it inspects the AST of the source file but the reference to method_nid at line 4719 reads as an undefined name when name_node is None — the AST-level checker didn't catch the indent difference.

The fix is a single 4-space indent on line 4719:

```python
emit_go_method_refs(node, method_nid, line) # move inside if name_node:
```

On the refactoring question — once this is fixed and merged, a follow-up PR for internal refactoring of the Go extractor methods is welcome, just keep it separate from the new functionality.

@TheFedaikin

TheFedaikin commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

When I changed the indent like you mentioned I saw the errors like so:

  ⎿    ✘ [Line 3875:16] Import "tree_sitter_sql" could not be resolved [reportMissingImports]
     (Pyright)
  ⎿    ✘ [Line 7799:16] Import "tree_sitter_pascal" could not be resolved
     [reportMissingImports] (Pyright)
  ⎿    ✘ [Line 3975:45] "lower" is not a known attribute of "None" [reportOptionalMemberAccess]
      (Pyright)
  ⎿    ✘ [Line 3976:71] "lower" is not a known attribute of "None" [reportOptionalMemberAccess]
      (Pyright)
  ⎿    ✘ [Line 3978:60] "lower" is not a known attribute of "None" [reportOptionalMemberAccess]
      (Pyright)
  ⎿    ✘ [Line 3976:98] Argument of type "str | None" cannot be assigned to parameter "parts"
     of type "str" in function "_make_id"
       Type "str | None" is not assignable to type "str"
         "None" is not assignable to "str" [reportArgumentType] (Pyright)
  ⎿    ✘ [Line 4352:22] Cannot access attribute "type" for class "object"
       Attribute "type" is unknown [reportAttributeAccessIssue] (Pyright)
  ⎿    ✘ [Line 4353:36] Cannot access attribute "children" for class "object"
       Attribute "children" is unknown [reportAttributeAccessIssue] (Pyright)
  ⎿    ✘ [Line 4559:32] Cannot access attribute "children" for class "object"
       Attribute "children" is unknown [reportAttributeAccessIssue] (Pyright)
  ⎿    ✘ [Line 6291:21] Cannot access attribute "type" for class "object"
       Attribute "type" is unknown [reportAttributeAccessIssue] (Pyright)

So it's definitely wasn't a fix here.
I think reading this was a chore so I refactored it a little - current change reads better and should be correct. Feel free to re-review!

@safishamsi
safishamsi merged commit 32aa053 into Graphify-Labs:v8 May 28, 2026
2 checks passed
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