使用Rust libclang解析C++ extern "C"函数失败的技术问询
解决libclang解析extern "C"块内函数的问题
你的问题出在extern "C"块在Clang AST中被标记为LinkageSpecDecl类型,而你当前的代码只遍历了顶层的FunctionDecl,完全没处理这种包含子节点的声明块,所以里面的函数根本没被收集到。
要解决这个问题,你需要递归遍历所有AST节点,遇到LinkageSpecDecl(或者更通用的,所有包含子节点的声明类型)就深入进去查找函数。
修改后的完整代码
use clang::*; use clap::{Arg, App}; // 递归收集所有函数声明,包括extern "C"块内的 fn collect_functions(entity: &Entity) -> Vec<Entity> { let mut functions = Vec::new(); match entity.get_kind() { // 找到函数声明,直接加入结果 EntityKind::FunctionDecl => { functions.push(entity.clone()); } // 遇到链接规范声明(extern "C"块)或未暴露声明,递归遍历其子节点 EntityKind::LinkageSpecDecl | EntityKind::UnexposedDecl => { for child in entity.get_children() { functions.extend(collect_functions(&child)); } } // 处理命名空间,避免漏掉命名空间内的函数 EntityKind::Namespace => { for child in entity.get_children() { functions.extend(collect_functions(&child)); } } // 其他类型直接忽略 _ => {} } functions } fn main() { let matches = App::new("Shared C++ API parser") .version("0.0.1") .author("Makogan") .about("Parse a cpp file that exposes functions to be loaded at runtime from \ A shared object (.so/.dll).") .arg(Arg::with_name("file") .short('f') .long("file") .takes_value(true) .help("The cpp file that will be parsed") .required(true)) .get_matches(); let cpp_file = matches.value_of("file").unwrap(); println!("The file passed is: {}", cpp_file); let clang = Clang::new().unwrap(); let index = Index::new(&clang, false, false); let tu = index.parser(cpp_file).parse().unwrap(); // 用递归函数收集所有函数,代替原来的顶层过滤 let funcs = collect_functions(&tu.get_entity()); for func_ in funcs { if let (Some(type_), Some(display_name)) = (func_.get_type(), func_.get_display_name()) { if let Some(size) = type_.get_sizeof() { println!("func: {:?} (size: {} bytes)", display_name, size); } } } }
关键改动说明
- 递归遍历AST节点:新增的
collect_functions会逐层遍历所有节点,遇到extern "C"对应的LinkageSpecDecl就深入处理其子节点,确保不会漏掉块内的函数。 - 兼容UnexposedDecl:部分场景下extern "C"块可能被识别为
UnexposedDecl,将其加入递归处理范围,提升代码兼容性。 - 空值安全处理:打印逻辑改用
if let处理可能的空值,避免unwrap导致的程序崩溃。
测试验证
用你提供的C++代码测试,修改后的脚本会输出:
The file passed is: test.cpp func: "InitializeRendering(void*, bool)" (size: 8 bytes)
内容的提问来源于stack exchange,提问作者Makogan
相关产品推荐
相关产品推荐

