feat(extraction): instantiates + decorates graph edges#3
Open
mschreib28 wants to merge 2 commits into
Open
Conversation
Two new structural edges that fill gaps in the call graph for
modern JS/TS / Java / C# / Python / Kotlin codebases.
1) `instantiates` edges from `new Foo(...)`:
The bulk-extraction and visitFunctionBody dispatchers only
recognised `call_expression`; `new_expression` (and the equivalent
`object_creation_expression` / `instance_creation_expression` in
other grammars) was silently ignored. Adds INSTANTIATION_KINDS,
extractInstantiation(), and dispatch from BOTH the top-level
visitNode and the per-function-body walker. Children are still
descended so nested calls inside constructor args (`new Foo(bar())`)
get their own `calls` refs.
Output: a `bootstrap` function that does `new UserService(); new
UserController(svc)` now produces two `instantiates` edges to those
class nodes — previously zero edges.
2) `decorates` edges from `@Decorator` annotations:
Tree-sitter places decorator nodes BEFORE the symbol they apply to
in the AST, so the original walk-time dispatch saw the wrong
nodeStack head (file/class instead of class/method). Replaced with
extractDecoratorsFor(declNode, decoratedId) that runs from inside
extractClass / extractFunction / extractMethod after the symbol's
node id is known.
Looks for decorator nodes in two places:
- Direct named children of the declaration (method/property style)
- Preceding siblings in the parent (TypeScript class style:
@foo class X {} parses as parent { decorator, class_decl })
Sibling check uses startIndex comparison rather than reference
identity — tree-sitter web bindings return fresh JS wrappers from
parent/namedChild navigation, so `===` is unreliable. Took a debug
session to spot this; flagging in the comment so the next reader
doesn't re-introduce the bug.
Output: a `@Controller` class decorator + `@Get` method decorator
on a NestJS-style controller now produce two `decorates` edges
(class→Controller, method→Get) with the correct source nodes.
Verified live on a synthetic NestJS-shape fixture; all 380
existing tests pass.
…ric constructors, property/field decorators, marker_annotation, tests
Five fixes from independent semantic review:
- extractDecoratorsFor sibling walk now iterates BACKWARD from the
declaration and stops at the first non-decorator/annotation
separator. Previous version walked forward up to declStart and
consumed every decorator-typed sibling — so two adjacent
decorated classes (`@A class Foo {} @b class Bar {}`) had `@A`
spuriously attributed to `Bar`.
- extractInstantiation strips the type-argument suffix from the
constructor field text. `new Map<K, V>()` was producing
referenceName 'Map<K, V>' (the constructor field is a generic_type
node) and resolution always failed.
- extractProperty and extractField now call extractDecoratorsFor
after their createNode calls. NestJS-style `@Inject() private
svc: Foo` and Java field annotations were being silently dropped.
- consider() in extractDecoratorsFor recognises 'marker_annotation'
in addition to 'decorator'/'annotation'. Java's tree-sitter grammar
emits marker_annotation for arg-less annotations like @OverRide
and @deprecated; without this every Java marker annotation was
silently skipped.
- 6 new extraction tests covering: instantiates ref for new Foo(),
generic-type stripping (`new Container<string>()` -> 'Container'),
qualified-new keeps trailing identifier (`new ns.Foo()` -> 'Foo'),
decorates ref for @foo class X {}, regression for adjacent
decorated classes (each gets its OWN decorator), decorates ref
for @foo method().
Full test suite: 386 passed (was 380, +6 new extraction tests).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary\n\nTwo new structural edges that fill significant gaps in the call graph for modern JS/TS / Java / C# / Python / Kotlin codebases.\n\n###
instantiatesedges fromnew Foo(...)\n\nPreviously the extractor only recognisedcall_expression;new_expression(and equivalents in other grammars) was silently ignored, so constructor invocations produced zero graph edges.\n\nAdds anINSTANTIATION_KINDSset coveringnew_expression,object_creation_expression, andinstance_creation_expression. Wired into both the top-levelvisitNodedispatcher AND the per-function-bodyvisitForCallsAndStructurewalker —newcalls inside function bodies were being missed by the body walker even after the top-level dispatch was hooked up.\n\nHandles three subtleties:\n- Generic types:new Map<K, V>()strips the angle-bracket suffix to produce'Map', so resolution can match the class node.\n- Qualified constructors:new ns.Foo()keeps the trailing identifier ('Foo') — same shape as the existingextractCalldoes for member-call.\n- Nested calls: children are still walked, sonew Foo(bar())produces both aninstantiatesref and acallsref.\n\n###decoratesedges from@Decoratorannotations\n\nTree-sitter places decorator nodes BEFORE the symbol they apply to, so a naive walk-time dispatch saw the wrong nodeStack head (file/class instead of class/method). The fix runs decorator extraction from insideextractClass/extractFunction/extractMethod/extractProperty/extractField, after the symbol's node id is known.\n\nLooks for decorator nodes in two places:\n- Direct named children of the declaration (method/property style).\n- Preceding siblings in the parent (TypeScript class style:@Foo class X {}parses asparent { decorator, class_decl }).\n\nHandles two subtleties:\n- Sibling boundary: walks BACKWARD from the declaration and stops at the first non-decorator separator. Without that stop,@A class Foo {} @B class Bar {}would attribute@AtoBar. Caught by reviewer; covered by a regression test.\n- Tree-sitter wrapper identity:parent/namedChildreturns fresh JS wrapper objects, sosibling === declNodeis unreliable — usesstartIndexcomparison instead.\n\nAlso recognises Java'smarker_annotation(no-args annotations like@Override/@Deprecated) alongsidedecorator/annotation.\n\n## Test plan\n\nVerified live on a synthetic NestJS-shape fixture:\n\n\nsrc kind tgt\nUserController decorates Controller (class decorator)\nlist decorates Get (method decorator)\nbootstrap instantiates UserService (new ...())\nbootstrap instantiates UserController\n\n\n- [x] 6 new extraction tests coveringnew Foo()/ generic-stripping / qualified-new /@Foo class X/ two-adjacent-decorated-classes regression /@Foo method()\n- [x]npx vitest run— 386 passed (was 380, +6 new)\n- [x]npx tsc --noEmitclean\n- [x]npm run buildsucceeds\n\n🤖 Generated with Claude Code\nCopied from colbymchenry/codegraph#134