vize_l2 0.643.0

L2 - the Davinci semantic IR (codename Disegno): the neutral op family every input dialect lowers into
Documentation
//! Actual original Model, Element, setup declaration and read/write custody.

extern crate std;

use crate::file::{
    DeclarationKind, InitializerKind, Namespace, NativeFileModelKind, NativeFileModelState,
};
use crate::lang::js::NativeTemplateIssueKind as Kind;
use crate::op::{BindingOp, Op};
use crate::resolution::Usage;
use vize_l0::Allocator;

mod checkbox;
mod lazy;
mod number;
mod refusal;
mod support;
mod trim;
use support::{completed, owner};

std::thread_local! {
    static FAULT: std::cell::Cell<u8> = const { std::cell::Cell::new(0) };
}
fn inject(point: u8) {
    FAULT.with(|fault| {
        if fault.get() == point {
            fault.set(0);
            panic!("interrupted original Model custody at {point}");
        }
    });
}
pub(super) fn before_observe() {
    inject(1);
}
pub(super) fn after_park() {
    inject(2);
}
pub(in crate::file::region::native) fn after_mint() {
    inject(3);
}
pub(in crate::file::region::native) fn after_body() {
    inject(4);
}
pub(in crate::file::region::native) fn after_close() {
    inject(5);
}
pub(super) fn after_failure_park() {
    inject(6);
}

#[test]
fn original_five_primitive_let_var_models_keep_one_real_readwrite_and_no_alias_scope() {
    for (source, kind) in [
        (
            "<script setup>let draft='snow'</script><template><input v-model='draft'/></template>",
            DeclarationKind::Let,
        ),
        (
            "<script setup>var draft=7</script><template><input v-model='draft'/></template>",
            DeclarationKind::Var,
        ),
        (
            "<script setup>let draft=false</script><template><input v-model='draft'/></template>",
            DeclarationKind::Let,
        ),
        (
            "<script setup>var draft=null</script><template><input v-model='draft'/></template>",
            DeclarationKind::Var,
        ),
        (
            "\r\n<script setup lang='ts'>let café:bigint=9n</script>\r\n<template><input v-model=\"café\" /></template>\r\n",
            DeclarationKind::Let,
        ),
    ] {
        let arena = Allocator::default();
        let output = completed(&arena, source);
        let setup = output.setup().unwrap();
        let file = output.file().unwrap();
        assert!(file.is_complete() && output.view().is_ok());
        assert!(core::ptr::eq(setup.file(), file));
        let [unit] = file.units() else {
            panic!("one actual Program")
        };
        assert_eq!(file.scopes().len(), 2);
        assert_eq!(file.artifact().node_count(), 2);
        let [record] = file.native_models() else {
            panic!("one original Model")
        };
        let NativeFileModelState::Admitted(node) = record.state() else {
            panic!("closed Model")
        };
        let [Op::Element(element)] = file.artifact().root().ops.as_slice() else {
            panic!("real input")
        };
        let [BindingOp::Model(model)] = element.bindings.as_slice() else {
            panic!("real attached Model")
        };
        let element_node = record.element_node().unwrap();
        assert_ne!(node, element_node);
        assert!(record.accepts(model) && record.accepts_element(element_node, element));
        assert!(core::ptr::eq(record, file.native_model(node).unwrap()));
        assert!(file.artifact().scopes().get(node).is_none());
        assert_eq!(record.scope(), unit.scope);
        assert_eq!(record.scope(), setup.scope());
        assert_eq!(element.tag, "input");
        assert!(element.attributes.is_empty() && element.children.ops.is_empty());
        assert!(model.argument.is_none() && model.attributes.is_empty());
        assert_eq!(record.input().attribute_span(), Some(model.span));
        assert_eq!(
            record.input().operand().name_span().slice(source),
            "v-model"
        );
        let (crate::expr::ExprRef::Js(read), crate::expr::ExprRef::Js(write)) =
            (model.contract.read, model.contract.write)
        else {
            panic!("same original expression")
        };
        assert!(core::ptr::eq(read, write));
        assert!(record.input().matches_expression(read));
        let resolution = file.expression(node).unwrap();
        assert_eq!(resolution.scope(), Some(unit.scope));
        let table = resolution.table().unwrap();
        let resolved = table.expression();
        assert!(!record.input().matches_expression(resolved));
        assert!(core::ptr::eq(resolved.ast, read.ast));
        assert!(core::ptr::eq(resolved.source, read.source));
        assert_eq!(resolved.span, read.span);
        assert!(core::ptr::eq(
            resolved.coordinates.unwrap(),
            read.coordinates.unwrap()
        ));
        let [occurrence] = table.occurrences() else {
            panic!("one genuine ReadWrite occurrence")
        };
        assert_eq!(occurrence.usage, Usage::ReadWrite);
        assert!(!occurrence.shorthand && !occurrence.constructor);
        assert_eq!(record.binding(), Some(occurrence.binding));
        let binding = resolution.binding(occurrence.binding).unwrap();
        assert!(resolution.accepts(binding));
        assert!(setup.binding(binding).is_ok());
        let declaration = binding.declaration().unwrap();
        assert_eq!(declaration.id, occurrence.binding);
        assert_eq!(declaration.kind, kind);
        assert_eq!(declaration.namespace, Namespace::Value);
        assert_eq!(declaration.initializer, InitializerKind::PrimitiveLiteral);
        assert!(declaration.is_direct_program());
        assert_eq!(declaration.scope, unit.scope);
        assert_eq!(declaration.unit, unit.id);
        assert_eq!(declaration.name.as_str(), occurrence.name);
        assert_eq!(
            record.input().operand().value_span().slice(source),
            occurrence.name
        );
        let identifier = read.ast.get_identifier_reference().unwrap();
        assert_eq!(
            read.ast_span_to_source(identifier.span),
            Some(occurrence.span)
        );
        assert_eq!(
            read.authored_span(occurrence.span).unwrap().slice(source),
            occurrence.name
        );
        assert!(file.native_interpolations().is_empty());
    }
}

