fluidattacks-blends-domain 0.17.1

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

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

#[allow(
    dead_code,
    reason = "wired when a consuming language reader lands, #26553"
)]
pub fn build_lambda_function_type_node(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    param_types: &[NodeId],
    return_type: NodeId,
) -> Result<NodeId, SyntaxGraphError> {
    args.syntax_graph
        .add_node(n_id, SyntaxNode::LambdaFunctionType);

    for param_type in param_types {
        let param_id = args.generic(*param_type)?;
        args.syntax_graph.add_ast_edge(n_id, param_id);
    }

    let return_type_id = args.generic(return_type)?;
    args.syntax_graph.add_ast_edge(n_id, return_type_id);

    Ok(n_id)
}

#[cfg(test)]
mod tests {
    use super::build_lambda_function_type_node;
    use crate::ast::{AstGraph, AstNode};
    use crate::syntax::{
        SyntaxGraph, SyntaxGraphArgs, SyntaxGraphError, SyntaxMetadata, SyntaxNode, SyntaxReader,
    };
    use crate::{Language, NodeId};
    use alloc::borrow::ToOwned;

    fn no_dispatch(_: &str) -> Option<SyntaxReader> {
        None
    }

    #[test]
    fn param_types_and_the_return_type_become_ast_children_of_the_lambda() {
        let mut ast = AstGraph::new();
        ast.add_node(NodeId(2), AstNode::new(1, 1, "type_identifier".to_owned()));
        ast.add_node(NodeId(3), AstNode::new(1, 5, "type_identifier".to_owned()));
        let mut graph = SyntaxGraph::new();
        let mut meta = SyntaxMetadata::seeded(NodeId(10));

        let result = {
            let mut args =
                SyntaxGraphArgs::new(Language::Java, &ast, &mut graph, &mut meta, no_dispatch);
            build_lambda_function_type_node(&mut args, NodeId(1), &[NodeId(2)], NodeId(3))
        };

        let built_id = result.expect("building the lambda function type node should succeed");
        assert_eq!(built_id, NodeId(1));
        assert!(matches!(
            graph.nodes.get(&NodeId(1)),
            Some(SyntaxNode::LambdaFunctionType)
        ));
        assert!(graph.edges.get(&NodeId(1)).is_some_and(|adjacent| adjacent
            .contains_key(&NodeId(2))
            && adjacent.contains_key(&NodeId(3))));
    }

    /// Any type the lambda references must already exist in the AST.
    ///
    /// Both the return type and each param type are resolved through the same
    /// `?`, so a missing node has to surface as an error rather than a node
    /// wired to nothing.
    #[test]
    fn a_type_missing_from_the_ast_propagates_the_error() {
        const NO_PARAM_TYPES: &[NodeId] = &[];
        const ONE_PARAM_TYPE: &[NodeId] = &[NodeId(2)];

        for (scenario, seed_return_type, param_types) in [
            ("the return type is absent", false, NO_PARAM_TYPES),
            ("a param type is absent", true, ONE_PARAM_TYPE),
        ] {
            let mut ast = AstGraph::new();
            if seed_return_type {
                ast.add_node(NodeId(3), AstNode::new(1, 5, "type_identifier".to_owned()));
            }
            let mut graph = SyntaxGraph::new();
            let mut meta = SyntaxMetadata::seeded(NodeId(10));

            let result = {
                let mut args =
                    SyntaxGraphArgs::new(Language::Java, &ast, &mut graph, &mut meta, no_dispatch);
                build_lambda_function_type_node(&mut args, NodeId(1), param_types, NodeId(3))
            };

            assert!(
                matches!(result, Err(SyntaxGraphError::MissingAstNode)),
                "must fail when {scenario}, got {result:?}"
            );
        }
    }
}