clang 0.9.5

A somewhat idiomatic Rust wrapper for libclang.
use clang::*;

pub fn test(clang: &Clang) {
    macro_rules! assert_declaration_eq {
        ($declaration:expr, $name:expr, SAME) => ({
            let declaration = $declaration;
            assert_eq!(declaration.name, $name);
            assert_eq!(declaration.entity.get_name(), Some($name.into()));
            assert!(declaration.source.is_none());
        });

        ($declaration:expr, $name:expr, DIFFERENT) => ({
            let declaration = $declaration;
            assert_eq!(declaration.name, $name);
            assert_eq!(declaration.entity.get_name(), None);
            assert_eq!(declaration.source.unwrap().get_name(), Some($name.into()));
        });
    }

    let source = "
        #define A 4
        #define B -322
        #define C 3.14159
        #define D -2.71828
    ";

    super::with_temporary_file("header.h", source, |_, f| {
        use clang::sonar::{DefinitionValue};

        let index = Index::new(&clang, false, false);
        let tu = index.parser(f).detailed_preprocessing_record(true).parse().unwrap();

        let definitions = sonar::find_definitions(tu.get_entity().get_children()).filter(|d| {
            !d.entity.is_in_system_header()
        }).collect::<Vec<_>>();
        assert_eq!(definitions.len(), 4);

        macro_rules! assert_definition_eq {
            ($definition:expr, $name:expr, $value:expr) => ({
                let definition = $definition;
                assert_eq!(definition.name, $name);
                assert_eq!(definition.value, $value);
                assert_eq!(definition.entity.get_name(), Some($name.into()));
            });
        }

        assert_definition_eq!(&definitions[0], "A", DefinitionValue::Integer(false, 4));
        assert_definition_eq!(&definitions[1], "B", DefinitionValue::Integer(true, 322));
        assert_definition_eq!(&definitions[2], "C", DefinitionValue::Real(3.14159));
        assert_definition_eq!(&definitions[3], "D", DefinitionValue::Real(-2.71828));
    });

    let source = "
        enum A {
            AA = 1,
            AB = 2,
            AC = 3,
        };

        typedef enum B {
            CA,
            CB,
            CC,
        } B;

        typedef enum {
            DA,
            DB,
            DC,
        } C;

        enum D {
            EA,
            EB,
            EC,
        };

        typedef enum D D;

        typedef enum E* OpaqueE;
    ";

    super::with_entity(&clang, source, |e| {
        let enums = sonar::find_enums(e.get_children()).filter(|e| {
            !e.entity.is_in_system_header()
        }).collect::<Vec<_>>();
        assert_eq!(enums.len(), 4);

        assert_declaration_eq!(&enums[0], "A", SAME);
        assert_declaration_eq!(&enums[1], "B", SAME);
        assert_declaration_eq!(&enums[2], "C", DIFFERENT);
        assert_declaration_eq!(&enums[3], "D", SAME);
    });

    let source = "
        void multiple(void);
        void multiple(void);

        int zero(void);

        float one(int a);
        float two(int a, int b);

        double many(int a, int b, ...);
    ";

    super::with_entity(&clang, source, |e| {
        let functions = sonar::find_functions(e.get_children()).filter(|f| {
            !f.entity.is_in_system_header()
        }).collect::<Vec<_>>();
        assert_eq!(functions.len(), 5);

        assert_declaration_eq!(&functions[0], "multiple", SAME);
        assert_declaration_eq!(&functions[1], "zero", SAME);
        assert_declaration_eq!(&functions[2], "one", SAME);
        assert_declaration_eq!(&functions[3], "two", SAME);
        assert_declaration_eq!(&functions[4], "many", SAME);
    });

    let source = "
        struct A {
            int a;
        };

        typedef struct B {
            int b;
        } B;

        typedef struct {
            int c;
        } C;

        struct D {
            int d;
        };

        typedef struct D D;

        typedef struct E* OpaqueE;
    ";

    super::with_entity(&clang, source, |e| {
        let structs = sonar::find_structs(e.get_children()).filter(|s| {
            !s.entity.is_in_system_header()
        }).collect::<Vec<_>>();
        assert_eq!(structs.len(), 4);

        assert_declaration_eq!(&structs[0], "A", SAME);
        assert_declaration_eq!(&structs[1], "B", SAME);
        assert_declaration_eq!(&structs[2], "C", DIFFERENT);
        assert_declaration_eq!(&structs[3], "D", SAME);
    });

    let source = "
        typedef int Integer;
        typedef Integer IntegerTypedef;
        typedef IntegerTypedef IntegerTypedefTypedef;

        typedef int* IntegerPointer;

        typedef int Function(int, float, double);
        typedef int (*FunctionPointer)(int a, float b, double c, ...);

        enum E { EA, EB, EC };
        typedef enum E Enum;
        typedef Enum EnumTypedef;

        struct S { int s; };
        typedef struct S Struct;
        typedef Struct StructTypedef;

        union U { int us; float uf; };
        typedef union U Union;
        typedef Union UnionTypedef;

        typedef enum E EArray[4];
        typedef struct S SArray[4];
        typedef union U UArray[4];

        typedef enum Enum_* OpaqueEnum;
        typedef struct Struct_* OpaqueStruct;
        typedef Union_* OpaqueUnion;
    ";

    super::with_entity(&clang, source, |e| {
        assert_eq!(sonar::find_enums(e.get_children()).count(), 1);
        assert_eq!(sonar::find_structs(e.get_children()).count(), 1);
        assert_eq!(sonar::find_unions(e.get_children()).count(), 1);

        let typedefs = sonar::find_typedefs(e.get_children()).filter(|t| {
            !t.entity.is_in_system_header()
        }).collect::<Vec<_>>();
        assert_eq!(typedefs.len(), 18);

        assert_declaration_eq!(&typedefs[0], "Integer", SAME);
        assert_declaration_eq!(&typedefs[1], "IntegerTypedef", SAME);
        assert_declaration_eq!(&typedefs[2], "IntegerTypedefTypedef", SAME);
        assert_declaration_eq!(&typedefs[3], "IntegerPointer", SAME);
        assert_declaration_eq!(&typedefs[4], "Function", SAME);
        assert_declaration_eq!(&typedefs[5], "FunctionPointer", SAME);
        assert_declaration_eq!(&typedefs[6], "Enum", SAME);
        assert_declaration_eq!(&typedefs[7], "EnumTypedef", SAME);
        assert_declaration_eq!(&typedefs[8], "Struct", SAME);
        assert_declaration_eq!(&typedefs[9], "StructTypedef", SAME);
        assert_declaration_eq!(&typedefs[10], "Union", SAME);
        assert_declaration_eq!(&typedefs[11], "UnionTypedef", SAME);
        assert_declaration_eq!(&typedefs[12], "EArray", SAME);
        assert_declaration_eq!(&typedefs[13], "SArray", SAME);
        assert_declaration_eq!(&typedefs[14], "UArray", SAME);
        assert_declaration_eq!(&typedefs[15], "OpaqueEnum", SAME);
        assert_declaration_eq!(&typedefs[16], "OpaqueStruct", SAME);
        assert_declaration_eq!(&typedefs[17], "OpaqueUnion", SAME);
    });

    let source = "
        union A {
            int ai;
            float af;
        };

        typedef union B {
            int bi;
            float bf;
        } B;

        typedef union {
            int ci;
            float cf;
        } C;

        union D {
            int di;
            float df;
        };

        typedef union D D;

        typedef union E* OpaqueE;
    ";

    super::with_entity(&clang, source, |e| {
        let unions = sonar::find_unions(e.get_children()).filter(|u| {
            !u.entity.is_in_system_header()
        }).collect::<Vec<_>>();
        assert_eq!(unions.len(), 4);

        assert_declaration_eq!(&unions[0], "A", SAME);
        assert_declaration_eq!(&unions[1], "B", SAME);
        assert_declaration_eq!(&unions[2], "C", DIFFERENT);
        assert_declaration_eq!(&unions[3], "D", SAME);
    });

    #[cfg(target_os="linux")]
    fn test_headers(clang: &Clang) {
        fn test(clang: &Clang, header: &str) {
            let index = Index::new(clang, false, false);
            let tu = index.parser(header).detailed_preprocessing_record(true).parse();
            if let Ok(tu) = tu {
                let entities = tu.get_entity().get_children();
                let _ = sonar::find_definitions(&entities[..]);
                let _ = sonar::find_enums(&entities[..]);
                let _ = sonar::find_functions(&entities[..]);
                let _ = sonar::find_structs(&entities[..]);
                let _ = sonar::find_typedefs(&entities[..]);
                let _ = sonar::find_unions(&entities[..]);
            }
        }

        test(clang, "/usr/include/assert.h");
        test(clang, "/usr/include/complex.h");
        test(clang, "/usr/include/ctype.h");
        test(clang, "/usr/include/errno.h");
        test(clang, "/usr/include/fenv.h");
        test(clang, "/usr/include/float.h");
        test(clang, "/usr/include/inttypes.h");
        test(clang, "/usr/include/limits.h");
        test(clang, "/usr/include/locale.h");
        test(clang, "/usr/include/math.h");
        test(clang, "/usr/include/setjmp.h");
        test(clang, "/usr/include/signal.h");
        test(clang, "/usr/include/stdarg.h");
        test(clang, "/usr/include/stdbool.h");
        test(clang, "/usr/include/stddef.h");
        test(clang, "/usr/include/stdint.h");
        test(clang, "/usr/include/stdio.h");
        test(clang, "/usr/include/stdlib.h");
        test(clang, "/usr/include/string.h");
        test(clang, "/usr/include/tgmath.h");
        test(clang, "/usr/include/time.h");
        test(clang, "/usr/include/uchar.h");
        test(clang, "/usr/include/wchar.h");
        test(clang, "/usr/include/wctype.h");
    }

    #[cfg(not(target_os="linux"))]
    fn test_headers(_: &Clang) { }

    test_headers(clang);
}