#[test]
fn equal_foreign_model_nodes_elements_and_bindings_never_satisfy_same_file_custody() {
    let arena = Allocator::default();
    let source = "<script setup>let draft=''</script><template><input v-model='draft'/></template>";
    let own = completed(&arena, source);
    let foreign = completed(&arena, source);
    let file = own.file().unwrap();
    let other = foreign.file().unwrap();
    let [record] = file.native_models() else {
        panic!("own original")
    };
    let [other_record] = other.native_models() else {
        panic!("foreign original")
    };
    assert_eq!(record.state(), other_record.state());
    assert_eq!(record.binding(), other_record.binding());
    assert_eq!(record.element_node(), other_record.element_node());
    let NativeFileModelState::Admitted(node) = record.state() else {
        panic!("actual node")
    };
    let [Op::Element(element)] = file.artifact().root().ops.as_slice() else {
        panic!("own input")
    };
    let [Op::Element(other_element)] = other.artifact().root().ops.as_slice() else {
        panic!("foreign input")
    };
    let [BindingOp::Model(model)] = element.bindings.as_slice() else {
        panic!("own Model")
    };
    let [BindingOp::Model(other_model)] = other_element.bindings.as_slice() else {
        panic!("foreign Model")
    };
    assert!(record.accepts(model));
    assert!(!record.accepts(other_model));
    assert!(record.accepts_element(record.element_node().unwrap(), element));
    assert!(!record.accepts_element(record.element_node().unwrap(), other_element));
    assert!(
        !file
            .expression(node)
            .unwrap()
            .accepts(other.binding(record.binding().unwrap()).unwrap())
    );
    assert!(
        !record
            .input()
            .matches_expression(other_record.input().expression().unwrap())
    );
    let copied = *record.input().expression().unwrap();
    assert!(!record.input().matches_expression(&copied));
    let mut interrupted = owner(&arena, source);
    {
        let mut walk = interrupted.begin().unwrap();
        let foreign_child = foreign.selected().children().next().unwrap();
        assert_eq!(
            walk.child(foreign_child).unwrap_err().kind,
            Kind::InvalidEvent
        );
        assert!(walk.root.facts.native_models().is_empty());
        assert_eq!(walk.complete().unwrap_err().kind, Kind::InvalidEvent);
    }
    let interrupted = interrupted.finish();
    assert!(interrupted.view().is_err());
    assert!(interrupted.file().unwrap().native_models().is_empty());
}

