1 | Zero CALLS edges for Dart files | Critical | Dart only
2 | importers_of query returns 0 results for Dart package: imports | Critical | Dart only
3 | inheritors_of query returns 0 results due to bare vs qualified name mismatch | Medium | All languages
Combined effect: callers_of, callees_of, importers_of, inheritors_of, get_impact_radius_tool, detect_changes_tool, and get_affected_flows_tool are all non-functional for Dart projects.
Bug 1: Zero CALLS edges for Dart
Observed behavior
After a full build on a Dart project with ~7K files:
Edges: 101,398
IMPORTS_FROM: 58,033
CONTAINS: 33,096
INHERITS: 10,036
CALLS: 233 ← all from 9 JavaScript files, 0 from Dart
query_graph_tool(pattern="callers_of", target="<any Dart method>") always returns 0 results.
Root cause
parser.py line ~200 — the _CALL_TYPES dictionary does not contain a "dart" entry:
_CALL_TYPES: dict[str, list[str]] = {
"python": ["call"],
"javascript": ["call_expression"],
"typescript": ["call_expression"],
# ... other languages
# "dart" is missing
}
When the parser walks the AST (_extract_from_tree), it does:
call_types = set(_CALL_TYPES.get(language, []))
For Dart, this returns an empty set, so no CALLS edges are ever created.
Why it's not a simple fix
Dart's tree-sitter grammar (tree-sitter-dart) does not provide a call_expression node type like most other languages. A function call like print('hello') produces:
expression_statement
identifier ← function name "print"
selector
argument_part
arguments ← the parenthesized args
And a method call like obj.method() produces:
expression_statement
identifier ← "obj"
selector
unconditional_assignable_selector ← ".method"
selector
argument_part
arguments
There is no single wrapper node type for the call. Adding "dart": ["call_expression"] to _CALL_TYPES would have no effect.
Suggested fix
Add a Dart-specific call extraction handler (similar to the existing _extract_r_constructs or _extract_lua_constructs). The handler would detect expression_statement nodes where a child selector contains argument_part, then trace back the preceding identifier / unconditional_assignable_selector chain to extract the callee name.
Bug 2: importers_of returns 0 for Dart package: imports
Observed behavior
query_graph_tool(pattern="importers_of", target="lib/features/casino_v2/constants/casino_tab_bar_category/casino_tab_bar_category.dart")
# → 0 results
Even though dozens of files import this file via import 'package:tiger_app/features/casino_v2/constants/...'.
Root cause
Edge storage: _extract_import (parser.py ~line 2388) correctly extracts the import string. But _resolve_module_to_file (parser.py ~line 2079) only resolves relative Dart imports (starting with .). For package: URIs, it returns None, so the edge target is stored as the raw URI string:
IMPORTS_FROM: /abs/path/to/file_a.dart → package:tiger_app/features/.../file_b.dart
Query: importers_of (query.py ~line 229) resolves the target to an absolute file path (/Users/.../lib/features/.../file_b.dart) and queries store.get_edges_by_target(abs_path). Since edges store package:tiger_app/... and the query uses /Users/.../lib/..., they never match.
Suggested fix
In _do_resolve_module, add package: URI resolution for Dart:
- Read
pubspec.yaml to get the package name
- Map
package:<name>/path/to/file.dart → <project_root>/lib/path/to/file.dart
- Store the resolved absolute path as the edge target
This would make importers_of work, and also improve get_impact_radius_tool accuracy since it traverses IMPORTS_FROM edges.
Bug 3: inheritors_of returns 0 (all languages)
Observed behavior
query_graph_tool(pattern="inheritors_of", target="Animal")
# → 0 results, even though Dog extends Animal in the same file
Root cause
Edge storage: _get_bases returns bare class names. INHERITS edges are stored with unqualified targets:
INHERITS: tests/fixtures/sample.dart::Dog → Animal ← bare name
INHERITS: tests/fixtures/sample.dart::Dog → SwimmingMixin ← bare name
Query: inheritors_of (query.py ~line 263) first resolves "Animal" to a node, getting the qualified name tests/fixtures/sample.dart::Animal. It then calls store.get_edges_by_target("tests/fixtures/sample.dart::Animal"). The SQL is WHERE target_qualified = ?. Since the stored target is "Animal" (bare), it never matches "tests/fixtures/sample.dart::Animal".
This bug is not Dart-specific — it affects all languages because _get_bases always returns bare class names.
Suggested fix
Either:
Option A (fix at storage time): In _get_bases or the INHERITS edge creation step, resolve bare class names to qualified names using the file's symbol table or a cross-file lookup during the graph merge phase.
Option B (fix at query time): In the inheritors_of query handler, add a fallback that also matches bare target names. Similar to how callers_of already has search_edges_by_target_name fallback logic.
Option B is lower risk and doesn't require reprocessing existing graphs.
Test gap
tests/test_parser.py lines 277-345 test Dart for:
- Language detection
- Class/Function/Mixin/Enum node extraction
- Import edges
- Inheritance edges
- Contains edges
- Method parent resolution
There are no tests for Dart CALLS edges, which is why Bug 1 was never caught. Adding a test like test_parse_dart_call_edges that parses a file with function/method calls and asserts CALLS edges exist would prevent regression.
Environment
- code-review-graph: v2.0.0
- Python: 3.13
- tree-sitter: <1, >=0.23.0
- tree-sitter-language-pack: <1, >=0.3.0
- OS: macOS Darwin 24.3.0
1 | Zero CALLS edges for Dart files | Critical | Dart only
2 | importers_of query returns 0 results for Dart package: imports | Critical | Dart only
3 | inheritors_of query returns 0 results due to bare vs qualified name mismatch | Medium | All languages
Combined effect:
callers_of,callees_of,importers_of,inheritors_of,get_impact_radius_tool,detect_changes_tool, andget_affected_flows_toolare all non-functional for Dart projects.Bug 1: Zero CALLS edges for Dart
Observed behavior
After a full build on a Dart project with ~7K files:
query_graph_tool(pattern="callers_of", target="<any Dart method>")always returns 0 results.Root cause
parser.pyline ~200 — the_CALL_TYPESdictionary does not contain a"dart"entry:When the parser walks the AST (
_extract_from_tree), it does:For Dart, this returns an empty set, so no CALLS edges are ever created.
Why it's not a simple fix
Dart's tree-sitter grammar (
tree-sitter-dart) does not provide acall_expressionnode type like most other languages. A function call likeprint('hello')produces:And a method call like
obj.method()produces:There is no single wrapper node type for the call. Adding
"dart": ["call_expression"]to_CALL_TYPESwould have no effect.Suggested fix
Add a Dart-specific call extraction handler (similar to the existing
_extract_r_constructsor_extract_lua_constructs). The handler would detectexpression_statementnodes where a childselectorcontainsargument_part, then trace back the precedingidentifier/unconditional_assignable_selectorchain to extract the callee name.Bug 2:
importers_ofreturns 0 for Dartpackage:importsObserved behavior
Even though dozens of files import this file via
import 'package:tiger_app/features/casino_v2/constants/...'.Root cause
Edge storage:
_extract_import(parser.py ~line 2388) correctly extracts the import string. But_resolve_module_to_file(parser.py ~line 2079) only resolves relative Dart imports (starting with.). Forpackage:URIs, it returnsNone, so the edge target is stored as the raw URI string:Query:
importers_of(query.py ~line 229) resolves the target to an absolute file path (/Users/.../lib/features/.../file_b.dart) and queriesstore.get_edges_by_target(abs_path). Since edges storepackage:tiger_app/...and the query uses/Users/.../lib/..., they never match.Suggested fix
In
_do_resolve_module, addpackage:URI resolution for Dart:pubspec.yamlto get the package namepackage:<name>/path/to/file.dart→<project_root>/lib/path/to/file.dartThis would make
importers_ofwork, and also improveget_impact_radius_toolaccuracy since it traverses IMPORTS_FROM edges.Bug 3:
inheritors_ofreturns 0 (all languages)Observed behavior
Root cause
Edge storage:
_get_basesreturns bare class names. INHERITS edges are stored with unqualified targets:Query:
inheritors_of(query.py ~line 263) first resolves"Animal"to a node, getting the qualified nametests/fixtures/sample.dart::Animal. It then callsstore.get_edges_by_target("tests/fixtures/sample.dart::Animal"). The SQL isWHERE target_qualified = ?. Since the stored target is"Animal"(bare), it never matches"tests/fixtures/sample.dart::Animal".This bug is not Dart-specific — it affects all languages because
_get_basesalways returns bare class names.Suggested fix
Either:
Option A (fix at storage time): In
_get_basesor the INHERITS edge creation step, resolve bare class names to qualified names using the file's symbol table or a cross-file lookup during the graph merge phase.Option B (fix at query time): In the
inheritors_ofquery handler, add a fallback that also matches bare target names. Similar to howcallers_ofalready hassearch_edges_by_target_namefallback logic.Option B is lower risk and doesn't require reprocessing existing graphs.
Test gap
tests/test_parser.pylines 277-345 test Dart for:There are no tests for Dart CALLS edges, which is why Bug 1 was never caught. Adding a test like
test_parse_dart_call_edgesthat parses a file with function/method calls and assertsCALLSedges exist would prevent regression.Environment