Skip to content

Sitting Duck v1.8.0

Release date: 2026-04-26

A feature release adding parse-time filtering, call graph macros, and cross-language modifier fixes.

Highlights

  • Parse-time filtering: max_depth and prune parameters on read_ast(). Reduce AST size at parse time instead of filtering after the fact. max_depth caps tree depth; prune removes categories of nodes (syntax, comments, literals, imports, types, punctuation, unnamed, leaves, internal) with automatic tree healing — parent/child relationships, sibling indices, and descendant counts stay valid.
  • ast_get_calls() and ast_call_graph() macros for extracting function/method calls with call-type classification (function, method, constructor, macro) and building caller-to-callee graphs.
  • ast_find_references() macro for scope-chain-aware symbol resolution — finds all references to a named symbol, correctly handling shadowed definitions.
  • C# method name extraction fix (#010). Methods like private async Task<int> PrivateAsync() now correctly extract PrivateAsync instead of the return type Task. Uses tree-sitter's field API (ts_node_child_by_field_name) for reliable name extraction.
  • Async/modifier extraction across 7 languages (#009). async, static, suspend, pub, unsafe, mutating, inline, and access modifiers now correctly populate the modifiers[] column for Python, JavaScript, TypeScript, Rust, Kotlin, Swift, Dart, and C#.

New parameters

max_depth

Type: INTEGER Default: -1 (unlimited)

Limit AST tree depth at parse time.

-- Root node only
SELECT * FROM read_ast('file.py', max_depth := 0);

-- Root + direct children
SELECT * FROM read_ast('file.py', max_depth := 1);

Boundary nodes at the depth limit have children_count = 0 and descendant_count = 0 — the tree is structurally valid at the cut.

prune

Type: LIST(VARCHAR) Default: [] (no pruning)

Remove categories of nodes at parse time with automatic tree healing.

-- Remove syntax-only nodes (keywords, punctuation)
SELECT * FROM read_ast('file.py', prune := ['syntax']);

-- Remove comments and literals
SELECT * FROM read_ast('file.py', prune := ['comments', 'literals']);

-- Combine with max_depth
SELECT * FROM read_ast('file.py', prune := ['syntax'], max_depth := 3);
Policy Removes Mode
syntax Syntax-only nodes (keywords, operators, brackets) Node prune (re-parents children)
comments Comment nodes Node prune
punctuation Parser punctuation tokens Node prune
unnamed Nodes with empty names Node prune
literals Literal value nodes Subtree prune
imports Import/use statements Subtree prune
types Type annotation nodes Subtree prune
leaves Leaf nodes (no children) Node prune
internal Non-exported internal definitions Subtree prune

Node prune removes the node but re-parents its children to the grandparent. Subtree prune removes the node and all descendants. Both modes heal the tree: parent_id, children_count, descendant_count, and sibling_index are all recomputed.

New macros

ast_get_calls(source, language := NULL)

Extract all function/method calls with caller attribution and call-type classification.

SELECT caller_name, called_name, call_type, start_line
FROM ast_get_calls('src/**/*.py')
WHERE call_type = 'method';
Column Description
file_path Source file
caller_name Containing function (or <module>)
called_name Called function/method name
call_expression Code preview
call_type function, method, constructor, or macro
language Source language
start_line Call site line
node_id Call node ID
caller_node_id Caller function node ID

ast_call_graph(source, language := NULL)

Build an aggregated caller-to-callee graph.

SELECT caller, callee, call_type, call_count
FROM ast_call_graph('src/**/*.py')
WHERE caller = 'main';
Column Description
file_path Source file
caller Caller function name
callee Called function name
call_type Call classification
call_count Number of calls

ast_find_references(source, target_name, language := NULL)

Find all uses of a symbol via scope-chain resolution. Handles shadowed names correctly.

SELECT ref_kind, start_line, scope_name, peek
FROM ast_find_references('src/**/*.py', 'process');
Column Description
file_path Source file
name Target symbol name
ref_kind definition, call, or reference
node_type AST node type
start_line Location
peek Code preview
scope_name Containing function (or <module>)
def_node_id Resolved definition's node ID

Bug fixes

  • C# method name extraction (#010): method_declaration nodes now use tree-sitter's field API for the name field instead of the generic FIND_IDENTIFIER strategy, which incorrectly matched return type identifiers.
  • Async modifier extraction (#009): Seven language adapters (Python, JavaScript, TypeScript, Rust, Kotlin, Swift, Dart, C#) now correctly extract async/modifier keywords into the modifiers[] column. Includes cross-language test audit harness with 88 assertions.
  • ast_imports/ast_exports correctness (#68): Fixed parent filter for nested import nodes and IS_EXPORTED flag propagation.
  • IS_EXPORTED flag (#65): New bit-4 flag for file-level visibility, used by ast_exports and the :exported CSS pseudo-class.

Other changes

  • Custom selector predicates via ast_selector_predicate_* macros (#67) with PRAGMA sitting_duck_enable_dynamic_predicates for opt-in.
  • Documentation restructured into Divio framework (tutorials, how-to guides, reference, explanation).
  • New doc pages: cookbook, call graphs tutorial, complexity analysis, security audit, parse-once-query-many, find dead code.

Stats

  • 108 tests, 6084 assertions, 0 failures
  • 44 commits since v1.7.4

Key commits

Commit Description
2de7b74 fix: C# method name extraction uses field API (#010)
de570c9 feat: add SemanticFilter struct and prune fields to ExtractionConfig
7e106bb feat: implement CompilePrunePolicy for prune parameter compilation
1edaf41 feat: register max_depth and prune parameters on all read_ast variants
7939f96 feat: implement max_depth cutoff and prune traversal with tree healing
d98cb59 fix: explicit subtree flag on SemanticFilter, re-parenting tests
6e924fb feat: add ast_get_calls() and ast_call_graph() macros (#017)
b0100cd feat: add ast_find_references() macro with scope-chain resolution (#016)
3b82918 fix: extract async/modifier keywords for 7 languages (#009)
a71cfb2 feat: add IS_EXPORTED flag for file-level visibility (#65)
1ba713f feat: custom selector predicates via ast_selector_predicate_* macros (#67)