Skip to content

fix(scala): extract Scala 3 enum definitions, their cases and methods - #3937

Closed
rajatnagda45 wants to merge 1 commit into
Graphify-Labs:v8from
rajatnagda45:fix/scala-enum-definitions
Closed

rajatnagda45 wants to merge 1 commit into
Graphify-Labs:v8from
rajatnagda45:fix/scala-enum-definitions

Conversation

@rajatnagda45

Copy link
Copy Markdown
Contributor

Problem

A Scala 3 enum produces no nodes at all. enum_definition was missing from the Scala class_types, so the enum type, its cases, and any methods declared in its body all vanish from the graph, and a reference to the enum resolves to nothing.

enum Color { case Red, Green, Blue }
enum Op[A] {
  case Add(x: Int, y: Int)
  case Noop
  def run: A = ???
}

Before this change the whole graph for that file is just the file node — Color, Op, every case, and run are all absent. Every other language with enums (Java's enum_constant #1719, Kotlin's enum_entry #1738, Swift, TypeScript) emits a node per member with a case_of edge; Scala was the odd one out.

Implementation

  • Add enum_definition to _SCALA_CONFIG.class_types so the enum gets a node and the class heritage/body machinery runs for it.
  • Add enum_body to _SCALA_CONFIG.body_fallback_child_types — an enum wraps its members in enum_body, not template_body, so the body walk needs it to reach the enum's methods and cases.
  • Add _scala_extra_walk, dispatched for tree_sitter_scala, to emit a node plus a case_of edge per case. Cases nest under an enum_case_definitions wrapper as simple_enum_case (case Red) or full_enum_case (case Add(x: Int)); both carry the case name as an identifier child. This mirrors the existing Java/Kotlin/Swift enum-member handlers.

Verification

  • New regression test test_scala_enum_definition_cases_and_methods — fails on the pre-fix code (no Color/Op/case nodes), passes after.
  • uv run pytest tests/ -q — 6100 passed, 15 skipped.
  • uv run ruff check . — clean.
  • uv run pyright — no new errors introduced (error count unchanged against the pre-existing baseline).

Limitations

Case constructor parameter types (case Add(x: Int)) and per-case extends arguments are not linked; the case node and its case_of edge are the fix for the dropped members.

`enum_definition` was missing from the Scala class_types, so a Scala 3
`enum` produced no nodes at all: the enum type, its cases, and any methods
declared in its body all vanished from the graph, and references to the
enum resolved to nothing.

Add `enum_definition` to the Scala class_types and `enum_body` to the
body fallbacks so the type gets a node and its body is walked, and add
`_scala_extra_walk` to emit a node plus a `case_of` edge per case. Cases
nest under an `enum_case_definitions` wrapper as `simple_enum_case`
(`case Red`) or `full_enum_case` (`case Add(x: Int)`); both carry the case
name as an `identifier` child. This is the Scala parity of Java's
enum_constant (Graphify-Labs#1719), Kotlin's enum_entry (Graphify-Labs#1738), and Swift.

Case constructor parameter types and per-case `extends` arguments are not
linked; the case node and its case_of edge are the fix for the dropped
members.
@github-actions

Copy link
Copy Markdown

Thanks for the pull request, @rajatnagda45. A maintainer will review it soon.

Want to talk it through while it is in review? Come join us on our Discord server. For longer-form discussion there is also GitHub Discussions.

A couple of things that speed up review: make sure the test suite passes on Python 3.10 and 3.13, and that the change keeps extraction deterministic.

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds Scala 3 enum support: enum_definition is now a class-like container, and the body walk reaches enum_body, so enums and their methods show up in the graph instead of disappearing entirely. _scala_extra_walk emits a node for each simple_enum_case and full_enum_case, with a case_of edge back to the enum, matching Java, Kotlin and Swift. Case constructor parameter types and per-case extends aren't linked, and when two cases differ only by letter case, the first declaration keeps the shared id.

No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 2800 functions depend on the 1093 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 719 callers, 48 callees
  • new: _rebuild_code() — 147 callers, 56 callees
  • new: _extract_generic() — 18 callers, 29 callees
  • new: extract_js() — 87 callers, 4 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: main() — 98 callers, 3 callees
  • new: dispatch_command() — 2 callers, 126 callees
  • new: extract_objc() — 27 callers, 9 callees
  • …and 45 more — each is listed as a finding

Verification — 2800 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 2620 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

131 of 308 test file(s) selected (43%) via static blast radius.

  • tests/test_astro_extraction.py — impact
  • tests/test_astro_import_ids.py — impact
  • tests/test_blade_extractor.py — impact
  • tests/test_build.py — impact
  • tests/test_builtin_global_type_refs.py — impact
  • tests/test_case_sensitive_resolution.py — impact
  • tests/test_cjs_module_extension.py — impact
  • tests/test_cobol_extractor.py — impact
  • tests/test_cpp_nested_and_cli.py — impact
  • tests/test_cpp_objc_cross_file_calls.py — impact
  • tests/test_cross_extension_reexport_self_cycle.py — impact
  • tests/test_cross_language_call_resolution.py — impact
  • tests/test_cross_repo_external_call_guards.py — impact
  • tests/test_cross_repo_member_calls.py — impact
  • tests/test_csharp_call_site_generic_args.py — impact
  • tests/test_csharp_enum_members.py — impact
  • tests/test_csharp_field_generic_args.py — impact
  • tests/test_csharp_generic_callsites.py — impact
  • tests/test_csharp_interface_dispatch.py — impact
  • tests/test_csharp_member_calls.py — impact
  • tests/test_csharp_member_nodes.py — impact
  • tests/test_csharp_object_creation.py — impact
  • tests/test_csharp_partial_classes.py — impact
  • tests/test_csharp_tuple_type_refs.py — impact
  • tests/test_csharp_type_resolution.py — impact
  • tests/test_definition_file_portability.py — impact
  • tests/test_detect.py — impact
  • tests/test_dotnet.py — impact
  • tests/test_duplicate_annotation_edges.py — impact
  • tests/test_elixir_import_resolution.py — impact
  • tests/test_erlang_extractor.py — impact
  • tests/test_extract.py — impact
  • tests/test_extract_cache_location.py — impact
  • tests/test_extract_php_closures.py — impact
  • tests/test_file_label_disambiguation.py — impact
  • tests/test_file_node_id_spec.py — impact
  • tests/test_forwarding_review_findings.py — impact
  • tests/test_go_builtin_call_targets.py — impact
  • tests/test_go_import_repoint.py — impact
  • tests/test_go_interface_methods.py — impact
  • tests/test_go_qualified_resolution.py — impact
  • tests/test_import_extension_resolution.py — impact
  • tests/test_import_self_loops.py — impact
  • tests/test_imported_export_forwarding.py — impact
  • tests/test_incremental.py — impact
  • tests/test_indirect_call_arrow_single_param_shadow.py — impact
  • tests/test_indirect_call_block_scoped_shadow.py — impact
  • tests/test_indirect_call_catch_binding_shadow.py — impact
  • tests/test_indirect_call_external_import_shadow.py — impact
  • tests/test_indirect_call_for_of_binding_shadow.py — impact
  • … and 81 more

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: no capturable inputs from the test suite; property tier: parameter `config` is annotated `LanguageConfig` — outside the synthesizable primitive/collection set

· 53 more finding(s) on lines outside this diff (see the check run).

safishamsi added a commit that referenced this pull request Sep 30, 2026
Rust/Zig/C++/Scala enum members as case_of nodes (#3938/#3940/#3939/#3937),
Java inherited + super call resolution (#3932), and a warn-once on
semantic file truncation (#3923).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@safishamsi

Copy link
Copy Markdown
Collaborator

Shipped in v0.9.73 (now on PyPI) via an authorship-preserving cherry-pick, so your commit keeps contributor-graph credit. Thanks @rajatnagda45!

Release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.73

@safishamsi safishamsi closed this Sep 30, 2026
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