use crate::state::{CheckerState, ParamTypeResolutionMode};
use tsz_common::interner::Atom;
use tsz_parser::parser::NodeIndex;
use tsz_parser::parser::syntax_kind_ext;
use tsz_scanner::SyntaxKind;
use tsz_solver::TypeId;
impl<'a> CheckerState<'a> {
pub(crate) fn call_signature_from_function(
&mut self,
func: &tsz_parser::parser::node::FunctionData,
func_idx: tsz_parser::parser::NodeIndex,
) -> tsz_solver::CallSignature {
let enclosing_updates = self.push_enclosing_type_parameters(func_idx);
let (type_params, type_param_updates) = self.push_type_parameters(&func.type_parameters);
let (params, this_type) = self.extract_params_from_parameter_list(&func.parameters);
let (return_type, type_predicate) =
self.return_type_and_predicate(func.type_annotation, ¶ms);
self.pop_type_parameters(type_param_updates);
self.pop_type_parameters(enclosing_updates);
tsz_solver::CallSignature {
type_params,
params,
this_type,
return_type,
type_predicate,
is_method: false,
}
}
pub(crate) fn call_signature_from_method(
&mut self,
method: &tsz_parser::parser::node::MethodDeclData,
method_idx: NodeIndex,
) -> tsz_solver::CallSignature {
self.call_signature_from_method_with_this(method, None, method_idx)
}
pub(crate) fn call_signature_from_method_with_this(
&mut self,
method: &tsz_parser::parser::node::MethodDeclData,
explicit_this_type: Option<TypeId>,
method_idx: NodeIndex,
) -> tsz_solver::CallSignature {
let (type_params, type_param_updates) = self.push_type_parameters(&method.type_parameters);
let (params, this_type) = self.extract_params_from_parameter_list(&method.parameters);
let (return_type, type_predicate) =
if method.type_annotation.is_none() && method.body.is_some() {
let pushed_this = if let Some(this_ty) = explicit_this_type {
self.ctx.this_type_stack.push(this_ty);
true
} else {
false
};
let inferred = self.infer_return_type_from_body(method_idx, method.body, None);
if pushed_this {
self.ctx.this_type_stack.pop();
}
(inferred, None)
} else {
self.return_type_and_predicate(method.type_annotation, ¶ms)
};
self.pop_type_parameters(type_param_updates);
tsz_solver::CallSignature {
type_params,
params,
this_type,
return_type,
type_predicate,
is_method: true,
}
}
pub(crate) fn call_signature_from_constructor(
&mut self,
ctor: &tsz_parser::parser::node::ConstructorData,
instance_type: TypeId,
class_type_params: &[tsz_solver::TypeParamInfo],
) -> tsz_solver::CallSignature {
let (type_params, type_param_updates) = self.push_type_parameters(&ctor.type_parameters);
let (params, this_type) = self.extract_params_from_parameter_list(&ctor.parameters);
self.pop_type_parameters(type_param_updates);
let mut all_type_params = Vec::with_capacity(class_type_params.len() + type_params.len());
all_type_params.extend_from_slice(class_type_params);
all_type_params.extend(type_params);
tsz_solver::CallSignature {
type_params: all_type_params,
params,
this_type,
return_type: instance_type,
type_predicate: None,
is_method: false,
}
}
pub(crate) fn instantiate_call_signature(
&self,
sig: &tsz_solver::CallSignature,
type_args: &[TypeId],
) -> tsz_solver::CallSignature {
use tsz_solver::{ParamInfo, TypeSubstitution, instantiate_type};
let substitution = TypeSubstitution::from_args(self.ctx.types, &sig.type_params, type_args);
let params: Vec<ParamInfo> = sig
.params
.iter()
.map(|param| ParamInfo {
name: param.name,
type_id: instantiate_type(self.ctx.types, param.type_id, &substitution),
optional: param.optional,
rest: param.rest,
})
.collect();
let this_type = sig
.this_type
.map(|type_id| instantiate_type(self.ctx.types, type_id, &substitution));
let return_type = instantiate_type(self.ctx.types, sig.return_type, &substitution);
let type_predicate =
sig.type_predicate
.as_ref()
.map(|predicate| tsz_solver::TypePredicate {
asserts: predicate.asserts,
target: predicate.target.clone(),
type_id: predicate
.type_id
.map(|type_id| instantiate_type(self.ctx.types, type_id, &substitution)),
parameter_index: predicate.parameter_index,
});
tsz_solver::CallSignature {
type_params: Vec::new(),
params,
this_type,
return_type,
type_predicate,
is_method: sig.is_method,
}
}
pub(crate) fn instantiate_constructor_signature(
&self,
sig: &tsz_solver::CallSignature,
type_args: &[TypeId],
) -> tsz_solver::CallSignature {
use tsz_solver::{
CallSignature, ParamInfo, TypePredicate, TypeSubstitution, instantiate_type,
};
let substitution = TypeSubstitution::from_args(self.ctx.types, &sig.type_params, type_args);
let params: Vec<ParamInfo> = sig
.params
.iter()
.map(|param| ParamInfo {
name: param.name,
type_id: instantiate_type(self.ctx.types, param.type_id, &substitution),
optional: param.optional,
rest: param.rest,
})
.collect();
let this_type = sig
.this_type
.map(|type_id| instantiate_type(self.ctx.types, type_id, &substitution));
let return_type = instantiate_type(self.ctx.types, sig.return_type, &substitution);
let type_predicate = sig.type_predicate.as_ref().map(|predicate| TypePredicate {
asserts: predicate.asserts,
target: predicate.target.clone(),
type_id: predicate
.type_id
.map(|type_id| instantiate_type(self.ctx.types, type_id, &substitution)),
parameter_index: predicate.parameter_index,
});
CallSignature {
type_params: Vec::new(),
params,
this_type,
return_type,
type_predicate,
is_method: sig.is_method,
}
}
pub(crate) fn extract_params_from_signature(
&mut self,
sig: &tsz_parser::parser::node::SignatureData,
) -> (Vec<tsz_solver::ParamInfo>, Option<TypeId>) {
let Some(ref params_list) = sig.parameters else {
return (Vec::new(), None);
};
self.extract_params_from_parameter_list_impl(params_list, ParamTypeResolutionMode::OfNode)
}
pub(crate) fn extract_params_from_parameter_list(
&mut self,
params_list: &tsz_parser::parser::NodeList,
) -> (Vec<tsz_solver::ParamInfo>, Option<TypeId>) {
self.extract_params_from_parameter_list_impl(
params_list,
ParamTypeResolutionMode::FromTypeNode,
)
}
pub(crate) fn extract_params_from_parameter_list_impl(
&mut self,
params_list: &tsz_parser::parser::NodeList,
mode: ParamTypeResolutionMode,
) -> (Vec<tsz_solver::ParamInfo>, Option<TypeId>) {
use tsz_solver::ParamInfo;
let mut params = Vec::new();
let mut this_type = None;
let this_atom = self.ctx.types.intern_string("this");
for ¶m_idx in ¶ms_list.nodes {
let Some(param_node) = self.ctx.arena.get(param_idx) else {
continue;
};
let Some(param) = self.ctx.arena.get_parameter(param_node) else {
continue;
};
let type_id = if param.type_annotation.is_some() {
match mode {
ParamTypeResolutionMode::InTypeLiteral => {
self.get_type_from_type_node_in_type_literal(param.type_annotation)
}
ParamTypeResolutionMode::FromTypeNode => {
self.get_type_from_type_node(param.type_annotation)
}
ParamTypeResolutionMode::OfNode => self.get_type_of_node(param.type_annotation),
}
} else {
TypeId::ANY
};
let name_node = self.ctx.arena.get(param.name);
if let Some(name_node) = name_node
&& name_node.kind == SyntaxKind::ThisKeyword as u16
{
if this_type.is_none() {
this_type = Some(type_id);
}
continue;
}
let name: Option<Atom> = if let Some(name_node) = name_node {
if let Some(name_data) = self.ctx.arena.get_identifier(name_node) {
Some(self.ctx.types.intern_string(&name_data.escaped_text))
} else {
None
}
} else {
None
};
let is_js_file =
self.ctx.file_name.ends_with(".js") || self.ctx.file_name.ends_with(".jsx");
let optional = param.question_token
|| param.initializer.is_some()
|| (is_js_file && param.type_annotation.is_none());
let rest = param.dot_dot_dot_token;
if let Some(name_atom) = name
&& name_atom == this_atom
{
if this_type.is_none() {
this_type = Some(type_id);
}
continue;
}
params.push(ParamInfo {
name,
type_id,
optional,
rest,
});
}
(params, this_type)
}
pub(crate) fn return_type_and_predicate(
&mut self,
type_annotation: NodeIndex,
params: &[tsz_solver::ParamInfo],
) -> (TypeId, Option<tsz_solver::TypePredicate>) {
use tsz_solver::{TypePredicate, TypePredicateTarget};
if type_annotation.is_none() {
return (TypeId::ANY, None);
}
let Some(predicate_node_idx) = self.find_type_predicate_node(type_annotation) else {
return (self.get_type_from_type_node(type_annotation), None);
};
let Some(node) = self.ctx.arena.get(predicate_node_idx) else {
return (TypeId::BOOLEAN, None);
};
let Some(data) = self.ctx.arena.get_type_predicate(node) else {
return (TypeId::BOOLEAN, None);
};
let return_type = if data.asserts_modifier {
TypeId::VOID
} else {
TypeId::BOOLEAN
};
let target = match self.type_predicate_target(data.parameter_name) {
Some(target) => target,
None => return (return_type, None),
};
let type_id = if data.type_node.is_none() {
None
} else {
Some(self.get_type_from_type_node(data.type_node))
};
let mut parameter_index = None;
if let TypePredicateTarget::Identifier(name) = &target {
parameter_index = params.iter().position(|p| p.name == Some(*name));
}
let predicate = TypePredicate {
asserts: data.asserts_modifier,
target,
type_id,
parameter_index,
};
(return_type, Some(predicate))
}
fn find_type_predicate_node(&self, node_idx: NodeIndex) -> Option<NodeIndex> {
let node = self.ctx.arena.get(node_idx)?;
match node.kind {
k if k == syntax_kind_ext::TYPE_PREDICATE => Some(node_idx),
k if k == syntax_kind_ext::PARENTHESIZED_TYPE => {
let wrapped = self.ctx.arena.get_wrapped_type(node)?;
self.find_type_predicate_node(wrapped.type_node)
}
k if k == syntax_kind_ext::INTERSECTION_TYPE => {
let composite = self.ctx.arena.get_composite_type(node)?;
for &member in &composite.types.nodes {
if let Some(found) = self.find_type_predicate_node(member) {
return Some(found);
}
}
None
}
_ => None,
}
}
pub(crate) fn return_type_and_predicate_in_type_literal(
&mut self,
type_annotation: NodeIndex,
params: &[tsz_solver::ParamInfo],
) -> (TypeId, Option<tsz_solver::TypePredicate>) {
use tsz_solver::{TypePredicate, TypePredicateTarget};
if type_annotation.is_none() {
return (TypeId::ANY, None);
}
let Some(predicate_node_idx) = self.find_type_predicate_node(type_annotation) else {
return (
self.get_type_from_type_node_in_type_literal(type_annotation),
None,
);
};
let Some(node) = self.ctx.arena.get(predicate_node_idx) else {
return (TypeId::BOOLEAN, None);
};
let Some(data) = self.ctx.arena.get_type_predicate(node) else {
return (TypeId::BOOLEAN, None);
};
let return_type = if data.asserts_modifier {
TypeId::VOID
} else {
TypeId::BOOLEAN
};
let target = match self.type_predicate_target(data.parameter_name) {
Some(target) => target,
None => return (return_type, None),
};
let type_id = if data.type_node.is_none() {
None
} else {
Some(self.get_type_from_type_node_in_type_literal(data.type_node))
};
let mut parameter_index = None;
if let TypePredicateTarget::Identifier(name) = &target {
parameter_index = params.iter().position(|p| p.name == Some(*name));
}
let predicate = TypePredicate {
asserts: data.asserts_modifier,
target,
type_id,
parameter_index,
};
(return_type, Some(predicate))
}
}