Commit Graph
83 Commits
Author SHA1 Message Date
Abdullah 89c27c7b80 feat: add Erlang language extractor
(cherry picked from commit 3a92853329)
2026-09-21 19:19:26 +01:00
Abdullah 9f069eafea feat: add Solidity language extractor
(cherry picked from commit 41a1575e5e)
2026-09-21 19:13:40 +01:00
Abdullah d68d6012bd feat: add R language extractor
(cherry picked from commit 250395de31)
2026-09-21 19:10:13 +01:00
Abdullah ebe1f32c81 feat: add VB.NET language extractor
(cherry picked from commit 304c82fe10)
2026-09-21 19:06:50 +01:00
Abdullah ab568c8f42 feat: add COBOL language extractor
(cherry picked from commit 6573ea6306)
2026-09-21 18:04:14 +01:00
L4XB ab80fa7413 fix(rust): extract module-level static and const declarations
The Rust extractor gated every non-function item on struct_item,
enum_item and trait_item, so tree-sitter-rust's static_item and
const_item never produced a node: a constant existed in the graph only
through the files that referenced it, never from the Rust that defines
it (0 of 783 names in a jdx/mise snapshot).

Add a branch for both node types: a module-level declaration gets a
node with a file-level contains edge, an associated const inside an
impl is attributed to the impl like a method, and the declared type is
referenced the way a struct field type is.

Fixes #3471

