133d722a9Sopenharmony_ciuse crate::clang::{Clang, Node};
233d722a9Sopenharmony_ciuse crate::syntax::attrs::OtherAttrs;
333d722a9Sopenharmony_ciuse crate::syntax::cfg::CfgExpr;
433d722a9Sopenharmony_ciuse crate::syntax::namespace::Namespace;
533d722a9Sopenharmony_ciuse crate::syntax::report::Errors;
633d722a9Sopenharmony_ciuse crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant};
733d722a9Sopenharmony_ciuse flate2::write::GzDecoder;
833d722a9Sopenharmony_ciuse memmap::Mmap;
933d722a9Sopenharmony_ciuse proc_macro2::{Delimiter, Group, Ident, TokenStream};
1033d722a9Sopenharmony_ciuse quote::{format_ident, quote, quote_spanned};
1133d722a9Sopenharmony_ciuse std::env;
1233d722a9Sopenharmony_ciuse std::fmt::{self, Display};
1333d722a9Sopenharmony_ciuse std::fs::File;
1433d722a9Sopenharmony_ciuse std::io::Write;
1533d722a9Sopenharmony_ciuse std::path::PathBuf;
1633d722a9Sopenharmony_ciuse std::str::FromStr;
1733d722a9Sopenharmony_ciuse syn::{parse_quote, Path};
1833d722a9Sopenharmony_ci
1933d722a9Sopenharmony_ciconst CXX_CLANG_AST: &str = "CXX_CLANG_AST";
2033d722a9Sopenharmony_ci
2133d722a9Sopenharmony_cipub fn load(cx: &mut Errors, apis: &mut [Api]) {
2233d722a9Sopenharmony_ci    let ref mut variants_from_header = Vec::new();
2333d722a9Sopenharmony_ci    for api in apis {
2433d722a9Sopenharmony_ci        if let Api::Enum(enm) = api {
2533d722a9Sopenharmony_ci            if enm.variants_from_header {
2633d722a9Sopenharmony_ci                if enm.variants.is_empty() {
2733d722a9Sopenharmony_ci                    variants_from_header.push(enm);
2833d722a9Sopenharmony_ci                } else {
2933d722a9Sopenharmony_ci                    let span = span_for_enum_error(enm);
3033d722a9Sopenharmony_ci                    cx.error(
3133d722a9Sopenharmony_ci                        span,
3233d722a9Sopenharmony_ci                        "enum with #![variants_from_header] must be written with no explicit variants",
3333d722a9Sopenharmony_ci                    );
3433d722a9Sopenharmony_ci                }
3533d722a9Sopenharmony_ci            }
3633d722a9Sopenharmony_ci        }
3733d722a9Sopenharmony_ci    }
3833d722a9Sopenharmony_ci
3933d722a9Sopenharmony_ci    let span = match variants_from_header.get(0) {
4033d722a9Sopenharmony_ci        None => return,
4133d722a9Sopenharmony_ci        Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
4233d722a9Sopenharmony_ci    };
4333d722a9Sopenharmony_ci
4433d722a9Sopenharmony_ci    let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
4533d722a9Sopenharmony_ci        Some(ast_dump_path) => PathBuf::from(ast_dump_path),
4633d722a9Sopenharmony_ci        None => {
4733d722a9Sopenharmony_ci            let msg = format!(
4833d722a9Sopenharmony_ci                "environment variable ${} has not been provided",
4933d722a9Sopenharmony_ci                CXX_CLANG_AST,
5033d722a9Sopenharmony_ci            );
5133d722a9Sopenharmony_ci            return cx.error(span, msg);
5233d722a9Sopenharmony_ci        }
5333d722a9Sopenharmony_ci    };
5433d722a9Sopenharmony_ci
5533d722a9Sopenharmony_ci    let memmap = File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) });
5633d722a9Sopenharmony_ci    let mut gunzipped;
5733d722a9Sopenharmony_ci    let ast_dump_bytes = match match memmap {
5833d722a9Sopenharmony_ci        Ok(ref memmap) => {
5933d722a9Sopenharmony_ci            let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b");
6033d722a9Sopenharmony_ci            if is_gzipped {
6133d722a9Sopenharmony_ci                gunzipped = Vec::new();
6233d722a9Sopenharmony_ci                let decode_result = GzDecoder::new(&mut gunzipped).write_all(memmap);
6333d722a9Sopenharmony_ci                decode_result.map(|_| gunzipped.as_slice())
6433d722a9Sopenharmony_ci            } else {
6533d722a9Sopenharmony_ci                Ok(memmap as &[u8])
6633d722a9Sopenharmony_ci            }
6733d722a9Sopenharmony_ci        }
6833d722a9Sopenharmony_ci        Err(error) => Err(error),
6933d722a9Sopenharmony_ci    } {
7033d722a9Sopenharmony_ci        Ok(bytes) => bytes,
7133d722a9Sopenharmony_ci        Err(error) => {
7233d722a9Sopenharmony_ci            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
7333d722a9Sopenharmony_ci            return cx.error(span, msg);
7433d722a9Sopenharmony_ci        }
7533d722a9Sopenharmony_ci    };
7633d722a9Sopenharmony_ci
7733d722a9Sopenharmony_ci    let ref root: Node = match serde_json::from_slice(ast_dump_bytes) {
7833d722a9Sopenharmony_ci        Ok(root) => root,
7933d722a9Sopenharmony_ci        Err(error) => {
8033d722a9Sopenharmony_ci            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
8133d722a9Sopenharmony_ci            return cx.error(span, msg);
8233d722a9Sopenharmony_ci        }
8333d722a9Sopenharmony_ci    };
8433d722a9Sopenharmony_ci
8533d722a9Sopenharmony_ci    let ref mut namespace = Vec::new();
8633d722a9Sopenharmony_ci    traverse(cx, root, namespace, variants_from_header, None);
8733d722a9Sopenharmony_ci
8833d722a9Sopenharmony_ci    for enm in variants_from_header {
8933d722a9Sopenharmony_ci        if enm.variants.is_empty() {
9033d722a9Sopenharmony_ci            let span = &enm.variants_from_header_attr;
9133d722a9Sopenharmony_ci            let name = CxxName(&enm.name);
9233d722a9Sopenharmony_ci            let msg = format!("failed to find any C++ definition of enum {}", name);
9333d722a9Sopenharmony_ci            cx.error(span, msg);
9433d722a9Sopenharmony_ci        }
9533d722a9Sopenharmony_ci    }
9633d722a9Sopenharmony_ci}
9733d722a9Sopenharmony_ci
9833d722a9Sopenharmony_cifn traverse<'a>(
9933d722a9Sopenharmony_ci    cx: &mut Errors,
10033d722a9Sopenharmony_ci    node: &'a Node,
10133d722a9Sopenharmony_ci    namespace: &mut Vec<&'a str>,
10233d722a9Sopenharmony_ci    variants_from_header: &mut [&mut Enum],
10333d722a9Sopenharmony_ci    mut idx: Option<usize>,
10433d722a9Sopenharmony_ci) {
10533d722a9Sopenharmony_ci    match &node.kind {
10633d722a9Sopenharmony_ci        Clang::NamespaceDecl(decl) => {
10733d722a9Sopenharmony_ci            let name = match &decl.name {
10833d722a9Sopenharmony_ci                Some(name) => name,
10933d722a9Sopenharmony_ci                // Can ignore enums inside an anonymous namespace.
11033d722a9Sopenharmony_ci                None => return,
11133d722a9Sopenharmony_ci            };
11233d722a9Sopenharmony_ci            namespace.push(name);
11333d722a9Sopenharmony_ci            idx = None;
11433d722a9Sopenharmony_ci        }
11533d722a9Sopenharmony_ci        Clang::EnumDecl(decl) => {
11633d722a9Sopenharmony_ci            let name = match &decl.name {
11733d722a9Sopenharmony_ci                Some(name) => name,
11833d722a9Sopenharmony_ci                None => return,
11933d722a9Sopenharmony_ci            };
12033d722a9Sopenharmony_ci            idx = None;
12133d722a9Sopenharmony_ci            for (i, enm) in variants_from_header.iter_mut().enumerate() {
12233d722a9Sopenharmony_ci                if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
12333d722a9Sopenharmony_ci                    if !enm.variants.is_empty() {
12433d722a9Sopenharmony_ci                        let span = &enm.variants_from_header_attr;
12533d722a9Sopenharmony_ci                        let qual_name = CxxName(&enm.name);
12633d722a9Sopenharmony_ci                        let msg = format!("found multiple C++ definitions of enum {}", qual_name);
12733d722a9Sopenharmony_ci                        cx.error(span, msg);
12833d722a9Sopenharmony_ci                        return;
12933d722a9Sopenharmony_ci                    }
13033d722a9Sopenharmony_ci                    let fixed_underlying_type = match &decl.fixed_underlying_type {
13133d722a9Sopenharmony_ci                        Some(fixed_underlying_type) => fixed_underlying_type,
13233d722a9Sopenharmony_ci                        None => {
13333d722a9Sopenharmony_ci                            let span = &enm.variants_from_header_attr;
13433d722a9Sopenharmony_ci                            let name = &enm.name.cxx;
13533d722a9Sopenharmony_ci                            let qual_name = CxxName(&enm.name);
13633d722a9Sopenharmony_ci                            let msg = format!(
13733d722a9Sopenharmony_ci                                "implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}",
13833d722a9Sopenharmony_ci                                qual_name, name,
13933d722a9Sopenharmony_ci                            );
14033d722a9Sopenharmony_ci                            cx.error(span, msg);
14133d722a9Sopenharmony_ci                            return;
14233d722a9Sopenharmony_ci                        }
14333d722a9Sopenharmony_ci                    };
14433d722a9Sopenharmony_ci                    let repr = translate_qual_type(
14533d722a9Sopenharmony_ci                        cx,
14633d722a9Sopenharmony_ci                        enm,
14733d722a9Sopenharmony_ci                        fixed_underlying_type
14833d722a9Sopenharmony_ci                            .desugared_qual_type
14933d722a9Sopenharmony_ci                            .as_ref()
15033d722a9Sopenharmony_ci                            .unwrap_or(&fixed_underlying_type.qual_type),
15133d722a9Sopenharmony_ci                    );
15233d722a9Sopenharmony_ci                    enm.repr = EnumRepr::Foreign { rust_type: repr };
15333d722a9Sopenharmony_ci                    idx = Some(i);
15433d722a9Sopenharmony_ci                    break;
15533d722a9Sopenharmony_ci                }
15633d722a9Sopenharmony_ci            }
15733d722a9Sopenharmony_ci            if idx.is_none() {
15833d722a9Sopenharmony_ci                return;
15933d722a9Sopenharmony_ci            }
16033d722a9Sopenharmony_ci        }
16133d722a9Sopenharmony_ci        Clang::EnumConstantDecl(decl) => {
16233d722a9Sopenharmony_ci            if let Some(idx) = idx {
16333d722a9Sopenharmony_ci                let enm = &mut *variants_from_header[idx];
16433d722a9Sopenharmony_ci                let span = enm
16533d722a9Sopenharmony_ci                    .variants_from_header_attr
16633d722a9Sopenharmony_ci                    .as_ref()
16733d722a9Sopenharmony_ci                    .unwrap()
16833d722a9Sopenharmony_ci                    .path
16933d722a9Sopenharmony_ci                    .get_ident()
17033d722a9Sopenharmony_ci                    .unwrap()
17133d722a9Sopenharmony_ci                    .span();
17233d722a9Sopenharmony_ci                let cxx_name = match ForeignName::parse(&decl.name, span) {
17333d722a9Sopenharmony_ci                    Ok(foreign_name) => foreign_name,
17433d722a9Sopenharmony_ci                    Err(_) => {
17533d722a9Sopenharmony_ci                        let span = &enm.variants_from_header_attr;
17633d722a9Sopenharmony_ci                        let msg = format!("unsupported C++ variant name: {}", decl.name);
17733d722a9Sopenharmony_ci                        return cx.error(span, msg);
17833d722a9Sopenharmony_ci                    }
17933d722a9Sopenharmony_ci                };
18033d722a9Sopenharmony_ci                let rust_name: Ident = match syn::parse_str(&decl.name) {
18133d722a9Sopenharmony_ci                    Ok(ident) => ident,
18233d722a9Sopenharmony_ci                    Err(_) => format_ident!("__Variant{}", enm.variants.len()),
18333d722a9Sopenharmony_ci                };
18433d722a9Sopenharmony_ci                let discriminant = match discriminant_value(&node.inner) {
18533d722a9Sopenharmony_ci                    ParsedDiscriminant::Constant(discriminant) => discriminant,
18633d722a9Sopenharmony_ci                    ParsedDiscriminant::Successor => match enm.variants.last() {
18733d722a9Sopenharmony_ci                        None => Discriminant::zero(),
18833d722a9Sopenharmony_ci                        Some(last) => match last.discriminant.checked_succ() {
18933d722a9Sopenharmony_ci                            Some(discriminant) => discriminant,
19033d722a9Sopenharmony_ci                            None => {
19133d722a9Sopenharmony_ci                                let span = &enm.variants_from_header_attr;
19233d722a9Sopenharmony_ci                                let msg = format!(
19333d722a9Sopenharmony_ci                                    "overflow processing discriminant value for variant: {}",
19433d722a9Sopenharmony_ci                                    decl.name,
19533d722a9Sopenharmony_ci                                );
19633d722a9Sopenharmony_ci                                return cx.error(span, msg);
19733d722a9Sopenharmony_ci                            }
19833d722a9Sopenharmony_ci                        },
19933d722a9Sopenharmony_ci                    },
20033d722a9Sopenharmony_ci                    ParsedDiscriminant::Fail => {
20133d722a9Sopenharmony_ci                        let span = &enm.variants_from_header_attr;
20233d722a9Sopenharmony_ci                        let msg = format!(
20333d722a9Sopenharmony_ci                            "failed to obtain discriminant value for variant: {}",
20433d722a9Sopenharmony_ci                            decl.name,
20533d722a9Sopenharmony_ci                        );
20633d722a9Sopenharmony_ci                        cx.error(span, msg);
20733d722a9Sopenharmony_ci                        Discriminant::zero()
20833d722a9Sopenharmony_ci                    }
20933d722a9Sopenharmony_ci                };
21033d722a9Sopenharmony_ci                enm.variants.push(Variant {
21133d722a9Sopenharmony_ci                    cfg: CfgExpr::Unconditional,
21233d722a9Sopenharmony_ci                    doc: Doc::new(),
21333d722a9Sopenharmony_ci                    attrs: OtherAttrs::none(),
21433d722a9Sopenharmony_ci                    name: Pair {
21533d722a9Sopenharmony_ci                        namespace: Namespace::ROOT,
21633d722a9Sopenharmony_ci                        cxx: cxx_name,
21733d722a9Sopenharmony_ci                        rust: rust_name,
21833d722a9Sopenharmony_ci                    },
21933d722a9Sopenharmony_ci                    discriminant,
22033d722a9Sopenharmony_ci                    expr: None,
22133d722a9Sopenharmony_ci                });
22233d722a9Sopenharmony_ci            }
22333d722a9Sopenharmony_ci        }
22433d722a9Sopenharmony_ci        _ => {}
22533d722a9Sopenharmony_ci    }
22633d722a9Sopenharmony_ci    for inner in &node.inner {
22733d722a9Sopenharmony_ci        traverse(cx, inner, namespace, variants_from_header, idx);
22833d722a9Sopenharmony_ci    }
22933d722a9Sopenharmony_ci    if let Clang::NamespaceDecl(_) = &node.kind {
23033d722a9Sopenharmony_ci        let _ = namespace.pop().unwrap();
23133d722a9Sopenharmony_ci    }
23233d722a9Sopenharmony_ci}
23333d722a9Sopenharmony_ci
23433d722a9Sopenharmony_cifn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path {
23533d722a9Sopenharmony_ci    let rust_std_name = match qual_type {
23633d722a9Sopenharmony_ci        "char" => "c_char",
23733d722a9Sopenharmony_ci        "int" => "c_int",
23833d722a9Sopenharmony_ci        "long" => "c_long",
23933d722a9Sopenharmony_ci        "long long" => "c_longlong",
24033d722a9Sopenharmony_ci        "signed char" => "c_schar",
24133d722a9Sopenharmony_ci        "short" => "c_short",
24233d722a9Sopenharmony_ci        "unsigned char" => "c_uchar",
24333d722a9Sopenharmony_ci        "unsigned int" => "c_uint",
24433d722a9Sopenharmony_ci        "unsigned long" => "c_ulong",
24533d722a9Sopenharmony_ci        "unsigned long long" => "c_ulonglong",
24633d722a9Sopenharmony_ci        "unsigned short" => "c_ushort",
24733d722a9Sopenharmony_ci        unsupported => {
24833d722a9Sopenharmony_ci            let span = &enm.variants_from_header_attr;
24933d722a9Sopenharmony_ci            let qual_name = CxxName(&enm.name);
25033d722a9Sopenharmony_ci            let msg = format!(
25133d722a9Sopenharmony_ci                "unsupported underlying type for {}: {}",
25233d722a9Sopenharmony_ci                qual_name, unsupported,
25333d722a9Sopenharmony_ci            );
25433d722a9Sopenharmony_ci            cx.error(span, msg);
25533d722a9Sopenharmony_ci            "c_int"
25633d722a9Sopenharmony_ci        }
25733d722a9Sopenharmony_ci    };
25833d722a9Sopenharmony_ci    let span = enm
25933d722a9Sopenharmony_ci        .variants_from_header_attr
26033d722a9Sopenharmony_ci        .as_ref()
26133d722a9Sopenharmony_ci        .unwrap()
26233d722a9Sopenharmony_ci        .path
26333d722a9Sopenharmony_ci        .get_ident()
26433d722a9Sopenharmony_ci        .unwrap()
26533d722a9Sopenharmony_ci        .span();
26633d722a9Sopenharmony_ci    let ident = Ident::new(rust_std_name, span);
26733d722a9Sopenharmony_ci    let path = quote_spanned!(span=> ::cxx::core::ffi::#ident);
26833d722a9Sopenharmony_ci    parse_quote!(#path)
26933d722a9Sopenharmony_ci}
27033d722a9Sopenharmony_ci
27133d722a9Sopenharmony_cienum ParsedDiscriminant {
27233d722a9Sopenharmony_ci    Constant(Discriminant),
27333d722a9Sopenharmony_ci    Successor,
27433d722a9Sopenharmony_ci    Fail,
27533d722a9Sopenharmony_ci}
27633d722a9Sopenharmony_ci
27733d722a9Sopenharmony_cifn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant {
27833d722a9Sopenharmony_ci    if clang.is_empty() {
27933d722a9Sopenharmony_ci        // No discriminant expression provided; use successor of previous
28033d722a9Sopenharmony_ci        // discriminant.
28133d722a9Sopenharmony_ci        return ParsedDiscriminant::Successor;
28233d722a9Sopenharmony_ci    }
28333d722a9Sopenharmony_ci
28433d722a9Sopenharmony_ci    loop {
28533d722a9Sopenharmony_ci        if clang.len() != 1 {
28633d722a9Sopenharmony_ci            return ParsedDiscriminant::Fail;
28733d722a9Sopenharmony_ci        }
28833d722a9Sopenharmony_ci
28933d722a9Sopenharmony_ci        let node = &clang[0];
29033d722a9Sopenharmony_ci        match &node.kind {
29133d722a9Sopenharmony_ci            Clang::ImplicitCastExpr => clang = &node.inner,
29233d722a9Sopenharmony_ci            Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) {
29333d722a9Sopenharmony_ci                Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant),
29433d722a9Sopenharmony_ci                Err(_) => return ParsedDiscriminant::Fail,
29533d722a9Sopenharmony_ci            },
29633d722a9Sopenharmony_ci            _ => return ParsedDiscriminant::Fail,
29733d722a9Sopenharmony_ci        }
29833d722a9Sopenharmony_ci    }
29933d722a9Sopenharmony_ci}
30033d722a9Sopenharmony_ci
30133d722a9Sopenharmony_cifn span_for_enum_error(enm: &Enum) -> TokenStream {
30233d722a9Sopenharmony_ci    let enum_token = enm.enum_token;
30333d722a9Sopenharmony_ci    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
30433d722a9Sopenharmony_ci    brace_token.set_span(enm.brace_token.span);
30533d722a9Sopenharmony_ci    quote!(#enum_token #brace_token)
30633d722a9Sopenharmony_ci}
30733d722a9Sopenharmony_ci
30833d722a9Sopenharmony_cistruct CxxName<'a>(&'a Pair);
30933d722a9Sopenharmony_ci
31033d722a9Sopenharmony_ciimpl<'a> Display for CxxName<'a> {
31133d722a9Sopenharmony_ci    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
31233d722a9Sopenharmony_ci        for namespace in &self.0.namespace {
31333d722a9Sopenharmony_ci            write!(formatter, "{}::", namespace)?;
31433d722a9Sopenharmony_ci        }
31533d722a9Sopenharmony_ci        write!(formatter, "{}", self.0.cxx)
31633d722a9Sopenharmony_ci    }
31733d722a9Sopenharmony_ci}
318