blob: 5a635f86d80bbc5b06563d6de195e4ad290cd3ad [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use indexmap::set::IndexSet as HashSet;
use autocxx_parser::IncludeCppConfig;
use syn::ItemType;
use crate::{
conversion::{
analysis::type_converter::{add_analysis, Annotated, TypeConversionContext, TypeConverter},
api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind},
apivec::ApiVec,
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::convert_apis,
parse::BindgenSemanticAttributes,
ConvertError,
},
types::QualifiedName,
};
pub(crate) struct TypedefAnalysis {
pub(crate) kind: TypedefKind,
pub(crate) deps: HashSet<QualifiedName>,
}
/// Analysis phase where typedef analysis has been performed but no other
/// analyses just yet.
pub(crate) struct TypedefPhase;
impl AnalysisPhase for TypedefPhase {
type TypedefAnalysis = TypedefAnalysis;
type StructAnalysis = ();
type FunAnalysis = ();
}
#[allow(clippy::needless_collect)] // we need the extra collect because the closure borrows extra_apis
pub(crate) fn convert_typedef_targets(
config: &IncludeCppConfig,
apis: ApiVec<NullPhase>,
) -> ApiVec<TypedefPhase> {
let mut type_converter = TypeConverter::new(config, &apis);
let mut extra_apis = ApiVec::new();
let mut results = ApiVec::new();
convert_apis(
apis,
&mut results,
Api::fun_unchanged,
Api::struct_unchanged,
Api::enum_unchanged,
|name, item, old_tyname, _| {
Ok(Box::new(std::iter::once(match item {
TypedefKind::Type(ity) => get_replacement_typedef(
name,
ity,
old_tyname,
&mut type_converter,
&mut extra_apis,
)?,
TypedefKind::Use { .. } => Api::Typedef {
name,
item: item.clone(),
old_tyname,
analysis: TypedefAnalysis {
kind: item,
deps: HashSet::new(),
},
},
})))
},
);
results.extend(extra_apis.into_iter().map(add_analysis));
results
}
fn get_replacement_typedef(
name: ApiName,
ity: ItemType,
old_tyname: Option<QualifiedName>,
type_converter: &mut TypeConverter,
extra_apis: &mut ApiVec<NullPhase>,
) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> {
if !ity.generics.params.is_empty() {
return Err(ConvertErrorWithContext(
ConvertError::TypedefTakesGenericParameters,
Some(ErrorContext::new_for_item(name.name.get_final_ident())),
));
}
let mut converted_type = ity.clone();
let metadata = BindgenSemanticAttributes::new_retaining_others(&mut converted_type.attrs);
metadata.check_for_fatal_attrs(&ity.ident)?;
let type_conversion_results = type_converter.convert_type(
(*ity.ty).clone(),
name.name.get_namespace(),
&TypeConversionContext::WithinReference,
);
match type_conversion_results {
Err(err) => Err(ConvertErrorWithContext(
err,
Some(ErrorContext::new_for_item(name.name.get_final_ident())),
)),
Ok(Annotated {
ty: syn::Type::Path(ref typ),
..
}) if QualifiedName::from_type_path(typ) == name.name => Err(ConvertErrorWithContext(
ConvertError::InfinitelyRecursiveTypedef(name.name.clone()),
Some(ErrorContext::new_for_item(name.name.get_final_ident())),
)),
Ok(mut final_type) => {
converted_type.ty = Box::new(final_type.ty.clone());
extra_apis.append(&mut final_type.extra_apis);
Ok(Api::Typedef {
name,
item: TypedefKind::Type(ity),
old_tyname,
analysis: TypedefAnalysis {
kind: TypedefKind::Type(converted_type),
deps: final_type.types_encountered,
},
})
}
}
}