use yog_ids::script_problem;
use super::Checker;
use super::model::Type;
use crate::ast::{self, TypeKind};
use crate::index::Side;
use crate::source::token::Span;
use crate::words;
use yog_script_ids::system::ROOT;
impl Checker<'_, '_, '_> {
pub(super) fn type_of(&mut self, t: &ast::Type) -> Type {
match &t.kind {
TypeKind::Nullable(inner) => self.type_of(inner).nullable(),
TypeKind::Array { item, rank } => Type::Array(Box::new(self.type_of(item)), *rank),
TypeKind::Tuple(items) => Type::Tuple(
items.iter().map(|(t, _)| self.type_of(t)).collect(),
items
.iter()
.map(|(_, n)| n.as_ref().map(|n| n.text.clone()))
.collect(),
),
TypeKind::Borrow { ty, .. } => self.type_of(ty),
TypeKind::Lifetime(_) => Type::Unknown,
TypeKind::Both(all) => all.first().map_or(Type::Unknown, |t| self.type_of(t)),
TypeKind::Path { path, args } => {
let args: Vec<Type> = args.iter().map(|a| self.type_of(a)).collect();
let typed = match path.as_slice() {
[one] => self.named(&one.text, args, t.span),
_ => match self.qualified(path, t.span) {
Type::Own(name, given) if given.is_empty() => Type::Own(name, args),
other => other,
},
};
self.type_meets(&typed, t.span);
typed
}
}
}
fn named(&mut self, name: &str, args: Vec<Type>, at: Span) -> Type {
if let Some(t) = super::builtin::by_word(name) {
return t;
}
if yog_script_ids::units::GAME_UNITS.contains(&name) {
return Type::Unit(name.to_string());
}
if name == yog_script_ids::system::VALUE_TUPLE && !args.is_empty() {
let names = vec![None; args.len()];
return Type::Tuple(args, names);
}
if self.own.units.iter().any(|u| u == name) {
return Type::Unit(name.to_string());
}
if let Some(t) = self.type_param(name) {
return t;
}
if let Some(b) = super::builtin::by_system_name(name)
&& self.own.namespaces.get(name).is_none_or(|ns| *ns == [ROOT])
{
return b;
}
let own = self.own.get(name, args.len()).is_some() && self.own_in_reach(name);
let yog = |c: &Self| c.own.namespaces.get(name).is_none_or(|ns| *ns == super::names::yog_ns());
if let Some(kind) = super::content_ids::content_id_kind(name)
&& args.is_empty()
&& (!own || yog(self))
{
return Type::Content(kind.to_string());
}
if name == yog_script_ids::system::CONTENT_ID && args.len() == 1 && yog(self) {
return super::content_ids::content_id_of(args);
}
if name == yog_script_ids::system::TAG_ID && args.len() == 1 && yog(self) {
return super::content_ids::tag_id_of(args);
}
if own {
return Type::Own(name.to_string(), args);
}
let t = self.imported(name, at);
let blind = self.nanites && self.lookup.is_none();
if !t.known() && !blind {
self.report.error(
script_problem::UNKNOWN_TYPE,
at,
format!("no type `{name}` here: is a `using` missing?"),
);
}
t
}
pub(super) fn check_side(&mut self, side: Side, name: &str, at: Span) {
if side == Side::Shared || side == self.side {
return;
}
let (theirs, ours) = (side_word(side), side_word(self.side));
self.report.error(
script_problem::WRONG_SIDE,
at,
format!("`{name}` exists only on the {theirs}: {ours} code can't reach it (mark the method `{theirs}`)"),
);
}
}
fn side_word(side: Side) -> &'static str {
match side {
Side::Client => words::client::WORD.text,
Side::Server => words::server::WORD.text,
Side::Shared => words::shared::WORD.text,
}
}