#[test]
fn caught_model_transition_unwinds_keep_actual_prefix_but_never_publish_complete_file_access() {
    for point in 1..=5 {
        let arena = Allocator::default();
        let source =
            "<script setup>let draft='snow'</script><template><input v-model='draft'/></template>";
        let mut owner = owner(&arena, source);
        {
            let mut walk = owner.begin().unwrap();
            let scope = walk.root.scope;
            let selected = walk.selected();
            let child = selected.children().next().unwrap();
            FAULT.with(|fault| fault.set(point));
            assert!(
                std::panic::catch_unwind(std::panic::AssertUnwindSafe(
                    || walk.child(child.reborrow())
                ))
                .is_err()
            );
            assert_eq!(walk.root.scope, scope);
            assert_eq!(walk.root.facts.scopes.len(), 2);
            assert!(walk.root.facts.template_declarations.is_empty());
            assert_eq!(walk.cursor, 0);
            assert!(walk.root.facts.template_walk.interruption().is_some());
            if point == 1 {
                assert!(walk.root.facts.native_models().is_empty());
            } else {
                let [record] = walk.root.facts.native_models() else {
                    panic!("parked actual Model")
                };
                assert_eq!(record.scope(), scope);
                assert_eq!(record.input().operand().raw_value(), "draft");
                assert!(core::ptr::eq(
                    record.input().operand().syntax().source().authored_root(),
                    source
                ));
                assert_eq!(record.input().expression().is_some(), point >= 3);
                assert_eq!(record.binding().is_some(), point >= 3);
                assert_eq!(record.original.is_some(), point >= 3);
                assert_eq!(record.element_node().is_some(), point == 5);
                if point < 5 {
                    assert_eq!(record.state(), NativeFileModelState::Pending);
                } else {
                    assert!(matches!(record.state(), NativeFileModelState::Admitted(_)));
                }
            }
            assert_eq!(walk.child(child).unwrap_err().kind, Kind::Interrupted);
            assert_eq!(walk.complete().unwrap_err().kind, Kind::Interrupted);
        }
        let output = owner.finish();
        assert!(output.view().is_err() && output.setup().is_err());
        assert!(output.retained_setup().is_some());
        assert!(core::ptr::eq(
            output.selected().component().block().root_source(),
            source
        ));
        if point == 3 || point == 4 {
            assert!(output.file().is_none());
            let rejected = output.rejected_file().unwrap();
            assert!(rejected.template_interruption().is_some());
            let [record] = rejected.native_models() else {
                panic!("whole pending owner")
            };
            assert_eq!(record.state(), NativeFileModelState::Pending);
            let [Op::Element(element)] = rejected.artifact().parts.root.ops.as_slice() else {
                panic!("retained pending input")
            };
            let [BindingOp::Model(model)] = element.bindings.as_slice() else {
                panic!("retained actual Model")
            };
            assert!(!record.accepts(model));
            let crate::expr::ExprRef::Js(read) = model.contract.read else {
                panic!("actual expression")
            };
            assert!(record.input().matches_expression(read));
            assert!(element.children.ops.is_empty());
        } else {
            let file = output.file().unwrap();
            assert!(!file.is_complete());
            assert!(file.template_interruption().is_some());
            if point == 5 {
                let [record] = file.native_models() else {
                    panic!("closed row, incomplete File")
                };
                let NativeFileModelState::Admitted(node) = record.state() else {
                    panic!("real closed allocation")
                };
                let [Op::Element(element)] = file.artifact().root().ops.as_slice() else {
                    panic!("closed input")
                };
                let [BindingOp::Model(model)] = element.bindings.as_slice() else {
                    panic!("closed Model")
                };
                assert!(record.accepts(model));
                assert!(record.accepts_element(record.element_node().unwrap(), element));
                assert!(file.expression(node).is_some());
                assert!(file.native_model(node).is_none());
            } else {
                assert!(file.artifact().root().ops.is_empty());
                assert_eq!(file.artifact().node_count(), 0);
            }
        }
    }
}

#[test]
fn interrupted_model_preparation_failure_keeps_the_original_failure_without_a_model_or_node() {
    let arena = Allocator::default();
    let source = "<script setup>let draft=''</script><template><input v-model/></template>";
    let mut owner = owner(&arena, source);
    {
        let mut walk = owner.begin().unwrap();
        let child = walk.selected().children().next().unwrap();
        FAULT.with(|fault| fault.set(6));
        assert!(
            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| walk.child(child))).is_err()
        );
        assert!(walk.root.facts.native_models().is_empty());
        let [failure] = walk.root.facts.native_model_failures() else {
            panic!("parked whole L1 failure")
        };
        assert_eq!(
            failure.kind(),
            vize_l1::markup::NativeAttributeOperandError::IncompleteValue
        );
        assert_eq!(walk.complete().unwrap_err().kind, Kind::Interrupted);
    }
    let output = owner.finish();
    assert!(output.view().is_err() && output.setup().is_err());
    let file = output.file().unwrap();
    assert!(!file.is_complete());
    assert!(file.template_interruption().is_some());
    assert_eq!(file.artifact().node_count(), 0);
    assert!(file.native_models().is_empty());
    let [failure] = file.native_model_failures() else {
        panic!("retained complete failure")
    };
    assert_eq!(
        failure.kind(),
        vize_l1::markup::NativeAttributeOperandError::IncompleteValue
    );
}