crisp-typeck 1.8.1

HM-style type inference and constraint solving (spec §3.4)
Documentation
use crisp_typeck::{TypeChecker, TypeError};
use std::path::PathBuf;

fn fixture(name: &str) -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures")
        .join(name)
}

#[test]
fn field_access_on_inferred_unique_struct_param() {
    let typed = TypeChecker::check_crate(&fixture("field_param_unique"))
        .expect("unique field should constrain param type");
    let sku = typed
        .signatures
        .values()
        .find(|s| s.name == "sku_of")
        .expect("sku_of");
    let rendered = format!("{:?}", sku.params);
    assert!(
        rendered.contains("Item") || rendered.contains("Named"),
        "expected Item param, got {rendered:?} / {sku:?}"
    );
}

#[test]
fn field_access_on_annotated_struct_param() {
    TypeChecker::check_crate(&fixture("field_param_unique")).expect("annotated param");
}

#[test]
fn ambiguous_field_on_param_asks_for_annotation() {
    let err = TypeChecker::check_crate(&fixture("field_param_ambiguous"))
        .expect_err("sku is on Item and StockLine");
    let msg = err.to_string();
    assert!(msg.contains("E0043"), "{msg}");
    match &err {
        TypeError::AmbiguousField {
            field, candidates, ..
        } => {
            assert_eq!(field, "sku");
            assert!(candidates.contains("Item"), "{candidates}");
            assert!(candidates.contains("StockLine"), "{candidates}");
        }
        other => panic!("expected AmbiguousField, got {other}"),
    }
}