Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Query Cookbook

Practical examples for using the Query API to traverse the AST.


Imports

#![allow(unused)]
fn main() {
// Option A (canonical): import directly from bsharp_syntax
use bsharp_syntax::node::ast_node::NodeRef;
use bsharp_syntax::query::Query;
use bsharp_syntax::{CompilationUnit, ClassDeclaration, MethodDeclaration};

// Option B (ergonomic in analysis code): re-exports via bsharp_analysis
// use bsharp_analysis::framework::{NodeRef, Query};
}

All classes in a file

#![allow(unused)]
fn main() {
fn all_classes(cu: &CompilationUnit) -> Vec<&ClassDeclaration> {
    Query::from(NodeRef::CompilationUnit(cu))
        .of::<ClassDeclaration>()
        .collect()
}
}

All methods in a class

#![allow(unused)]
fn main() {
fn all_methods_in_class(c: &ClassDeclaration) -> Vec<&MethodDeclaration> {
    Query::from(NodeRef::from(c))
        .of::<MethodDeclaration>()
        .collect()
}
}

Public methods only

#![allow(unused)]
fn main() {
use bsharp_syntax::modifiers::Modifier;

fn public_methods(cu: &CompilationUnit) -> Vec<&MethodDeclaration> {
    Query::from(NodeRef::CompilationUnit(cu))
        .filter_typed::<MethodDeclaration>(|m| m.modifiers.iter().any(|mm| *mm == Modifier::Public))
        .collect()
}
}

Count await expressions

#![allow(unused)]
fn main() {
use bsharp_syntax::expressions::AwaitExpression;

fn await_count(cu: &CompilationUnit) -> usize {
    Query::from(NodeRef::CompilationUnit(cu))
        .of::<AwaitExpression>()
        .count()
}
}

Find invocations of a method name

#![allow(unused)]
fn main() {
use bsharp_syntax::expressions::{InvocationExpression, Expression};

fn invocations_of(cu: &CompilationUnit, name: &str) -> Vec<&InvocationExpression> {
    Query::from(NodeRef::CompilationUnit(cu))
        .filter_typed::<InvocationExpression>(|inv| {
            // Match simple Variable(...) calls; extend for MemberAccess as needed
            match &*inv.expression {
                Expression::Variable(id) => id.name == name,
                _ => false,
            }
        })
        .collect()
}
}

Methods with deep nesting

#![allow(unused)]
fn main() {
use bsharp_syntax::statements::statement::Statement;

fn deeply_nested_methods(cu: &CompilationUnit, threshold: usize) -> Vec<&MethodDeclaration> {
    Query::from(NodeRef::CompilationUnit(cu))
        .filter_typed::<MethodDeclaration>(|m| {
            if let Some(body) = &m.body {
                max_nesting(body, 0) > threshold
            } else {
                false
            }
        })
        .collect()
}

fn max_nesting(s: &Statement, cur: usize) -> usize {
    match s {
        Statement::If(i) => {
            let then_d = max_nesting(&i.consequence, cur + 1);
            let else_d = i.alternative.as_ref().map(|a| max_nesting(a, cur + 1)).unwrap_or(cur);
            then_d.max(else_d)
        }
        Statement::Block(stmts) => stmts.iter().map(|st| max_nesting(st, cur)).max().unwrap_or(cur),
        Statement::For(f) => max_nesting(&f.body, cur + 1),
        Statement::ForEach(f) => max_nesting(&f.body, cur + 1),
        Statement::While(w) => max_nesting(&w.body, cur + 1),
        Statement::DoWhile(d) => max_nesting(&d.body, cur + 1),
        _ => cur,
    }
}
}

Tips

  • Chain filters sparingly: Prefer a single filter_typed with a clear predicate.
  • Use NodeRef::from(x): Start from any AST node to scope queries.
  • Profile: For hot paths, consider a custom walker when you need full control.
2025-11-17 15:18:26 • commit: 03a4e25