mod byvalue_checker;
use indexmap::map::IndexMap as HashMap;
use indexmap::set::IndexSet as HashSet;
use autocxx_parser::IncludeCppConfig;
use byvalue_checker::ByValueChecker;
use syn::{ItemStruct, Type, Visibility};
use crate::{
conversion::{
analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
api::{AnalysisPhase, Api, ApiName, NullPhase, StructDetails, TypeKind},
apivec::ApiVec,
check_for_fatal_attrs,
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::convert_apis,
ConvertErrorFromCpp,
},
types::{Namespace, QualifiedName},
ParseCallbackResults,
};
use super::tdef::{TypedefAnalysis, TypedefPhase};
#[derive(std::fmt::Debug)]
pub(crate) struct FieldInfo {
pub(crate) ty: Type,
pub(crate) type_kind: type_converter::TypeKind,
pub(crate) bindgen_opaque_data: bool,
}
#[derive(std::fmt::Debug)]
pub(crate) struct PodAnalysis {
pub(crate) kind: TypeKind,
pub(crate) bases: HashSet<QualifiedName>,
pub(crate) castable_bases: HashSet<QualifiedName>,
pub(crate) field_deps: HashSet<QualifiedName>,
pub(crate) field_definition_deps: HashSet<QualifiedName>,
pub(crate) field_info: Vec<FieldInfo>,
pub(crate) num_generics: usize,
pub(crate) in_anonymous_namespace: bool,
}
#[derive(std::fmt::Debug)]
pub(crate) struct PodPhase;
impl AnalysisPhase for PodPhase {
type TypedefAnalysis = TypedefAnalysis;
type StructAnalysis = PodAnalysis;
type FunAnalysis = ();
}
pub(crate) fn analyze_pod_apis(
apis: ApiVec<TypedefPhase>,
config: &IncludeCppConfig,
parse_callback_results: &ParseCallbackResults,
) -> Result<ApiVec<PodPhase>, ConvertErrorFromCpp> {
let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
let mut extra_apis = ApiVec::new();
let mut type_converter = TypeConverter::new(config, &apis);
let mut results = ApiVec::new();
convert_apis(
apis,
&mut results,
Api::fun_unchanged,
|name, details, _| {
analyze_struct(
&byvalue_checker,
&mut type_converter,
&mut extra_apis,
name,
details,
config,
parse_callback_results,
)
},
|name, item| analyze_enum(name, item, parse_callback_results),
Api::typedef_unchanged,
);
let extra_apis: ApiVec<PodPhase> = extra_apis.into_iter().map(add_analysis).collect();
let mut more_extra_apis = ApiVec::new();
convert_apis(
extra_apis,
&mut results,
Api::fun_unchanged,
|name, details, _| {
analyze_struct(
&byvalue_checker,
&mut type_converter,
&mut more_extra_apis,
name,
details,
config,
parse_callback_results,
)
},
|name, item| analyze_enum(name, item, parse_callback_results),
Api::typedef_unchanged,
);
assert!(more_extra_apis.is_empty());
Ok(results)
}
fn analyze_enum(
name: ApiName,
item: crate::minisyn::ItemEnum,
parse_callback_results: &ParseCallbackResults,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
check_for_fatal_attrs(parse_callback_results, &name.name)?;
Ok(Box::new(std::iter::once(Api::Enum { name, item })))
}
fn analyze_struct(
byvalue_checker: &ByValueChecker,
type_converter: &mut TypeConverter,
extra_apis: &mut ApiVec<NullPhase>,
name: ApiName,
details: Box<StructDetails>,
config: &IncludeCppConfig,
parse_callback_results: &ParseCallbackResults,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
let id = name.name.get_final_ident();
check_for_fatal_attrs(parse_callback_results, &name.name)?;
let bases = get_bases(&details.item);
let mut field_deps = HashSet::new();
let mut field_definition_deps = HashSet::new();
let mut field_info = Vec::new();
let field_conversion_errors = get_struct_field_types(
type_converter,
name.name.get_namespace(),
&details.item,
&mut field_deps,
&mut field_definition_deps,
&mut field_info,
extra_apis,
);
let type_kind = if field_info.iter().any(|fi| fi.bindgen_opaque_data) {
TypeKind::Opaque
} else if byvalue_checker.is_pod(&name.name) {
if details.has_rvalue_reference_fields {
return Err(ConvertErrorWithContext(
ConvertErrorFromCpp::RValueReferenceField,
Some(ErrorContext::new_for_item(id)),
));
}
if let Some(err) = field_conversion_errors.into_iter().next() {
return Err(ConvertErrorWithContext(
err,
Some(ErrorContext::new_for_item(id)),
));
}
TypeKind::Pod
} else {
TypeKind::NonPod
};
let castable_bases = bases
.iter()
.filter(|(_, is_public)| **is_public)
.map(|(base, _)| base)
.filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
.cloned()
.collect();
let num_generics = details.item.generics.params.len();
let in_anonymous_namespace = name
.name
.ns_segment_iter()
.any(|ns| ns.starts_with("_bindgen_mod"));
Ok(Box::new(std::iter::once(Api::Struct {
name,
details,
analysis: PodAnalysis {
kind: type_kind,
bases: bases.into_keys().collect(),
castable_bases,
field_deps,
field_definition_deps,
field_info,
num_generics,
in_anonymous_namespace,
},
})))
}
fn get_struct_field_types(
type_converter: &mut TypeConverter,
ns: &Namespace,
s: &ItemStruct,
field_deps: &mut HashSet<QualifiedName>,
field_definition_deps: &mut HashSet<QualifiedName>,
field_info: &mut Vec<FieldInfo>,
extra_apis: &mut ApiVec<NullPhase>,
) -> Vec<ConvertErrorFromCpp> {
let mut convert_errors = Vec::new();
let struct_type_params = s
.generics
.type_params()
.map(|tp| tp.ident.clone())
.collect();
let type_conversion_context = TypeConversionContext::WithinStructField { struct_type_params };
for f in &s.fields {
let annotated = type_converter.convert_type(f.ty.clone(), ns, &type_conversion_context);
match annotated {
Ok(mut r) => {
extra_apis.append(&mut r.extra_apis);
if !f
.ident
.as_ref()
.map(|id| {
id.to_string().starts_with("_base")
|| id.to_string().starts_with("__bindgen_padding")
})
.unwrap_or(false)
{
field_deps.extend(r.types_encountered);
if let Type::Path(typ) = &r.ty {
field_definition_deps.insert(QualifiedName::from_type_path(typ));
}
field_info.push(FieldInfo {
ty: r.ty,
type_kind: r.kind,
bindgen_opaque_data: f
.ident
.as_ref()
.map(|id| id == "_bindgen_opaque_blob")
.unwrap_or_default(),
});
}
}
Err(e) => convert_errors.push(e),
};
}
convert_errors
}
fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> {
item.fields
.iter()
.filter_map(|f| {
let is_public = matches!(f.vis, Visibility::Public(_));
match &f.ty {
Type::Path(typ) => f
.ident
.as_ref()
.filter(|id| id.to_string().starts_with("_base"))
.map(|_| (QualifiedName::from_type_path(typ), is_public)),
_ => None,
}
})
.collect()
}