_kotlin_collect_type_refs had no equivalent of _JAVA_BUILTIN_TYPES /
_PYTHON_ANNOTATION_NOISE / _GO_PREDECLARED_TYPES, so every Boolean/String/
Int/List/... type annotation in a .kt file became a real graph node/edge.
Unrelated files sharing only these language-level types were getting
merged into the same community by cluster-only.
Add _KOTLIN_BUILTIN_TYPES (kotlin.* scalars, collections, throwables -
deliberately not Android/JVM framework types like Context/View/Bundle,
matching how _JAVA_BUILTIN_TYPES doesn't filter framework types either)
and check it plus _JAVA_BUILTIN_TYPES (Kotlin freely references
java.util.Date/UUID/etc) at the three call sites that previously appended
unconditionally.
Deliberately excludes "Result" from the filter list even though
kotlin.Result exists - it collides with the very common user-defined
sealed-class name (confirmed against this repo's own sample.kt fixture,
which defines its own Result<T>).
Verified on a ~400-file Android/Kotlin project: 3693->3239 nodes (-12%),
6481->5221 edges (-19%), with a previously merged 5-file grab-bag
community cleanly splitting out an unrelated screen-dimension utility.
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>
Adds a _JAVA_BUILTIN_TYPES skip list so ubiquitous java.lang/util/io/time/etc.
type names (String, List, Map, Optional, ...) are not emitted as references
edges (they never resolve to a project node). Mirrors _GO_PREDECLARED_TYPES /
_PYTHON_ANNOTATION_NOISE. Nested user-type generic args still resolve.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
interface X extends A, B captured the parent list in iface_re group 2 but the handler only read group 1, so no inheritance edge was emitted. Split the parent list and emit one extends edge per parent (mirroring the class branch).
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).
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.
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.
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.
`@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.
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.
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.
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.
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>
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>
`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>
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>
`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>
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>
`self.product.name` dot-syntax now emits an `accesses` edge and
`@selector(method)` emits a `calls` edge, both resolved only to an
unambiguous in-scope definition (a sibling method of the same class for
dot-syntax; exactly one method by exact selector name for @selector) so
no false-edge fan-out occurs when multiple classes share a name.
Hardened over the original PR: resolution now matches the method node id
EXACTLY (a method id is _make_id(container, name)) rather than by
`endswith` suffix. The substring match would mis-resolve `self.name` to a
sibling `-surname` (false positive) and, when a substring-colliding
sibling existed, suppress the correct edge (false negative); exact
matching fixes both. Adds substring-collision regression tests
(`-name`/`-surname`, `-doThing`/`-reallyDoThing`).
Completes the #1475 ObjC follow-ups (Bug 5 dot-syntax accesses, Bug 6b
@selector target-action).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Three residual ObjC extractor bugs from the #1475 thread, each reproduced
against the real tree-sitter-objc grammar:
1. NS_ASSUME_NONNULL_BEGIN before @interface made the parser fail to emit
a class_interface node at all (the whole interface was swallowed into
ERROR nodes), so headers using the macro produced no class node. Blank
the two argument-less annotation macros to equal-length spaces before
parsing (offset-preserving; macro-free files are byte-identical). The
reporter's "@class breaks it" hypothesis was wrong — only the macro does.
2. Quoted `#import "X.h"` edges dangled once a `.h`/`.m` pair existed: the
target used the bare stem, which the post-pass canonicalizes and then
_disambiguate_colliding_node_ids salts apart by path, so the import
target no longer matched. Resolve the include to a real file (mirroring
_import_c), and repoint imports/imports_from edges to the header variant
in _disambiguate_colliding_node_ids — taking precedence over the
same-source-file salt so a `.m` importing its own `.h` resolves to the
header instead of self-looping. Also repairs the equivalent latent
C-include dangling bug.
3. `[[Foo alloc] init]` produced no edge — walk_calls only reconstructed
selectors and skipped the receiver. Emit a `references` edge from the
allocating method to the class, resolved via the unique-class stub guard
(ensure_named_node + _rewire_unique_stub_nodes) so unknown/ambiguous
names produce no false edge. The calls-to-init edge is deliberately
deferred (init selectors are ambiguous across classes).
Reported by JabberYQ with a precise repro and test repo. Adds regression
tests incl. a self-loop guard on the import edges. Still open on #1475:
dot-syntax property accesses (Bug 5) and @selector target-action (Bug 6b).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A Java record's component types produced no edges, so a record's data dependencies
were invisible. The record_declaration handler now emits `references` edges from
each component type (field context; generic_arg for type arguments), skipping
primitives and — via #1518's in-scope type-parameter filtering — the record's own
type parameters. Handles standard, generic, primitive, and varargs components.
Ported from PR #1520 by @oleksii-tumanov (clean 3-way onto v8; builds on #1518).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The generic-parent support (#1511) emitted a spurious references/generic_arg edge
(and a sourceless stub node) for a bare type parameter — the T in
`class Box<T> extends Container<T>` — because the Java extractor had no type-
parameter tracking. _java_collect_type_refs now collects the in-scope type-
parameter names (walking class/interface/record/method/constructor declarations,
including bounded and multiple params) and skips references to them, while keeping
every real type. Scoped to the declaring node, so a real class that shares a name
with a type parameter elsewhere is still referenced.
Ported from PR #1518 by @oleksii-tumanov. Closes the nit flagged when #1511 landed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A generic parent — `class Foo extends Bar<T>` or `implements List<T>` — is a
`generic_type` node in the tree, but the inheritance extractor only fired on a
bare `type_identifier`, so those inherits/implements edges were silently dropped.
The parent type is now unwrapped to its base (`Bar<T>` -> `Bar`) for the
inherits/implements edge, and the type arguments are emitted as `generic_arg`
references.
Ported from PR #1511 by @oleksii-tumanov. Known pre-existing limitation (not
introduced here, worth a separate follow-up): the extractor has no type-parameter
tracking, so a bare parameter like `T` in `extends Container<T>` still produces a
`generic_arg` reference to `T`; the inherits/implements edge itself always targets
the real base type, never `T`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Java `enum` and `@interface` (annotation) declarations weren't in _JAVA_CONFIG's
class_types, so they were never emitted as type nodes — a field typed as an enum or
a class annotated with a project annotation referenced a node that didn't exist
(dangling). enum_declaration and annotation_type_declaration are now extracted as
first-class (sourced) type nodes, so those references resolve onto them.
Ported from PR #1513 by @oleksii-tumanov.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Annotations on Java classes/interfaces/records (e.g. @Service, @Entity) produced
no edge, so type-level framework wiring was invisible even though method-level
annotations were already captured. The class/interface/record handler now emits
`references` edges with the existing `attribute` context, reusing the renamed
`_java_annotation_names` helper (was `_java_method_annotation_names`; logic
unchanged) at both the type and method call sites.
Ported from PR #1487 by @oleksii-tumanov. Resolved an additive test conflict with
#1485 by keeping both new test functions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Java field declarations produced no `references` edge for their type, so a class's
data dependencies (its field types) were missing from the graph even though
parameter and return types were already captured. The field handler now collects
the declared type via the same `_java_collect_type_refs` helper used elsewhere,
preserving the `field` and `generic_arg` contexts and skipping primitives
(int/boolean/etc.), matching the existing C#/PHP/Kotlin field handlers.
Ported from PR #1485 by @oleksii-tumanov.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A detailed report (#1475) showed the ObjC extractor silently dropping ~60% of
code-level relationships. Five fixes:
1. Dispatch: ObjC `.h` headers were parsed by extract_c (1 node, 0 edges, losing
every @interface/@protocol/@property/method). _get_extractor now routes a `.h`
to extract_objc when it contains an ObjC-only directive
(@interface/@protocol/@implementation/@import) — these are illegal in C/C++, so
the sniff never hijacks a genuine C/C++ header (verified: a plain C/C++ `.h`
stays on its existing extractor).
2. Calls: the method-body pass produced zero `calls` edges because it matched
child types `selector`/`keyword_argument_list`, but tree-sitter-objc tags
selector parts with the field name `method` (type `identifier`). The selector
is now reconstructed from every `method`-field child, explicitly skipping the
`receiver` field — so self/super/ClassName receivers are never mistaken for a
selector, and compound sends ([self a:x b:y]) resolve too. (Avoids the report's
suggested `"identifier"` fix, which would have matched receivers as selectors.)
3. Generic property types: NSArray<Product *> * wraps the type in a
generic_specifier, so the old direct-type_identifier scan saw nothing. The
element (Product) and container (NSArray) are now both referenced.
4. Class methods: `+ (…)shared` was labeled -shared; the +/- sigil is now read
from the method node's first child.
5. @import: `@import Foundation;` (a module_import node) now emits an imports edge.
Dot-syntax property `accesses` (Bug 5) and @selector(...) target-action edges
(Bug 6b) need type/name resolution policy and are left as follow-ups. Added six
regression tests; the reporter's claim that compound messages lack a
message_expression wrapper (Bug 6a) was checked against the real AST and refuted —
they share Bug 2's root cause, fixed by the same field-name change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
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>
extract_markdown only emitted heading nodes + contains edges and never parsed
link syntax, so a doc full of [text](./other.md) links (index.md,
table-of-contents.md) had no edges to the docs it links and never became a hub.
Add a deterministic link pass: inline, reference-style, and [[wikilinks]],
resolved relative to the source file, external URLs/anchors/images skipped, with
the target id built via the same _make_id recipe so the edge merges onto the
real doc node instead of an orphan.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Adopts the approach from #1330 (thanks @duncan-daydream) on top of the v0.8.40
Swift import fix: _import_swift returns (id,label) module pairs, the extractor
materializes a type=module anchor node per import, and _disambiguate_colliding_node_ids
exempts type=module nodes so the same module imported from N files collapses to
one shared node (enables reverse traversal "what imports CoreKit"). The --no-cluster
writer now dedupes nodes by id and edges to match the clustered build_from_json path.
Replaces the interim _import_label/synthesize_import_module_nodes mechanism.
Adds tests/test_swift_import_resolution.py (cross-file collapse, build survival)
and dedupe_nodes coverage. Refs #1327, #1330.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- #1315: add .psm1 to CODE_EXTENSIONS + _DISPATCH so PowerShell modules are indexed
- #1327: synthesize a module node for Swift import targets (new LanguageConfig
flag synthesize_import_module_nodes) so imports edges survive build.py pruning;
strengthen the Swift dangling-edge test to also assert edge targets
- #1317: dedupe parallel edges by (source,target,relation) in the --no-cluster
and incremental update write paths so edge counts are deterministic and
`update` is idempotent
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#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>
tree-sitter-dm (BYOND DreamMaker) publishes only a Windows wheel, so on
Linux/macOS uv/pip compiled it from source and aborted the entire
`uv tool install graphifyy` when a C toolchain or python3-dev was missing.
It is the only core grammar lacking Linux/Mac wheels. Move it from core
dependencies to an optional `dm` extra (also in `all`); the default install
now needs no compiler. extract_dm already imports the grammar lazily with a
graceful fallback, and .dmi/.dmm/.dmf use no tree-sitter, so only .dm/.dme
AST parsing is gated behind the extra.
Also: guard the .dm grammar tests with pytest.importorskip so a plain
`uv sync` (no extras) doesn't hard-fail; bump to 0.8.28 with a changelog
upgrade note; switch the README extras table + type notes from pip to
`uv tool install` (uv is the recommended installer, and pip-into-a-uv-tool-env
is the exact ModuleNotFoundError the README already warns about). 0.8.28 also
ships Kilo Code (#512) and the Dart parser modernization (#1098).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Security: _is_sensitive now flags underscore-prefixed names (api_token.txt, oauth_token.json) by replacing \b with lookarounds; adds _SENSITIVE_DIRS check on parent path components (parts[:-1]) so .ssh/, secrets/, .aws/ directories are always skipped; aligns both patterns to (?![a-zA-Z]) for consistent underscore-after-keyword behavior (#920)
Fix: --wiki Relationships section always empty because _cross_community_links read community from node attrs (always None) instead of the communities dict; _god_node_article had the same bug and never linked to the owning community; fixed by building a node->community map in to_wiki() and threading it through (#925)
Fix: --watch now respects .graphifyignore; patterns loaded once at startup, handler checks _is_ignored before extension filter so node_modules/, .venv/, build/ churn no longer triggers rebuilds (#928)
Fix: C++ struct inheritance edges via base_class_clause; initialize base="" per iteration to prevent stale carryover (#915)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>