use crate::EmitEffects;
use crate::Planner;
use crate::context::expression::ExpressionContext;
use crate::names::go_name;
use syntax::ast::Expression;
use syntax::program::DefinitionBody;
use syntax::types::{Type, unqualified_name};
impl Planner<'_> {
pub(crate) fn emit_enum_variant_dot(
&mut self,
member: &str,
result_ty: &Type,
fx: &mut EmitEffects,
) -> Option<String> {
if let Some(s) = self.emit_enum_variant_constructor(member, result_ty, fx) {
return Some(s);
}
self.emit_unit_variant_constructor(member, result_ty, fx)
}
pub(crate) fn emit_static_method_dot(
&mut self,
expression: &Expression,
member: &str,
result_ty: &Type,
ctx: ExpressionContext<'_>,
fx: &mut EmitEffects,
) -> Option<String> {
if let Some(s) =
self.emit_cross_module_static_method(expression, member, result_ty, ctx, fx)
{
return Some(s);
}
if let Some(s) = self.emit_alias_static_method(expression, member, result_ty, fx) {
return Some(s);
}
None
}
fn emit_enum_variant_constructor(
&mut self,
variant_name: &str,
result_ty: &Type,
fx: &mut EmitEffects,
) -> Option<String> {
let Type::Function(f) = result_ty else {
return None;
};
let fn_params = &f.params;
let Type::Nominal {
id: enum_id,
params: ret_params,
..
} = f.return_type.as_ref()
else {
return None;
};
let enum_name = unqualified_name(enum_id);
let constructor_key = format!("{}.{}", enum_name, variant_name);
let make_fn_name = self.facts.make_function_name(&constructor_key)?.to_string();
let enum_module = self.facts.module_for_qualified_name(enum_id)?;
let needs_qualifier = !self.facts.is_current_module(enum_module);
let needs_type_args = ret_params.len() > fn_params.len();
let type_args = if needs_type_args {
self.format_type_args(ret_params, fx)
} else {
String::new()
};
let make_fn = if needs_qualifier {
if make_fn_name.starts_with(go_name::PRELUDE_PREFIX) {
let resolved = go_name::resolve(&make_fn_name);
if resolved.needs_stdlib {
fx.require_stdlib();
}
format!("{}{}", resolved.name, type_args)
} else {
let pkg = self.require_module_import_fx(enum_module, fx);
format!("{}.{}{}", pkg, make_fn_name, type_args)
}
} else {
format!("{}{}", make_fn_name, type_args)
};
Some(make_fn)
}
fn emit_unit_variant_constructor(
&mut self,
variant_name: &str,
result_ty: &Type,
fx: &mut EmitEffects,
) -> Option<String> {
let Type::Nominal {
id: enum_id,
params,
..
} = result_ty
else {
return None;
};
let enum_module = self.facts.module_for_qualified_name(enum_id)?;
let is_prelude = enum_module == go_name::PRELUDE_MODULE;
let is_cross_module = !self.facts.is_current_module(enum_module) && !is_prelude;
let definition = self.facts.definition(enum_id.as_str())?;
let DefinitionBody::Enum { variants, .. } = &definition.body else {
return None;
};
let variant = variants.iter().find(|v| v.name == variant_name)?;
if !variant.fields.is_empty() {
return None;
}
let enum_name = unqualified_name(enum_id);
let key = format!("{}.{}", enum_name, variant_name);
let make_fn = self.facts.make_function_name(&key)?.to_string();
let type_args = self.format_type_args(params, fx);
if is_prelude {
let resolved = go_name::resolve(&make_fn);
if resolved.needs_stdlib {
fx.require_stdlib();
}
Some(format!("{}{}()", resolved.name, type_args))
} else if is_cross_module {
let pkg = self.require_module_import_fx(enum_module, fx);
Some(format!("{}.{}{}()", pkg, make_fn, type_args))
} else {
Some(format!("{}{}()", make_fn, type_args))
}
}
fn emit_alias_static_method(
&mut self,
expression: &Expression,
member: &str,
result_ty: &Type,
fx: &mut EmitEffects,
) -> Option<String> {
let func_ty = result_ty.unwrap_forall();
if !matches!(func_ty, Type::Function(_)) {
return None;
}
let Expression::Identifier { value, .. } = expression else {
return None;
};
let real_type = self.resolve_alias_type_name(value)?;
let resolved_name = format!("{}.{}", real_type, member);
let capitalized = self.capitalize_static_method_if_public(&resolved_name);
let go_name = self.resolve_go_name(&capitalized, fx);
Some(go_name)
}
pub(crate) fn emit_instance_method_value_dot(
&mut self,
expression: &Expression,
member: &str,
result_ty: &Type,
is_exported: bool,
is_pointer_receiver: bool,
fx: &mut EmitEffects,
) -> Option<String> {
if let Expression::Identifier { value, .. } = expression {
let go_method = if is_exported {
go_name::snake_to_camel(member)
} else {
go_name::escape_keyword(member).into_owned()
};
let type_name = self
.resolve_alias_type_name(value)
.unwrap_or_else(|| value.to_string());
let type_args = self.method_expression_type_args(result_ty, fx);
return Some(if is_pointer_receiver {
format!("(*{}{}).{}", type_name, type_args, go_method)
} else {
format!("{}{}.{}", type_name, type_args, go_method)
});
}
let Expression::DotAccess {
expression: inner_expression,
member: type_name,
..
} = expression
else {
return None;
};
let inner_ty = inner_expression.get_type();
let module_name = if let Some(synthetic_module) = inner_ty.as_import_namespace() {
synthetic_module.to_string()
} else if matches!(&inner_ty, Type::Nominal { .. })
&& let Expression::Identifier { value, .. } = inner_expression.as_ref()
{
value.to_string()
} else {
return None;
};
let module_name = module_name.as_str();
let go_method = if is_exported {
go_name::snake_to_camel(member)
} else {
go_name::escape_keyword(member).into_owned()
};
let pkg = self.require_module_import_fx(module_name, fx);
let go_type_name = go_name::snake_to_camel(type_name);
let type_args = self.method_expression_type_args(result_ty, fx);
let method_expression = if is_pointer_receiver {
format!("(*{}.{}{}).{}", pkg, go_type_name, type_args, go_method)
} else {
format!("{}.{}{}.{}", pkg, go_type_name, type_args, go_method)
};
Some(method_expression)
}
fn method_expression_type_args(&mut self, result_ty: &Type, fx: &mut EmitEffects) -> String {
let Type::Function(f) = result_ty.unwrap_forall() else {
return String::new();
};
let Some(first_param) = f.params.first() else {
return String::new();
};
let Type::Nominal {
params: receiver_params,
..
} = first_param.strip_refs()
else {
return String::new();
};
if receiver_params.is_empty() {
String::new()
} else {
self.format_type_args(&receiver_params, fx)
}
}
fn emit_cross_module_static_method(
&mut self,
expression: &Expression,
member: &str,
result_ty: &Type,
ctx: ExpressionContext<'_>,
fx: &mut EmitEffects,
) -> Option<String> {
if !matches!(result_ty.unwrap_forall(), Type::Function(_)) {
return None;
}
let Expression::DotAccess {
expression: inner_expression,
member: type_name,
..
} = expression
else {
return None;
};
let inner_ty = inner_expression.get_type();
let module_name = if let Some(synthetic_module) = inner_ty.as_import_namespace() {
synthetic_module.to_string()
} else if matches!(inner_ty, Type::Nominal { .. }) {
if let Expression::Identifier { value, .. } = inner_expression.as_ref() {
value.to_string()
} else {
return None;
}
} else {
return None;
};
let module_name = module_name.as_str();
let qualified_type = format!("{}.{}", module_name, type_name);
let definition = self.facts.definition(qualified_type.as_str())?;
let is_go_type = go_name::is_go_import(module_name);
if !is_go_type
&& !matches!(
definition.body,
DefinitionBody::Struct { .. }
| DefinitionBody::Enum { .. }
| DefinitionBody::TypeAlias { .. }
)
{
return None;
}
let (qualified_type, _type_name) =
if matches!(definition.body, DefinitionBody::TypeAlias { .. }) {
let id = self.peel_alias_id(&qualified_type);
let resolved_name = unqualified_name(&id).to_string();
(id, resolved_name)
} else {
(qualified_type, type_name.to_string())
};
let qualified_method = format!("{}.{}", qualified_type, member);
let is_public = definition.visibility().is_public() || self.method_needs_export(member);
let qualified_name = self.qualify_method_call(&qualified_type, member, is_public, fx);
let type_args = if !ctx.is_callee() {
self.format_cross_module_type_args(&qualified_method, result_ty, fx)
.unwrap_or_default()
} else {
String::new()
};
Some(format!("{}{}", qualified_name, type_args))
}
}