mod check;
mod infer;
mod usefulness;
pub use check::{check, check_with};
use nova_hir::Ty;
use nova_resolver::Definitions;
#[derive(Debug)]
pub struct CheckResult {
pub module: nova_hir::Module,
pub diagnostics: Vec<nova_diagnostics::Diagnostic>,
pub probe: ProbeResult,
pub index: Option<nova_resolver::Index>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProbePoint {
pub file: nova_diagnostics::FileId,
pub offset: u32,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CheckOptions<'a> {
pub probe: Option<ProbePoint>,
pub index: bool,
pub sources: Option<&'a nova_diagnostics::FileDb>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct ProbeResult {
pub receiver: Option<Ty>,
pub members: Vec<Member>,
pub locals: Vec<(String, Ty)>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Member {
pub name: String,
pub kind: MemberKind,
pub decl: Option<nova_diagnostics::Span>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemberKind {
Field,
Method,
}
pub fn display_ty(ty: &Ty, defs: &Definitions) -> String {
match ty {
Ty::Int => "Int".to_string(),
Ty::Float => "Float".to_string(),
Ty::Bool => "Bool".to_string(),
Ty::Char => "Char".to_string(),
Ty::String => "String".to_string(),
Ty::Bytes => "Bytes".to_string(),
Ty::Unit => "()".to_string(),
Ty::Fn { params, ret } => {
let params = params
.iter()
.map(|p| display_ty(p, defs))
.collect::<Vec<_>>()
.join(", ");
format!("fn({params}) -> {}", display_ty(ret, defs))
}
Ty::Sum { def_id, args } | Ty::Record { def_id, args } => {
let name = &defs.def(*def_id).name;
if args.is_empty() {
name.clone()
} else {
let args = args
.iter()
.map(|a| display_ty(a, defs))
.collect::<Vec<_>>()
.join(", ");
format!("{name}<{args}>")
}
}
Ty::Array(elem) => format!("[{}]", display_ty(elem, defs)),
Ty::Future(out) => format!("Future<{}>", display_ty(out, defs)),
Ty::Param(i) => format!("T{i}"),
Ty::Assoc { on, assoc } => {
format!("{}::{}", display_ty(on, defs), defs.def(*assoc).name)
}
Ty::Var(v) => format!("?{v}"),
Ty::Never => "!".to_string(),
Ty::Error => "{error}".to_string(),
}
}
pub fn display_ty_named(ty: &Ty, defs: &Definitions, names: &[String]) -> String {
let show = |t: &Ty| display_ty_named(t, defs, names);
let list = |ts: &[Ty]| ts.iter().map(show).collect::<Vec<_>>().join(", ");
match ty {
Ty::Fn { params, ret } => format!("fn({}) -> {}", list(params.as_slice()), show(ret)),
Ty::Sum { def_id, args } | Ty::Record { def_id, args } => {
let name = &defs.def(*def_id).name;
if args.is_empty() {
name.clone()
} else {
format!("{name}<{}>", list(args.as_slice()))
}
}
Ty::Array(elem) => format!("[{}]", show(elem)),
Ty::Future(out) => format!("Future<{}>", show(out)),
Ty::Param(i) => names
.get(*i as usize)
.filter(|n| !n.is_empty())
.cloned()
.unwrap_or_else(|| format!("T{i}")),
Ty::Assoc { on, assoc } => format!("{}::{}", show(on), defs.def(*assoc).name),
Ty::Var(_) | Ty::Error => "_".to_string(),
other => display_ty(other, defs),
}
}
pub fn builtin_text(builtin: nova_resolver::Builtin, defs: &Definitions) -> String {
let (params, ret) = check::builtin_signature(builtin);
let names: Vec<String> = ["T", "U", "V", "W"].iter().map(|s| s.to_string()).collect();
let params = params
.iter()
.map(|p| display_ty_named(p, defs, &names))
.collect::<Vec<_>>()
.join(", ");
format!(
"fn {}({params}) -> {}",
builtin.name(),
display_ty_named(&ret, defs, &names)
)
}