vize_l3 0.489.0

L3 - the Davinci reactivity IR (codename Impeto): flat scheduled ops with explicit state and effect edges
Documentation
use vize_l0::{
    Allocator,
    config::{VueDialect, VueVersion},
};
use vize_l1::{SurfaceParseOptions, container::vue::DescriptorOptions};
use vize_l1_to_l2::native_file::lower_selected_setup_sfc_native;
use vize_l2::{file::DeclarationKind, resolution::Usage};
use vize_l3::decision::ssr::{
    SsrPart, SsrSetupReadKind, SsrUnsupported, build_native_selected_setup_ssr_decisions,
};

fn descriptor() -> DescriptorOptions {
    DescriptorOptions {
        version: VueVersion::V3,
        dialect: VueDialect::Vue,
        template: SurfaceParseOptions::default(),
    }
}

#[test]
fn original_setup_reads_retain_actual_occurrences_bindings_and_scope_roles() {
    for (source, expected) in [
        (
            "<script setup>const value=42</script><template>{{value}}</template>",
            SsrSetupReadKind::SetupConst,
        ),
        (
            "<script setup lang='ts'>let value:string='雪'</script><template>{{value}}</template>",
            SsrSetupReadKind::SetupLet,
        ),
        (
            "<script setup>var 雪=42</script><template>{{雪}}<i/></template>",
            SsrSetupReadKind::SetupLet,
        ),
    ] {
        let arena = Allocator::default();
        let observation = lower_selected_setup_sfc_native(&arena, source, descriptor());
        let admitted = observation.admitted().unwrap();
        let setup = admitted.setup();
        let file = setup.file();
        let provenance = file.artifact().provenance().to_vec();
        let analysis = build_native_selected_setup_ssr_decisions(setup).unwrap();
        assert!(core::ptr::eq(analysis.setup(), setup));
        assert!(core::ptr::eq(analysis.file(), file));
        assert!(core::ptr::eq(
            analysis.owner(),
            observation.original().template().unwrap()
        ));
        assert!(core::ptr::eq(analysis.artifact().source(), source));
        let facts = analysis.ssr().unwrap();
        assert!(facts.unsupported().is_empty());
        let interpolations: Vec<_> = facts
            .parts()
            .iter()
            .filter_map(|part| {
                let SsrPart::Interpolation {
                    node,
                    interpolation,
                } = part
                else {
                    return None;
                };
                Some((*node, interpolation))
            })
            .collect();
        let [(node, interpolation)] = interpolations.as_slice() else {
            panic!("one root read")
        };
        let original = file.native_interpolation(*node).unwrap();
        assert_eq!(original.input().operand().full_span(), interpolation.span);
        let expression = analysis.expression(*node).unwrap();
        let resolution = expression.resolution();
        assert!(core::ptr::eq(resolution.file(), file));
        assert_eq!(resolution.scope(), Some(setup.scope()));
        let table = resolution.table().unwrap();
        let [occurrence] = table.occurrences() else {
            panic!("actual identifier occurrence")
        };
        let [read] = expression.reads() else {
            panic!("actual immutable read")
        };
        assert!(core::ptr::eq(read.occurrence(), occurrence));
        assert_eq!(occurrence.usage, Usage::Read);
        assert_eq!(read.kind(), expected);
        let binding = read.binding();
        assert!(resolution.accepts(binding));
        assert!(setup.binding(binding).is_ok());
        let declaration = binding.declaration().unwrap();
        assert_eq!(declaration.unit, setup.unit());
        assert_eq!(declaration.scope, setup.scope());
        assert_eq!(declaration.name.as_str(), occurrence.name);
        let span = table.expression().authored_span(occurrence.span).unwrap();
        assert_eq!(span.slice(source), occurrence.name);
        assert_eq!(
            expected == SsrSetupReadKind::SetupConst,
            declaration.kind == DeclarationKind::Const
        );
        assert_eq!(file.artifact().provenance(), provenance);
        assert_eq!(
            analysis.tables().nodes.len(),
            file.artifact().node_count() as usize
        );
    }
}

#[test]
fn original_primitive_literals_have_complete_same_file_resolution_without_reads() {
    for value in ["true", "null", "42", "123n", "'雪 & <one>'"] {
        let arena = Allocator::default();
        let source =
            format!("<script setup>const unused=0</script><template>{{{{{value}}}}}</template>");
        let observation = lower_selected_setup_sfc_native(&arena, &source, descriptor());
        let admitted = observation.admitted().unwrap();
        let analysis = build_native_selected_setup_ssr_decisions(admitted.setup()).unwrap();
        let facts = analysis.ssr().unwrap();
        assert!(facts.unsupported().is_empty());
        let [SsrPart::Interpolation { node, .. }] = facts.parts() else {
            panic!("one literal")
        };
        let expression = analysis.expression(*node).unwrap();
        assert!(expression.reads().is_empty());
        assert!(
            expression
                .resolution()
                .table()
                .unwrap()
                .occurrences()
                .is_empty()
        );
        assert!(core::ptr::eq(
            expression.resolution().file(),
            admitted.setup().file()
        ));
    }
}

#[test]
fn complete_original_expressions_keep_the_exact_bounded_target_refusal() {
    for (template, expected) in [
        ("{{value+1}}", SsrUnsupported::Expression),
        ("{{value.length}}", SsrUnsupported::Expression),
        ("hello {{value}}!", SsrUnsupported::RootTextGrouping),
        ("{{value}}{{true}}", SsrUnsupported::RootTextGrouping),
        ("{{value}}<i/>{{value}}", SsrUnsupported::Operation),
    ] {
        let arena = Allocator::default();
        let source =
            format!("<script setup>const value='ab'</script><template>{template}</template>");
        let observation = lower_selected_setup_sfc_native(&arena, &source, descriptor());
        let admitted = observation.admitted().unwrap();
        assert!(admitted.setup().file().is_complete());
        let analysis = build_native_selected_setup_ssr_decisions(admitted.setup()).unwrap();
        let rejected = &analysis.ssr().unwrap().unsupported()[0];
        assert_eq!(rejected.reason, expected);
        assert!(
            source
                .get(rejected.span.start as usize..rejected.span.end as usize)
                .is_some()
        );
    }
}