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

Tree-sitter Queries

Tree-sitter queries use S-expression syntax to match patterns in parsed syntax trees. This reference covers the query language and common patterns.

Query Syntax

Basic Pattern

Match a node by type:

(identifier)

Named Children

Access specific child nodes:

(function_item name: (identifier))

Captures

Capture matched nodes with @name:

(function_item name: (identifier) @fn_name)

Anonymous Nodes

Match literal tokens with quotes:

(binary_expression operator: "+" @plus)

Wildcards

Match any node type:

(call_expression function: (_) @fn)

Alternations

Match multiple patterns:

[
  (function_item name: (identifier) @fn)
  (struct_item name: (type_identifier) @fn)
]

Quantifiers

  • ? - Optional (0 or 1)
  • * - Zero or more
  • + - One or more
(function_item
  (attribute_item)* @attrs
  name: (identifier) @name)

Anchors

  • . - Anchor to start or end of siblings
(block . (expression_statement) @first)  ; First statement
(block (expression_statement) @last .)   ; Last statement

Predicates

#eq?

Match exact text:

((identifier) @fn
  (#eq? @fn "main"))

#match?

Match regex pattern:

((identifier) @fn
  (#match? @fn "^test_"))

#not-eq?, #not-match?

Negated versions:

((identifier) @fn
  (#not-eq? @fn "main"))

Language Examples

Rust

#![allow(unused)]
fn main() {
// Function definitions
"(function_item name: (identifier) @fn)"

// Async functions
"(function_item (function_modifiers (async)) name: (identifier) @async_fn)"

// Public functions
"(function_item (visibility_modifier) name: (identifier) @pub_fn)"

// Struct definitions
"(struct_item name: (type_identifier) @struct)"

// Enum definitions
"(enum_item name: (type_identifier) @enum)"

// Impl blocks
"(impl_item type: (type_identifier) @impl_type)"

// Trait definitions
"(trait_item name: (type_identifier) @trait)"

// Use statements
"(use_declaration argument: (_) @import)"

// Macro invocations
"(macro_invocation macro: (identifier) @macro)"

// Method calls
"(call_expression
  function: (field_expression field: (field_identifier) @method))"

// Unsafe blocks
"(unsafe_block) @unsafe"

// Attribute macros
"(attribute_item (attribute) @attr)"

// String literals
"(string_literal) @string"

// Function calls with specific name
"((call_expression
    function: (identifier) @fn)
  (#eq? @fn \"unwrap\"))"
}

TypeScript/JavaScript

#![allow(unused)]
fn main() {
// Function declarations
"(function_declaration name: (identifier) @fn)"

// Arrow functions
"(arrow_function) @arrow"

// Variable with arrow function
"(variable_declarator
  name: (identifier) @name
  value: (arrow_function))"

// Class declarations
"(class_declaration name: (type_identifier) @class)"

// Method definitions
"(method_definition name: (property_identifier) @method)"

// Interface declarations
"(interface_declaration name: (type_identifier) @interface)"

// Type aliases
"(type_alias_declaration name: (type_identifier) @type)"

// Import statements
"(import_statement) @import"

// Export statements
"(export_statement) @export"

// JSX elements
"(jsx_element
  open_tag: (jsx_opening_element name: (_) @tag))"

// React hooks (functions starting with use)
"((call_expression
    function: (identifier) @hook)
  (#match? @hook \"^use\"))"

// Async functions
"(function_declaration (async) name: (identifier) @async_fn)"
}

Python

#![allow(unused)]
fn main() {
// Function definitions
"(function_definition name: (identifier) @fn)"

// Class definitions
"(class_definition name: (identifier) @class)"

// Method definitions (in class)
"(class_definition
  body: (block
    (function_definition name: (identifier) @method)))"

// Decorated functions
"(decorated_definition
  definition: (function_definition name: (identifier) @fn))"

// Import statements
"(import_statement) @import"
"(import_from_statement) @from_import"

// Specific imports
"(import_from_statement
  module_name: (dotted_name) @module)"

// Async functions
"(function_definition (async) name: (identifier) @async_fn)"

// Lambda expressions
"(lambda) @lambda"

// Docstrings
"(function_definition
  body: (block . (expression_statement (string)) @docstring))"

