use std::io::{self, Write};
use crate::{
formatting::{
attributes,
helpers::{assembly_scoped_name, quote_identifier},
},
metadata::{
tables::{GenericParamAttributes, GenericParamList},
typesystem::CilFlavor,
},
CilObject,
};
fn format_generic_params(params: &GenericParamList, asm: &CilObject) -> String {
if params.is_empty() {
return String::new();
}
let mut result = String::from("<");
for (i, param) in params.iter() {
if i > 0 {
result.push_str(", ");
}
let vk = param.flags.variance_keyword();
if !vk.is_empty() {
result.push_str(vk);
}
let has_class = param
.flags
.contains(GenericParamAttributes::REFERENCE_TYPE_CONSTRAINT);
let has_valuetype = param
.flags
.contains(GenericParamAttributes::NOT_NULLABLE_VALUE_TYPE_CONSTRAINT);
if has_class {
result.push_str("class ");
}
if has_valuetype {
result.push_str("valuetype ");
}
if param
.flags
.contains(GenericParamAttributes::DEFAULT_CONSTRUCTOR_CONSTRAINT)
{
result.push_str(".ctor ");
}
if param
.flags
.contains(GenericParamAttributes::ALLOW_BY_REF_LIKE)
{
result.push_str("allow(byreflike) ");
}
let mut type_constraints = Vec::new();
for (_, constraint_ref) in param.constraints.iter() {
if let Some(constraint_type) = constraint_ref.upgrade() {
if has_valuetype
&& constraint_type.name == "ValueType"
&& constraint_type.namespace == "System"
{
continue;
}
if has_class
&& constraint_type.name == "Object"
&& constraint_type.namespace == "System"
{
continue;
}
let name = if let CilFlavor::GenericParameter { index, method } =
constraint_type.flavor()
{
if *method {
format!("!!{index}")
} else {
format!("!{index}")
}
} else {
assembly_scoped_name(&constraint_type, asm)
};
type_constraints.push(name);
}
}
if !type_constraints.is_empty() {
result.push('(');
result.push_str(&type_constraints.join(", "));
result.push_str(") ");
}
result.push_str("e_identifier(¶m.name));
}
result.push('>');
result
}
pub(super) fn write_generic_params(
w: &mut dyn Write,
params: &GenericParamList,
asm: &CilObject,
) -> io::Result<()> {
let formatted = format_generic_params(params, asm);
if !formatted.is_empty() {
write!(w, "{formatted}")?;
}
Ok(())
}
pub(super) fn format_generic_param_custom_attributes(
w: &mut dyn Write,
params: &GenericParamList,
indent: &str,
asm: &CilObject,
) -> io::Result<()> {
for (_, param) in params.iter() {
if param.custom_attributes.is_empty() {
continue;
}
writeln!(w, "{indent}.param type {}", quote_identifier(¶m.name))?;
attributes::format_custom_attributes(w, ¶m.custom_attributes, indent, asm)?;
}
Ok(())
}