<commit_before>#![allow(non_upper_case_globals)]

use std::collections::{HashMap, HashSet};
use std::cell::RefCell;
use std::rc::Rc;
use std::path::Path;
use std::cmp;

use syntax::abi;

use types as il;
use types::*;
use clang as cx;
use clang::{ast_dump, Comment, Cursor, Diagnostic, TranslationUnit, type_to_str, kind_to_str};
use clangll::*;

use super::Logger;

#[derive(Clone)]
pub struct ClangParserOptions {
    pub builtin_names: HashSet<String>,
    pub builtins: bool,
    pub match_pat: Vec<String>,
    pub emit_ast: bool,
    pub fail_on_unknown_type: bool,
    pub ignore_functions: bool,
    pub enable_cxx_namespaces: bool,
    pub override_enum_ty: Option<il::IKind>,
    pub clang_args: Vec<String>,
    pub opaque_types: Vec<String>,
    pub blacklist_type: Vec<String>,
}

struct ClangParserCtx<'a> {
    options: ClangParserOptions,
    name: HashMap<Cursor, Global>,
    builtin_defs: Vec<Cursor>,
    module_map: ModuleMap,
    current_module_id: ModuleId,
    logger: &'a (Logger+'a),
    err_count: i32,
    anonymous_modules_found: usize,
}

impl<'a> ClangParserCtx<'a> {
    fn module(&self, id: &ModuleId) -> &Module {
        self.module_map.get(id).expect("Module not found!")
    }

    fn current_module(&self) -> &Module {
        self.module(&self.current_module_id)
    }

    fn current_module_mut(&mut self) -> &mut Module {
        self.module_map.get_mut(&self.current_module_id).expect("Module not found!")
    }
}

fn match_pattern(ctx: &mut ClangParserCtx, cursor: &Cursor) -> bool {
    let (file, _, _, _) = cursor.location().location();

    let name = match file.name() {
        None => return ctx.options.builtins,
        Some(name) => name,
    };

    if ctx.options.match_pat.is_empty() {
        return true;
    }

    ctx.options.match_pat.iter().any(|pat| name.contains(pat))
}

fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global {
    let cursor = cursor.canonical();
    let override_enum_ty = ctx.options.override_enum_ty;
    let new_decl = !ctx.name.contains_key(&cursor);

    let decl = if new_decl {
        let spelling = cursor.spelling();
        let comment = cursor.raw_comment();
        let (file, _, _, _) = cursor.location().location();
        let ty = cursor.cur_type();
        let layout = Layout::new(ty.size(), ty.align());
        let filename = match Path::new(&file.name().unwrap_or("".to_owned())).file_name() {
            Some(name) => name.to_string_lossy().replace(".", "_"),
            _ => "".to_string()
        };
        let glob_decl = match cursor.kind() {
            CXCursor_UnionDecl |
            CXCursor_ClassTemplate |
            CXCursor_ClassDecl |
            CXCursor_StructDecl => {
                let kind = if cursor.kind() == CXCursor_UnionDecl {
                    CompKind::Union
                } else {
                    CompKind::Struct
                };

                let mut opaque = ctx.options.opaque_types.iter().any(|name| *name == spelling);
                let hide = ctx.options.blacklist_type.iter().any(|name| *name == spelling);

                let mut has_non_type_template_params = false;
                let args = match cursor.kind() {
                    CXCursor_ClassDecl => {
                        match ty.num_template_args() {
                            -1 => vec![],
                            len => {
                                let mut list = Vec::with_capacity(len as usize);
                                for i in 0..len {
                                    let arg_type = ty.template_arg_type(i);
                                    if arg_type.kind() != CXType_Invalid {
                                        list.push(conv_ty(ctx, &arg_type, &cursor));
                                    } else {
                                        has_non_type_template_params = true;
                                        ctx.logger.warn("warning: Template parameter is not a type");
                                    }
                                }
                                list
                            }
                        }
                    }
                    _ => vec![],
                };

                let mut module_id = ctx.current_module_id;
                let mut has_dtor = false;

                // If it's an instantiation of another template,
                // find the canonical declaration to find the module
                // it belongs to and if it's opaque.
                let parent = cursor.specialized();
                if let Some(parent) = ctx.name.get(&parent) {
                    match *parent {
                        GComp(ref ci) |
                        GCompDecl(ref ci) => {
                            opaque |= ci.borrow().opaque;
                            has_dtor |= ci.borrow().has_destructor;
                            module_id = ci.borrow().module_id;
                        }
                        _ => {}
                    }
                }

                let mut ci = CompInfo::new(spelling, module_id, filename, comment, kind, vec![], layout);
                ci.opaque = opaque;
                ci.hide = hide;
                ci.args = args;
                ci.has_destructor = has_dtor;
                ci.has_non_type_template_params = has_non_type_template_params;

                let ci = Rc::new(RefCell::new(ci));
                GCompDecl(ci)
            }
            CXCursor_EnumDecl => {
                let kind = match override_enum_ty {
                    Some(t) => t,
                    None => match cursor.enum_type().kind() {
                        CXType_SChar | CXType_Char_S => ISChar,
                        CXType_UChar | CXType_Char_U => IUChar,
                        CXType_UShort => IUShort,
                        CXType_UInt => IUInt,
                        CXType_ULong => IULong,
                        CXType_ULongLong => IULongLong,
                        CXType_Short => IShort,
                        CXType_Int => IInt,
                        CXType_Long => ILong,
                        CXType_LongLong => ILongLong,
                        _ => IInt,
                    }
                };

                let ei = Rc::new(RefCell::new(EnumInfo::new(spelling, ctx.current_module_id, filename, kind, vec!(), layout)));
                GEnumDecl(ei)
            }
            CXCursor_TypeAliasDecl | CXCursor_TypedefDecl => {
                let opaque = ctx.options.opaque_types.iter().any(|name| *name == spelling);
                let mut ti = TypeInfo::new(spelling, ctx.current_module_id, TVoid, layout);
                ti.opaque = opaque;

                let ti = Rc::new(RefCell::new(ti));
                GType(ti)
            }
            CXCursor_VarDecl => {
                let mangled = cursor.mangling();
                let vi = Rc::new(RefCell::new(VarInfo::new(spelling, mangled, comment, TVoid)));
                GVar(vi)
            }
            CXCursor_MacroDefinition => {
                let vi = Rc::new(RefCell::new(VarInfo::new(spelling, String::new(), comment, TVoid)));
                GVar(vi)
            }
            CXCursor_FunctionDecl => {
                let mangled = cursor.mangling();
                let vi = Rc::new(RefCell::new(VarInfo::new(spelling, mangled, comment, TVoid)));
                GFunc(vi)
            }
            _ => GOther,
        };

        ctx.name.insert(cursor, glob_decl.clone());
        glob_decl
    } else {
        ctx.name.get(&cursor).unwrap().clone()
    };

    if new_decl && ctx.options.builtin_names.contains(&cursor.spelling()) {
        ctx.builtin_defs.push(cursor);
    }

    decl
}

