Skip to content

Dart extractor: bare keyword-less calls not extracted; multi-line function endLine truncated; bodyless constructors not extracted #2082

Description

@carlos-alm

Discovered while adding Dart integration-test coverage to PR #2028 (constructor-call attribution, #1892).

Context: PR #2028 adds new ClassName() → constructor-method attribution. Java, PHP, and Groovy fixtures using new ClassName() verify this correctly. Attempting the analogous Dart fixture surfaced two separate, pre-existing gaps in src/extractors/dart.ts, unrelated to constructor attribution itself:

1. Bare (keyword-less) calls are not extracted at all.

Modern Dart permits omitting new for both plain function calls and constructor invocations (var x = Foo();, helper();). Neither is extracted as a Call by extractDartSymbols — confirmed empirically:

// helper(); inside main() → symbols.calls === []
// var w = Foo(); (no `new`) → symbols.calls === []
// var w = new Foo(); (explicit `new`) → symbols.calls === [{ name: 'Foo', line: N }]  ✓

Only the explicit new_expression node type is dispatched to handleDartConstructorCall in walkDartNode's switch (src/extractors/dart.ts). constructor_invocation is also mapped to the same handler, but apparently doesn't fire for a bare capitalized call the way the code comments assume — and there is no handler at all for a bare plain (non-capitalized) function call.

This means the existing hand-annotated fixtures in tests/benchmarks/resolution/fixtures/dart/*.dart (which use the idiomatic keyword-less style throughout, e.g. var repo = UserRepository(); in main.dart) do not actually resolve any of their "constructor" or "static" mode expected edges today. This is consistent with (and likely explains) the dart: { precision: 0.0, recall: 0.0 } floor in tests/benchmarks/resolution/resolution-benchmark.test.ts — the threshold is a placeholder because current recall is genuinely ~0%, not because the floor merely hasn't been ratcheted up yet.

2. Multi-line block-bodied function/method definitions get endLine truncated to the signature line, not the body's closing brace.

// Waldo makeWaldo() {\n  return new Waldo();\n}
// → definition: { name: 'makeWaldo', kind: 'function', line: 5, endLine: 5 }
// → call: { name: 'Waldo', line: 6 }   (line 6 > endLine 5!)

Because the call's line falls outside the recorded [line, endLine] span, downstream caller-attribution (findEnclosingBinding-style enclosing-function lookup during graph build) cannot attribute the call to makeWaldo — it falls back to file-level attribution (src: 'repro.dart', srcKind: 'file') instead. Single-line arrow-bodied functions (Waldo makeWaldo() => new Waldo();) do not hit this, since the whole function (signature + body) sits on one line.

This affects essentially any real-world multi-line Dart function/method (block bodies are the common case — e.g. UserService.createUser/getUser/removeUser in the resolution-benchmark fixture all use block bodies), so it is likely a second, independent contributor to Dart's current ~0% resolution recall, on top of gap #1.

Suggested fix directions (not investigated in depth — flagging for triage):

  • For Bump actions/setup-node from 4 to 6 #1: determine what constructor_invocation actually covers in the installed tree-sitter-dart grammar version, and add a real dispatch path for bare identifier-call syntax (likely needs a new node-type case in walkDartNode, mirroring how other languages disambiguate a capitalized bare call as a constructor vs. a plain function call).
  • For Bump actions/checkout from 4 to 6 #2: handleDartClass's extractDartClassMembers (for methods) and handleDartFunction/handleDartMethodSig (for top-level functions) currently call nodeEndLine(member)/nodeEndLine(node) on the method_signature/function_signature node itself; needs to instead measure through to the enclosing declaration/body node so endLine covers the full definition, not just the signature.

Impact: Both are scoped entirely to src/extractors/dart.ts (WASM) — have not checked whether crates/codegraph-core's Rust Dart extractor (if one exists) has analogous or different behavior; that should be checked as part of any fix for engine parity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    follow-upDeferred work from PR reviews that needs tracking

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions