Skip to content

fix(julia): port abstract-def / macro-def / signature-call WASM bugs#1130

Open
carlos-alm wants to merge 3 commits into
fix/1111-julia-wasm-extractor-bugsfrom
fix/1126-julia-wasm-extractor-bugs
Open

fix(julia): port abstract-def / macro-def / signature-call WASM bugs#1130
carlos-alm wants to merge 3 commits into
fix/1111-julia-wasm-extractor-bugsfrom
fix/1126-julia-wasm-extractor-bugs

Conversation

@carlos-alm
Copy link
Copy Markdown
Contributor

Summary

Ports three additional Julia WASM extractor bugs that diverged from the native Rust extractor. Surfaced while reviewing #1111 (#1128); filed and fixed separately per scope discipline.

  • handleAbstractDeffindChild(node, 'identifier') only inspects direct children of abstract_definition, but tree-sitter-julia nests the name inside type_head. Result: no abstract type was ever recorded on the WASM side. Falls back to findBaseName(typeHead) like the native code.
  • handleMacroDeffindChild(node, 'identifier') was resolving to the body's first identifier instead of the macro name (e.g. macro mymac(x) x end recorded @x instead of @mymac). Now unwraps via signatureCall to reach the signature's call_expression name.
  • handleCall — the guard parent.type === 'function_definition' never matched: the signature's call_expression is parented by signature, whose own parent is the function/macro definition. Result: every long-form function greet(...) ... end was recording greet as both a definition and a call. Now mirrors the native walk: skip when parent is signature and grandparent is function_definition or macro_definition.

Test plan

Added WASM tests mirroring the native ones — each fails on main's WASM extractor and passes after the fix:

  • extracts abstract typeabstract type AbstractShape end
  • extracts parameterized abstract type base nameabstract type AbstractVector{T} <: AbstractArray{T,1} end
  • extracts macro definitions with correct name — guards against @x regression
  • does not record function signature as call — guards against duplicate-call regression
  • All 14 tests in tests/parsers/julia.test.ts pass

Notes

Stacked on top of #1128 (which adds the signatureCall and findBaseName helpers this PR reuses). Will retarget to main once #1128 merges.

Closes #1126

The WASM Julia extractor diverged from the native Rust extractor in three
ways that no existing WASM fixture exercised:

- handleAbstractDef: `findChild(node, 'identifier')` only looks at direct
  children of `abstract_definition`, but tree-sitter-julia nests the
  identifier inside `type_head`. Result: no abstract type was ever
  recorded. Fall back to `findBaseName(typeHead)` like the native code.
- handleMacroDef: `findChild(node, 'identifier')` resolves to the body's
  first identifier rather than the macro name (e.g. `macro mymac(x) x end`
  recorded `@x` instead of `@mymac`). Unwrap via `signatureCall` to reach
  the call_expression name.
- handleCall: the guard `parent.type === 'function_definition'` never
  matched — the signature's call_expression is parented by `signature`,
  whose own parent is the function/macro definition. Result: every
  long-form `function greet(...) ... end` recorded `greet` as both a
  definition and a call. Match the native walk: skip when parent is
  `signature` and grandparent is `function_definition` or
  `macro_definition`.

Adds WASM tests mirroring the native cases: extracts_abstract_type,
extracts_parameterized_abstract_type_base_name, extracts_macro_def, and
does_not_record_function_signature_as_call.

Closes #1126
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 15, 2026

Greptile Summary

Ports three WASM-specific bugs in the Julia extractor that caused divergence from the native Rust extractor: abstract type names were never emitted, macro definitions were recording the wrong name, and function signatures were double-recorded as calls.

  • handleAbstractDef: Replaces the broken childForFieldName('name') || findChild(node, 'identifier') lookup with findChild(node, 'type_head') + findBaseName(), mirroring handleStructDef and correctly handling parameterized/supered abstract types.
  • handleMacroDef: Unwraps the macro name via signatureCall(node)?.child(0) instead of a raw findChild(node, 'identifier') that resolved to the body's first identifier.
  • handleCall: Replaces the dead parent.type === 'function_definition' guard with a two-level check (parent.type === 'signature' + grandparent.type ∈ {function_definition, macro_definition}), matching the actual tree-sitter-julia grammar shape and preventing both functions and macros from being double-counted as calls.

