vize_l2 0.587.0

L2 - the Davinci semantic IR (codename Disegno): the neutral op family every input dialect lowers into
Documentation
use super::{
    Allocator, FileIssueKind, FileProducer, Parser, PositionQueryError, ProgramInput, ProgramScope,
    ScriptUnit, SourceRoot, SourceType, String, lower,
};
use oxc_parser::ParseOptions;
use vize_l2::lang::js::{CallEvent, DeclaredEvent, FileObserver, StatementEvent};
use vize_l2::resolution::ResolutionErrorKind;

#[test]
fn array_annotations_keep_source_options_and_original_module_authority() {
    let arena = Allocator::default();
    let source = String::from("const items:number[]=[];");
    let copied = source.clone();
    let block = SourceRoot::new(&source).unwrap().whole_block();
    let original =
        Parser::new(&arena, &source, SourceType::ts().with_module(true)).parse_observed();
    assert!(matches!(
        ProgramInput::checked(
            original.admitted().unwrap(),
            SourceRoot::new(&copied).unwrap().whole_block(),
            7,
        ),
        Err(issue) if issue.kind == FileIssueKind::InvalidSource
    ));
    let changed = Parser::new(&arena, &source, SourceType::ts().with_module(true))
        .with_options(ParseOptions {
            enable_ident_hashes: false,
            ..ParseOptions::default()
        })
        .parse_observed();
    assert!(matches!(
        ProgramInput::checked(changed.admitted().unwrap(), block, 7),
        Err(issue) if issue.kind == FileIssueKind::InvalidProfile
    ));
    for profile in [
        SourceType::ts().with_script(true),
        SourceType::tsx().with_script(true),
    ] {
        let observed = Parser::new(&arena, &source, profile).parse_observed();
        assert!(observed.diagnostics().is_empty());
        let file = lower(&arena, &source, block, &observed);
        assert!(!file.is_complete());
        assert!(
            file.issues()
                .iter()
                .any(|issue| issue.kind == FileIssueKind::InvalidProfile)
        );
    }
    let ambient = "let items:number[];";
    let observed =
        Parser::new(&arena, ambient, SourceType::d_ts().with_module(true)).parse_observed();
    assert!(observed.diagnostics().is_empty());
    let file = lower(
        &arena,
        ambient,
        SourceRoot::new(ambient).unwrap().whole_block(),
        &observed,
    );
    assert!(!file.is_complete());
    assert!(
        file.issues()
            .iter()
            .any(|issue| issue.kind == FileIssueKind::InvalidProfile)
    );
}

struct Interrupt;

impl<'a> FileObserver<'a> for Interrupt {
    type Checkpoint = ();
    fn unit(&mut self, _: &ScriptUnit) {}
    fn statement(&mut self, _: StatementEvent<'_, 'a>) {}
    fn declared(&mut self, _: DeclaredEvent<'_, 'a>) {
        panic!("actual array declaration event");
    }
    fn checkpoint(&self) {}
    fn call(&mut self, _: CallEvent<'_, 'a>) -> Result<(), ResolutionErrorKind> {
        Ok(())
    }
    fn rollback(&mut self, _: ()) {}
}

#[test]
fn interrupted_original_array_event_never_completes_the_file() {
    let arena = Allocator::default();
    let source = "const items:{id:number}[]=[{id:1}];";
    let observed = Parser::new(&arena, source, SourceType::ts().with_module(true)).parse_observed();
    let mut producer = FileProducer::new(&arena, source).unwrap();
    let block = SourceRoot::new(source).unwrap().whole_block();
    let interrupted = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        producer
            .program_observed(
                ProgramInput::checked(observed.admitted().unwrap(), block, 7).unwrap(),
                ProgramScope::Module,
                &mut Interrupt,
            )
            .unwrap();
    }));
    assert!(interrupted.is_err());
    let file = producer.finish().unwrap();
    assert!(!file.is_complete());
    assert_eq!(file.interrupted_programs().count(), 1);
    assert_eq!(file.bindings().count(), 1);
    assert!(matches!(
        file.binding_at_offset(source.find("items").unwrap() as u32),
        Err(PositionQueryError::IncompleteFile)
    ));
}