Home > front end >  rust libclang, why am I parsing only c functions from a cpp file?
rust libclang, why am I parsing only c functions from a cpp file?

Time:07-25

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)
  • Related