(cherry picked from commit 70ff464064)
2026-09-10 22:44:38 +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
akshitj11 6a06b652cc fix(sql): extract CREATE TABLE inside BEGIN/COMMIT blocks (#2953)
DDL wrapped in a transaction parses under a `transaction` node the statement walker never
descended into, so a CREATE TABLE inside BEGIN/COMMIT was never extracted while a top-level
one was. Recurse into transaction blocks; nested statements get the same handling, no
double-emit.
2026-08-24 14:06:55 +01:00
fadeandClaude Opus 4.8 038ada6f0f feat(commonlisp): add Common Lisp .lisp/.cl/.lsp/.asd extractor (optional tree-sitter-commonlisp extra)
tree-sitter-commonlisp-backed extractor for packages, classes, functions,
methods, generics, macros, variable definers, and same-file calls. Handles the
grammar's dedicated defun node (defun/defmacro/defmethod/defgeneric via
defun_header) and the generic list_lit + leading-symbol forms (defvar/defclass/
defpackage), recurses into wrapper macros (eval-when/progn) and reader
conditionals, and maps CL operator chars (= < > ? ! + *) to readable id
suffixes. Wired into detect/extract dispatch, the extractor registry, a
[commonlisp] optional extra, and the README; ships a fixture and a 23-test suite
behind an importorskip guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-17 16:05:33 +01:00
Ousama Ben YounesandClaude Opus 4.8 54b3b78955 fix(cpp): recover doctest/Catch2 string-named TEST_CASE nodes (#2594)
doctest/Catch2 register tests with a string-literal name (TEST_CASE("...")),
which tree-sitter-cpp parses as an ERROR node, so every test function was
silently dropped. Adds a line-anchored regex fallback that appends one code
node per top-level TEST_CASE/TEST_CASE_TEMPLATE/SCENARIO plus a contains edge
from the file node, mirroring the Groovy Spock fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-15 17:17:29 +01:00
safishamsiandClaude Opus 4.8 10ad921b42 fix: JS/Kotlin/Swift/SQL extractor correctness batch; bump to 0.9.38 (#2568, #2565, #2561, #2577, #2575)
These changes are interleaved across engine.py/extract.py by function, so
they land as one batch.

#2568 (thanks @imagineers-tyler): the 0.9.37 #2552 callback-body fix
unioned sibling closures' local names under the shared declaration, so a
local in one callback suppressed a real indirect_call in a sibling. Locals
are now scoped per body (keyed by body id, via walk_calls' extra_locals).
Restore-only, never fabricates; #2552 capture preserved.

#2565 (thanks @kskchaitanya1993): Kotlin property initializers — class,
top-level, companion, and `by lazy {}` — now seed call extraction, so
`val repo = createRepo()` produces a calls edge; literal initializers
produce none; FQ calls compose with the #2550 resolver.

#2561 (thanks @fakewaffle): Swift receiver typing now handles
`@Environment(Store.self)` (whitelisted; @Query/keypath/dotted skipped to
avoid a wrong edge) and in-corpus factory bindings via a marked concrete
return type; opaque/array/out-of-corpus returns stay unresolved.

#2577 (thanks @wilyan09007, PR #2579): the SQL extractor no longer emits a
reads_from edge to a CTE name. WITH names are scoped per query (a subquery
CTE no longer suppresses an outer real table of the same name), so a CTE
no longer mints a bare stub that binds to an unrelated same-named symbol.

#2575 (thanks @phudayyy, PR #2574): a dynamic `import('…')` inside a nested
function or at module scope now produces an edge, dynamic_import is
included in affected, and calls inside nested named functions are
collected; a dynamic import already captured as a deferred imports_from is
not double-counted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-09 23:19:08 +01:00
Souptik Chakraborty ffa2a2471a fix: recover every declared SQL routine from unparseable PL/pgSQL (#2180)
tree-sitter-sql cannot parse PL/pgSQL-only statements, and #1910's ERROR-node
name recovery only covered one of the shapes that produces. Two others dropped
the routine silently -- no node, no warning, exit code 0:

1. The statement is shredded into loose top-level tokens (keyword_create,
   keyword_function, object_reference, ..., keyword_begin) and the ERROR node
   holds only the offending body line, e.g. `PERFORM other_fn();` or `x := 1;`.
   No ERROR node contains any CREATE text, so scanning ERROR nodes finds
   nothing. This is what still dropped PERFORM and := after #1910.
2. The routine name is a quoted identifier -- CREATE OR REPLACE FUNCTION
   "public"."fn"(...) -- which the recovery's bare [\w$.]+ pattern cannot match,
   because it stops dead at the leading quote. Generated schema dumps quote
   every identifier, so whole files recovered nothing.

Verified on the reported repro: the same body that drops under a quoted name is
recovered fine under an unquoted one, which is why the drop looked like it
depended only on the body statement.

Fix mirrors the global REFERENCES fallback already in this extractor: after the
tree walk, scan the raw source for every CREATE [OR REPLACE] FUNCTION/PROCEDURE
and emit any routine the walk missed. Name parts accept bare or double-quoted
identifiers. _add_node dedupes by node id, so routines already recovered from
the tree are not emitted twice.

Adds tests/fixtures/sample_plpgsql_quoted.sql -- generated-style quoted DDL whose
bodies use RAISE, RAISE NOTICE, PERFORM, :=, IF..THEN and bare NULL; -- plus
tests that every routine is recovered and that the file stays clean (tables
before and after still extract, no duplicate ids or labels, no empty/ERROR
labels, and every routine keeps its contains edge from the file node).
2026-07-26 11:08:10 +01:00
safishamsiandClaude Opus 4.8 49df466385 fix(sql): recover CREATE FUNCTION/PROCEDURE from tree-sitter ERROR nodes (#1910)
tree-sitter-sql parses PL/pgSQL CREATE FUNCTION statements (OUT/INOUT
params, tagged dollar quotes, PERFORM/:= body statements) as ERROR
nodes, and the dispatch loop had no branch for them, so the functions
were silently dropped from the graph.

Handle ERROR nodes inside walk() (they can nest inside a merged
create_function during multi-statement error recovery) and dispatch
top-level ERROR statements to it. The branch regex-scans the raw node
text for every CREATE [OR REPLACE] FUNCTION/PROCEDURE, mirroring the
existing fb_proc_or_trigger and has_error fallbacks, with a name class
that keeps schema-qualified names (exposed.important_function) whole.
The PL/pgSQL body is not scanned for FROM/JOIN references to avoid
junk reads_from targets, and node ids match the clean create_function
branch so seen_ids dedups consistently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:58:57 +01:00
richtextandClaude Opus 4.8 d89efbf9ef fix(extract): scope Pascal/Delphi call resolution + resolve inherited calls across files (#1739)
Both Pascal extractors resolved every call via a single file-wide
{method_name: node_id} dict, so two unrelated classes declaring a same-named
method (property accessors, generated COM/TLB wrapper classes) collapsed onto
whichever declaration was inserted last, producing wrong cross-class `calls`
edges. Resolution is now scoped: own class -> ancestor chain (inherits) ->
file-level free function -> unambiguous file-wide match; ambiguous at every
level emits no edge rather than guessing (same god-node guard as the Ruby
resolver).

Adds graphify/pascal_resolution.py, a corpus-wide post-extraction resolver
(registered via resolver_registry) that walks the inherits chain across file
boundaries, so a call from a manual descendant to a method it inherits from a
base class in a separate unit (the generated-base/manual-descendant split)
resolves. Also stops both extractors from emitting a duplicate base-class stub
carrying the referencing file's source_file, which collided with the real node
under cross-file id disambiguation. cache.py gives the new raw_calls bucket the
same portable-path treatment as nodes/edges so it round-trips.

Re-applied to the post-#1737 module layout (extractor hunks land in
graphify/extractors/pascal.py; registration stays in extract.py). Added one
adaptation the original PR predated: the cross-file resolver's god-node guard
now counts DISTINCT method nids, because the tree-sitter extractor emits a
method edge for both the interface declaration and the implementation, so the
same method_nid arrives twice -- without deduping, every inherited call looked
ambiguous and resolved to nothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 00:32:21 +01:00
ivanzhilovichandClaude Opus 4.8 00dd978d17 fix(extract): emit Kotlin enum entries as nodes with case_of edges (#1700 Kotlin half, #1738)
Kotlin enum entries weren't extracted: the walker never descended into the enum
body (`enum_class_body` wasn't in _KOTLIN_CONFIG.body_fallback_child_types, so
_find_body returned None). Add `enum_class_body` to the fallback body types and a
`_kotlin_extra_walk` (dispatched for tree_sitter_kotlin, mirroring the Java/Swift
handling) that emits each enum_entry as a node with a `case_of` edge to the enum.

Re-applied from PR #1738 (@ivanzhl) onto the post-#1737 module layout: the walk
engine now lives in graphify/extractors/engine.py while _KOTLIN_CONFIG stays in
extract.py. Closes the Kotlin half of #1700 (Java was #1719).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 22:49:01 +01:00
ivanzhilovich cf36d10292 fix(extract): emit Java enum constants as nodes with case_of edges 2026-07-08 00:42:56 +01:00
Synvoya 9b040224e1 fix(kotlin): emit implements edge for interface delegation (by)
class Foo : Bar by baz produced no edge because the delegation_specifier loop only handled constructor_invocation and bare user_type children; the by form wraps user_type in an explicit_delegation node. Add that branch so the implements edge (and generic-arg recovery) fires.
2026-07-04 11:26:55 +01:00
Synvoya ad7015262b fix(swift): emit references for enum associated-value types
The Swift `enum_entry` handler in `_swift_extra_walk` iterated the entry's
children only for the `simple_identifier` case name (emitting a `case_of`
edge) and never descended into the sibling `enum_type_parameters` node, where
associated-value types live (`enum_type_parameters -> user_type ->
type_identifier`). As a result `case started(Session)` silently dropped the
`Event -> Session` type reference.

Descend into each `enum_type_parameters` child after emitting `case_of`, run
`_swift_collect_type_refs` over its named children, and emit a `references`
edge from the enum node to each collected type (context `type`, or
`generic_arg` for generic roles), guarding target != enum node. Mirrors the
existing Swift property/parameter/return-type emit style.

Fixture: add `case failed(Config)` to `NetworkError` in sample.swift.
Test: assert (`NetworkError`, `Config`) in references(context=type).
2026-07-01 16:36:53 +01:00
Synvoya 21bcb436b5 fix(cpp): emit generic_arg references for base-class template arguments
The C++ base_class_clause handler's `template_type` branch read the base
name (`sub.child_by_field_name("name")`) and emitted the `inherits` edge,
but never descended into the base's `template_argument_list`. As a result
`class Car : public Base<Dep>` emitted `Car -> Base` (inherits) yet dropped
the `Car -> Dep` generic_arg reference entirely.

The Java handler `_emit_java_parent_type` already emits these generic_arg
references for base-class type arguments; C++ was the asymmetric gap.

Fix: after emitting the `inherits` edge, grab the base's `arguments` field
(the `template_argument_list`) and run `_cpp_collect_type_refs` over each
named argument with the generic flag set, emitting a `references` edge
(context "generic_arg") per collected type, guarding target != class node.
`_cpp_collect_type_refs` already handles nested/qualified args, so
`Base<std::vector<Dep>>` is covered too.

Adds a templated base (`Connection<T>`) + derived class
(`PooledClient : public Connection<HttpClient>`) to tests/fixtures/sample.cpp
and a test mirroring the Java generic-parents test.
2026-07-01 16:36:52 +01:00
Synvoya bb5e5192df fix(csharp): emit type references for properties
The C# class-body walker only handled field_declaration, so a
property's type produced no references(field) edge. In idiomatic C#,
auto-properties (`public Widget Main { get; set; }`) — not bare fields
— are the standard way to declare state, so this silently dropped most
of a class's type relationships.

Add a property_declaration branch alongside the field_declaration
handler, guarded the same way (ts_module == tree_sitter_c_sharp,
parent_class_nid set). A property exposes its type on the node directly
(no variable_declaration wrapper), so read it via
child_by_field_name("type") and collect refs with
_csharp_collect_type_refs, mirroring the Java/PHP/Kotlin siblings so
List<Widget> yields both the List field ref and the Widget generic_arg
ref. Only emit when target != parent_class_nid.
2026-07-01 16:36:50 +01:00
Synvoya 51f805e953 fix(php): emit type references for promoted constructor properties
PHP 8 constructor property promotion (`__construct(private Repo $repo)`)
parses the promoted parameter as `property_promotion_parameter`, not
`simple_parameter`. The PHP parameter loop filtered on `simple_parameter`
only, so promoted params were skipped entirely: their type emitted no
`parameter_type` edge on the constructor, and — because a promoted param
is also a real class field — no `field` edge on the class either. A
non-promoted param in the same signature still emitted `parameter_type`,
so the type reference was silently dropped for exactly the promoted case.

The promoted param's type sits in the same direct named-child shape the
loop already reads for `simple_parameter`, so widening the filter to
accept `property_promotion_parameter` makes the existing type extraction
emit the `parameter_type` edge. Additionally, for a promoted param, emit
a `field`-context references edge on the class (mirroring the
`property_declaration` handler), guarded so it only fires when a parent
class is in scope and the target is not the class node itself. Normal
`simple_parameter` behaviour is unchanged.

Adds a promoted-property constructor to tests/fixtures/sample.php and
test_php_constructor_property_promotion_contexts asserting the promoted
type appears as both `field` and `parameter_type`, and that a
non-promoted param does not leak a field edge.
2026-07-01 16:36:49 +01:00
Synvoya cd3a376030 fix(objc): emit implements edge for protocol-to-protocol adoption
`@protocol Derived <Base>` dropped the protocol-adoption (inheritance)
edge. The protocol_declaration handler in extract_objc walked children
for method declarations but ignored the protocol_reference_list child
that holds the adopted protocols, so no implements edge was ever emitted
for protocol-on-protocol adoption.

The extractor already handled `@interface Foo <Proto>` adoption, but that
nests the protocol name under a parameterized_arguments node; protocol-on-
protocol adoption uses a different grammar node (protocol_reference_list)
whose adopted-name is a direct `identifier` child, so it was never
matched. Walk protocol_reference_list and emit an implements edge for each
adopted protocol, mirroring the @interface handling.

Adds a defined Base/Derived protocol pair to the ObjC fixture and a
regression test asserting the Derived->Base implements edge.
2026-07-01 16:36:48 +01:00
Synvoya a129ff2cd6 fix(powershell): emit inherits/implements edges for class base types
The `class_statement` handler read only the first `simple_name` child —
the class name — and never inspected the base type(s) after the `:`
token. As a result `class Dog : Animal` dropped the Dog->Animal
inheritance edge entirely; derived classes appeared as isolated nodes.

Walk the class_statement children, and once the `:` token is seen treat
each following `simple_name` as a base type. Matching the C# convention
(PowerShell has no syntactic base-vs-interface split), the first base is
emitted as `inherits` and the rest as `implements`, resolved via
ensure_named_node.

Adds a Shape/Circle inheritance pair to tests/fixtures/sample.ps1 and a
regression test asserting ("Circle","Shape") in the inherits edges.
2026-07-01 16:36:47 +01:00
Synvoya 67b4525f32 fix(scala): emit field type references for var declarations
The Scala field handler matched only `val_definition`, so a mutable field
(`var b: Repo`), which parses as `var_definition`, had its type reference
silently dropped from the graph. val and var nodes are structurally identical
(both expose a `type` field), so the existing type-collection logic works
unchanged. Widen the guard to accept var_definition.

Adds a var field to the Scala fixture and a regression test.
2026-07-01 16:36:47 +01:00
Synvoya 297075c3f3 fix(systemverilog): emit field references for qualified class properties
The SystemVerilog class-body field regex in _augment_systemverilog_semantics
matched only unqualified `<type> <name>;` declarations. Its `^\s*` prefix
consumes leading whitespace but not leading class-property qualifiers, so a
qualified field such as `rand Config m_cfg;` (three tokens) failed the
two-token shape and its type reference was silently dropped from the graph.

Consume optional leading qualifiers (rand/randc/local/protected/static/const/
automatic/var) before the type token. Zero qualifiers preserves the existing
behavior; the type and name capture are unchanged.

Adds test_systemverilog_qualified_field_references plus rand- and
protected-qualified fields (and a Config class) to the shared .sv fixture.
2026-07-01 13:45:53 +01:00
Synvoya 7eb847bcf7 fix(rust): emit field type references for tuple structs
extract_rust() only traversed field_declaration_list (named-struct
bodies), so tuple structs -- whose positional fields nest under
ordered_field_declaration_list -- had every field type reference
silently dropped from the graph.

This is the same node shape the enum handler already accounts for
(tuple variants nest their types under ordered_field_declaration_list);
the struct path was simply left behind. Add an additive branch that,
for each type node in a tuple struct's ordered_field_declaration_list,
collects type refs via _rust_collect_type_refs and emits references
edges with the appropriate field / generic_arg context. The
named-struct path is untouched.

For `struct Wrapper(Logger, Config);` with Logger/Config defined
in-file, no field edges were produced before; both are now emitted.

Adds test_rust_tuple_struct_field_references and a tuple struct to the
shared Rust fixture covering plain and generic positional field types.
2026-07-01 13:45:53 +01:00
SynvoyaandClaude Opus 4.8 984a6a8f0a fix(julia): emit imports for qualified, relative, and scoped-selected forms
Only bare-identifier imports (`using Foo`) emitted edges. tree-sitter-julia
wraps qualified paths in `scoped_identifier` (`using Base.Threads`), relative
paths in `import_path` (`using ..Sibling`), and the package of a
`selected_import` may itself be a `scoped_identifier`
(`import Base.Threads: nthreads`). None of those were matched, so qualified and
relative imports were silently dropped, and scoped selected-imports pointed at
the selected symbol instead of the module.

Resolve the module name from identifier / scoped_identifier / import_path in
all three positions. Adds fixture lines + a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:45:52 +01:00
SynvoyaandClaude Opus 4.8 674184d462 fix(rust): emit references edges for enum variant field types
Enum variant payload types were silently dropped — `struct_item` and
`trait_item` had type-reference handlers but `enum_item` had none, so the
variant field types were never traversed.

Add an `enum_item` branch that walks
`enum_variant_list -> enum_variant -> ordered_field_declaration_list`
(tuple variants, `Click(Logger)`) and `field_declaration_list` (struct
variants, `Resize { size: Dim }`), emitting a `references` edge from the enum
to each field type. Reuses the same type collection as the struct path. Adds
an enum to the fixture and a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:58:39 +01:00
SynvoyaandClaude Opus 4.8 b8f41c77eb fix(fortran): emit calls edges for function invocations
Function calls (`y = f(x)`) were silently dropped — only `subroutine_call`
(`call sub(...)`) was handled in walk_calls. tree-sitter-fortran represents a
function invocation as a `call_expression`, which had no branch, so every
function-to-function call produced no edge.

Handle `call_expression`. Because Fortran uses the same `name(...)` syntax for
array indexing, the callee is resolved against procedures defined in the file
(`target_nid in seen_ids`) before emitting — so array accesses like `arr(i)`
cannot fabricate spurious `calls` edges. Adds a function + caller to the
fixture and a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:58:38 +01:00
SynvoyaandClaude Opus 4.8 f2ea6a6087 fix(elixir): expand multi-alias brace form into per-module imports edges
`alias Foo.{Bar, Baz}` (and the same import/require/use brace form) emitted
NO imports edges. tree-sitter-elixir represents it as a `dot` node holding the
base alias plus a trailing `tuple` of member aliases, but the import handler
only matched a bare `alias` child, so every multi-alias import was silently
dropped.

Add `_get_alias_modules`, which expands the brace form to `Foo.Bar`,
`Foo.Baz`, … while leaving the single form (`alias Foo.Bar`) unchanged. Adds a
fixture line + regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:58:38 +01:00
hibinmandClaude Opus 4.8 64a6093376 fix(groovy): emit inherits/implements edges for extends/implements
tree-sitter-groovy exposes inheritance via the same `superclass` and
`interfaces`/`type_list` fields as tree-sitter-java, but the inheritance-
emitting block in `_extract_generic` was gated on
`ts_module == "tree_sitter_java"`. Groovy was the only class-based JVM
language in the file with no inheritance handler, so every Groovy
`extends`/`implements` was silently dropped (contains/methods/imports/calls
were unaffected).

Widen the gate to include `tree_sitter_groovy`; the existing
`_emit_java_parent_type` path handles the identical node shapes verbatim.
Adds a base class + interface to the Groovy fixture and two regression
tests (extends -> inherits, implements -> implements).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:47:10 +01:00
hibinmandClaude Opus 4.8 a19b9e90ec fix(ruby): emit inherits edge for class superclass
`class Dog < Animal` exposes the base in the `superclass` field, but the
inheritance handler in `_extract_generic` had branches for
java/kotlin/c#/scala/cpp/php/swift/python and none for Ruby, so every Ruby
`inherits` edge was silently dropped (contains/methods/calls unaffected).

Add a Ruby branch that reads the `superclass` field, handling both a bare
`constant` (`< Animal`) and a `scope_resolution` (`< Foo::Bar` -> Bar).
Adds a subclass to the Ruby fixture and a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:47:10 +01:00
safishamsiandClaude Opus 4.8 3bc3feed54 fix(extract): merge header/impl class fragmentation + C++/ObjC header routing (#1556, #1547)
A class declared in a header (Foo.h/@interface) and defined in its impl
(Foo.cpp/Foo.m/@implementation) fragmented into two nodes: _file_stem
drops the extension so Foo.h and Foo.cpp share a node id, which
_disambiguate_colliding_node_ids then split apart by path — and the two
"defs" tripped every resolver's single-definition god-node guard,
cascading into missing .h<->.m/.cpp linkage and cross-file/cross-language
edges.

- Routing: a `.h` using `#import` now routes to extract_objc (#1556 bridging
  headers — extract_c drops `#import` as a preproc_call), and a `.h` with
  C++-only signals (class/namespace/template/::/access-specifiers) routes
  to extract_cpp (#1547 — the C grammar has no class_specifier, so a C++
  header previously yielded a junk node and lost every method). ObjC sniff
  keeps priority; a plain C header still routes to extract_c.
- Merge: a new _merge_decl_def_classes post-pass collapses the header/impl
  id-collision onto the header (declaration) variant, modeled on
  _merge_swift_extensions, gated so it fires ONLY for a clean sibling
  header/impl pair (same dir, same base stem, exactly one header) — two
  same-named classes in different directories have different stems and
  never collide, so they are never merged (god-node guard verified). C++
  method definitions retain their `Foo::` qualifier so a `Foo::bar` def
  keys onto the header declaration (one method node, not two); free
  functions keep their bare-name ids.

Result: one canonical class node per .h/.m or .h/.cpp pair with methods
unified, which unblocks the existing member-call resolvers (verified
Swift->ObjC calls and Swift `extension` folding now resolve). Strict
improvement over v8 (which produced junk/fragmented nodes here, verified).
Still open as follow-ups: cross-file C++ #include edge resolution and a
C++/ObjC cross-file member-call resolver (a pre-existing gap, not a
regression).

Reported by @JabberYQ (#1556) and @c0dezer019 (#1547).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 17:23:46 +01:00
guy oronandClaude Opus 4.8 1801da0634 feat(ts): resolve this.field.method() calls via constructor-injection types (#1316)
A member call through a constructor-injected dependency
(`constructor(private db: Database)` ... `this.db.query()`) now produces
a calls edge to the field type's method. The field->type map is captured
from constructor parameter-properties, and resolution reuses the existing
single-definition god-node guard (like the Swift/Python/Ruby member-call
resolvers): the edge is emitted only when the field's type name resolves
to exactly one class definition that owns the method, so an ambiguous or
unknown/untyped field produces no edge — no global name-match fan-out.
Edges are EXTRACTED (the type is explicit from the annotation). TS/JS-only
and additive; scope is constructor parameter-property injection.

Adds the decisive regression tests the implementation needed: two classes
defining the same method name where the injected field is typed to one of
them (must resolve to that one only), and an ambiguous type-name case
(must emit no edge).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:46:16 +01:00
jiangyq9andClaude Opus 4.8 652ba42e61 feat(extract): index Metal (.metal) shader files (#1480)
Metal Shading Language is C++14, so .metal files are classified as code and routed
through the existing C++ extractor, mirroring the CUDA .cu/.cuh reuse. Also adds
.cu/.cuh/.metal to build.py's _LANG_FAMILY map (they were all missing), so the
cross-language phantom-`calls`-edge filter treats them as C++. README language
table updated.

Ported from PR #1480 by @jiangyq9. This supersedes the earlier #1450 by
@GoodOlClint (same .metal -> C++ approach and fixture) — credit to @GoodOlClint
for the original; #1480 additionally closes the CUDA family-map gap and updates
the docs, and merges clean against current v8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 10:19:15 +01:00
Michael KatsoulakisandClaude Opus 4.8 905e0a7a2e feat(extract): link XAML views to ViewModels and extract binding references (#1473)
Builds on the initial XAML support (#1460). Resolves a view to its ViewModel from
an explicit <Window.DataContext><vm:MainViewModel/>, a design-time
d:DataContext="{d:DesignInstance Type=...}", the View->ViewModel naming
convention, or Prism ViewModelLocator.AutoWireViewModel="True". Resolution is
always against an actually-extracted C# class node, so a name matching no class
(or an ambiguous 2+) emits no edge -- explicit DataContext is EXTRACTED,
convention/Prism are INFERRED. Also extracts binding paths ({Binding User.Name},
Path=Order.Total), commands (Command="{Binding SaveCommand}"), converters, and
CommunityToolkit [ObservableProperty]/[RelayCommand] generated members.

The #1460 event-handler hardening is preserved unchanged: events still resolve
only to methods with a .NET (object sender, ...EventArgs e) signature, and the
free-form-attribute denylist still prevents values like Content="Save" from
fabricating event edges (both regression tests still pass). ViewModel discovery is
bounded to the active extraction root.

Ported from PR #1473 by @MikeKatsoulakis (clean 3-way merge onto current v8).
Maintainer fix on top: the CommunityToolkit member reader now reads the
code-behind with errors="replace", so a non-UTF8 ViewModel .cs can't raise
UnicodeDecodeError and abort extract_xaml (matches every other reader in the
module). Added a regression test for that case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:23:10 +01:00
Michael KatsoulakisandClaude Opus 4.8 7dc5d968a3 feat(extract): WPF/XAML structural extraction with code-behind bridge (#1460)
Makes .xaml a first-class code input. extract_xaml() uses stdlib XML (no new
parser dependency) behind the same DOCTYPE/ENTITY and size guards as the .csproj
extractor, and captures: the root element, named controls (x:Name/Name) and their
control types, {Binding ...} references, x:Class, and -- the useful part -- a
bridge from the view markup to its .xaml.cs code-behind by resolving event-handler
attributes to the matching methods on the partial class.

Ported from PR #1460 by @MikeKatsoulakis onto current v8.

Maintainer hardening on top of the original PR: event resolution is now gated so
it can't fabricate edges. The original matched any attribute value against
code-behind method names, so Content="Save" next to a business method Save(), or
Tag="<a-handler-name>", produced spurious "event" edges. Resolution now requires
(a) the attribute is not a known free-form/identity property (Content, Text, Tag,
Title, ToolTip, Header, ...), (b) the value is a bare identifier, and (c) the
matched method actually has the .NET event-handler signature
(object sender, <T>EventArgs e) -- read from the code-behind source since the C#
extractor does not record parameter lists on method nodes. Added regression tests
for both false-positive cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 13:00:47 +01:00
raylei50653andClaude Opus 4.8 dbce4532f4 Add CUDA (.cu/.cuh) support via the C++ extractor
CUDA is a C++ superset, so .cu/.cuh files parse cleanly with
tree-sitter-cpp (already a dependency). Two registrations wire it up:

- detect.py: add .cu/.cuh to CODE_EXTENSIONS so they're detected and
  watched (watch.py's _WATCHED_EXTENSIONS derives from CODE_EXTENSIONS).
- extract.py: route .cu/.cuh through extract_cpp in _DISPATCH, which
  also makes collect_files() pick them up (_EXTENSIONS = _DISPATCH.keys()).

Adds tests/fixtures/sample.cu (kernel + __device__/host functions +
struct + includes) and CUDA cases in test_languages.py covering kernel/
device function extraction, structs, includes, and host call edges.
Documents the new extensions in the README extension table and CHANGELOG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:57:06 +01:00
geektan123 f117aaccc6 feat: PowerShell .psd1 manifest parsing & Import-Module / dot-source edge emission (#1331) (#1341)
Index PowerShell .psd1 manifests + emit Import-Module/dot-source edges (closes #1331). Builds on the shipped .psm1 support. Validated: full suite 2107 passed, 18 new tests. Thanks @geektan123.
2026-06-17 10:46:12 +01:00
Danil Tarasov b525549a24 feat(extract): SystemVerilog class-level extraction; Dart with-clause mixin fix 2026-06-11 19:52:56 +03:00
Rasmus Bækgaardandbakgaard 29e57cd295 feat: add .slnx solution file support (#1189)
Adds support for the XML-based `.slnx` solution format (VS 2022 17.13+ replacement for `.sln`). Extracts project references as `contains` edges and build dependencies as `imports` edges. XXE-protected XML parsing with size cap. Wired into `_DISPATCH` and `CODE_EXTENSIONS`. 6 new tests passing.

Co-authored-by: bakgaard <bakgaard@users.noreply.github.com>
2026-06-08 10:35:32 +01:00
SafiandClaude Sonnet 4.6 7467c1b6a4 feat/fix: land PRs #1118 #1110 #1159 #1107 #1103 (graph quality + new features)
#1118 — prune stale AST nodes on full re-extraction (#1116)
Stamps every AST-extracted node with _origin="ast" in extract(). On a
full rebuild _rebuild_code drops any AST-marked node absent from the
fresh output even when its source file survives, fixing stale symbols.
Backward-compat: marker-less nodes from pre-1118 graphs survive one
cycle then self-heal.

#1110 — stop reading images and PDFs as garbage in headless extract
Images route through per-backend vision payloads (base64/data-URI/bytes
for claude/openai/bedrock); non-vision backends get _strip_pixels for
graceful degradation. PDFs reuse pypdf. 5MB cap, 20-image chunk limit.

#1159 — Salesforce Apex extractor (.cls, .trigger)
Regex-based extractor: classes, interfaces, enums, methods, triggers,
SOQL/DML edges. No new dependency. Dispatched as .cls and .trigger.

#1107 — Azure OpenAI Service backend (--backend azure)
Uses AzureOpenAI SDK client (from existing openai package). Auto-detects
when AZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT both set. Uses
max_completion_tokens (not deprecated max_tokens).

#1103 — live PostgreSQL introspection (--postgres DSN)
graphify extract --postgres "postgresql://..." introspects tables, views,
routines, and FK relations via information_schema (SERIALIZABLE READ ONLY).
Credentials sanitized on error. New graphify[postgres] extra (psycopg3).

Union-resolved llm.py conflict: Azure functions + bedrock images= param.
Fixed test_image_vision.py mock to accept timeout= kwarg (our #1112).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 01:20:38 +01:00
Danil Tarasov 0080fbd13c feat: add objc, julia, c, c++, scala, fortran, powershell semantic contexts 2026-05-29 00:05:28 +03:00
TheFedaikin 32aa053e6c feat: semantic type-reference edges for Swift, Kotlin, PHP, Rust, and Go (#1015) 2026-05-28 14:46:03 +01:00
SafiandClaude Sonnet 4.6 dacbdb539a feat: BYOND DreamMaker support, --mode deep flag, changelog fixes (#884, #1030)
- Feat: extract_dm (tree-sitter-dm), extract_dmi (PNG icon states),
  extract_dmm (tile dict uses edges), extract_dmf (window/elem hierarchy)
  for .dm .dme .dmi .dmm .dmf; 26 tests, fixtures, pyproject.toml dep
- Feat: graphify extract --mode deep flag; deep_mode threaded through all
  four LLM backends via extract_corpus_parallel
- Fix: CHANGELOG 0.8.21 entries for #1050, #1046, #1047 that were missing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 23:55:12 +01:00
2c01a89b28 feat: MCP config extractor (.mcp.json, claude_desktop_config.json, mcp.json)
Adds graphify/mcp_ingest.py — extracts MCP server configurations into the
knowledge graph. Captures server nodes, NuGet/npm/pip package refs, commands,
env var requirements, and inter-server edges. Dispatched by filename before
the suffix lookup so generic .json extraction is unaffected. Env values are
discarded to prevent secret leakage. File size capped at 1 MiB. 29 tests.

Fixes: server_count budget now checked after validity guard so invalid entries
don't consume capacity; removed misleading uv run docstring example.

Co-Authored-By: adityachaudhary99 <adityachaudhary99@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 20:16:41 +01:00
8bcfffdf62 add .NET project file support (.sln, .csproj, .fsproj, .vbproj, .razor, .cshtml)
Adds extract_sln, extract_csproj, and extract_razor extractors. Captures NuGet
package refs, project-to-project dependencies, target frameworks, SDK attribute,
@using/@inject/@inherits/@model directives, Blazor component refs, and @code
methods. Resolves relative project paths to absolute paths so sln/csproj nodes
link correctly when the graph is assembled. Closes #515.

Co-Authored-By: aksrathore <aksrathore@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 12:27:28 +01:00
TheFedaikin ab4e5424ca feat: add cross-language semantic contexts for Python, JS/TS, C#, and Java (#996) 2026-05-24 20:38:10 +01:00
deXterbed 1494874e25 feat: track JS/TS barrel re-exports as explicit graph edges
- Add 'export_statement' to import_types for JS/TS/TSX configs
- Extend _import_js to detect 'export { X } from ./mod' re-exports
- Emit 're_exports' edges linking barrel files to source symbols
- Preserve walk-through for 'export function/const' declarations
- Add 're_exports' to clean_edges allowlist for cross-file edges

Tested on a 976-file Next.js codebase: detects 162 re_exports edges
and 5760 symbol-level imports (previously 0 for both).
2026-05-22 13:28:35 +01:00
Alex Ubillus 406bea47b5 fix swift extension nodes duplicating across files (#969)
tree-sitter-swift parses both `class Foo` and `extension Foo` as
`class_declaration`, and node ids carry the file stem, so `extension Foo`
in a sibling file produced a second `Foo` node instead of attaching to
the original. Same-file extensions already dedupe via seen_ids; only the
cross-file case leaked.

Per-file extraction now tags `extension` class_declarations, and the
corpus-level `extract()` runs a merge pass: when exactly one
non-extension declaration shares the label, the extension nodes redirect
onto it and their edges are rewritten (self-loops dropped, duplicates
collapsed). Extensions of types outside the corpus and ambiguous label
matches stay untouched.

On a 25-file Swift project this collapses Parser from 6 split nodes
(top of the god-node list, four entries) to one canonical node, and
lets the generic cross-file call resolver attach previously ambiguous
call edges to the right target.
2026-05-22 13:22:42 +01:00