fn opaque_decl(ctx: &mut ClangParserCtx, decl: &Cursor) {
    let name = decl_name(ctx, decl);
    ctx.current_module_mut().globals.push(name);
}

fn fwd_decl<F:FnOnce(&mut ClangParserCtx)->()>(ctx: &mut ClangParserCtx, cursor: &Cursor, f: F) {
    let def = &cursor.definition();
    if cursor == def {
        f(ctx);
    } else if def.kind() == CXCursor_NoDeclFound ||
              def.kind() == CXCursor_InvalidFile {
        opaque_decl(ctx, cursor);
    }
}

fn get_abi(cc: Enum_CXCallingConv) -> abi::Abi {
    match cc {
        CXCallingConv_Default => abi::Abi::C,
        CXCallingConv_C => abi::Abi::C,
        CXCallingConv_X86StdCall => abi::Abi::Stdcall,
        CXCallingConv_X86FastCall => abi::Abi::Fastcall,
        CXCallingConv_AAPCS => abi::Abi::Aapcs,
        CXCallingConv_X86_64Win64 => abi::Abi::Win64,
        other => panic!("unsupported calling convention: {}", other),
    }
}

fn conv_ptr_ty_resolving_typedefs(ctx: &mut ClangParserCtx,
                                  ty: &cx::Type,
                                  cursor: &Cursor,
                                  is_ref: bool,
                                  layout: Layout,
                                  resolve_typedefs: bool) -> il::Type {
    let is_const = ty.is_const();
    match ty.kind() {
        CXType_Void => {
            return TPtr(Box::new(TVoid), is_const, is_ref, layout)
        }
        CXType_Unexposed |
        CXType_FunctionProto |
        CXType_FunctionNoProto => {
            let ret_ty = ty.ret_type();
            return if ret_ty.kind() != CXType_Invalid {
                TFuncPtr(mk_fn_sig(ctx, ty, cursor))
            } else if cursor.kind() == CXCursor_VarDecl {
                let can_ty = ty.canonical_type();
                conv_ty_resolving_typedefs(ctx, &can_ty, cursor, resolve_typedefs)
            } else {
                TPtr(Box::new(conv_decl_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs)), ty.is_const(), is_ref, layout)
            };
        }
        CXType_Typedef => {
            let decl = ty.declaration();
            let def_ty = decl.typedef_type();
            if def_ty.kind() == CXType_FunctionProto ||
               def_ty.kind() == CXType_FunctionNoProto {
                return TPtr(Box::new(conv_ptr_ty_resolving_typedefs(ctx, &def_ty, cursor, is_ref, layout, resolve_typedefs)), is_const, is_ref, layout);
            } else {
                return TPtr(Box::new(conv_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs)), is_const, is_ref, layout);
            }
        }
        _ => return TPtr(Box::new(conv_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs)), is_const, is_ref, layout),
    }
}

