feat(graph): full Dart support via tree-sitter AST#73
Conversation
Dart previously fell through to the regex symbol fallback, which cannot match type-first signatures (void foo(), Future<int> baz() async), so classes, methods, and calls were invisible to codebase_symbol, codebase_flow, and codebase_impact, and files were chunked by line count instead of declaration boundaries. - Register @ast-grep/lang-dart as a dynamic grammar (per-grammar failure isolation keeps missing prebuilds on the regex fallback). - Add extractFromDart: classes, mixins (trait), enums, extensions, typedefs, type-first top-level functions, getters/setters, and constructors including named and factory forms. Scope ranges are stitched from Dart's sibling function_signature/function_body pairs. Calls are recovered from argument_part nodes: method calls, bare calls, constructor invocations, prefixed calls, and cascades. - Add dart to TOP_LEVEL_KINDS so chunking follows declaration boundaries; signature and body kinds are both listed so the overlap-merge fuses each pair into one region. - Add dart main() to ENTRY_POINT_NAMES for entry-point detection. - Move Dart to Full Support in the README language matrix.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR upgrades Dart to full AST-driven support: adds the ast-grep Dart package and registers it; implements extractFromDart to parse symbols and calls; extends TOP_LEVEL_KINDS and ENTRY_POINT_NAMES for AST chunking and main() detection; and adds tests for symbols, calls, chunking, and entrypoints. ChangesFull Dart Support via AST Extraction
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/services/graph-symbols.ts`:
- Around line 407-414: The class/extension handling in extractSymbols misses
Dart "extension type" nodes; update the nodeKind check in the block that
currently tests for "class_definition" | "mixin_declaration" |
"extension_declaration" (the code that finds nameNode, calls addSym, and uses
childOfKind(..., "class_body") ?? childOfKind(..., "extension_body") then
walkMembers) to also include "extension_type_declaration" and ensure you look
for its body via childOfKind(..., "extension_type_body"); additionally, in
src/services/indexer.ts add "extension_type_declaration" to the Dart
TOP_LEVEL_KINDS constant so chunking treats these declarations as top-level
boundaries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 11a130bf-c3ec-45c3-98a1-7d6195ab5893
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (9)
README.mdpackage.jsonsrc/constants.tssrc/services/code-graph.tssrc/services/graph-symbols.tssrc/services/indexer.tstests/unit/graph-entrypoints.test.tstests/unit/graph-symbols.test.tstests/unit/indexer.test.ts
The vendored @ast-grep/lang-dart 0.0.7 grammar predates Dart 3.3 extension types and parses them to ERROR nodes; the kinds a newer tree-sitter-dart exposes (extension_type_declaration) do not exist in this version, so matching on them would be unreachable code. Document the limitation at the walk site and lock in the contract: no throw, no bogus symbols from the ERROR region, and the rest of the file still extracts normally.
Summary
Dart moves from the regex symbol fallback to full tree-sitter support. The regex fallback matches
function NAME/def NAMEstyle declarations and cannot see Dart's type-first signatures (void foo(),Future<int> baz() async), so until now classes, mixins, methods, and call sites were invisible to the symbol graph:codebase_symbolandcodebase_flowreturned empty for Dart,codebase_impacthad no source nodes,main()was never detected as an entry point, and files were chunked by line count instead of declaration boundaries. This PR fixes all five gaps reported in the issue in one pass.Changes
@ast-grep/lang-dart@0.0.7inensureDynamicLanguages(same family and version as the Lua grammar shipped in feat(graph): Lua symbol/call extraction + fix file discovery for whitelist .gitignore #67). The existing per-grammarlibraryPathpre-validation isolates platforms without a prebuilt binary, which degrade to today's regex behavior instead of breaking.extractFromDartin graph-symbols.ts): classes, mixins (astrait), enums, extensions, typedefs (asinterface), type-first top-level functions, getters and setters, and constructors in all three forms (plain, named, factory) asconstructorwith dotted qualified names. Two Dart grammar quirks are handled explicitly: a function is afunction_signaturefollowed by a SIBLINGfunction_body, so scope ranges are stitched from each pair (otherwise calls inside bodies would attribute to<module>); and plain constructors live inside a genericdeclarationwrapper.call_expressionnode kind. Every invocation wraps anargument_part, so calls are recovered from those: bare calls (helper(5)), method calls (f.bar(2)), constructor invocations (Foo(1), nonewin modern Dart), prefixed calls (mat.runApp()), and cascades (f..bar(3)..load()viacascade_selector). Each call is attributed to its enclosing function scope.dartadded toTOP_LEVEL_KINDS. Bothfunction_signatureandfunction_bodyare listed so the existing overlap-merge infindAstBoundariesfuses each sibling pair into one region; class/mixin/enum/extension nodes already span their bodies.dart: mainadded toENTRY_POINT_NAMES; detection is name-based per language, so Dartmain()now surfaces through the existing well-known-name heuristic with no new path logic.Out of scope, per the issue's own framing: the Flutter-specific layer (Widget awareness, pubspec-based resolution) and plain field symbols (not callable, no call-graph value).
The node kinds and ranges used by the extractor were derived empirically from the actual
@ast-grep/lang-darttree output, not assumed.Type of change
Testing
npm run test:unit): 846/846npm run test:integration) — if applicablenpx tsc --noEmit)New tests encode the behavior that matters, not just coverage: the symbol test asserts the exact declarations the regex fallback could never match (constructors in all three forms, getter/setter pairs, mixin as trait, type-first signatures); a dedicated test proves a top-level function's scope reaches its sibling body's closing brace (the failure mode being calls attributing to
<module>); the call test covers method calls, cascades, constructor invocations, and prefixed calls; the chunking test proves an entire large class lands in one chunk where a fixed 100-line window would split it mid-body, and that a signature/body pair is never severed; the entry-point test asserts Dartmain()fires the well-known-name heuristic on a non-orphan file. The old "handles Dart via regex fallback" test is removed and the fallback block retitled, since Dart no longer routes there.Backward compatibility: no migration needed. Existing indexed Dart chunks are untouched until a file changes (hash-skip), changed files have old chunks deleted before re-insert, cached symbol graphs keep working and pick up AST symbols on their next rebuild, and platforms without a Dart prebuild keep exactly the current regex behavior.
Checklist
Related issues
Fixes #71
Summary by CodeRabbit
New Features
Documentation
Tests