Skip to main content
The VisitorMut trait implements functions to listen for specific nodes and tokens in the AST. Unlike Visitor, nodes and tokens passed are mutable, allowing you to transform the AST.

Trait Definition

pub trait VisitorMut {
    /// Visit the nodes of an Ast
    fn visit_ast(&mut self, ast: Ast) -> Ast where Self: Sized {
        let eof = ast.eof().to_owned();
        let nodes = ast.nodes.visit_mut(self);

        Ast {
            nodes,
            eof: self.visit_eof(eof),
        }
    }

    // AST node visit methods
    fn visit_anonymous_call(&mut self, node: FunctionArgs) -> FunctionArgs { node }
    fn visit_anonymous_call_end(&mut self, node: FunctionArgs) -> FunctionArgs { node }
    fn visit_anonymous_function(&mut self, node: AnonymousFunction) -> AnonymousFunction { node }
    fn visit_anonymous_function_end(&mut self, node: AnonymousFunction) -> AnonymousFunction { node }
    fn visit_assignment(&mut self, node: Assignment) -> Assignment { node }
    fn visit_assignment_end(&mut self, node: Assignment) -> Assignment { node }
    fn visit_block(&mut self, node: Block) -> Block { node }
    fn visit_block_end(&mut self, node: Block) -> Block { node }
    fn visit_call(&mut self, node: Call) -> Call { node }
    fn visit_call_end(&mut self, node: Call) -> Call { node }
    fn visit_contained_span(&mut self, node: ContainedSpan) -> ContainedSpan { node }
    fn visit_contained_span_end(&mut self, node: ContainedSpan) -> ContainedSpan { node }
    fn visit_do(&mut self, node: Do) -> Do { node }
    fn visit_do_end(&mut self, node: Do) -> Do { node }
    fn visit_else_if(&mut self, node: ElseIf) -> ElseIf { node }
    fn visit_else_if_end(&mut self, node: ElseIf) -> ElseIf { node }
    fn visit_eof(&mut self, node: TokenReference) -> TokenReference { node }
    fn visit_eof_end(&mut self, node: TokenReference) -> TokenReference { node }
    fn visit_expression(&mut self, node: Expression) -> Expression { node }
    fn visit_expression_end(&mut self, node: Expression) -> Expression { node }
    fn visit_field(&mut self, node: Field) -> Field { node }
    fn visit_field_end(&mut self, node: Field) -> Field { node }
    fn visit_function_args(&mut self, node: FunctionArgs) -> FunctionArgs { node }
    fn visit_function_args_end(&mut self, node: FunctionArgs) -> FunctionArgs { node }
    fn visit_function_body(&mut self, node: FunctionBody) -> FunctionBody { node }
    fn visit_function_body_end(&mut self, node: FunctionBody) -> FunctionBody { node }
    fn visit_function_call(&mut self, node: FunctionCall) -> FunctionCall { node }
    fn visit_function_call_end(&mut self, node: FunctionCall) -> FunctionCall { node }
    fn visit_function_declaration(&mut self, node: FunctionDeclaration) -> FunctionDeclaration { node }
    fn visit_function_declaration_end(&mut self, node: FunctionDeclaration) -> FunctionDeclaration { node }
    fn visit_function_name(&mut self, node: FunctionName) -> FunctionName { node }
    fn visit_function_name_end(&mut self, node: FunctionName) -> FunctionName { node }
    fn visit_generic_for(&mut self, node: GenericFor) -> GenericFor { node }
    fn visit_generic_for_end(&mut self, node: GenericFor) -> GenericFor { node }
    fn visit_if(&mut self, node: If) -> If { node }
    fn visit_if_end(&mut self, node: If) -> If { node }
    fn visit_index(&mut self, node: Index) -> Index { node }
    fn visit_index_end(&mut self, node: Index) -> Index { node }
    fn visit_local_assignment(&mut self, node: LocalAssignment) -> LocalAssignment { node }
    fn visit_local_assignment_end(&mut self, node: LocalAssignment) -> LocalAssignment { node }
    fn visit_local_function(&mut self, node: LocalFunction) -> LocalFunction { node }
    fn visit_local_function_end(&mut self, node: LocalFunction) -> LocalFunction { node }
    fn visit_last_stmt(&mut self, node: LastStmt) -> LastStmt { node }
    fn visit_last_stmt_end(&mut self, node: LastStmt) -> LastStmt { node }
    fn visit_method_call(&mut self, node: MethodCall) -> MethodCall { node }
    fn visit_method_call_end(&mut self, node: MethodCall) -> MethodCall { node }
    fn visit_numeric_for(&mut self, node: NumericFor) -> NumericFor { node }
    fn visit_numeric_for_end(&mut self, node: NumericFor) -> NumericFor { node }
    fn visit_parameter(&mut self, node: Parameter) -> Parameter { node }
    fn visit_parameter_end(&mut self, node: Parameter) -> Parameter { node }
    fn visit_prefix(&mut self, node: Prefix) -> Prefix { node }
    fn visit_prefix_end(&mut self, node: Prefix) -> Prefix { node }
    fn visit_return(&mut self, node: Return) -> Return { node }
    fn visit_return_end(&mut self, node: Return) -> Return { node }
    fn visit_repeat(&mut self, node: Repeat) -> Repeat { node }
    fn visit_repeat_end(&mut self, node: Repeat) -> Repeat { node }
    fn visit_stmt(&mut self, node: Stmt) -> Stmt { node }
    fn visit_stmt_end(&mut self, node: Stmt) -> Stmt { node }
    fn visit_suffix(&mut self, node: Suffix) -> Suffix { node }
    fn visit_suffix_end(&mut self, node: Suffix) -> Suffix { node }
    fn visit_table_constructor(&mut self, node: TableConstructor) -> TableConstructor { node }
    fn visit_table_constructor_end(&mut self, node: TableConstructor) -> TableConstructor { node }
    fn visit_token_reference(&mut self, node: TokenReference) -> TokenReference { node }
    fn visit_token_reference_end(&mut self, node: TokenReference) -> TokenReference { node }
    fn visit_un_op(&mut self, node: UnOp) -> UnOp { node }
    fn visit_un_op_end(&mut self, node: UnOp) -> UnOp { node }
    fn visit_var(&mut self, node: Var) -> Var { node }
    fn visit_var_end(&mut self, node: Var) -> Var { node }
    fn visit_var_expression(&mut self, node: VarExpression) -> VarExpression { node }
    fn visit_var_expression_end(&mut self, node: VarExpression) -> VarExpression { node }
    fn visit_while(&mut self, node: While) -> While { node }
    fn visit_while_end(&mut self, node: While) -> While { node }

