I am trying to write a rust script that parses a C file, finds all extern C
declared functions and prints them out.
To that effect I have this first attempt:
use clang::*;
use clap::{Arg, App};
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 = tu.get_entity().get_children().into_iter().filter(
|e| {
e.get_kind() == EntityKind::FunctionDecl
}).collect::<Vec<_>>();
for func_ in funcs
{
let type_ = func_.get_type().unwrap();
let size = type_.get_sizeof().unwrap();
println!("func: {:?} (size: {} bytes)", func_.get_display_name().unwrap(), size);
}
}
I am giving it a true cpp file with lots of functions, this is one such declared funciton:
void DrawOffScreen(
void* vk_data_ptr,
const NECore::RenderRequest& render_request,
const NECore::UniformBufferDataContainer& uniform_container);
When I give it a very very simple file I stil don't get an output
extern "C"
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-type-c-linkage"
void InitializeRendering(void* window, bool use_vsync)
{
}
}
If I comment out the extern C part I do get the correct output. It seems that when the defientiion is inside an extern C block clang thinks the funciton is undeclared:
Some(Entity { kind: UnexposedDecl, display_name: None, location: Some(SourceLocation { file: Some(File { path: "/path/dummy.cpp" }), line: 1, column: 8, offset: 7 }) })
What do I do?
CodePudding user response:
UnexposedDecl
is actually not the function definition, but the extern 'C'
itself.
If you print all items recursively, you will see what the tree actually looks like:
fn print_rec(entity: Entity, depth: usize) {
for _ in 0..depth {
print!(" ");
}
println!("{:?}", entity);
for child in entity.get_children() {
print_rec(child, depth 1);
}
}
fn main() {
// ...
print_rec(tu.get_entity(), 0);
}
Entity { kind: TranslationUnit, display_name: Some("simple.cpp"), location: None }
Entity { kind: UnexposedDecl, display_name: None, location: Some(SourceLocation { file: Some(File { path: "simple.cpp" }), line: 1, column: 8, offset: 7 }) }
Entity { kind: FunctionDecl, display_name: Some("InitializeRendering(void *, bool)"), location: Some(SourceLocation { file: Some(File { path: "simple.cpp" }), line: 7, column: 6, offset: 105 }) }
Entity { kind: ParmDecl, display_name: Some("window"), location: Some(SourceLocation { file: Some(File { path: "simple.cpp" }), line: 7, column: 32, offset: 131 }) }
Entity { kind: ParmDecl, display_name: Some("use_vsync"), location: Some(SourceLocation { file: Some(File { path: "simple.cpp" }), line: 7, column: 45, offset: 144 }) }
Entity { kind: CompoundStmt, display_name: None, location: Some(SourceLocation { file: Some(File { path: "simple.cpp" }), line: 8, column: 1, offset: 155 }) }
So your actual problem is that you only iterate over the topmost level of the tree, and don't recurse into the segments.
To iterate recursively, use visit_children()
instead of get_children()
:
use clang::*;
use clap::{App, Arg};
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 mut funcs = vec![];
tu.get_entity().visit_children(|e, _| {
if e.get_kind() == EntityKind::FunctionDecl {
funcs.push(e);
EntityVisitResult::Continue
} else {
EntityVisitResult::Recurse
}
});
for func_ in funcs {
let type_ = func_.get_type().unwrap();
let size = type_.get_sizeof().unwrap();
println!(
"func: {:?} (size: {} bytes)",
func_.get_display_name().unwrap(),
size
);
}
}
The file passed is: simple.cpp
func: "InitializeRendering(void *, bool)" (size: 1 bytes)