vize_l2 0.475.0

L2 - the Davinci semantic IR (codename Disegno): the neutral op family every input dialect lowers into
Documentation
use super::{Allocator, DeclarationKind, Namespace, Parser, SourceType, finish};
use vize_l0::cstr;
use vize_l2::file::{FileIssueKind, ReferenceTarget};

#[test]
fn unsupported_optional_types_retain_both_original_issue_sites() {
    let arena = Allocator::default();
    for annotation in [
        "T",
        "Namespace.T",
        "number[]",
        "{id:number}",
        "number|string",
        "[number]",
        "any",
        "unknown",
        "never",
        "void",
    ] {
        let source = cstr!("function f(value?: {annotation}) {{ return value; }}");
        let observed =
            Parser::new(&arena, &source, SourceType::ts().with_module(true)).parse_observed();
        assert!(
            observed.admitted().is_some(),
            "{annotation}: {:?}",
            observed.diagnostics()
        );
        let file = finish(&arena, &observed).unwrap();
        assert!(!file.is_complete(), "{annotation}");
        assert_eq!(file.scopes().len(), 2);
        assert_eq!(file.bindings().count(), 1);
        assert!(
            file.lookup(file.scopes()[1].id, "value", Namespace::Value)
                .is_none()
        );
        let sites = file
            .issues()
            .iter()
            .map(|issue| (issue.kind, issue.span.slice(&source)))
            .collect::<Vec<_>>();
        assert_eq!(
            sites,
            [
                (
                    FileIssueKind::UnsupportedSyntax,
                    cstr!("value?: {annotation}").as_str()
                ),
                (FileIssueKind::UnresolvedReference, "value")
            ],
            "{annotation}"
        );
        assert_eq!(file.references().len(), 1);
        assert_eq!(file.references()[0].target, ReferenceTarget::Unresolved);
        assert_eq!(observed.admitted().unwrap().program().body.len(), 1);
    }
}

#[test]
fn direct_optional_typed_exports_refuse_and_later_export_events_survive() {
    let arena = Allocator::default();
    let source = "export function f(value?: number) { return value; }";
    let observed = Parser::new(&arena, source, SourceType::ts().with_module(true)).parse_observed();
    let file = finish(&arena, &observed).unwrap();
    assert!(!file.is_complete());
    assert_eq!(file.exports().len(), 1);
    let function = file
        .lookup(file.units()[0].scope, "f", Namespace::Value)
        .unwrap();
    assert_eq!(file.exports()[0].local, Some(function.id()));
    assert_eq!(
        file.issues()
            .iter()
            .map(|issue| (issue.kind, issue.span.slice(source)))
            .collect::<Vec<_>>(),
        [
            (FileIssueKind::UnsupportedSyntax, "value?: number"),
            (FileIssueKind::UnresolvedReference, "value")
        ]
    );
    for source in [
        "export function f(value) { return value; }",
        "function f(value?: number) { return value; } export { f };",
    ] {
        let observed =
            Parser::new(&arena, source, SourceType::ts().with_module(true)).parse_observed();
        let file = finish(&arena, &observed).unwrap();
        assert!(file.is_complete(), "{source}: {:?}", file.issues());
        assert_eq!(file.exports().len(), 1);
        let root = file.units()[0].scope;
        let function = file.lookup(root, "f", Namespace::Value).unwrap();
        let parameter = file
            .lookup(file.scopes()[1].id, "value", Namespace::Value)
            .unwrap();
        assert_eq!(
            parameter.declaration().unwrap().kind,
            DeclarationKind::Parameter
        );
        assert_eq!(file.exports()[0].local, Some(function.id()));
        assert_eq!(
            file.references()[0].target,
            ReferenceTarget::Resolved(parameter.id())
        );
    }
}

#[test]
fn optional_keyword_does_not_complete_other_function_or_body_fields() {
    let arena = Allocator::default();
    for source in [
        "async function f(value?:number){return value;}",
        "function* f(value?:number){yield value;}",
        "function f<T>(value?:number){return value;}",
        "function f(this:object,value?:number){return value;}",
        "function f(value:number=1){return value;}",
        "function f(...value:number[]){return value;}",
        "function f({value}:{value:number}){return value;}",
        "function f([value]:number[]){return value;}",
        "function f(value?:number){'use strict';return value;}",
        "function f(value?:number){function nested(){return value;}return value;}",
        "function f(value?:number){if(value)return value;}",
        "function f(value?:number){{const local=value;}return value;}",
        "function f(value?:number){return missing+value;}",
        "function f(value?:number){var value=1;return value;}",
        "function f(value?:number){return value;}class Unsupported{}",
    ] {
        let observed =
            Parser::new(&arena, source, SourceType::ts().with_module(true)).parse_observed();
        assert!(
            observed.admitted().is_some(),
            "{source}: {:?}",
            observed.diagnostics()
        );
        let body = observed.admitted().unwrap().program().body.as_ptr();
        let file = finish(&arena, &observed).unwrap();
        assert!(!file.is_complete(), "{source}");
        assert!(!file.issues().is_empty(), "{source}");
        assert!(core::ptr::eq(file.artifact().source(), source));
        assert_eq!(observed.admitted().unwrap().program().body.as_ptr(), body);
    }
}

#[test]
fn the_exact_unannotated_optional_negative_remains_a_whole_two_site_refusal() {
    let arena = Allocator::default();
    let source = "function f(value?) { return value; }";
    let observed = Parser::new(&arena, source, SourceType::ts().with_module(true)).parse_observed();
    let file = finish(&arena, &observed).unwrap();
    assert!(!file.is_complete());
    assert_eq!(file.bindings().count(), 1);
    assert_eq!(
        file.issues()
            .iter()
            .map(|issue| (issue.kind, issue.span.slice(source)))
            .collect::<Vec<_>>(),
        [
            (FileIssueKind::UnsupportedSyntax, "value?"),
            (FileIssueKind::UnresolvedReference, "value"),
        ]
    );
    assert_eq!(file.references().len(), 1);
    assert_eq!(file.references()[0].target, ReferenceTarget::Unresolved);
    assert!(
        file.lookup(file.scopes()[1].id, "value", Namespace::Value)
            .is_none()
    );
    assert!(core::ptr::eq(file.artifact().source(), source));
}