use yog_ids::script_problem;
use yog_script_ids::system::{ASYNC_ENUMERABLE, ENUMERABLE, ENUMERATOR};
use super::Checker;
use super::model::Type;
use crate::ast::{Expr, MethodBody, Stmt};
use crate::source::token::Span;
pub fn item_of(t: &Type) -> Option<&Type> {
match t.non_null() {
Type::Own(name, args)
if args.len() == 1
&& [ENUMERABLE, ENUMERATOR, ASYNC_ENUMERABLE].contains(&name.as_str()) =>
{
args.first()
}
_ => None,
}
}
pub fn body_yields(body: Option<&MethodBody>) -> bool {
match body {
Some(MethodBody::Block(b)) => b.stmts.iter().any(stmt_yields),
_ => false,
}
}
pub fn stmt_yields(s: &Stmt) -> bool {
match s {
Stmt::Yield(..) => true,
Stmt::If {
then, otherwise, ..
} => stmt_yields(then) || otherwise.as_deref().is_some_and(stmt_yields),
Stmt::Foreach { body, .. } | Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
stmt_yields(body)
}
Stmt::For {
init, step, body, ..
} => {
init.as_deref().is_some_and(stmt_yields)
|| step.as_deref().is_some_and(stmt_yields)
|| stmt_yields(body)
}
Stmt::Block(b) | Stmt::Unsafe(b, _) | Stmt::Checked(b, _) => b.stmts.iter().any(stmt_yields),
Stmt::Switch { sections, .. } => sections.iter().flat_map(|s| &s.body).any(stmt_yields),
_ => false,
}
}
impl Checker<'_, '_, '_> {
pub(super) fn yielded(&mut self, v: Option<&Expr>, at: Span) {
let item = self.yields.clone();
match (item, v) {
(Some(t), Some(v)) => self.fits(v, &t),
(Some(_), None) => {}
(None, v) => {
if let Some(v) = v {
self.expr(v);
}
self.report.error(
script_problem::ITERATOR,
at,
"`yield` is an iterator's: a method giving an `IEnumerable<T>`, an `IEnumerator<T>` or an `IAsyncEnumerable<T>`",
);
}
}
}
pub(super) fn returned_from_iterator(&mut self, at: Span) {
self.report.error(
script_problem::ITERATOR,
at,
"an iterator gives its items by `yield return` and ends by `yield break`, not `return` with a value",
);
}
}