vize_l2 0.532.0

L2 - the Davinci semantic IR (codename Disegno): the neutral op family every input dialect lowers into
Documentation
use super::{
    Allocator, Artifact, ArtifactError, ArtifactParts, Box, ElementOp, ExprRef, ForBinding, ForOp,
    IfBranch, IfOp, IfShape, InterpolationOp, JsExpr, Namespace, NodeId, Op, PageWalk,
    ProvenanceRecord, ScopeTag, Span, String, Vec, opaque, parts, region, scope, text,
};
use alloc::vec;

#[test]
fn source_spans_reject_inversion_outside_and_utf8_cuts() {
    let a = Allocator::default();
    for span in [Span::new(3, 1), Span::new(0, 4), Span::new(1, 2)] {
        let rejected = Artifact::try_new(parts("éx", region(&a, [text(&a, span)]))).unwrap_err();
        assert_eq!(
            rejected.error,
            ArtifactError::InvalidSpan {
                node: Some(NodeId::FIRST),
                span
            }
        );
        assert_eq!(rejected.parts.root.ops.len(), 1);
    }
    let artifact = Artifact::try_new(parts("éx", region(&a, [text(&a, Span::new(2, 3))]))).unwrap();
    assert_eq!(artifact.node_count(), 1);
}

#[test]
fn child_and_expression_ranges_must_stay_in_their_source_owner() {
    let a = Allocator::default();
    let root = region(
        &a,
        [Op::Interpolation(Box::new_in(
            InterpolationOp {
                expression: opaque(&a, Span::new(1, 3)),
                span: Span::new(0, 2),
            },
            &&a,
        ))],
    );
    assert_eq!(
        Artifact::try_new(parts("abc", root)).unwrap_err().error,
        ArtifactError::OutsideOwner {
            node: NodeId::FIRST,
            span: Span::new(1, 3),
            owner: Span::new(0, 2)
        }
    );
    let root = region(
        &a,
        [Op::Element(Box::new_in(
            ElementOp {
                tag: "p",
                namespace: Namespace::Html,
                attributes: Vec::new_in(&&a),
                bindings: Vec::new_in(&&a),
                children: region(&a, [text(&a, Span::new(1, 3))]),
                span: Span::new(0, 2),
            },
            &&a,
        ))],
    );
    assert!(matches!(
        Artifact::try_new(parts("abc", root)).unwrap_err().error,
        ArtifactError::OutsideOwner { .. }
    ));
}

#[test]
fn canonical_if_shapes_are_checked_on_native_branches() {
    let a = Allocator::default();
    let span = Span::new(0, 1);
    for (conditions, expected) in [
        (vec![], IfShape::Empty),
        (vec![false], IfShape::LeadingElse),
        (vec![true, false, true], IfShape::NonTrailingElse),
    ] {
        let mut branches = Vec::new_in(&&a);
        for condition in conditions {
            branches.push(IfBranch {
                condition: condition.then(|| opaque(&a, span)),
                region: region(&a, []),
                span,
            });
        }
        let root = region(&a, [Op::If(Box::new_in(IfOp { branches, span }, &&a))]);
        assert_eq!(
            Artifact::try_new(parts("x", root)).unwrap_err().error,
            ArtifactError::InvalidIf {
                node: NodeId::FIRST,
                shape: expected
            }
        );
    }
}

#[test]
fn stale_provenance_and_non_introducing_scope_keys_are_rejected() {
    let a = Allocator::default();
    let span = Span::new(0, 1);
    let mut input = parts("x", region(&a, [text(&a, span)]));
    let stale = NodeId::from_index(1).unwrap();
    input.provenance.push(ProvenanceRecord {
        rule: String::from("test"),
        node: Some(stale),
        before: String::from("x"),
        after: String::from("ui.text"),
        span,
    });
    assert_eq!(
        Artifact::try_new(input).unwrap_err().error,
        ArtifactError::DanglingProvenance {
            record: 0,
            node: stale
        }
    );
    let mut input = parts("x", region(&a, [text(&a, span)]));
    input.scopes.insert(NodeId::FIRST, scope(0));
    assert_eq!(
        Artifact::try_new(input).unwrap_err().error,
        ArtifactError::InvalidScopeSite {
            node: NodeId::FIRST
        }
    );
}