fn mk_fn_sig(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::FuncSig {
    mk_fn_sig_resolving_typedefs(ctx, ty, cursor, &[])
}

fn mk_fn_sig_resolving_typedefs(ctx: &mut ClangParserCtx,
                                ty: &cx::Type,
                                cursor: &Cursor,
                                typedefs: &[String]) -> il::FuncSig {
    let args_lst: Vec<(String, il::Type)> = match cursor.kind() {
        CXCursor_FunctionDecl | CXCursor_CXXMethod => {
            // For CXCursor_FunctionDecl, cursor.args() is the reliable way to
            // get parameter names and types.
            cursor.args().iter().map(|arg| {
                let arg_name = arg.spelling();
                let is_class_typedef = arg.cur_type().sanitized_spelling_in(typedefs);
                (arg_name, conv_ty_resolving_typedefs(ctx, &arg.cur_type(), arg, is_class_typedef))
            }).collect()
        }
        _ => {
            // For non-CXCursor_FunctionDecl, visiting the cursor's children is
            // the only reliable way to get parameter names.
            let mut args_lst = vec!();
            cursor.visit(|c: &Cursor, _: &Cursor| {
                if c.kind() == CXCursor_ParmDecl {
                    let is_class_typedef = c.cur_type().sanitized_spelling_in(typedefs);
                    args_lst.push((c.spelling(), conv_ty_resolving_typedefs(ctx, &c.cur_type(), c, is_class_typedef)));
                }
                CXChildVisit_Continue
            });
            args_lst
        }
    };

    let ret_ty = Box::new(conv_ty(ctx, &ty.ret_type(), cursor));
    let abi = get_abi(ty.call_conv());

    // Function is presumed unsafe if it takes a pointer argument.
    let is_unsafe = args_lst.iter().any(|arg| match arg.1 {
        TPtr(..) => true,
        _ => false
    });

    il::FuncSig {
        ret_ty: ret_ty,
        args: args_lst,
        is_variadic: ty.is_variadic(),
        is_safe: !is_unsafe,
        abi: abi,
    }
}

fn conv_decl_ty(ctx: &mut ClangParserCtx,
                ty: &cx::Type,
                cursor: &Cursor) -> il::Type {
    conv_decl_ty_resolving_typedefs(ctx, ty, cursor, false)
}

fn conv_decl_ty_resolving_typedefs(ctx: &mut ClangParserCtx,
                                   ty: &cx::Type,
                                   cursor: &Cursor,
                                   resolve_typedefs: bool) -> il::Type {
    let ty_decl = ty.declaration();
    // println!("conv_ty_decl: `{}`, ty kind {}: {}, decl `{}` kind {}: {}", cursor.spelling(), ty.kind(), type_to_str(ty.kind()), ty_decl.spelling(), ty_decl.kind(), kind_to_str(ty_decl.kind()));
    return match ty_decl.kind() {
        CXCursor_StructDecl |
        CXCursor_UnionDecl |
        CXCursor_ClassTemplate |
        CXCursor_ClassDecl => {
            let decl = decl_name(ctx, &ty_decl);
            // NB: This will only return a number greater than 0 if this is a **full** class
            // template specialization.
            //
            // If the cursor kind is CXCursor_ClassTemplate, this will still return -1
            // and we'll have to keep traversing the cursor.
            let args = match ty.num_template_args() {
                -1 => vec!(),
                len => {
                    let mut list = Vec::with_capacity(len as usize);
                    for i in 0..len {
                        let arg_type = ty.template_arg_type(i);
                        if arg_type.kind() != CXType_Invalid {
                            list.push(conv_ty(ctx, &arg_type, &cursor));
                        } else {
                            ctx.logger.warn("warning: Template parameter is not a type");
                        }
                    }
                    list
                }
            };
            let ci = decl.compinfo();
            if !args.is_empty() {
                ci.borrow_mut().args = args;
                cursor.visit(|c, _: &Cursor| {
                    if c.kind() == CXCursor_TemplateRef {
                        let decl = decl_name(ctx, &c.referenced());
                        ci.borrow_mut().ref_template = Some(decl.to_type());
                    }
                    CXChildVisit_Continue
                });
            }

            TComp(ci)
        }
        CXCursor_EnumDecl => {
            let decl = decl_name(ctx, &ty_decl);
            let ei = decl.enuminfo();
            TEnum(ei)
        }
        CXCursor_TypeAliasDecl |
        CXCursor_TypedefDecl => {
            if resolve_typedefs {
                return conv_ty_resolving_typedefs(ctx, &ty_decl.typedef_type(), &ty_decl.typedef_type().declaration(), resolve_typedefs);
            }

            let decl = decl_name(ctx, &ty_decl);
            let ti = decl.typeinfo();
            TNamed(ti)
        }
        CXCursor_NoDeclFound => {
            let layout = Layout::new(ty.size(), ty.align());
            TNamed(Rc::new(RefCell::new(TypeInfo::new(ty.spelling().replace("const ", ""), ctx.current_module_id, TVoid, layout))))
        }
        _ => {
            let fail = ctx.options.fail_on_unknown_type;
            log_err_warn(ctx,
                &format!("unsupported decl `{}` ({})",
                    kind_to_str(ty_decl.kind()), ty_decl.location()
                ),
                fail
            );
            TVoid
        }
    };
}

fn conv_ty(ctx: &mut ClangParserCtx,
           ty: &cx::Type,
           cursor: &Cursor) -> il::Type {
    conv_ty_resolving_typedefs(ctx, ty, cursor, false)
}

fn conv_ty_resolving_typedefs(ctx: &mut ClangParserCtx,
                              ty: &cx::Type,
                              cursor: &Cursor,
                              resolve_typedefs: bool) -> il::Type {
    let layout = Layout::new(ty.size(), ty.align());
    // println!("conv_ty: `{}` layout: {:?}, kind {}: {}", cursor.spelling(), layout, ty.kind(), type_to_str(ty.kind()));

    match ty.kind() {
        CXType_Void => TVoid,
        CXType_Invalid => {
            log_err_warn(ctx,
                &format!("invalid type `{}` ({})",
                    cursor.spelling(), cursor.location()
                ),
                false
            );
            TVoid
        }
        CXType_Bool => TInt(IBool, layout),
        CXType_SChar |
        CXType_Char_S => TInt(ISChar, layout),
        CXType_UChar |
        CXType_Char_U => TInt(IUChar, layout),
        CXType_WChar => TInt(IShort, layout),
        CXType_Char16 |
        CXType_UShort => TInt(IUShort, layout),
        CXType_UInt => TInt(IUInt, layout),
        CXType_ULong => TInt(IULong, layout),
        CXType_ULongLong => TInt(IULongLong, layout),
        CXType_Short => TInt(IShort, layout),
        CXType_Int => TInt(IInt, layout),
        CXType_Long => TInt(ILong, layout),
        CXType_LongLong => TInt(ILongLong, layout),
        CXType_Float => TFloat(FFloat, layout),
        CXType_Double => TFloat(FDouble, layout),
        CXType_LongDouble => TFloat(FDouble, layout),
        CXType_Pointer => conv_ptr_ty_resolving_typedefs(ctx, &ty.pointee_type(), cursor, false, layout, resolve_typedefs),
        CXType_LValueReference => conv_ptr_ty_resolving_typedefs(ctx, &ty.pointee_type(), cursor, true, layout, resolve_typedefs),
        // XXX DependentSizedArray is wrong
        CXType_VariableArray | CXType_DependentSizedArray | CXType_IncompleteArray => {
            conv_ptr_ty_resolving_typedefs(ctx, &ty.elem_type(), cursor, false, layout, resolve_typedefs)
        }
        CXType_FunctionProto => TFuncProto(mk_fn_sig(ctx, ty, cursor)),
        CXType_Record |
        CXType_Typedef  |
        CXType_Unexposed |
        CXType_Enum => conv_decl_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs),
        CXType_ConstantArray => TArray(Box::new(conv_ty_resolving_typedefs(ctx, &ty.elem_type(), cursor, resolve_typedefs)), ty.array_size(), layout),
        _ => {
            let fail = ctx.options.fail_on_unknown_type;
            log_err_warn(ctx,
                &format!("unsupported type `{}` ({})",
                    type_to_str(ty.kind()), cursor.location()
                ),
                fail
            );
            TVoid
        },
    }
}

fn opaque_ty(ctx: &mut ClangParserCtx, ty: &cx::Type) {
    if ty.kind() == CXType_Record || ty.kind() == CXType_Enum {
        let decl = ty.declaration();
        let def = decl.definition();
        if def.kind() == CXCursor_NoDeclFound ||
           def.kind() == CXCursor_InvalidFile {
            opaque_decl(ctx, &decl);
        }
    }
}

struct Annotations {
    opaque: bool,
    hide: bool,
    use_as: Option<String>,
}

impl Annotations {
    fn new(cursor: &Cursor) -> Annotations {
        let mut anno = Annotations {
            opaque: false,
            hide: false,
            use_as: None,
        };

        anno.parse(&cursor.comment());
        anno
    }

    fn parse(&mut self, comment: &Comment) {
        if comment.kind() == CXComment_HTMLStartTag &&
           comment.get_tag_name() == "div" &&
           comment.get_num_tag_attrs() > 1 &&
           comment.get_tag_attr_name(0) == "rustbindgen" {
            for i in 0..comment.get_num_tag_attrs() {
                let name = comment.get_tag_attr_name(i);
                match name.as_str() {
                    "opaque" => self.opaque = true,
                    "hide" => self.hide = true,
                    "replaces" => self.use_as = Some(comment.get_tag_attr_value(i)),
                    _ => (),
                }
            }
        }

        for i in 0..comment.num_children() {
            self.parse(&comment.get_child(i));
        }
    }
}