    // Token visit methods
    fn visit_identifier(&mut self, token: Token) -> Token { token }
    fn visit_multi_line_comment(&mut self, token: Token) -> Token { token }
    fn visit_number(&mut self, token: Token) -> Token { token }
    fn visit_single_line_comment(&mut self, token: Token) -> Token { token }
    fn visit_string_literal(&mut self, token: Token) -> Token { token }
    fn visit_symbol(&mut self, token: Token) -> Token { token }
    fn visit_token(&mut self, token: Token) -> Token { token }
    fn visit_whitespace(&mut self, token: Token) -> Token { token }
}

Available Methods

The VisitorMut trait provides methods for visiting and transforming:

AST Nodes

For each AST node type, two methods are provided:
  • visit_<node>(&mut self, node: <NodeType>) -> <NodeType> - Called when entering the node
  • visit_<node>_end(&mut self, node: <NodeType>) -> <NodeType> - Called when leaving the node
Key node types include:
  • visit_local_assignment / visit_local_assignment_end - Local variable assignments
  • visit_function_declaration / visit_function_declaration_end - Function declarations
  • visit_function_call / visit_function_call_end - Function calls
  • visit_if / visit_if_end - If statements
  • visit_while / visit_while_end - While loops
  • visit_expression / visit_expression_end - Expressions
  • And many more…

Tokens

Token visitor methods include:
  • visit_identifier - Identifiers
  • visit_number - Number literals
  • visit_string_literal - String literals
  • visit_symbol - Symbols (operators, punctuation)
  • visit_whitespace - Whitespace
  • visit_single_line_comment - Single-line comments
  • visit_multi_line_comment - Multi-line comments
  • visit_token - Any token

Key Differences from Visitor

  1. Ownership: Methods take ownership of nodes and return transformed nodes
  2. Mutation: Allows modifying the AST structure
  3. Transformation: Used for code transformation and formatting

Example: Renaming Variables

use full_moon::ast;
use full_moon::visitors::VisitorMut;
use full_moon::tokenizer::{Token, TokenType, TokenReference};

struct VariableRenamer {
    from: String,
    to: String,
}

impl VisitorMut for VariableRenamer {
    fn visit_token_reference(&mut self, token: TokenReference) -> TokenReference {
        if let TokenType::Identifier { identifier } = token.token_type() {
            if identifier.as_str() == self.from {
                // Create a new token with the renamed identifier
                return TokenReference::new(
                    Vec::new(),
                    Token::new(TokenType::Identifier {
                        identifier: self.to.clone().into(),
                    }),
                    Vec::new(),
                );
            }
        }
        token
    }
}

let ast = full_moon::parse("local foo = 1; print(foo)").unwrap();
let mut renamer = VariableRenamer {
    from: "foo".to_string(),
    to: "bar".to_string(),
};
let new_ast = renamer.visit_ast(ast);
// Result: "local bar = 1; print(bar)"

Example: Adding Comments

use full_moon::ast;
use full_moon::visitors::VisitorMut;
use full_moon::tokenizer::{Token, TokenType};

struct CommentAdder;

impl VisitorMut for CommentAdder {
    fn visit_function_declaration(&mut self, node: ast::FunctionDeclaration) -> ast::FunctionDeclaration {
        // Add a comment before function declarations
        // This would require modifying the token's leading trivia
        node
    }
}

Example: Code Formatting

use full_moon::ast;
use full_moon::visitors::VisitorMut;
use full_moon::tokenizer::{Token, TokenType, TokenReference};

struct WhitespaceNormalizer;

impl VisitorMut for WhitespaceNormalizer {
    fn visit_whitespace(&mut self, token: Token) -> Token {
        // Normalize all whitespace to single spaces
        Token::new(TokenType::Whitespace {
            characters: " ".into(),
        })
    }
}

Use Cases

The VisitorMut trait is ideal for:
  • Code transformation: Renaming variables, refactoring
  • Code formatting: Normalizing whitespace, adding/removing comments
  • AST manipulation: Modifying the structure of the code
  • Code generation: Building or modifying AST programmatically
  • Linting fixes: Automatically fixing lint violations

Performance Considerations

  • VisitorMut takes ownership and clones nodes, which can be more expensive than Visitor
  • Use Visitor for read-only analysis and VisitorMut only when transformation is needed
  • The visitor pattern traverses the entire AST, so performance scales with code size

See Also

  • Visitor - Immutable visitor trait
  • Node - Node trait for AST elements

Build docs developers (and LLMs) love