fluidattacks-blends-domain 0.16.1

Blends functional core: pure AST graph to syntax graph (no_std)
Documentation
//! Counterpart of `blends/syntax/builders/using_statement.py`.

use crate::syntax::SyntaxNode;
use crate::syntax::{SyntaxGraphArgs, SyntaxGraphError};
use crate::NodeId;

pub fn build_using_statement_node(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    block_id: NodeId,
    declaration_id: Option<NodeId>,
) -> Result<NodeId, SyntaxGraphError> {
    args.syntax_graph.add_node(
        n_id,
        SyntaxNode::UsingStatement {
            block_id,
            declaration_id,
        },
    );

    let built_block = args.generic(block_id)?;
    args.syntax_graph.add_ast_edge(n_id, built_block);

    if let Some(declaration_id) = declaration_id {
        let built_declaration = args.generic(declaration_id)?;
        args.syntax_graph.add_ast_edge(n_id, built_declaration);
    }

    Ok(n_id)
}

#[cfg(test)]
mod tests {
    use super::build_using_statement_node;
    use crate::ast::{AstGraph, AstNode};
    use crate::syntax::builders::utils::register_symbol_in_scope;
    use crate::syntax::{
        SyntaxGraph, SyntaxGraphArgs, SyntaxGraphError, SyntaxMetadata, SyntaxNode, SyntaxReader,
    };
    use crate::{Language, NodeId};
    use alloc::string::String;
    use alloc::vec;

    const USING: NodeId = NodeId(1);
    const BLOCK: NodeId = NodeId(2);
    const DECLARATION: NodeId = NodeId(3);
    const SCOPE: NodeId = NodeId(1);

    // Stands in for the real variable declaration builder, which registers its own symbol.
    fn declaration_reader(
        args: &mut SyntaxGraphArgs<'_>,
        n_id: NodeId,
    ) -> Result<Option<NodeId>, SyntaxGraphError> {
        let variable = args
            .ast_graph
            .nodes
            .get(&n_id)
            .and_then(|node| node.text.clone())
            .ok_or(SyntaxGraphError::UnexpectedAstShape)?;
        args.syntax_graph.add_node(
            n_id,
            SyntaxNode::VariableDeclaration {
                variable: variable.clone(),
                variable_type: None,
                value_id: None,
                variable_id: None,
                access_modifier: None,
            },
        );
        register_symbol_in_scope(args.syntax_graph, args.metadata, variable, n_id);
        Ok(Some(n_id))
    }

    fn dispatch(node_type: &str) -> Option<SyntaxReader> {
        match node_type {
            "variable_declaration" => Some(declaration_reader),
            _ => None,
        }
    }

    #[test]
    fn the_declaration_is_the_only_indexed_definition_of_the_bound_symbol() {
        let mut ast = AstGraph::new();
        ast.add_node(BLOCK, AstNode::new(1, 1, String::from("block")));
        ast.add_node(
            DECLARATION,
            AstNode {
                text: Some(String::from("stream")),
                ..AstNode::new(1, 1, String::from("variable_declaration"))
            },
        );
        let mut graph = SyntaxGraph::new();
        let mut metadata = SyntaxMetadata::seeded(DECLARATION);

        {
            let mut args =
                SyntaxGraphArgs::new(Language::CSharp, &ast, &mut graph, &mut metadata, dispatch);
            build_using_statement_node(&mut args, USING, BLOCK, Some(DECLARATION))
                .expect("the builder must succeed");
        }

        assert_eq!(
            graph.symbol_index.get("stream"),
            Some(&vec![(SCOPE, vec![DECLARATION])].into_iter().collect())
        );
    }
}