Commit Graph
134 Commits
Author SHA1 Message Date
Fabio Rueda 364b258749 fix(powershell): dot-source quoted/$PSScriptRoot paths and script-scope calls (#4356)
Two gaps in the PowerShell extractor dropped edges from real-world
scripts:

- Dot-source only read the path when tree-sitter parsed it as
  command_name. `. "$PSScriptRoot\lib.ps1"` and `. '.\lib.ps1'` parse as
  string_literal, and unquoted `. $PSScriptRoot\lib.ps1` as
  path_command_name, so no imports_from edge was emitted. Accept all
  three and strip surrounding quotes; the existing basename logic
  already handles the rest.
- Calls were collected only from function/method bodies, so commands at
  script scope (the main body of most .ps1 scripts) produced no calls
  edge or raw_call. Walk the file root too, with the file node as the
  caller. walk_calls stops at function_statement/class_statement, so
  function bodies are not walked twice (covered by a control test).

`. (Join-Path $PSScriptRoot 'lib.ps1')` stays unresolved: the path is
only known at runtime.

AST cache schema 8 -> 9 so cached results for unchanged .ps1 files are
re-extracted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LcXGsdFdpLjYVHemLogkE
(cherry picked from commit 69e20c70e3)
2026-10-12 22:38:11 +01:00
ayarodionovandClaude Opus 5.5 2fc8e9c94a fix(julia): resolve module-qualified functor types, never fall back to the variable
The functor branch picked the type from the typed expression's identifier and
parametrized-type children. A module-qualified type (`Mod.Box`) is a
`field_expression`, so `(b::Mod.Box)(x)` fell back to the variable `b`, and
`(::Mod.Box)(x)` / `(b::Mod.Box{T})(x)` were dropped. Read the last child (the
type) through a new `_functor_type_name`, which unwraps parametric and
qualified names and returns None for anything else.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:01:09 +01:00
ayarodionovandClaude Opus 5.5 fac1491665 fix(julia): name parametric functors by their type, not the variable
`(b::Box{T})(x)` puts the type in a `parametrized_type_expression`, so taking
the last identifier in the typed expression picked the variable `b` and
emitted a bogus `b#call` node, split from `Box#call`. Read the last operand
through `_type_name_of`, which already unwraps parametric heads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:01:09 +01:00
ayarodionov e1892efd29 fix(julia): extract return-typed, qualified, operator and functor methods; broadcast/pipe calls
- Unwrap `::RetType` (alongside `where`) around a definition's call head, so
  `function f(x)::Float64` is no longer dropped.
- Name method definitions via `_def_name`: `Base.show`, `Base.:+`, bare
  operators, and functors `(c::Circle)(x)`. The id uses the bare name so all
  methods of a generic function share one node; the label keeps the qualifier.
- Emit `calls` edges for broadcast calls (`f.(xs)`) and pipes (`x |> f`, `.|>`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit cf3648e762)
2026-10-11 20:47:44 +01:00
rajatnagda45 b28c2c5368 fix(fortran): extract named generic interfaces as callable nodes
A named generic interface (`interface area; module procedure area_circle,
area_square; end interface`) names a callable that dispatches to its
specific procedures by argument type. The interface block was never turned
into a node, so a call to the generic name dangled — a Fortran function
call_expression only resolves to an in-file node — and the dispatch set
was invisible, even though the specific procedures were extracted. Generic
interfaces (and operator overload sets) are routine in scientific Fortran.

Emit a node for the generic name, link it to the procedures it groups with
`dispatches_to` edges (resolved after the walk, like type-bound
procedures, so a procedure from another module becomes a collapsible
sourceless stub), and the existing call resolution then binds `area(x)`
to it. Unnamed abstract/explicit interfaces keep recursing so their
procedure declarations are still extracted.

(cherry picked from commit ccfb4018fb)
2026-10-10 22:10:52 +01:00
safishamsiandClaude Opus 4.8 a009ce7131 revert(fortran): drop re-implementation of #4141 in favor of PR #4142
My #4141 fix used a one-off calls/generic_dispatch relation; PR #4142
(@rajatnagda45) implements the same fix with the codebase-standard
dispatches_to relation and earns author-preserving credit. Reverting the
Fortran hunk here so #4142 can be cherry-picked; the Julia fixes from the
same original commit are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-10 22:10:34 +01:00
safishamsiandClaude Opus 4.8 a041facc84 fix(julia,fortran): extract where-clause funcs, parametric types, and named generic interfaces (#4126, #4128, #4141)
- Julia: a `where` clause wraps the call head in a `where_expression` and a
  parametric type name sits inside a `parametrized_type_expression`, so the
  name-lookup helpers missed both and dropped the method/type entirely. Unwrap
  the wrapper nodes in `_func_name_from_signature`, the short-function assignment
  branch, and `_type_head_names`. Declared type parameters (`value::T`) no longer
  leak as phantom field-type reference nodes (#4126, #4128).
- Fortran: a named generic `interface` had no walk branch, so the generic name
  was dropped and a call to it dangled. Emit a node for the generic name and
  defer-resolve a `generic_dispatch` edge to each listed specific procedure,
  mirroring the type-bound-procedure pattern; unnamed/operator interfaces are
  untouched and their bodies still extracted (#4141).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-10 21:20:29 +01:00
safishamsiandClaude Opus 4.8 da287edfb5 fix(powershell): extract functions inside file-scope begin/process/end blocks (#4270)
File-scope begin/process/end/dynamicparam/clean parse as pseudo-commands
(command_name 'begin'), not named_block, so the command branch returned before
the child walk and any function/class defined inside was dropped. Descend into
those five block keywords' children; ids stay name-based and stable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-10 01:01:10 +01:00
SoloDrex52andClaude Opus 4.8 f949947003 fix: make the test suite pass on Windows (#4260)
Refuse a read-only destination in _atomic_replace on Windows (nt-only guard;
os.replace there raises PermissionError and the fallback would silently clobber
it), plus test-only guards: utf-8 encoding kwargs, posix-shape path assertions,
FIFO/AF_UNIX/symlink skips, and a replace-retry for AV/indexer locks. No POSIX
behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 19:18:06 +01:00
Yyunozor fee41a65b5 fix(markdown): resolve links to file names that contain a space
CommonMark writes a link destination with a space either in angle
brackets ([x](<My Note.md>)) or percent-encoded ([x](My%20Note.md)).
The inline and reference-definition regexes stopped the capture at the
first space inside <...>, and the destination was never decoded, so
both forms kept an id derived from a nonexistent path and the
references edge dropped at build.

Capture the whole bracketed destination in both regexes; an unclosed <
falls back to the bare form, so it does not swallow the links after it.
Percent-decode inline and reference destinations, keep a file whose name
literally contains the escape (with or without the .md completion), and
re-run the external-link guard on the decoded form. Wikilinks stay
verbatim.

Assisted-by: assayer harness on Claude Code (https://github.com/Yyunozor/assayer-memory-mcp)
(cherry picked from commit 49ba8aafc0)
2026-10-07 17:26:00 +01:00
oleksii-tumanovandClaude Opus 4.8 84d0ec76d9 fix(powershell): resolve member calls by receiver owner (#4163)
Replaces the name-only member-call resolver with receiver-proven binding:
$this.M() binds to the enclosing class only when its name is unique, and
[Type]::M() only to a single unambiguous local class; value/cast/dynamic/
duplicate/external receivers fall through to raw_calls. Fixes the same-name-
method caveat from the 0.9.77 member-call work (#3992).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-06 20:54:25 +01:00
rohit-jsfreaky e82d97d9e6 fix(dm): key .dmf element ids on the window name, not its node id
extract_dmf minted each element id from its window's node id, which
already embeds the file stem. The stem is absolute at extraction time and
extract()'s id remap only rewrites the leading copy, so the nested copy
kept the checkout path and the OS username inside every element id.

Use the window name instead. Element ids no longer depend on where the
repo is checked out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
(cherry picked from commit 3d9021fab7)
2026-10-06 20:54:15 +01:00
rajatnagda45 76576865b6 fix(powershell): record method calls ($this.M(), [Type]::M())
The call walker only visited `command` nodes, so every method invocation
was dropped from the call graph. A method call parses as an
invokation_expression (callee in the member_name child, args in
argument_list); a bare property read ($this.Name) is a separate
member_access node and correctly produces no call.

Handle invokation_expression and resolve the member name against a
method-only label map so a member call binds to a method, never to a free
function that merely shares the name (fail-closed on unknown receivers).

Fixes #3992

(cherry picked from commit 1e6a9029e4)
2026-10-05 20:11:30 +01:00
CLOCKZW 438a050e08 test(elixir): assert defguardp private guards are extracted
#4027 taught the Elixir extractor defmacro, defmacrop, defguard and
defguardp, but the regression test only exercised the first three. Add a
defguardp clause to the fixture and assert its node is extracted, so
dropping defguardp from the extractor's keyword list fails this test.

Closes #4076

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 345b66b01f)
2026-10-05 19:40:04 +01:00
Jarvis-J-Jacob 9f6c0b9721 test(zig): assert a tagged-union nested-struct payload's fields are not minted
#4050 emits a node + case_of edge per `union(enum)` variant but
deliberately does not recurse into a variant's nested-struct payload.
Extend the tagged-union test to pin that: `rectangle: struct { w, h }`
is minted as a variant, while its `w`/`h` fields are not, and
`rectangle` owns no case_of edges of its own.

Fixes #4074

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 994bb2e534)
2026-10-05 19:40:04 +01:00
rajatnagda45 282e2bbc56 fix(powershell): link enum members with case_of instead of contains
A PowerShell enum member is a discriminant case, not a declared field, so it
should hang off its enum with a case_of edge like every other language that
has enums (Java #1719, C#, Swift, Rust, VB.NET). PowerShell was routing enum
members through the same contains path used for class members.

The relation is not cosmetic: case_of targets are excluded from constructor
binding, so an enum member named like a type could previously be mistaken for
one.

Update the existing enum test to expect case_of and add a focused regression
test asserting enum members get case_of (and not contains) while the file
still contains the enum type via contains.

(cherry picked from commit 011dda9b0f)
2026-10-05 19:40:03 +01:00
rajatnagda45 11736158a4 fix(zig): emit a node per tagged-union variant with a case_of edge
(cherry picked from commit 1ed509f4d3)
2026-10-04 20:23:13 +01:00
rajatnagda45 23d3a2af58 fix(elixir): extract defmacro and defguard definitions
defmacro/defmacrop (macros) and defguard/defguardp (guard macros) define
named, invocable members with the same head shape as def/defp — a call
head, optionally wrapped in a when binary_operator. Only def/defp were
handled, so these fell through to the generic recursion and were dropped
entirely: the member was never a node, and a call to a locally-defined
macro had nothing to resolve to.

Handle them in the same branch as def/defp. They are already listed in
the call-pass skip keywords, so their defining keyword is never mistaken
for a call.

Adds a regression test proving the macro, private macro, and guard were
previously dropped and that a call to a local macro now resolves.

(cherry picked from commit 237d358e39)
2026-10-04 01:43:04 +01:00
rajatnagda45 648b4d9d03 fix(php): extract enum cases with case_of edges
A PHP 8.1 `enum` is in the PHP class_types, so the enum type and its
methods were captured, but the cases (`case Hearts = 'H';`) nest in the
`enum_declaration_list` body as `enum_case` nodes that no handler
visited. They were dropped, leaving the enum a caseless leaf.

Add a PHP extra-walk that emits a node plus a case_of edge per case,
keying the name off the case's `name` field. This brings PHP to parity
with Java #1719 (enum_constant), Scala, Swift, and C++. Backed enums
(`: string`) and pure enums are both handled, and enum methods are
untouched.

Adds a regression test proving the cases were previously dropped.

(cherry picked from commit 3a9d305277)
2026-10-04 01:43:04 +01:00
rajatnagda45 ff53a2cb3d fix(zig): extract error set declarations and their members
A Zig error set (`const E = error{ A, B };`) parses as a
variable_declaration whose value is an error_set_declaration. That value
node type was not among the recognised forms, so the declaration fell
through the variable_declaration handler and both the error type and its
members were dropped entirely.

An error set is a named enumeration of error values, the direct parallel
of a Zig enum. Recognise error_set_declaration and emit the type node
plus a node and a case_of edge per member identifier, matching the
existing enum-member handling. Struct and union fields are untouched.

Adds a regression test proving the whole declaration was previously
dropped.

(cherry picked from commit 3e7f4a5905)
2026-10-04 01:43:04 +01:00
Alex Tumanov 4315c604d7 feat(extract): include Verilog header files
Register .vh with the existing Verilog detection and extraction paths,
including incremental and watch coverage.

Refs #3749

AI-Assisted-By: OpenAI Codex
(cherry picked from commit 3b74b8ad3d)
2026-10-02 17:10:08 +01:00
Yyunozor 038954a2e7 fix(markdown): resolve wikilinks to note names that contain a dot (#3904)
A wikilink such as [[note.en]], [[v1.2 release]] or [[Mr. Smith]] was
dropped: Path("note.en").suffix is ".en", so the target skipped the
extension-less .md completion and was rejected as a non-document link,
although note.en.md exists and is a node. The docstring already promised
that extension-less wikilink targets resolve to sibling .md.

For wikilinks only, a target whose suffix is not a document extension is
now retried as <name>.md through the same sibling-then-vault resolution,
and kept only when that document exists. A literal target that graphify
indexes itself keeps precedence: [[pic.png]] beside pic.png and
pic.png.md still yields no edge. [[missing.en]] and inline/reference
links keep their current behavior.

Assisted-by: assayer harness on Claude Code (https://github.com/Yyunozor/assayer-memory-mcp)
(cherry picked from commit 3d281a674a)
2026-10-02 17:10:08 +01:00
rajatnagda45 9e291b2c50 fix(cpp): treat a member-function declaration as a method, not a field
Inside a class body a `virtual double area() = 0;` (pure virtual), a
`virtual void draw();` and a plain prototype `int run();` parse as a
`field_declaration` whose declarator is a `function_declarator`. The field
branch recognised that shape only to skip emitting a type reference — it still
emitted a node for every declarator as a DATA MEMBER (a bare `area` label with
a `defines`/field edge).

So a pure-virtual interface class had no method nodes, only phantom fields; a
header-declared method and its out-of-line/overriding definition did not share
one node (`area` field vs `.area()` method); and a call to such a method
resolved to a field-shaped node.

Fix: in the field-declaration branch, emit a function_declarator with the same
shape a defined method uses — a `.name()` label, a `method` edge, and callable
— so the declaration and its definition collapse to one method node (parity
with the Java/TypeScript/Scala abstract-method contract). A genuine data member
still gets its `defines` field edge.

This corrects the label of every header-declared method, so a few existing
cross-file tests that asserted the old bare `bar`/`run` field label now assert
`.bar()`/`.run()`; the resolved call edges (source, target, confidence) are
unchanged, and the decl/def merge still produces exactly one node carrying the
definition site.

Added tests/test_cpp_method_declarations.py.

(cherry picked from commit 0038af9394)
2026-10-02 17:10:07 +01:00
rajatnagda45 35207e9b4b fix(scala): extract Scala 3 enum definitions, their cases and methods
`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 (#1719), Kotlin's enum_entry (#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.

(cherry picked from commit 175af52419)
2026-09-30 20:10:34 +01:00
rajatnagda45 244f777323 fix(zig): emit a node per enum member with a case_of edge
The enum body walk only emitted an enum's methods; its members — the
`container_field` nodes (`red`, `north = 0`) — were dropped, so an
`enum { red, green, blue }` produced a node for the enum with no members at
all, leaving it a leaf.

Emit a node plus a `case_of` edge per enum member. The handler is gated on
the `enum_declaration` parent so struct and union fields, which share the
`container_field` node shape, stay untouched. This brings Zig to parity with
the other languages that have enums: Java's enum_constant (#1719), Swift,
Scala, and Rust.

(cherry picked from commit 1e44311e22)
2026-09-30 20:10:34 +01:00
rajatnagda45 4f6a4efd1f fix(razor): extract methods from @functions blocks, not just @code
@code is the Blazor (.razor) spelling of an embedded C# member block;
@functions is the equivalent Razor Pages / MVC (.cshtml) spelling. Both
compile to class members, and this extractor serves both file types, but
only @code was matched. A .cshtml page that declared its methods inside an
@functions block lost every one of them.

Match @functions alongside @code in the block regex; the existing
brace-balanced body scan and method extraction are reused unchanged.

(cherry picked from commit e3693430b3)
2026-09-29 13:49:39 +01:00
Peter Pshenichny 5793226ff4 fix(markdown): parse escaped wikilink aliases
(cherry picked from commit f50f96f3c8)
2026-09-28 10:15:38 +01:00
abhirupone 57a263ce8f fix(markdown): respect ignore rules when building wikilink index (#3822)
(cherry picked from commit 48a8da896b)
2026-09-28 09:13:29 +01:00
rajatnagda45 4536c5cdc0 fix(groovy): extract enum declarations and their constants
enum_declaration was missing from the Groovy config's class_types, so a Groovy
enum and every constant it declares were dropped from the graph — a consumer
could not see which value a branch selects. tree-sitter-groovy exposes the same
enum_constant shape (a name field, optional class_body) that Java uses, so the
enum type now joins class_types and the existing Java enum-constant handler is
reused to emit a node per member with a case_of edge.

Added a regression test asserting the enum type and its constants are present,
attached to the enum rather than the file.

(cherry picked from commit 2c386de21a)
2026-09-28 09:13:29 +01:00
rajatnagda45 6c0636c426 fix(julia): extract macro definitions and @enum types
Two core Julia constructs were dropped by the extractor:

- macro name(...) ... end produced no node, even though macros are first-class,
  heavily used definitions. The grammar gives a macro the same signature shape
  as a function, so it now mints a definition (labelled @name) and its body is
  walked for calls -- with the signature skipped so the macro does not call
  itself.
- @enum Name a b c (and the @enum Name begin ... end block form) is a
  macrocall_expression that was ignored entirely. The first identifier in the
  argument list is the enum type; the rest, including members inside a begin/end
  block, are contained by it. Other macro calls still fall through so
  definitions nested in e.g. an @testset block are walked.

Added tests covering macro extraction with body-call attribution (no self-loop)
and both @enum forms with members contained by their type.

(cherry picked from commit 87edb5a278)
2026-09-26 13:48:37 +01:00
safishamsiandClaude Opus 4.8 2cb9743731 test(kotlin): drop dead imports, add plain-param-not-a-field negative test
Polish on #3848: remove the unused mid-file tree_sitter imports (F401)
and add a negative test asserting a bare constructor param without
val/var is not emitted as a field property.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-26 13:43:42 +01:00
nikhil2004 5757fb56e7 fix(extract): extract Kotlin annotations and primary constructor properties
Resolves #3842.

- Adds `_kotlin_annotation_names` helper to correctly extract annotations for Kotlin.
- Emits references edges with context='attribute' for class, function, and property annotations.
- Correctly skips use-site targets like `@field:` when extracting annotation names.
- Correctly parses bracketed annotations with multiple targets (e.g. `@[Inject VisibleForTesting]`).
- Traverses `primary_constructor` to extract `val`/`var` property dependencies as field edges.
- Resolves annotations using simple names to prevent dangling fully-qualified nodes.

Co-Authored-By: Antigravity <noreply@google.com>
(cherry picked from commit 579bce264e)
2026-09-26 13:39:55 +01:00
rajatnagda45 a0c1359154 fix(fortran): link type-bound procedures to their derived type
A derived type's contains section binds procedures as the type's methods
(procedure :: area => circle_area, or plain procedure :: scale), but the
derived_type_definition branch only minted the type node and ignored the
binding list entirely. The type ended up in the graph with no connection to
its own methods, and calls dispatched through the type were unrepresented.

Collect the bindings while walking the type and, after the whole tree is
walked (so the module's internal procedures already have nodes), add a method
edge from the type to each implementing procedure. A binding to a procedure
from another module resolves to a sourceless stub via ensure_named_node, the
same treatment parameter and return types already get, so the corpus rewire
can collapse it and no dangling edge is produced.

(cherry picked from commit 75f8fff533)
2026-09-26 13:39:37 +01:00
rajatnagda45 1ebbcdbfb7 fix(elixir): extract defprotocol and defimpl as containers
defprotocol and defimpl were not recognized as definition keywords, so the
walker fell through to generic recursion with no parent module. The protocol
and implementation nodes were never created, and their functions were attached
to the file node instead of their container (two orphaned size() nodes hanging
off the file rather than off the protocol and its List implementation).

Handle both like module-like containers: mint the protocol/impl node, contain
it in its enclosing scope, and walk the do-block so each def becomes a method
of the protocol or implementation. The implementation also gets an implements
edge to its protocol; a cross-file protocol target is filtered by the existing
dangling-edge guard rather than left hanging.

(cherry picked from commit 5f4e979693)
2026-09-26 13:39:37 +01:00
rajatnagda45 199d741f67 fix(verilog): link a module instantiation to the local definition, not a phantom
A module instantiation minted its target id with a bare, unscoped
_make_id(name), while a module definition uses _make_id(stem, name). The
two never matched, so instantiating a module defined in the same file:

    module counter(...); endmodule
    module top;
      counter u1(...);
    endmodule

split `counter` into a real definition node and a second, sourced phantom
that carried the `instantiates` edge. `top`'s dependency on the module you
actually defined was invisible, and because the phantom was sourced it also
baked this file's path into the id and blocked the corpus-level rewire that
would otherwise collapse it (the #1402 duplicate shape the other
extractors guard against).

Pre-scan module names so a same-file instantiation (Verilog allows
forward/any-order instantiation) resolves to the definition id. A module
defined in another file becomes a SOURCELESS stub instead, so the
corpus-level rewire collapses it onto the real definition rather than a
per-file duplicate — mirroring the Go extractor's ensure_named_node.

Adds regression coverage for both the local-definition link and the
cross-file sourceless stub.

(cherry picked from commit bca8bbac72)
2026-09-20 13:59:25 +01:00
ayushcodes10 f97cf99b87 Add a regression test for the PHP JS id collision edge drop
Covers a same name PHP/JS function pair in one file: exactly one
surviving node, the JS call to the colliding name absent as a calls
edge entirely rather than misattached, and the unrelated JS symbol's
own contains edge from the shared file node still present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit f59ce06c8c)
2026-09-18 15:15:58 +01:00
ayushcodes10 cc80351872 Add regression tests for the PHP inline script extraction
Covers the issue's exact reproduction (two functions plus the call
edge between them), that line numbers survive the masking pass, that
a script tag with only a src attribute contributes nothing without
crashing, and that a plain PHP file with no script tag is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 2d408de529)
2026-09-18 15:15:58 +01:00
hopstreax 02957036b2 fix: normalize definition_file on the watch and direct-extract paths (#3223)
Companion to #3243: extend definition_file portability to the paths build_from_json does not
cover — the watch/incremental relativize + cache-root rebase and the direct extract() return
normalization — via a shared _PORTABLE_PATH_KEYS list so source_file and definition_file stay
in lockstep. Together with #3243 this closes all four persistence paths (graph.json, AST
cache, watch, direct extract).
2026-09-02 04:00:08 +01:00
Synvoya 588dc041f7 fix(go): don't emit embeds for interface type-set constraints (#1818) 2026-08-30 15:53:16 +01:00
Synvoya 9aeade7199 fix(csharp): classify an interface base as inherits, not implements (#1817) 2026-08-30 15:53:16 +01:00
Synvoya c361189046 fix(kotlin): resolve a qualified supertype to its tail type name (#1793)
A qualified Kotlin supertype (`class Foo : com.example.Base()`) resolved to the first
identifier (`com`), collapsing distinct bases onto a bogus node. Take the last segment of the
user type (handling generics like `Container<String>` -> `Container`), so the edge targets
the real base; simple and delegated supertypes are unaffected.
2026-08-30 15:52:38 +01:00
Synvoya bf385a5052 fix(scala): normalize qualified heritage names to the tail type (#1794)
A qualified Scala base (`extends pkg.Base with other.Trait`) parses as a
stable_type_identifier (or generic_type wrapping one), which the heritage handler did not
match, so the edge was dropped or targeted the full qualified string. Add a helper that
takes the tail type name across all three shapes, emitting inherits for the first parent and
mixes_in for the rest, with no package/generic-arg false edges.
2026-08-30 15:52:04 +01:00
Synvoya 5a8a84d7ac fix(scala): capture trait declarations as class-like nodes (#1792) 2026-08-30 15:51:26 +01:00
Synvoya 88f53962dc fix(php): capture interface/enum/trait heritage as class-like nodes (#1791)
Only class_declaration was treated as a class type, so PHP interfaces, enums, and traits
produced no node and no heritage edges. Add them as class-like containers (and walk the
enum body) so their extends/implements/use heritage is captured as inherits/implements/
mixes_in edges to the right targets.
2026-08-30 15:50:48 +01:00
nshiveg 832256f824 feat: Robot Framework extractor for .robot/.resource files (#3192)
Add a Robot Framework extractor using the official robot.api parser (pure-Python, optional
[robot] extra), emitting suite/test/keyword nodes plus keyword-call, resource-import, and
library edges. Keyword resolution is case/space/underscore-insensitive (RF's real semantics)
and handles BDD prefixes and ${CURDIR}/${EXECDIR} path resolution; degrades to an actionable
error when robotframework is absent.
2026-08-30 15:14:33 +01:00
santhiprakash c66465c77e fix(elixir): extract functions whose only clause has a when guard (#3111)
A guarded function head (def foo(x) when is_integer(x), do: ...) nests the head call under a
binary_operator, so a def whose only clause carries a guard was not extracted. Unwrap the
guard to the head call (the head is always the first child, so the guard predicate is never
selected) and emit the real function name; multi-clause, multi-condition guards, and defp
are handled.
2026-08-27 20:56:15 +01:00
guitelesc e906227cf6 fix(commonlisp): derive node ids from the full path stem
The Common Lisp extractor minted its id prefix from the bare path.stem instead of the
canonical _file_stem used by every other extractor, so two same-basename .lisp files in
different directories collided (one file's nodes vanished on merge) and nested .lisp files
tripped a spurious legacy-id nudge. Use _make_id(_file_stem(path)) to match the shared
convention; cross-file rewire is unaffected (it matches on label, not id).
2026-08-27 20:20:36 +01:00
hopstreax b48005276e fix(ruby): preserve suffixed method node IDs (#3077)
normalize_id strips !/?/= from Ruby method names, so save and save! (or foo and foo=) minted
the same node id and the second was dropped as a seen_ids collision. Add a Ruby symbol
sanitizer (!->_bang, ?->_pred, =->_eq) applied to the id only (the label keeps the raw
spelling), and rekey method_index on the raw name so member-call resolution still matches.
Ids stay stable across incremental re-extraction.
2026-08-25 18:10:43 +01:00
winesoft-namjin-yun da8ca1684c fix(extract): keep the definition site when a decl/def pair is merged (#2990)
When a C/C++/ObjC declaration (header prototype) and its definition (implementation body)
merge into one node, the survivor kept the declaration's site, so the node pointed at the
prototype instead of the body. Record the definition's file/location on the survivor and
surface it as a Defined in: line; the pick is deterministic regardless of file order, and a
declaration with no definition keeps its own site.
2026-08-24 14:07:48 +01:00
rajatnagda45 be5110a789 fix(powershell): extract enum definitions and their members
A PowerShell enum definition and its members were not extracted, so a [Color] type
reference resolved to a sourceless phantom stub instead of the real enum. Emit the enum as
a real sourced node with its members as contained children (mirroring class_statement), so
references resolve to it; no builtins are fabricated.
2026-08-24 13:16:45 +01:00