fn loops<'a>(a: &'a Allocator) -> ArtifactParts<'a> {
    let span = Span::new(0, 1);
    let mut root = region(a, []);
    for _ in 0..2 {
        root.ops.push(Op::For(Box::new_in(
            ForOp {
                binding: ForBinding {
                    source: opaque(a, span),
                    value: opaque(a, span),
                    key: None,
                    index: None,
                },
                region: region(a, []),
                span,
            },
            &a,
        )));
    }
    parts("x", root)
}

#[test]
fn every_scope_site_has_a_unique_dense_tag_even_without_enumerated_names() {
    let a = Allocator::default();
    let second = NodeId::from_index(1).unwrap();
    assert_eq!(
        Artifact::try_new(loops(&a)).unwrap_err().error,
        ArtifactError::MissingScope {
            node: NodeId::FIRST
        }
    );
    for tag in [0, 2] {
        let mut input = loops(&a);
        input.scopes.insert(NodeId::FIRST, scope(0));
        input.scopes.insert(second, scope(tag));
        assert_eq!(
            Artifact::try_new(input).unwrap_err().error,
            ArtifactError::InvalidScopeTag {
                node: second,
                tag: ScopeTag::from_index(tag)
            }
        );
    }
    let mut input = loops(&a);
    input.scopes.insert(second, scope(1));
    input.scopes.insert(NodeId::FIRST, scope(0));
    assert_eq!(Artifact::try_new(input).unwrap().node_count(), 2);
}

#[test]
fn retained_js_root_uses_its_own_text_coordinates_without_reparsing() {
    let a = Allocator::default();
    let parsed = JsExpr::parse_in(&a, "longName", Span::new(0, 1)).unwrap();
    let malformed = a.alloc(JsExpr {
        ast: parsed.ast,
        source: "x",
        span: Span::new(0, 1),
        coordinates: None,
    });
    let root = region(
        &a,
        [Op::Interpolation(Box::new_in(
            InterpolationOp {
                expression: ExprRef::Js(malformed),
                span: Span::new(0, 1),
            },
            &&a,
        ))],
    );
    assert_eq!(
        Artifact::try_new(parts("x", root)).unwrap_err().error,
        ArtifactError::InvalidJsSpan {
            node: NodeId::FIRST,
            span: Span::new(0, 8)
        }
    );
}

#[test]
fn skipped_attached_ids_do_not_dispatch_extra_region_op_visits() {
    let mut by_mint = PageWalk::new();
    let mut by_skip = PageWalk::new();
    assert_eq!(by_mint.mint(), by_skip.mint());
    assert_eq!(by_mint.mint_attached().unwrap().index(), 1);
    assert_eq!(by_mint.mint_attached().unwrap().index(), 2);
    by_skip.skip(2);
    assert_eq!(by_mint.mint(), by_skip.mint());
    assert_eq!(by_mint.visits(), 2);
    assert_eq!(by_skip.visits(), 2);
    assert_eq!(by_mint.minted(), by_skip.minted());
}

#[test]
fn id_exhaustion_is_reported_without_wrapping_or_unbalanced_fake_nodes() {
    let a = Allocator::default();
    let root = region(&a, [text(&a, Span::new(0, 1))]);
    let mut walk = PageWalk::new();
    walk.skip(u32::MAX as usize);
    let mut events = 0;
    assert_eq!(
        crate::walk::visit_events(&mut walk, &root.ops, &mut |_| events += 1),
        Err(crate::walk::NodeLimit)
    );
    assert_eq!(events, 0);
    assert!(walk.is_exhausted());
}