use super::ExpressionAnalyzer;
use crate::context::Context;
use mir_types::Union;
use php_ast::ast::{ExprKind, MatchExpr, NullCoalesceExpr, TernaryExpr};
impl<'a> ExpressionAnalyzer<'a> {
pub(super) fn analyze_ternary<'arena, 'src>(
&mut self,
t: &TernaryExpr<'arena, 'src>,
ctx: &mut Context,
) -> Union {
let cond_ty = self.analyze(t.condition, ctx);
match &t.then_expr {
Some(then_expr) => {
let mut then_ctx = ctx.fork();
crate::narrowing::narrow_from_condition(
t.condition,
&mut then_ctx,
true,
self.db,
&self.file,
);
let then_ty = self.analyze(then_expr, &mut then_ctx);
let mut else_ctx = ctx.fork();
crate::narrowing::narrow_from_condition(
t.condition,
&mut else_ctx,
false,
self.db,
&self.file,
);
let else_ty = self.analyze(t.else_expr, &mut else_ctx);
for name in then_ctx.read_vars.iter().chain(else_ctx.read_vars.iter()) {
ctx.read_vars.insert(name.clone());
}
Union::merge(&then_ty, &else_ty)
}
None => {
let else_ty = self.analyze(t.else_expr, ctx);
let truthy_ty = cond_ty.narrow_to_truthy();
if truthy_ty.is_empty() {
else_ty
} else {
Union::merge(&truthy_ty, &else_ty)
}
}
}
}
pub(super) fn analyze_null_coalesce<'arena, 'src>(
&mut self,
nc: &NullCoalesceExpr<'arena, 'src>,
ctx: &mut Context,
) -> Union {
let left_ty = self.analyze(nc.left, ctx);
let right_ty = self.analyze(nc.right, ctx);
let non_null_left = left_ty.remove_null();
if non_null_left.is_empty() {
right_ty
} else {
Union::merge(&non_null_left, &right_ty)
}
}
pub(super) fn analyze_match<'arena, 'src>(
&mut self,
m: &MatchExpr<'arena, 'src>,
ctx: &mut Context,
) -> Union {
let subject_ty = self.analyze(m.subject, ctx);
let subject_var = match &m.subject.kind {
ExprKind::Variable(name) => Some(name.as_str().trim_start_matches('$').to_string()),
_ => None,
};
let mut result = Union::empty();
for arm in m.arms.iter() {
let mut arm_ctx = ctx.fork();
if let (Some(var), Some(conditions)) = (&subject_var, &arm.conditions) {
let mut arm_ty = Union::empty();
for cond in conditions.iter() {
let cond_ty = self.analyze(cond, ctx);
arm_ty = Union::merge(&arm_ty, &cond_ty);
}
if !arm_ty.is_empty() && !arm_ty.is_mixed() {
let narrowed = subject_ty.intersect_with(&arm_ty);
if !narrowed.is_empty() {
arm_ctx.set_var(var, narrowed);
}
}
}
if let Some(conditions) = &arm.conditions {
for cond in conditions.iter() {
crate::narrowing::narrow_from_condition(
cond,
&mut arm_ctx,
true,
self.db,
&self.file,
);
}
}
let arm_body_ty = self.analyze(&arm.body, &mut arm_ctx);
result = Union::merge(&result, &arm_body_ty);
for name in &arm_ctx.read_vars {
ctx.read_vars.insert(name.clone());
}
}
if result.is_empty() {
Union::mixed()
} else {
result
}
}
}