// f-strings
"(string (interpolation) @fstring)"

// Type annotations
"(type) @type_annotation"
}

Go

#![allow(unused)]
fn main() {
// Function declarations
"(function_declaration name: (identifier) @fn)"

// Method declarations (with receiver)
"(method_declaration
  receiver: (parameter_list) @receiver
  name: (field_identifier) @method)"

// Struct types
"(type_declaration
  (type_spec name: (type_identifier) @struct type: (struct_type)))"

// Interface types
"(type_declaration
  (type_spec name: (type_identifier) @interface type: (interface_type)))"

// Package declaration
"(package_clause (package_identifier) @package)"

// Import declarations
"(import_declaration) @import"
"(import_spec path: (interpreted_string_literal) @path)"

// Function calls
"(call_expression function: (identifier) @fn)"

// Method calls
"(call_expression function: (selector_expression field: (field_identifier) @method))"

// Struct literals
"(composite_literal type: (type_identifier) @struct_type)"

// Variable declarations
"(var_declaration) @var"
"(short_var_declaration left: (expression_list (identifier) @var))"

// Constants
"(const_declaration) @const"

// Go routines
"(go_statement) @goroutine"

// Defer statements
"(defer_statement) @defer"

// Error handling pattern
"(if_statement
  condition: (binary_expression
    left: (identifier) @err
    right: (nil))
  (#eq? @err \"err\"))"
}

Java

#![allow(unused)]
fn main() {
// Class declarations
"(class_declaration name: (identifier) @class)"

// Interface declarations
"(interface_declaration name: (identifier) @interface)"

// Enum declarations
"(enum_declaration name: (identifier) @enum)"

// Method declarations
"(method_declaration name: (identifier) @method)"

// Constructor declarations
"(constructor_declaration name: (identifier) @constructor)"

// Field declarations
"(field_declaration declarator: (variable_declarator name: (identifier) @field))"

// Package declaration
"(package_declaration) @package"

// Import statements
"(import_declaration) @import"

// Annotations
"(annotation name: (identifier) @annotation)"
"(marker_annotation name: (identifier) @annotation)"

// Static methods
"(method_declaration
  (modifiers (static))
  name: (identifier) @static_method)"

// Abstract methods
"(method_declaration
  (modifiers (abstract))
  name: (identifier) @abstract_method)"

// Lambda expressions
"(lambda_expression) @lambda"

// Method invocations
"(method_invocation name: (identifier) @call)"

// Object creation
"(object_creation_expression type: (type_identifier) @type)"

// Try-catch blocks
"(try_statement) @try"
"(catch_clause) @catch"

// Spring annotations
"((annotation name: (identifier) @ann)
  (#match? @ann \"^(Controller|Service|Repository|Component|Autowired)\"))"
}

C#

#![allow(unused)]
fn main() {
// Class declarations
"(class_declaration name: (identifier) @class)"

// Interface declarations
"(interface_declaration name: (identifier) @interface)"

// Struct declarations
"(struct_declaration name: (identifier) @struct)"

// Record declarations (C# 9+)
"(record_declaration name: (identifier) @record)"

// Enum declarations
"(enum_declaration name: (identifier) @enum)"

// Method declarations
"(method_declaration name: (identifier) @method)"

// Property declarations
"(property_declaration name: (identifier) @property)"

// Field declarations
"(field_declaration (variable_declaration
  (variable_declarator (identifier) @field)))"

// Constructor declarations
"(constructor_declaration name: (identifier) @constructor)"

// Namespace declarations
"(namespace_declaration name: (_) @namespace)"

// Using directives
"(using_directive) @using"

// Attributes
"(attribute name: (identifier) @attribute)"

// Async methods
"(method_declaration
  (modifier (async))
  name: (identifier) @async_method)"

// Static methods
"(method_declaration
  (modifier (static))
  name: (identifier) @static_method)"

// Lambda expressions
"(lambda_expression) @lambda"

// LINQ queries
"(query_expression) @linq"

// Method invocations
"(invocation_expression
  function: (member_access_expression name: (identifier) @call))"

// Object creation
"(object_creation_expression type: (identifier) @type)"

// Pattern matching
"(switch_expression) @switch_expression"
"(is_pattern_expression) @is_pattern"

// Null-conditional access
"(conditional_access_expression) @null_conditional"

