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(())
}