mod byvalue_checker;
use std::collections::{HashMap, HashSet};
use autocxx_parser::IncludeCppConfig;
use byvalue_checker::ByValueChecker;
use syn::{ItemEnum, ItemStruct, Type, Visibility};
use crate::{
conversion::{
analysis::type_converter::{add_analysis, TypeConversionContext, TypeConverter},
api::{AnalysisPhase, Api, ApiName, CppVisibility, StructDetails, TypeKind, UnanalyzedApi},
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::convert_apis,
ConvertError,
},
types::{Namespace, QualifiedName},
};
use super::tdef::{TypedefAnalysis, TypedefPhase};
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) struct PodPhase;
impl AnalysisPhase for PodPhase {
type TypedefAnalysis = TypedefAnalysis;
type StructAnalysis = PodAnalysis;
type FunAnalysis = ();
}
pub(crate) fn analyze_pod_apis(
apis: Vec<Api<TypedefPhase>>,
config: &IncludeCppConfig,
) -> Result<Vec<Api<PodPhase>>, ConvertError> {
let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
let mut extra_apis = Vec::new();
let mut type_converter = TypeConverter::new(config, &apis);
let mut results = Vec::new();
convert_apis(
apis,
&mut results,
Api::fun_unchanged,
|name, details, _| {
analyze_struct(
&byvalue_checker,
&mut type_converter,
&mut extra_apis,
name,
details,
config,
)
},
analyze_enum,
Api::typedef_unchanged,
);
let extra_apis: Vec<Api<PodPhase>> = extra_apis.into_iter().map(add_analysis).collect();
let mut more_extra_apis = Vec::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,
)
},
analyze_enum,
Api::typedef_unchanged,
);
assert!(more_extra_apis.is_empty());
Ok(results)
}
fn analyze_enum(
name: ApiName,
mut item: ItemEnum,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
super::remove_bindgen_attrs(&mut item.attrs, name.name.get_final_ident())?;
Ok(Box::new(std::iter::once(Api::Enum { name, item })))
}
fn analyze_struct(
byvalue_checker: &ByValueChecker,
type_converter: &mut TypeConverter,
extra_apis: &mut Vec<UnanalyzedApi>,
name: ApiName,
mut details: Box<StructDetails>,
config: &IncludeCppConfig,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
let id = name.name.get_final_ident();
if details.vis != CppVisibility::Public {
return Err(ConvertErrorWithContext(
ConvertError::NonPublicNestedType,
Some(ErrorContext::Item(id)),
));
}
super::remove_bindgen_attrs(&mut details.item.attrs, id.clone())?;
let bases = get_bases(&details.item);
let mut field_deps = HashSet::new();
let type_kind = if byvalue_checker.is_pod(&name.name) {
get_struct_field_types(
type_converter,
name.name.get_namespace(),
&details.item,
&mut field_deps,
extra_apis,
)
.map_err(|e| ConvertErrorWithContext(e, Some(ErrorContext::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();
Ok(Box::new(std::iter::once(Api::Struct {
name,
details,
analysis: PodAnalysis {
kind: type_kind,
bases: bases.into_keys().collect(),
castable_bases,
field_deps,
},
})))
}
fn get_struct_field_types(
type_converter: &mut TypeConverter,
ns: &Namespace,
s: &ItemStruct,
deps: &mut HashSet<QualifiedName>,
extra_apis: &mut Vec<UnanalyzedApi>,
) -> Result<(), ConvertError> {
for f in &s.fields {
let annotated =
type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::CxxInnerType)?;
extra_apis.extend(annotated.extra_apis);
deps.extend(annotated.types_encountered);
}
Ok(())
}
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()
}