fluidattacks-blends-domain 0.3.0

Blends functional core: pure AST graph to syntax graph (no_std)
Documentation
//! Counterpart of `blends/syntax/builders/method_declaration.py` (the
//! `StackGraph` scope and definition wiring is excluded: the migration covers
//! only AST, Syntax and CFG).

use alloc::string::String;
use alloc::vec::Vec;

use crate::syntax::builders::utils::{
    enter_scope_stack, exit_scope_stack_if_current, register_symbol_in_scope,
};
use crate::syntax::metadata::java::add_node_range_to_method;
use crate::syntax::SyntaxNode;
use crate::syntax::{SyntaxGraphArgs, SyntaxGraphError};
use crate::{Language, NodeId};

#[allow(
    clippy::struct_field_names,
    reason = "mirrors the Python DirectChildren keys"
)]
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct MethodDirectChildren {
    pub block_id: Option<NodeId>,
    pub modifiers_id: Option<NodeId>,
    pub parameters_id: Option<NodeId>,
}

#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct MethodListChildren {
    pub modifiers_ids: Vec<NodeId>,
    pub parameters_ids: Vec<NodeId>,
}

fn add_child(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    child_id: NodeId,
) -> Result<(), SyntaxGraphError> {
    let built = args.generic(child_id)?;
    args.syntax_graph.add_ast_edge(n_id, built);
    Ok(())
}

fn process_method_children(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    children: &MethodDirectChildren,
) -> Result<(), SyntaxGraphError> {
    if let Some(parameters_id) = children.parameters_id {
        add_child(args, n_id, parameters_id)?;
    }
    if let Some(block_id) = children.block_id {
        add_child(args, n_id, block_id)?;
    }
    if let Some(modifiers_id) = children.modifiers_id {
        add_child(args, n_id, modifiers_id)?;
    }
    Ok(())
}

fn process_method_list_children(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    children: &MethodListChildren,
) -> Result<(), SyntaxGraphError> {
    for child_id in children
        .modifiers_ids
        .iter()
        .chain(&children.parameters_ids)
    {
        add_child(args, n_id, *child_id)?;
    }
    Ok(())
}

#[allow(
    clippy::too_many_arguments,
    reason = "mirrors the Python build_method_declaration_node inputs"
)]
pub fn build_method_declaration_node(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    name: Option<String>,
    direct_children: &MethodDirectChildren,
    list_children: &MethodListChildren,
    access_modifiers: Option<String>,
) -> Result<NodeId, SyntaxGraphError> {
    let name = name.filter(|value| !value.is_empty());

    args.syntax_graph.add_node(
        n_id,
        SyntaxNode::MethodDeclaration {
            name: name.clone(),
            access_modifiers: access_modifiers.filter(|value| !value.is_empty()),
            block_id: direct_children.block_id,
            modifiers_id: direct_children.modifiers_id,
            parameters_id: direct_children.parameters_id,
        },
    );

    if let Some(name) = &name {
        register_symbol_in_scope(args.syntax_graph, args.metadata, name.clone(), n_id);
    }
    enter_scope_stack(args.syntax_graph, args.metadata, n_id);

    process_method_children(args, n_id, direct_children)?;
    process_method_list_children(args, n_id, list_children)?;

    if args.language == Language::Java && args.syntax_graph.nodes.contains_key(&NodeId(0)) {
        if let Some(name) = &name {
            add_node_range_to_method(args, n_id, name)?;
        }
    }

    exit_scope_stack_if_current(args.metadata, n_id);

    Ok(n_id)
}