fluidattacks-blends-domain 0.6.1

Blends functional core: pure AST graph to syntax graph (no_std)
Documentation
//! Counterpart of `blends/path_search/search/assignment.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::CodeGraph;

const COMPOUND_OPERATORS: [&str; 3] = ["+=", "*=", "/="];

pub fn search(args: &SearchArgs<'_>) -> Vec<SearchResult> {
    let Some(SyntaxNode::Assignment {
        variable_id,
        operator,
        ..
    }) = args.graph.nodes.get(&args.n_id)
    else {
        return Vec::new();
    };
    let assign_id = *variable_id;
    let target = args.graph.nodes.get(&assign_id);

    if target.and_then(SyntaxNode::symbol) == Some(args.symbol) {
        let compound = operator
            .as_deref()
            .is_some_and(|value| COMPOUND_OPERATORS.contains(&value));
        if compound {
            if !args.def_only {
                return vec![(false, args.n_id)];
            }
            return Vec::new();
        }
        return vec![(true, args.n_id)];
    }

    if args.graph.label_type(assign_id) == Some("ArgumentList") {
        let mut out: Vec<SearchResult> = Vec::new();
        for c_id in adj_ast(args.graph, assign_id, Some(1), &["SymbolLookup"]) {
            if args.graph.nodes.get(&c_id).and_then(SyntaxNode::symbol) == Some(args.symbol) {
                out.push((true, args.n_id));
            }
        }
        return out;
    }

    if !args.def_only
        && target
            .and_then(SyntaxNode::expression)
            .is_some_and(|expression| !expression.is_empty() && expression.contains(args.symbol))
    {
        return vec![(false, args.n_id)];
    }

    Vec::new()
}