knave 0.13.2

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use super::problems::OwnershipProblems;
use crate::core::BindingId;
use crate::diagnostic::{Diagnostic, Diagnostics};
use crate::source::Span;
use crate::types::{
    CopyRules, ParameterCopy, ReturnedValues, TypedBinder, TypedExpr, TypedExprKind, TypedLambda,
    TypedLeaf, TypedProgram,
};

pub struct ReturnedClosureUses<'a> {
    program: &'a TypedProgram,
    copy: &'a CopyRules<'a>,
}

impl<'a> ReturnedClosureUses<'a> {
    pub fn new(program: &'a TypedProgram, copy: &'a CopyRules<'a>) -> Self {
        Self { program, copy }
    }

    pub fn check(&self) -> Diagnostics {
        let mut diagnostics = Diagnostics::default();
        for definition in self
            .program
            .modules
            .iter()
            .flat_map(|module| &module.definitions)
        {
            if let TypedExprKind::Fn(lambda) = &definition.value.kind {
                self.check_function(definition.binding, lambda, &mut diagnostics);
            }
        }
        diagnostics
    }

    fn check_function(
        &self,
        function: BindingId,
        lambda: &TypedLambda,
        diagnostics: &mut Diagnostics,
    ) {
        let closures = ReturnedValues::new(self.program).closures(lambda);
        for param in lambda
            .params
            .iter()
            .filter(|param| self.program.parameter_modes.changes(param.binding))
        {
            if let Some(usage) = closures
                .iter()
                .find_map(|closure| Self::use_site(closure, param.binding))
            {
                diagnostics.push(self.problem(function, param, usage));
            }
        }
    }

    fn problem(&self, function: BindingId, param: &TypedBinder, usage: Span) -> Diagnostic {
        let name = self.program.bindings.get(param.binding).name();
        OwnershipProblems::changed_parameter_in_returned_closure(
            self.program.bindings.get(function).name(),
            ParameterCopy::of(name, param, self.copy),
            usage,
        )
    }

    fn use_site(expr: &TypedExpr, param: BindingId) -> Option<Span> {
        if matches!(expr.kind, TypedExprKind::Leaf(TypedLeaf::Local(binding)) if binding == param) {
            return Some(expr.span);
        }
        expr.children()
            .into_iter()
            .find_map(|child| Self::use_site(child, param))
    }
}