// ASP.NET attributes
"((attribute name: (identifier) @attr)
  (#match? @attr \"^(HttpGet|HttpPost|Route|Authorize|ApiController)\"))"
}

Ruby

#![allow(unused)]
fn main() {
// Class definitions
"(class name: (constant) @class)"

// Module definitions
"(module name: (constant) @module)"

// Method definitions
"(method name: (identifier) @method)"

// Singleton method definitions (class methods)
"(singleton_method name: (identifier) @class_method)"

// Blocks
"(block) @block"
"(do_block) @do_block"

// Lambda expressions
"(lambda) @lambda"

// Require statements
"(call method: (identifier) @req (#eq? @req \"require\"))"
"(call method: (identifier) @req (#eq? @req \"require_relative\"))"

// Include/extend
"(call method: (identifier) @inc (#eq? @inc \"include\"))"
"(call method: (identifier) @ext (#eq? @ext \"extend\"))"

// Attr accessors
"(call method: (identifier) @attr (#match? @attr \"^attr_\"))"

// Instance variables
"(instance_variable) @ivar"

// Class variables
"(class_variable) @cvar"

// Constants
"(constant) @const"

// Symbols
"(simple_symbol) @symbol"
"(hash_key_symbol) @symbol"

// Method calls
"(call method: (identifier) @call)"

// Method calls with receiver
"(call
  receiver: (_)
  method: (identifier) @method_call)"

// Rescue blocks
"(rescue) @rescue"
"(ensure) @ensure"

// Yield
"(yield) @yield"

// Rails-specific patterns
// Model callbacks
"((call method: (identifier) @cb)
  (#match? @cb \"^(before_|after_|around_)\"))"

// Associations
"((call method: (identifier) @assoc)
  (#match? @assoc \"^(has_many|has_one|belongs_to|has_and_belongs_to_many)\"))"

// Validations
"((call method: (identifier) @val)
  (#match? @val \"^validates\"))"

// Controller actions
"(method name: (identifier) @action
  (#match? @action \"^(index|show|new|create|edit|update|destroy)\"))"
}

Query Debugging

Using tree-sitter CLI

# Install tree-sitter CLI
npm install -g tree-sitter-cli

# Parse a file and see the tree
tree-sitter parse file.rs

# Run a query against a file
tree-sitter query query.scm file.rs

Online Playground

Use the tree-sitter playground to interactively develop queries.

In Refactor DSL

#![allow(unused)]
fn main() {
use refactor::prelude::*;

// Test query validity
let result = Rust.query("(function_item @fn)");
match result {
    Ok(_) => println!("Valid query"),
    Err(e) => println!("Invalid: {:?}", e),
}

// See what matches
let matches = AstMatcher::new()
    .query("(function_item name: (identifier) @fn)")
    .find_matches(source, &Rust)?;

for m in matches {
    println!("{:?}", m);
}
}

Common Patterns

Find All Definitions

#![allow(unused)]
fn main() {
// Functions, structs, enums in Rust
"[
  (function_item name: (identifier) @def)
  (struct_item name: (type_identifier) @def)
  (enum_item name: (type_identifier) @def)
]"
}

Find Specific Function Calls

#![allow(unused)]
fn main() {
// Calls to deprecated_api
"((call_expression
    function: (identifier) @fn)
  (#eq? @fn \"deprecated_api\"))"

// Method calls to .unwrap()
"((call_expression
    function: (field_expression field: (field_identifier) @method))
  (#eq? @method \"unwrap\"))"
}

Find TODO Comments

#![allow(unused)]
fn main() {
// Line comments containing TODO
"((line_comment) @comment
  (#match? @comment \"TODO\"))"
}

Find Test Functions

#![allow(unused)]
fn main() {
// Rust test functions
"(attribute_item (attribute) @attr
  (#eq? @attr \"test\"))
(function_item name: (identifier) @test_fn)"

// Python test functions
"((function_definition name: (identifier) @fn)
  (#match? @fn \"^test_\"))"
}

Performance Tips

  1. Be specific - More specific patterns are faster
  2. Use predicates sparingly - #eq? is faster than #match?
  3. Capture only what you need - Extra captures add overhead
  4. Test incrementally - Start simple, add complexity

Resources