fn process_mod()

in src/bindgen/parser.rs [294:408]


    fn process_mod(
        &mut self,
        pkg: &PackageRef,
        mod_dir: Option<&FilePath>,
        submod_dir: Option<&FilePath>,
        items: &[syn::Item],
        depth: usize,
        is_inline: bool,
        is_in_mod_rs: bool,
    ) -> Result<(), Error> {
        debug_assert_eq!(mod_dir.is_some(), submod_dir.is_some());
        // We process the items first then the nested modules.
        let nested_modules = self.out.load_syn_crate_mod(
            self.config,
            &self.binding_crate_name,
            &pkg.name,
            Cfg::join(&self.cfg_stack).as_ref(),
            items,
        );

        for item in nested_modules {
            let next_mod_name = item.ident.unraw().to_string();
            let cfg = Cfg::load(&item.attrs);
            if let Some(ref cfg) = cfg {
                self.cfg_stack.push(cfg.clone());
            }

            if let Some((_, ref inline_items)) = item.content {
                // TODO(emilio): This should use #[path] attribute if present,
                // rather than next_mod_name.
                let next_submod_dir = submod_dir.map(|dir| dir.join(&next_mod_name));
                let next_mod_dir = mod_dir.map(|dir| dir.join(&next_mod_name));
                self.process_mod(
                    pkg,
                    next_mod_dir.as_deref(),
                    next_submod_dir.as_deref(),
                    inline_items,
                    depth,
                    /* is_inline = */ true,
                    is_in_mod_rs,
                )?;
            } else if let Some(mod_dir) = mod_dir {
                let submod_dir = submod_dir.unwrap();
                let next_mod_path1 = submod_dir.join(next_mod_name.clone() + ".rs");
                let next_mod_path2 = submod_dir.join(next_mod_name.clone()).join("mod.rs");

                if next_mod_path1.exists() {
                    self.parse_mod(pkg, next_mod_path1.as_path(), depth + 1)?;
                } else if next_mod_path2.exists() {
                    self.parse_mod(pkg, next_mod_path2.as_path(), depth + 1)?;
                } else {
                    // Last chance to find a module path
                    let mut path_attr_found = false;
                    for attr in &item.attrs {
                        if let syn::Meta::NameValue(syn::MetaNameValue {
                            path,
                            value: syn::Expr::Lit(syn::ExprLit { lit, .. }),
                            ..
                        }) = &attr.meta
                        {
                            match lit {
                                syn::Lit::Str(ref path_lit) if path.is_ident("path") => {
                                    path_attr_found = true;
                                    // https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute
                                    //
                                    //     For path attributes on modules not inside inline module blocks, the file path
                                    //     is relative to the directory the source file is located.
                                    //
                                    //     For path attributes inside inline module blocks, the relative location of the
                                    //     file path depends on the kind of source file the path attribute is located
                                    //     in.  "mod-rs" source files are root modules (such as lib.rs or main.rs) and
                                    //     modules with files named mod.rs. "non-mod-rs" source files are all other
                                    //     module files.
                                    //
                                    //     Paths for path attributes inside inline module blocks in a mod-rs file are
                                    //     relative to the directory of the mod-rs file including the inline module
                                    //     components as directories. For non-mod-rs files, it is the same except the
                                    //     path starts with a directory with the name of the non-mod-rs module.
                                    //
                                    let base = if is_inline && !is_in_mod_rs {
                                        submod_dir
                                    } else {
                                        mod_dir
                                    };
                                    self.parse_mod(pkg, &base.join(path_lit.value()), depth + 1)?;
                                    break;
                                }
                                _ => (),
                            }
                        }
                    }

                    // This should be an error, but it's common enough to
                    // just elicit a warning
                    if !path_attr_found {
                        warn!(
                            "Parsing crate `{}`: can't find mod {}`.",
                            pkg.name, next_mod_name
                        );
                    }
                }
            } else {
                warn!(
                    "Parsing expanded crate `{}`: can't find mod {}`.",
                    pkg.name, next_mod_name
                );
            }

            if cfg.is_some() {
                self.cfg_stack.pop();
            }
        }

        Ok(())
    }