| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
…body Closes #2182 docs check acknowledged: README's language-support table already shows Dart's Dataflow column as a wired-but-not-fully-complete boolean, unaffected by this fix (this closes a scope-visibility bug in the existing wiring, it doesn't add net-new coverage the table would need to newly claim); the resolution precision/recall table is a separate, script-generated benchmark for call resolution, untouched by this change. No architecture/language list changes. tree-sitter-dart's function_signature/method_signature never contain their own body — it's a SIBLING node (function_body) under the same parent, not a child. Every walker that recurses only into a matched function node's own children (src/ast-analysis/visitor.ts's shared DFS walker, plus the standalone computeFunctionComplexity/computeHalsteadMetrics in src/features/complexity.ts) exits/pops the function's scope before ever reaching the body, so complexity/Halstead would silently compute the trivial base case, and dataflowDart's return/assignment/call/mutation tracking (b2.ts) never saw a single statement. ## Fix Added a generic bodySiblingTypes mechanism (ComplexityRules/HalsteadRules/ DataflowRulesConfig, Visitor/WalkOptions) so a language's rules can declare that a function boundary's body lives in a sibling node. visitor.ts's walker now finds and walks that sibling — with the function's scope frame still active — before firing exitFunction/popping, instead of only ever seeing the boundary node's own subtree. Wired dataflowDart to it now, since it's the only live Dart consumer; COMPLEXITY_RULES/HALSTEAD_RULES don't have a Dart entry yet (#1923's tier-2 rollout), but the mechanism is ready for when they do. A real bug surfaced while building this: the natural "don't re-walk a sibling already consumed" guard used a Set<TreeSitterNode> keyed by object reference, but tree-sitter node wrapper objects aren't stable across separate .child()/.parent() calls for the same AST position (confirmed by the existing .id-based comparison already used in extractors/dart.ts) — so the guard silently never matched, and the sibling was walked twice. Caught by a dedicated regression test, not by output inspection (dataflow's own scope-name gating happened to mask the symptom for parameters/returns in the cases I checked by hand; an ungated visitor like the future complexity one would have silently doubled cyclomatic/cognitive counts for every Dart function's body). Fixed by tracking node .id instead of the node itself. ## Additional dataflowDart bugs found while verifying the fix end-to-end The fix produced nothing observable until these were also corrected — all independent of the sibling-body issue itself, contrary to this repo's #2182 report assuming parameter extraction "happened to work": - paramListField: 'parameters' can never match — neither function_signature nor method_signature has a named `parameters` field in this grammar (confirmed via node-types.json); formal_parameter_list is an unnamed positional child. Replaced with a getParamListNode override. - method_signature has no `name` field of its own — a method's name lives on its nested function_signature/getter_signature/setter_signature/ constructor_signature child. Without a matching nameExtractor, every return/assignment/call inside a Dart METHOD's body was silently dropped (handleReturn et al. gate on scope.funcName being truthy). - extractDartParamName checked for 'optional_formal_parameter'/ 'named_formal_parameter' node types that don't exist in this grammar — only the plural optional_formal_parameters (a group wrapper for `[..]`/`{..}` param lists) exists. Added recursive handling for it. ## Scope boundary Filed rather than fixed inline (independent of the sibling-body architecture bug specifically): - #2356 — arrow-body (=>) functions never record an implicit return - #2357 — local variable declarations/assignments and calls still entirely unconfigured for Dart - #2358 — extractParams gives every name from one grouped-param wrapper the same paramIndex (a cross-language generic-mechanism gap, not Dart- specific) - #2359 — Rust engine has no Dart dataflow at all; needs a body_sibling_types-aware DartDataflowRules port, noting the Rust-native tree-sitter-dart grammar structurally differs from the WASM one (a function_declaration wrapper node, and a real `parameters` field that the WASM grammar lacks) Added crate::shared::ast_nodes::find_body_sibling_node (Rust mirror of the new TS helper, unit-tested against the real Rust-native tree-sitter-dart grammar) plus doc comments at both engines' language-rules registries pointing future implementers at it, but did not touch LangRules/ DataflowRules' ~19 existing per-language struct literals for a mechanism with zero current Rust consumers. ## Verification - New tests/unit/visitor.test.ts (#2182) suite: scope stays active through the sibling, the sibling is walked exactly once, and two sibling functions each get their own scope frame. - New tests/parsers/dataflow-dart.test.ts: parameters (top-level function, class method with no double-count, optional/named groups) and returns (top-level function, class method, two independent functions). - Manually verified computeFunctionComplexity/computeAllMetrics against a synthetic Dart complexity config with an if/else-if/else body: cyclomatic 3 both before and after wiring through bodySiblingTypes (no doubling). - Full npm test: 289 files, 4630 passed, 30 skipped, 2 todo. - cargo test --lib: 823 passed (3 new). - npx tsc --noEmit, npm run lint, cargo fmt --check: clean. - scripts/parity-compare.mjs --langs javascript,typescript,tsx,dart: node/ edge counts identical between engines (the only parity dimension this change could affect — the shared walker fix is TS/WASM-only and gated entirely behind a bodySiblingTypes check that stays empty, hence a no-op, for every language but Dart). Impact: 18 functions changed, 0 affected
Greptile SummaryThe PR extends the TypeScript AST walker to traverse sibling function bodies while retaining function scope, then enables that behavior for Dart dataflow and corrects Dart method-name and parameter extraction.
Confidence Score: 5/5The PR appears safe to merge with no concrete blocking or independently actionable non-blocking issue identified. The new traversal keeps Dart sibling bodies inside their function scope, suppresses their later natural traversal by stable node ID, and does not currently collide with other Dart analysis visitors. Important Files Changed
Sequence DiagramsequenceDiagram
participant Walker
participant Signature as Dart function signature
participant Visitor as Dataflow visitor
participant Body as Sibling function_body
Walker->>Signature: enter function boundary
Signature->>Visitor: push function scope
Walker->>Signature: traverse signature children
Walker->>Body: locate sibling body
Walker->>Visitor: traverse body with scope active
Walker->>Walker: record body node ID as consumed
Signature->>Visitor: exit function and pop scope
Walker-->>Body: skip later natural traversal
Reviews (1): Last reviewed commit: "fix(ast-analysis): keep function scope a..." | Re-trigger Greptile |
Sorry, something went wrong.
Codegraph Impact Analysis11 functions changed → 27 callers affected across 8 files
|
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Summary
Closes #2182
tree-sitter-dart's function_signature/method_signature never contain their own body — it's a SIBLING node (function_body) under the same parent, not a child. Every walker that recurses only into a matched function node's own children (src/ast-analysis/visitor.ts's shared DFS walker, plus the standalone computeFunctionComplexity/computeHalsteadMetrics in src/features/complexity.ts) exits/pops the function's scope before ever reaching the body, so complexity/Halstead would silently compute the trivial base case, and dataflowDart's return/assignment/call/mutation tracking (b2.ts) never saw a single statement.
Fix
Added a generic bodySiblingTypes mechanism (ComplexityRules/HalsteadRules/DataflowRulesConfig, Visitor/WalkOptions) so a language's rules can declare that a function boundary's body lives in a sibling node. visitor.ts's walker now finds and walks that sibling — with the function's scope frame still active — before firing exitFunction/popping, instead of only ever seeing the boundary node's own subtree. Wired dataflowDart to it now, since it's the only live Dart consumer; COMPLEXITY_RULES/HALSTEAD_RULES don't have a Dart entry yet (#1923's tier-2 rollout), but the mechanism is ready for when they do.
A real bug surfaced while building this: the "don't re-walk an already-consumed sibling" guard used a Set<TreeSitterNode> keyed by object reference, but tree-sitter node wrapper objects aren't stable across separate .child()/.parent() calls for the same AST position (confirmed by the existing .id-based comparison already used in extractors/dart.ts) — so the guard silently never matched, and the sibling was walked twice. Caught by a dedicated regression test, not by output inspection (dataflow's own scope-name gating happened to mask the symptom for parameters/returns in the cases I checked by hand; an ungated visitor like the future complexity one would have silently doubled cyclomatic/cognitive counts for every Dart function's body). Fixed by tracking node .id instead of the node itself.
Additional dataflowDart bugs found while verifying the fix end-to-end
The fix produced nothing observable until these were also corrected — all independent of the sibling-body issue itself, contrary to this repo's #2182 report assuming parameter extraction "happened to work":
Scope boundary
Filed rather than fixed inline (independent of the sibling-body architecture bug specifically):
Added crate::shared::ast_nodes::find_body_sibling_node (Rust mirror of the new TS helper, unit-tested against the real Rust-native tree-sitter-dart grammar) plus doc comments at both engines' language-rules registries pointing future implementers at it, but did not touch LangRules/DataflowRules' ~19 existing per-language struct literals for a mechanism with zero current Rust consumers.
Test plan