Confidence Score: 5/5

Safe to merge — all three fixes are narrow, well-tested corrections to WASM extractor paths that were already broken.

Each fix targets a clearly-documented regression, mirrors the already-working native extractor logic, and is guarded by a dedicated regression test that fails on the old code and passes after the fix. No existing test coverage was removed or weakened, and the changes are isolated to the Julia extractor.

No files require special attention.

Important Files Changed

Filename Overview
src/extractors/julia.ts Three targeted bug fixes to handleAbstractDef, handleMacroDef, and handleCall, all closely mirroring the native extractor logic. Implementation is sound and well-commented.
tests/parsers/julia.test.ts Four new regression tests added, each pinned to the exact wrong output the old code produced. Coverage is focused and sufficient.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[walkJuliaNode] --> B{node.type}

    B -->|abstract_definition| C[handleAbstractDef]
    C --> C1[findChild: type_head]
    C1 -->|null| C2[return — skip]
    C1 -->|found| C3[findBaseName typeHead]
    C3 -->|null| C2
    C3 -->|identifier node| C4[ctx.definitions.push kind=type]

    B -->|macro_definition| D[handleMacroDef]
    D --> D1[signatureCall node]
    D1 -->|found| D2[callSig.child 0 = name identifier]
    D1 -->|null| D3[fallback: childForFieldName OR findChild identifier]
    D2 --> D4[ctx.definitions.push @name]
    D3 --> D4

    B -->|call_expression| E[handleCall]
    E --> E1{parent.type === signature?}
    E1 -->|yes| E2{grandparent.type?}
    E2 -->|function_definition or macro_definition| E3[return — skip signature]
    E2 -->|other| E4[record as call]
    E1 -->|no| E5{parent = assignment LHS?}
    E5 -->|yes| E3
    E5 -->|no| E4
Loading

Fix All in Claude Code

Reviews (3): Last reviewed commit: "fix(julia): align abstract-def name reso..." | Re-trigger Greptile

Comment thread src/extractors/julia.ts Outdated
Comment on lines +276 to +282
let nameNode = node.childForFieldName('name') || findChild(node, 'identifier');
if (!nameNode) {
const typeHead = findChild(node, 'type_head');
if (!typeHead) return;
nameNode = findBaseName(typeHead);
if (!nameNode) return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The legacy childForFieldName('name') || findChild(node, 'identifier') path is kept as the primary lookup before the type_head fallback. handleStructDef (which handles the identical type_head grammar shape) skips this step entirely and goes straight to findChild(node, 'type_head') + findBaseName. If a grammar revision ever makes childForFieldName('name') return the full type_head node rather than null, nameNode.text would emit the raw AbstractVector{T} <: AbstractArray{T,1} string and the parameterized-abstract-type test would silently break. Aligning with handleStructDef removes that fragility.

Suggested change
let nameNode = node.childForFieldName('name') || findChild(node, 'identifier');
if (!nameNode) {
const typeHead = findChild(node, 'type_head');
if (!typeHead) return;
nameNode = findBaseName(typeHead);
if (!nameNode) return;
}
const typeHead = findChild(node, 'type_head');
if (!typeHead) return;
const nameNode = findBaseName(typeHead);
if (!nameNode) return;

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — dropped the legacy childForFieldName('name') lookup and aligned with handleStructDef's type_head + findBaseName pattern.

@carlos-alm
Copy link
Copy Markdown
Contributor Author

@greptileai

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

Codegraph Impact Analysis

3 functions changed2 callers affected across 1 files

  • handleAbstractDef in src/extractors/julia.ts:284 (2 transitive callers)
  • handleMacroDef in src/extractors/julia.ts:303 (2 transitive callers)
  • handleCall in src/extractors/julia.ts:374 (2 transitive callers)

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.

1 participant