/// Recursively visits a cursor that represents a composite (struct or union)
/// type and fills members with CompMember instances representing the fields and
/// nested composites that make up the visited composite.
fn visit_composite(cursor: &Cursor, parent: &Cursor,
                   ctx: &mut ClangParserCtx,
                   ci: &mut CompInfo) -> Enum_CXVisitorResult {
    fn is_bitfield_continuation(field: &il::FieldInfo, _ty: &il::Type, width: u32) -> bool {
        match (&field.bitfields, field.ty.layout()) {
            (&Some(ref bitfields), Some(layout)) => {
                let actual_width = bitfields.iter().map(|&(_, w)| w).fold(0u32, |acc, w| acc + w);
                actual_width + width <= (layout.size * 8) as u32
            },
            _ => false
        }
    }

    match cursor.kind() {
        CXCursor_TypeAliasDecl | CXCursor_TypedefDecl => {
            ci.typedefs.push(cursor.spelling().to_owned());
        }
        CXCursor_FieldDecl => {
            let anno = Annotations::new(cursor);
            if anno.hide {
                return CXChildVisit_Continue;
            }

            let is_class_typedef = cursor.cur_type().sanitized_spelling_in(&ci.typedefs);

            // NB: Overwritten in the case of non-integer bitfield
            let mut ty = conv_ty_resolving_typedefs(ctx, &cursor.cur_type(), cursor, is_class_typedef);
            let comment = cursor.raw_comment();

            let (name, bitfields) = match (cursor.bit_width(), ci.members.last_mut()) {
                // The field is a continuation of an exising bitfield
                (Some(width), Some(&mut il::CompMember::Field(ref mut field)))
                    if is_bitfield_continuation(field, &ty, width) => {

                    // println!("found bitfield continuation {} (width: {})", cursor.spelling(), width);

                    field.bitfields.as_mut().unwrap().push((cursor.spelling(), width));
                    return CXChildVisit_Continue;
                },
                // The field is the start of a new bitfield
                (Some(width), _) => {
                    // Bitfields containing enums are not supported by the c standard
                    // https://stackoverflow.com/questions/11983231/is-it-safe-to-use-an-enum-in-a-bit-field
                    match ty {
                        il::TInt(_, _) => (),
                        _ => {
                            // NOTE: We rely on the name of the type converted to rust types,
                            // and on the alignment.
                            let bits = cmp::max(width, ty.size() as u32 * 8);
                            let layout_size = cmp::max(1, bits.next_power_of_two() / 8) as usize;

                            let msg = format!("Enums in bitfields are not supported ({}::{}). Trying to recover with width: {}",
                                parent.spelling(), cursor.spelling(), layout_size * 8);
                            ctx.logger.warn(&msg);

                            let name = match layout_size {
                                1 => "uint8_t",
                                2 => "uint16_t",
                                4 => "uint32_t",
                                8 => "uint64_t",
                                _ => panic!("bitfield width not supported: {}", layout_size),
                            };

                            // NB: We rely on the ULongLong not being translated 
                            // (using the common uintxx_t name)
                            let ti = TypeInfo::new(name.into(),
                                                   ctx.current_module_id,
                                                   TInt(IKind::IULongLong, Layout::new(layout_size, layout_size)),
                                                   Layout::new(layout_size, layout_size));
                            ty = TNamed(Rc::new(RefCell::new(ti)))
                        }
                    }
                    ("".to_owned(), Some(vec!((cursor.spelling(), width))))
                },
                // The field is not a bitfield
                (None, _) => (cursor.spelling(), None)
            };

            // The Clang C api does not fully expose composite fields, but it
            // does expose them in a way that can be detected. When the current
            // field kind is TComp, TPtr or TArray and the previous member is a
            // composite type - the same type as this field - then this is a
            // composite field.  e.g.:
            //
            //     struct foo {
            //         union {
            //             int a;
            //             char b;
            //         } bar;
            //     };
            //
            //     struct foo {
            //         union {
            //             int a;
            //             char b;
            //         } **bar;
            //     };
            //
            //     struct foo {
            //         union {
            //             int a;
            //             char b;
            //         } bar[3][2];
            //     };
            //

            //let is_composite = match (inner_composite(&ty), ci.members.last()) {
            //    (Some(ty_compinfo), Some(&CompMember::Comp(ref c))) => {
            //        c.borrow().deref() as *const _ == ty_compinfo.borrow().deref() as *const _
            //    },
            //    _ => false
            //};

            if let Some(&mut CompMember::Field(ref mut info)) = ci.members.last_mut() {
                if bitfields.is_none() && info.bitfields.is_none() {
                    if info.ty.was_unnamed() && ty.was_unnamed() &&
                        info.ty.name() == ty.name() {
                        *info = FieldInfo::new(name, ty, comment, bitfields);
                        return CXChildVisit_Continue;
                    }
                }
            }

            let field = FieldInfo::new(name, ty, comment, bitfields);
            ci.members.push(CompMember::Field(field));
        }
        CXCursor_StructDecl |
        CXCursor_UnionDecl |
        CXCursor_ClassTemplate |
        CXCursor_ClassDecl => {
            fwd_decl(ctx, cursor, |ctx_| {
                // If the struct is anonymous (i.e. declared here) then it
                // cannot be used elsewhere and so does not need to be added
                // to globals otherwise it will be declared later and a global.
                let decl = decl_name(ctx_, cursor);
                let ci2 = decl.compinfo();

                // Mangle the name to prevent multiple definitions
                // of the same inner type to cause conflicts
                let new_name = [&*ci.name, &*ci2.borrow().name].join("_").to_owned();
                ci2.borrow_mut().name = new_name;

                // Propagate template arguments and typedefs to inner structs
                ci2.borrow_mut().args.extend(ci.args.clone().into_iter());
                ci2.borrow_mut().typedefs.extend(ci.typedefs.clone().into_iter());<commit_msg>parser: Restructure anonymous struct generation to prevent some double borrows<commit_after>#![allow(non_upper_case_globals)]

use std::collections::{HashMap, HashSet};
use std::cell::RefCell;
use std::rc::Rc;
use std::path::Path;
use std::cmp;

use syntax::abi;

use types as il;
use types::*;
use clang as cx;
use clang::{ast_dump, Comment, Cursor, Diagnostic, TranslationUnit, type_to_str, kind_to_str};
use clangll::*;

use super::Logger;

#[derive(Clone)]
pub struct ClangParserOptions {
    pub builtin_names: HashSet<String>,
    pub builtins: bool,
    pub match_pat: Vec<String>,
    pub emit_ast: bool,
    pub fail_on_unknown_type: bool,
    pub ignore_functions: bool,
    pub enable_cxx_namespaces: bool,
    pub override_enum_ty: Option<il::IKind>,
    pub clang_args: Vec<String>,
    pub opaque_types: Vec<String>,
    pub blacklist_type: Vec<String>,
}

struct ClangParserCtx<'a> {
    options: ClangParserOptions,
    name: HashMap<Cursor, Global>,
    builtin_defs: Vec<Cursor>,
    module_map: ModuleMap,
    current_module_id: ModuleId,
    logger: &'a (Logger+'a),
    err_count: i32,
    anonymous_modules_found: usize,
}

impl<'a> ClangParserCtx<'a> {
    fn module(&self, id: &ModuleId) -> &Module {
        self.module_map.get(id).expect("Module not found!")
    }

    fn current_module(&self) -> &Module {
        self.module(&self.current_module_id)
    }

    fn current_module_mut(&mut self) -> &mut Module {
        self.module_map.get_mut(&self.current_module_id).expect("Module not found!")
    }
}

fn match_pattern(ctx: &mut ClangParserCtx, cursor: &Cursor) -> bool {
    let (file, _, _, _) = cursor.location().location();

    let name = match file.name() {
        None => return ctx.options.builtins,
        Some(name) => name,
    };

    if ctx.options.match_pat.is_empty() {
        return true;
    }

    ctx.options.match_pat.iter().any(|pat| name.contains(pat))
}

fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global {
    let cursor = cursor.canonical();
    let override_enum_ty = ctx.options.override_enum_ty;
    let new_decl = !ctx.name.contains_key(&cursor);

    let decl = if new_decl {
        let spelling = cursor.spelling();
        let comment = cursor.raw_comment();
        let (file, _, _, _) = cursor.location().location();
        let ty = cursor.cur_type();
        let layout = Layout::new(ty.size(), ty.align());
        let filename = match Path::new(&file.name().unwrap_or("".to_owned())).file_name() {
            Some(name) => name.to_string_lossy().replace(".", "_"),
            _ => "".to_string()
        };
        let glob_decl = match cursor.kind() {
            CXCursor_UnionDecl |
            CXCursor_ClassTemplate |
            CXCursor_ClassDecl |
            CXCursor_StructDecl => {
                let kind = if cursor.kind() == CXCursor_UnionDecl {
                    CompKind::Union
                } else {
                    CompKind::Struct
                };

                let mut opaque = ctx.options.opaque_types.iter().any(|name| *name == spelling);
                let hide = ctx.options.blacklist_type.iter().any(|name| *name == spelling);

                let mut has_non_type_template_params = false;
                let args = match cursor.kind() {
                    CXCursor_ClassDecl => {
                        match ty.num_template_args() {
                            -1 => vec![],
                            len => {
                                let mut list = Vec::with_capacity(len as usize);
                                for i in 0..len {
                                    let arg_type = ty.template_arg_type(i);
                                    if arg_type.kind() != CXType_Invalid {
                                        list.push(conv_ty(ctx, &arg_type, &cursor));
                                    } else {
                                        has_non_type_template_params = true;
                                        ctx.logger.warn("warning: Template parameter is not a type");
                                    }
                                }
                                list
                            }
                        }
                    }
                    _ => vec![],
                };

                let mut module_id = ctx.current_module_id;
                let mut has_dtor = false;

                // If it's an instantiation of another template,
                // find the canonical declaration to find the module
                // it belongs to and if it's opaque.
                let parent = cursor.specialized();
                if let Some(parent) = ctx.name.get(&parent) {
                    match *parent {
                        GComp(ref ci) |
                        GCompDecl(ref ci) => {
                            opaque |= ci.borrow().opaque;
                            has_dtor |= ci.borrow().has_destructor;
                            module_id = ci.borrow().module_id;
                        }
                        _ => {}
                    }
                }

                let mut ci = CompInfo::new(spelling, module_id, filename, comment, kind, vec![], layout);
                ci.opaque = opaque;
                ci.hide = hide;
                ci.args = args;
                ci.has_destructor = has_dtor;
                ci.has_non_type_template_params = has_non_type_template_params;

                let ci = Rc::new(RefCell::new(ci));
                GCompDecl(ci)
            }
            CXCursor_EnumDecl => {
                let kind = match override_enum_ty {
                    Some(t) => t,
                    None => match cursor.enum_type().kind() {
                        CXType_SChar | CXType_Char_S => ISChar,
                        CXType_UChar | CXType_Char_U => IUChar,
                        CXType_UShort => IUShort,
                        CXType_UInt => IUInt,
                        CXType_ULong => IULong,
                        CXType_ULongLong => IULongLong,
                        CXType_Short => IShort,
                        CXType_Int => IInt,
                        CXType_Long => ILong,
                        CXType_LongLong => ILongLong,
                        _ => IInt,
                    }
                };

                let ei = Rc::new(RefCell::new(EnumInfo::new(spelling, ctx.current_module_id, filename, kind, vec!(), layout)));
                GEnumDecl(ei)
            }
            CXCursor_TypeAliasDecl | CXCursor_TypedefDecl => {
                let opaque = ctx.options.opaque_types.iter().any(|name| *name == spelling);
                let mut ti = TypeInfo::new(spelling, ctx.current_module_id, TVoid, layout);
                ti.opaque = opaque;

                let ti = Rc::new(RefCell::new(ti));
                GType(ti)
            }
            CXCursor_VarDecl => {
                let mangled = cursor.mangling();
                let vi = Rc::new(RefCell::new(VarInfo::new(spelling, mangled, comment, TVoid)));
                GVar(vi)
            }
            CXCursor_MacroDefinition => {
                let vi = Rc::new(RefCell::new(VarInfo::new(spelling, String::new(), comment, TVoid)));
                GVar(vi)
            }
            CXCursor_FunctionDecl => {
                let mangled = cursor.mangling();
                let vi = Rc::new(RefCell::new(VarInfo::new(spelling, mangled, comment, TVoid)));
                GFunc(vi)
            }
            _ => GOther,
        };

        ctx.name.insert(cursor, glob_decl.clone());
        glob_decl
    } else {
        ctx.name.get(&cursor).unwrap().clone()
    };

    if new_decl && ctx.options.builtin_names.contains(&cursor.spelling()) {
        ctx.builtin_defs.push(cursor);
    }

    decl
}

fn opaque_decl(ctx: &mut ClangParserCtx, decl: &Cursor) {
    let name = decl_name(ctx, decl);
    ctx.current_module_mut().globals.push(name);
}

fn fwd_decl<F:FnOnce(&mut ClangParserCtx)->()>(ctx: &mut ClangParserCtx, cursor: &Cursor, f: F) {
    let def = &cursor.definition();
    if cursor == def {
        f(ctx);
    } else if def.kind() == CXCursor_NoDeclFound ||
              def.kind() == CXCursor_InvalidFile {
        opaque_decl(ctx, cursor);
    }
}

fn get_abi(cc: Enum_CXCallingConv) -> abi::Abi {
    match cc {
        CXCallingConv_Default => abi::Abi::C,
        CXCallingConv_C => abi::Abi::C,
        CXCallingConv_X86StdCall => abi::Abi::Stdcall,
        CXCallingConv_X86FastCall => abi::Abi::Fastcall,
        CXCallingConv_AAPCS => abi::Abi::Aapcs,
        CXCallingConv_X86_64Win64 => abi::Abi::Win64,
        other => panic!("unsupported calling convention: {}", other),
    }
}

fn conv_ptr_ty_resolving_typedefs(ctx: &mut ClangParserCtx,
                                  ty: &cx::Type,
                                  cursor: &Cursor,
                                  is_ref: bool,
                                  layout: Layout,
                                  resolve_typedefs: bool) -> il::Type {
    let is_const = ty.is_const();
    match ty.kind() {
        CXType_Void => {
            return TPtr(Box::new(TVoid), is_const, is_ref, layout)
        }
        CXType_Unexposed |
        CXType_FunctionProto |
        CXType_FunctionNoProto => {
            let ret_ty = ty.ret_type();
            return if ret_ty.kind() != CXType_Invalid {
                TFuncPtr(mk_fn_sig(ctx, ty, cursor))
            } else if cursor.kind() == CXCursor_VarDecl {
                let can_ty = ty.canonical_type();
                conv_ty_resolving_typedefs(ctx, &can_ty, cursor, resolve_typedefs)
            } else {
                TPtr(Box::new(conv_decl_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs)), ty.is_const(), is_ref, layout)
            };
        }
        CXType_Typedef => {
            let decl = ty.declaration();
            let def_ty = decl.typedef_type();
            if def_ty.kind() == CXType_FunctionProto ||
               def_ty.kind() == CXType_FunctionNoProto {
                return TPtr(Box::new(conv_ptr_ty_resolving_typedefs(ctx, &def_ty, cursor, is_ref, layout, resolve_typedefs)), is_const, is_ref, layout);
            } else {
                return TPtr(Box::new(conv_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs)), is_const, is_ref, layout);
            }
        }
        _ => return TPtr(Box::new(conv_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs)), is_const, is_ref, layout),
    }
}

