autocxx-engine 0.24.0

Safe autogenerated interop between Rust and C++
Documentation
// Copyright 2022 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 autocxx_parser::IncludeCppConfig;
use indexmap::set::IndexSet as HashSet;

use crate::{
    conversion::{
        analysis::tdef::TypedefAnalysis,
        api::Api,
        apivec::ApiVec,
        convert_error::{ConvertErrorWithContext, ErrorContext},
        ConvertErrorFromCpp,
    },
    types::QualifiedName,
};

use super::pod::PodPhase;
/// Where we find a typedef pointing at something we can't represent,
/// e.g. because it uses too many template parameters, break the link.
/// Use the typedef as a first-class type.
pub(crate) fn replace_hopeless_typedef_targets(
    config: &IncludeCppConfig,
    apis: ApiVec<PodPhase>,
) -> ApiVec<PodPhase> {
    let ignored_types: HashSet<QualifiedName> = apis
        .iter()
        .filter_map(|api| match api {
            Api::IgnoredItem { .. } => Some(api.name()),
            _ => None,
        })
        .cloned()
        .collect();
    let ignored_forward_declarations: HashSet<QualifiedName> = apis
        .iter()
        .filter_map(|api| match api {
            Api::ForwardDeclaration { err: Some(_), .. } => Some(api.name()),
            _ => None,
        })
        .cloned()
        .collect();
    // Convert any Typedefs which depend on these things into OpaqueTypedefs
    // instead.
    // And, after this point we no longer need special knowledge of forward
    // declarations with errors, so just convert them into regular IgnoredItems too.
    apis.into_iter()
        .map(|api| match api {
            Api::Typedef {
                ref name,
                analysis: TypedefAnalysis { ref deps, .. },
                ..
            } if !ignored_types.is_disjoint(deps) =>
            // This typedef depended on something we ignored.
            // Ideally, we'd turn it into an opaque item.
            // We can't do that if this is an inner type,
            // because we have no way to know if it's abstract or not,
            // and we can't represent inner types in cxx without knowing
            // that.
            {
                let name_id = name.name.get_final_ident();
                if api
                    .cpp_name()
                    .as_ref()
                    .map(|n| n.contains("::"))
                    .unwrap_or_default()
                {
                    Api::IgnoredItem {
                        name: api.name_info().clone(),
                        err: ConvertErrorFromCpp::NestedOpaqueTypedef,
                        ctx: Some(ErrorContext::new_for_item(name_id)),
                    }
                } else {
                    Api::OpaqueTypedef {
                        name: api.name_info().clone(),
                        forward_declaration: !config
                            .instantiable
                            .contains(&name.name.to_cpp_name()),
                    }
                }
            }
            Api::Typedef {
                analysis: TypedefAnalysis { ref deps, .. },
                ..
            } if !ignored_forward_declarations.is_disjoint(deps) => Api::OpaqueTypedef {
                name: api.name_info().clone(),
                forward_declaration: true,
            },
            Api::ForwardDeclaration {
                name,
                err: Some(ConvertErrorWithContext(err, ctx)),
            } => Api::IgnoredItem { name, err, ctx },
            _ => api,
        })
        .collect()
}