fluidattacks-blends-domain 0.3.0

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

use alloc::vec;
use alloc::vec::Vec;

use crate::path_search::search::{SearchArgs, SearchResult};
use crate::query::adj_ast;
use crate::syntax::SyntaxNode;
use crate::NodeId;

fn method_modifies_symbol(
    args: &SearchArgs<'_>,
    expression: &str,
    object_id: Option<NodeId>,
) -> bool {
    let object_match = object_id
        .and_then(|o_id| args.graph.nodes.get(&o_id))
        .and_then(SyntaxNode::symbol)
        == Some(args.symbol);
    let dotted = expression
        .split_once('.')
        .is_some_and(|(head, _)| head == args.symbol);
    let arrow = expression
        .split_once("->")
        .is_some_and(|(head, _)| head.trim_start_matches('$') == args.symbol);
    object_match || dotted || arrow
}

fn method_calls_symbol(args: &SearchArgs<'_>, arguments_id: Option<NodeId>) -> bool {
    let Some(al_id) = arguments_id else {
        return false;
    };
    adj_ast(args.graph, al_id, Some(1), &["SymbolLookup"])
        .into_iter()
        .any(|n_id| args.graph.nodes.get(&n_id).and_then(SyntaxNode::symbol) == Some(args.symbol))
}

pub fn search(args: &SearchArgs<'_>) -> Vec<SearchResult> {
    let Some(SyntaxNode::MethodInvocation {
        expression,
        object_id,
        arguments_id,
        ..
    }) = args.graph.nodes.get(&args.n_id)
    else {
        return Vec::new();
    };
    let modifies = !args.def_only && method_modifies_symbol(args, expression, *object_id);
    let calls = args.all_related && method_calls_symbol(args, *arguments_id);
    if modifies || calls {
        return vec![(false, args.n_id)];
    }
    Vec::new()
}