fn mk_fn_sig(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::FuncSig {
    mk_fn_sig_resolving_typedefs(ctx, ty, cursor, &[])
}

fn mk_fn_sig_resolving_typedefs(ctx: &mut ClangParserCtx,
                                ty: &cx::Type,
                                cursor: &Cursor,
                                typedefs: &[String]) -> il::FuncSig {
    let args_lst: Vec<(String, il::Type)> = match cursor.kind() {
        CXCursor_FunctionDecl | CXCursor_CXXMethod => {
            // For CXCursor_FunctionDecl, cursor.args() is the reliable way to
            // get parameter names and types.
            cursor.args().iter().map(|arg| {
                let arg_name = arg.spelling();
                let is_class_typedef = arg.cur_type().sanitized_spelling_in(typedefs);
                (arg_name, conv_ty_resolving_typedefs(ctx, &arg.cur_type(), arg, is_class_typedef))
            }).collect()
        }
        _ => {
            // For non-CXCursor_FunctionDecl, visiting the cursor's children is
            // the only reliable way to get parameter names.
            let mut args_lst = vec!();
            cursor.visit(|c: &Cursor, _: &Cursor| {
                if c.kind() == CXCursor_ParmDecl {
                    let is_class_typedef = c.cur_type().sanitized_spelling_in(typedefs);
                    args_lst.push((c.spelling(), conv_ty_resolving_typedefs(ctx, &c.cur_type(), c, is_class_typedef)));
                }
                CXChildVisit_Continue
            });
            args_lst
        }
    };

    let ret_ty = Box::new(conv_ty(ctx, &ty.ret_type(), cursor));
    let abi = get_abi(ty.call_conv());

    // Function is presumed unsafe if it takes a pointer argument.
    let is_unsafe = args_lst.iter().any(|arg| match arg.1 {
        TPtr(..) => true,
        _ => false
    });

    il::FuncSig {
        ret_ty: ret_ty,
        args: args_lst,
        is_variadic: ty.is_variadic(),
        is_safe: !is_unsafe,
        abi: abi,
    }
}

fn conv_decl_ty(ctx: &mut ClangParserCtx,
                ty: &cx::Type,
                cursor: &Cursor) -> il::Type {
    conv_decl_ty_resolving_typedefs(ctx, ty, cursor, false)
}

fn conv_decl_ty_resolving_typedefs(ctx: &mut ClangParserCtx,
                                   ty: &cx::Type,
                                   cursor: &Cursor,
                                   resolve_typedefs: bool) -> il::Type {
    let ty_decl = ty.declaration();
    // println!("conv_ty_decl: `{}`, ty kind {}: {}, decl `{}` kind {}: {}", cursor.spelling(), ty.kind(), type_to_str(ty.kind()), ty_decl.spelling(), ty_decl.kind(), kind_to_str(ty_decl.kind()));
    return match ty_decl.kind() {
        CXCursor_StructDecl |
        CXCursor_UnionDecl |
        CXCursor_ClassTemplate |
        CXCursor_ClassDecl => {
            let decl = decl_name(ctx, &ty_decl);
            // NB: This will only return a number greater than 0 if this is a **full** class
            // template specialization.
            //
            // If the cursor kind is CXCursor_ClassTemplate, this will still return -1
            // and we'll have to keep traversing the cursor.
            let args = match ty.num_template_args() {
                -1 => vec!(),
                len => {
                    let mut list = Vec::with_capacity(len as usize);
                    for i in 0..len {
                        let arg_type = ty.template_arg_type(i);
                        if arg_type.kind() != CXType_Invalid {
                            list.push(conv_ty(ctx, &arg_type, &cursor));
                        } else {
                            ctx.logger.warn("warning: Template parameter is not a type");
                        }
                    }
                    list
                }
            };
            let ci = decl.compinfo();
            if !args.is_empty() {
                ci.borrow_mut().args = args;
                cursor.visit(|c, _: &Cursor| {
                    if c.kind() == CXCursor_TemplateRef {
                        let decl = decl_name(ctx, &c.referenced());
                        ci.borrow_mut().ref_template = Some(decl.to_type());
                    }
                    CXChildVisit_Continue
                });
            }

            TComp(ci)
        }
        CXCursor_EnumDecl => {
            let decl = decl_name(ctx, &ty_decl);
            let ei = decl.enuminfo();
            TEnum(ei)
        }
        CXCursor_TypeAliasDecl |
        CXCursor_TypedefDecl => {
            if resolve_typedefs {
                return conv_ty_resolving_typedefs(ctx, &ty_decl.typedef_type(), &ty_decl.typedef_type().declaration(), resolve_typedefs);
            }

            let decl = decl_name(ctx, &ty_decl);
            let ti = decl.typeinfo();
            TNamed(ti)
        }
        CXCursor_NoDeclFound => {
            let layout = Layout::new(ty.size(), ty.align());
            TNamed(Rc::new(RefCell::new(TypeInfo::new(ty.spelling().replace("const ", ""), ctx.current_module_id, TVoid, layout))))
        }
        _ => {
            let fail = ctx.options.fail_on_unknown_type;
            log_err_warn(ctx,
                &format!("unsupported decl `{}` ({})",
                    kind_to_str(ty_decl.kind()), ty_decl.location()
                ),
                fail
            );
            TVoid
        }
    };
}

fn conv_ty(ctx: &mut ClangParserCtx,
           ty: &cx::Type,
           cursor: &Cursor) -> il::Type {
    conv_ty_resolving_typedefs(ctx, ty, cursor, false)
}

fn conv_ty_resolving_typedefs(ctx: &mut ClangParserCtx,
                              ty: &cx::Type,
                              cursor: &Cursor,
                              resolve_typedefs: bool) -> il::Type {
    let layout = Layout::new(ty.size(), ty.align());
    // println!("conv_ty: `{}` layout: {:?}, kind {}: {}", cursor.spelling(), layout, ty.kind(), type_to_str(ty.kind()));

    match ty.kind() {
        CXType_Void => TVoid,
        CXType_Invalid => {
            log_err_warn(ctx,
                &format!("invalid type `{}` ({})",
                    cursor.spelling(), cursor.location()
                ),
                false
            );
            TVoid
        }
        CXType_Bool => TInt(IBool, layout),
        CXType_SChar |
        CXType_Char_S => TInt(ISChar, layout),
        CXType_UChar |
        CXType_Char_U => TInt(IUChar, layout),
        CXType_WChar => TInt(IShort, layout),
        CXType_Char16 |
        CXType_UShort => TInt(IUShort, layout),
        CXType_UInt => TInt(IUInt, layout),
        CXType_ULong => TInt(IULong, layout),
        CXType_ULongLong => TInt(IULongLong, layout),
        CXType_Short => TInt(IShort, layout),
        CXType_Int => TInt(IInt, layout),
        CXType_Long => TInt(ILong, layout),
        CXType_LongLong => TInt(ILongLong, layout),
        CXType_Float => TFloat(FFloat, layout),
        CXType_Double => TFloat(FDouble, layout),
        CXType_LongDouble => TFloat(FDouble, layout),
        CXType_Pointer => conv_ptr_ty_resolving_typedefs(ctx, &ty.pointee_type(), cursor, false, layout, resolve_typedefs),
        CXType_LValueReference => conv_ptr_ty_resolving_typedefs(ctx, &ty.pointee_type(), cursor, true, layout, resolve_typedefs),
        // XXX DependentSizedArray is wrong
        CXType_VariableArray | CXType_DependentSizedArray | CXType_IncompleteArray => {
            conv_ptr_ty_resolving_typedefs(ctx, &ty.elem_type(), cursor, false, layout, resolve_typedefs)
        }
        CXType_FunctionProto => TFuncProto(mk_fn_sig(ctx, ty, cursor)),
        CXType_Record |
        CXType_Typedef  |
        CXType_Unexposed |
        CXType_Enum => conv_decl_ty_resolving_typedefs(ctx, ty, cursor, resolve_typedefs),
        CXType_ConstantArray => TArray(Box::new(conv_ty_resolving_typedefs(ctx, &ty.elem_type(), cursor, resolve_typedefs)), ty.array_size(), layout),
        _ => {
            let fail = ctx.options.fail_on_unknown_type;
            log_err_warn(ctx,
                &format!("unsupported type `{}` ({})",
                    type_to_str(ty.kind()), cursor.location()
                ),
                fail
            );
            TVoid
        },
    }
}

fn opaque_ty(ctx: &mut ClangParserCtx, ty: &cx::Type) {
    if ty.kind() == CXType_Record || ty.kind() == CXType_Enum {
        let decl = ty.declaration();
        let def = decl.definition();
        if def.kind() == CXCursor_NoDeclFound ||
           def.kind() == CXCursor_InvalidFile {
            opaque_decl(ctx, &decl);
        }
    }
}

struct Annotations {
    opaque: bool,
    hide: bool,
    use_as: Option<String>,
}

impl Annotations {
    fn new(cursor: &Cursor) -> Annotations {
        let mut anno = Annotations {
            opaque: false,
            hide: false,
            use_as: None,
        };

        anno.parse(&cursor.comment());
        anno
    }

    fn parse(&mut self, comment: &Comment) {
        if comment.kind() == CXComment_HTMLStartTag &&
           comment.get_tag_name() == "div" &&
           comment.get_num_tag_attrs() > 1 &&
           comment.get_tag_attr_name(0) == "rustbindgen" {
            for i in 0..comment.get_num_tag_attrs() {
                let name = comment.get_tag_attr_name(i);
                match name.as_str() {
                    "opaque" => self.opaque = true,
                    "hide" => self.hide = true,
                    "replaces" => self.use_as = Some(comment.get_tag_attr_value(i)),
                    _ => (),
                }
            }
        }

        for i in 0..comment.num_children() {
            self.parse(&comment.get_child(i));
        }
    }
}

/// Recursively visits a cursor that represents a composite (struct or union)
/// type and fills members with CompMember instances representing the fields and
/// nested composites that make up the visited composite.
fn visit_composite(cursor: &Cursor, parent: &Cursor,
                   ctx: &mut ClangParserCtx,
                   ci: &mut CompInfo) -> Enum_CXVisitorResult {
    fn is_bitfield_continuation(field: &il::FieldInfo, _ty: &il::Type, width: u32) -> bool {
        match (&field.bitfields, field.ty.layout()) {
            (&Some(ref bitfields), Some(layout)) => {
                let actual_width = bitfields.iter().map(|&(_, w)| w).fold(0u32, |acc, w| acc + w);
                actual_width + width <= (layout.size * 8) as u32
            },
            _ => false
        }
    }

    match cursor.kind() {
        CXCursor_TypeAliasDecl | CXCursor_TypedefDecl => {
            ci.typedefs.push(cursor.spelling().to_owned());
        }
        CXCursor_FieldDecl => {
            let anno = Annotations::new(cursor);
            if anno.hide {
                return CXChildVisit_Continue;
            }

            let is_class_typedef = cursor.cur_type().sanitized_spelling_in(&ci.typedefs);

            // NB: Overwritten in the case of non-integer bitfield
            let mut ty = conv_ty_resolving_typedefs(ctx, &cursor.cur_type(), cursor, is_class_typedef);
            let comment = cursor.raw_comment();

            let (name, bitfields) = match (cursor.bit_width(), ci.members.last_mut()) {
                // The field is a continuation of an exising bitfield
                (Some(width), Some(&mut il::CompMember::Field(ref mut field)))
                    if is_bitfield_continuation(field, &ty, width) => {

                    // println!("found bitfield continuation {} (width: {})", cursor.spelling(), width);

                    field.bitfields.as_mut().unwrap().push((cursor.spelling(), width));
                    return CXChildVisit_Continue;
                },
                // The field is the start of a new bitfield
                (Some(width), _) => {
                    // Bitfields containing enums are not supported by the c standard
                    // https://stackoverflow.com/questions/11983231/is-it-safe-to-use-an-enum-in-a-bit-field
                    match ty {
                        il::TInt(_, _) => (),
                        _ => {
                            // NOTE: We rely on the name of the type converted to rust types,
                            // and on the alignment.
                            let bits = cmp::max(width, ty.size() as u32 * 8);
                            let layout_size = cmp::max(1, bits.next_power_of_two() / 8) as usize;

                            let msg = format!("Enums in bitfields are not supported ({}::{}). Trying to recover with width: {}",
                                parent.spelling(), cursor.spelling(), layout_size * 8);
                            ctx.logger.warn(&msg);

                            let name = match layout_size {
                                1 => "uint8_t",
                                2 => "uint16_t",
                                4 => "uint32_t",
                                8 => "uint64_t",
                                _ => panic!("bitfield width not supported: {}", layout_size),
                            };

                            // NB: We rely on the ULongLong not being translated
                            // (using the common uintxx_t name)
                            let ti = TypeInfo::new(name.into(),
                                                   ctx.current_module_id,
                                                   TInt(IKind::IULongLong, Layout::new(layout_size, layout_size)),
                                                   Layout::new(layout_size, layout_size));
                            ty = TNamed(Rc::new(RefCell::new(ti)))
                        }
                    }
                    ("".to_owned(), Some(vec!((cursor.spelling(), width))))
                },
                // The field is not a bitfield
                (None, _) => (cursor.spelling(), None)
            };

            // The Clang C api does not fully expose composite fields, but it
            // does expose them in a way that can be detected. When the current
            // field kind is TComp, TPtr or TArray and the previous member is a
            // composite type - the same type as this field - then this is a
            // composite field.  e.g.:
            //
            //     struct foo {
            //         union {
            //             int a;
            //             char b;
            //         } bar;
            //     };
            //
            //     struct foo {
            //         union {
            //             int a;
            //             char b;
            //         } **bar;
            //     };
            //
            //     struct foo {
            //         union {
            //             int a;
            //             char b;
            //         } bar[3][2];
            //     };
            //

            //let is_composite = match (inner_composite(&ty), ci.members.last()) {
            //    (Some(ty_compinfo), Some(&CompMember::Comp(ref c))) => {
            //        c.borrow().deref() as *const _ == ty_compinfo.borrow().deref() as *const _
            //    },
            //    _ => false
            //};

            if let Some(&mut CompMember::Field(ref mut info)) = ci.members.last_mut() {
                if bitfields.is_none() && info.bitfields.is_none() {
                    let should_replace = if let TComp(ref ci) = info.ty {
                        if ci.borrow().was_unnamed && ty.was_unnamed() &&
                            Some(&ci.borrow().name) == ty.name().as_ref() {
                            true
                        } else {
                            false
                        }
                    } else {
                        false
                    };

                    if should_replace {
                        *info = FieldInfo::new(name, ty, comment, bitfields);
                        return CXChildVisit_Continue;
                    }
                }
            }

            let field = FieldInfo::new(name, ty, comment, bitfields);
            ci.members.push(CompMember::Field(field));
        }
        CXCursor_StructDecl |
        CXCursor_UnionDecl |
        CXCursor_ClassTemplate |
        CXCursor_ClassDecl => {
            fwd_decl(ctx, cursor, |ctx_| {
                // If the struct is anonymous (i.e. declared here) then it
                // cannot be used elsewhere and so does not need to be added
                // to globals otherwise it will be declared later and a global.
                let decl = decl_name(ctx_, cursor);
                let ci2 = decl.compinfo();

                // Mangle the name to prevent multiple definitions
                // of the same inner type to cause conflicts
                let new_name = [&*ci.name, &*ci2.borrow().name].join("_").to_owned();
                ci2.borrow_mut().name = new_name;

                // Propagate template arguments and typedefs to inner structs
                ci2.borrow_mut().args.extend(ci.args.clone().into_iter());
                ci2.borrow_mut().typedefs.extend(ci.typedefs.clone().into_iter());
{'fraction_non_alphanumeric': 0.11730232048637103, 'fraction_numerical': 0.0024464608474978546, 'mean_word_length': 1.5181132769400514, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 261, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 68, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '710098', 'n_tokens_mistral': 16236, 'n_tokens_neox': 15264, 'n_words': 3234}