diff options
-rw-r--r-- | CONTRIBUTING.md | 86 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rwxr-xr-x | src/bin/bindgen.rs | 266 | ||||
-rw-r--r-- | src/bin/options.rs | 269 | ||||
-rw-r--r-- | src/ir/comp.rs | 3 | ||||
-rw-r--r-- | src/ir/item.rs | 3 | ||||
-rw-r--r-- | src/ir/ty.rs | 50 | ||||
-rw-r--r-- | src/ir/var.rs | 4 | ||||
-rwxr-xr-x | src/lib.rs | 24 | ||||
-rw-r--r-- | tests/expectations/tests/forward-inherit-struct-with-fields.rs | 17 | ||||
-rw-r--r-- | tests/expectations/tests/forward-inherit-struct.rs | 18 | ||||
-rw-r--r-- | tests/expectations/tests/inherit-namespaced.rs | 18 | ||||
-rw-r--r-- | tests/expectations/tests/multiple-inherit-empty-correct-layout.rs | 45 | ||||
-rw-r--r-- | tests/headers/forward-inherit-struct-with-fields.hpp | 8 | ||||
-rw-r--r-- | tests/headers/forward-inherit-struct.hpp | 5 | ||||
-rw-r--r-- | tests/headers/inherit-namespaced.hpp | 4 | ||||
-rw-r--r-- | tests/headers/multiple-inherit-empty-correct-layout.hpp | 3 |
17 files changed, 571 insertions, 253 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb3208ff..ce95e21c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,7 @@ yourself. * [Authoring New Tests](#tests-new) * [Automatic Code Formatting](#formatting) * [Debug Logging](#logs) +* [Using `creduce` to Minimize Test Cases](#creduce) ## Code of Conduct <span id="coc"/> @@ -138,3 +139,88 @@ This logging can also be used when debugging failing tests under ``` $ RUST_LOG=bindgen ./tests/tools/run-bindgen.py ./target/debug/bindgen tests/headers/whatever.h ``` + +## Using `creduce` to Minimize Test Cases <span id="creduce"/> + +If you are hacking on `bindgen` and find a test case that causes an unexpected +panic, results in bad Rust bindings, or some other incorrectness in `bindgen`, +then using `creduce` can help reduce the test case to a minimal one. + +[Follow these instructions for building and/or installing `creduce`.](https://github.com/csmith-project/creduce/blob/master/INSTALL) + +Running `creduce` requires two things: + +1. Your isolated test case, and + +2. A script to act as a predicate script describing whether the behavior you're + trying to isolate occurred. + +With those two things in hand, running `creduce` looks like this: + + $ creduce ./predicate.sh ./isolated_test_case.h + +### Isolating Your Test Case + +Use the `-save-temps` flag to make Clang spit out its intermediate +representations when compiling the test case into an object file. + + $ clang[++ -x c++ --std=c++14] -save-temps -c my_test_case.h + +There should now be a `my_test_case.ii` file, which is the results after the C +pre-processor has processed all the `#include`s, `#define`s, and `#ifdef`s. This +is generally what we're looking for. + +### Writing a Predicate Script + +Writing a `predicate.sh` script for a `bindgen` test case is fairly +straightforward. One potential gotcha is that `creduce` can and will attempt to +reduce test cases into invalid C/C++ code. That might be useful for C/C++ +compilers, but we generally only care about valid C/C++ input headers. + +Here is a skeleton predicate script: + +```bash +#!/usr/bin/env bash + +# Exit the script with a nonzero exit code if: +# * any individual command finishes with a nonzero exit code, or +# * we access any undefined variable. +set -eu + +# Print out Rust backtraces on panic. Useful for minimizing a particular panic. +export RUST_BACKTRACE=1 + +# If the `libclang.so` you're using for `bindgen` isn't the system +# `libclang.so`, let the linker find it. +export LD_LIBRARY_PATH=~/path/to/your/directory/containing/libclang + +# Make sure that the reduced test case is valid C/C++ by compiling it. If it +# isn't valid C/C++, this command will exit with a nonzero exit code and cause +# the whole script to do the same. +clang[++ --std=c++14] -c ./pre_processed_header.hpp + +# Run `bindgen` and `grep` for the thing your hunting down! Make sure to include +# `2>&1` to get at stderr if you're hunting down a panic. +~/src/rust-bindgen/target/debug/bindgen \ + ./pre_processed_header.hpp \ + [ <extra flags> ] \ + 2>&1 \ + | grep "<pattern in generated bindings or a panic string or ...>" +``` + +When hunting down a panic, I `grep`ed like this: + + ... | grep "thread main panicked at '<panic error message here>'" + +When hunting down bad codegen for a base member, I `grep`ed like this: + + ... | grep "pub _base: MyInvalidBaseTypeThatShouldntBeHere" + +That's pretty much it! I want to impress upon you that `creduce` is *really* +helpful and has enabled me to reduce 30k lines of test case into 5 lines. And it +works pretty quickly too. Super valuable tool to have in your belt when hacking +on `bindgen`! + +Happy bug hunting and test case reducing! + +[More information on using `creduce`.](https://embed.cs.utah.edu/creduce/using/) @@ -24,6 +24,7 @@ quasi_codegen = "0.20" [dependencies] cfg-if = "0.1.0" clang-sys = "0.8.0" +clap = "2" lazy_static = "0.1.*" libc = "0.2" log = "0.3" diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs index bdb3b292..55d3fdde 100755 --- a/src/bin/bindgen.rs +++ b/src/bin/bindgen.rs @@ -6,248 +6,14 @@ extern crate env_logger; #[macro_use] extern crate log; extern crate clang_sys; +extern crate clap; extern crate rustc_serialize; -use bindgen::{BindgenOptions, Bindings, LinkType, clang_version}; -use std::default::Default; +use bindgen::clang_version; use std::env; -use std::fs; -use std::io; -use std::path; -use std::process; -const USAGE: &'static str = " -Usage: - bindgen [options] \ - [--link=<lib>...] \ - [--static-link=<lib>...] \ - [--framework-link=<framework>...] \ - [--raw-line=<raw>...] \ - [--opaque-type=<type>...] \ - [--blacklist-type=<type>...] \ - [--whitelist-type=<type>...] \ - [--whitelist-function=<name>...] \ - [--whitelist-var=<name>...] \ - [--bitfield-enum=<name>...] \ - <input-header> \ - [-- <clang-args>...] - - bindgen (-h | --help) - -Options: - -h, --help Display this help message. - - -l=<lib>, --link=<lib> Link to a dynamic library, can be provided - multiple times. - - --static-link=<lib> Link to a static library, can be provided - multiple times. - - --framework-link=<framework> Link to a framework. - - -o=<output-rust-file> Write bindings to <output-rust-file> - (defaults to stdout) - - --builtins Output bindings for builtin definitions (for - example __builtin_va_list) - - --ignore-functions Don't generate bindings for functions and - methods. This is useful when you only care - about struct layouts. - - --ignore-methods Avoid generating all kind of methods. - - --enable-cxx-namespaces Enable support for C++ namespaces. - - --emit-clang-ast Output the ast (for debugging purposes) - - --use-msvc-mangling Handle MSVC C++ ABI mangling; requires that - target be set to (i686|x86_64)-pc-win32 - - --no-convert-floats Don't convert floats automatically to f32/f64. - - --raw-line=<raw> Add a raw line at the beginning of the output. - - --no-unstable-rust Avoid generating unstable rust. - - --use-core Use built-in types from core instead of std. - - --ctypes-prefix=<prefix> Use the given prefix before the raw types - instead of ::std::os::raw::. - - --opaque-type=<type> Mark a type as opaque. - - --blacklist-type=<type> Mark a type as hidden. - - --whitelist-type=<type> Whitelist the type. If this set or any other - of the whitelisting sets is not empty, then - all the non-whitelisted types (or dependant) - won't be generated. - - --whitelist-function=<regex> Whitelist all the free-standing functions - matching <regex>. Same behavior on emptyness - than the type whitelisting. - - --whitelist-var=<regex> Whitelist all the free-standing variables - matching <regex>. Same behavior on emptyness - than the type whitelisting. - - --bitfield-enum=<regex> Mark any enum whose name matches <regex> as a - set of bitfield flags instead of an - enumeration. - - --dummy-uses=<path> For testing purposes, generate a C/C++ file - containing dummy uses of all types defined in - the input header. - - <clang-args> Options other than stated above are passed - directly through to clang. -"; - -// FIXME(emilio): Replace this with docopt if/when they fix their exponential -// algorithm for argument parsing. -// -// FIXME(fitzgen): Switch from `BindgenOptions` to the non-deprecated `Builder`. -#[allow(deprecated)] -fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) { - let mut options = BindgenOptions::default(); - let mut dest_file = None; - let mut source_file = None; - - let mut iter = args.into_iter().skip(1); - loop { - let next = match iter.next() { - Some(arg) => arg, - _ => break, - }; - - match &*next { - "-h" | "--help" => { - println!("{}", USAGE); - process::exit(0); - } - "-l" | "--link" => { - let lib = iter.next().expect("--link needs an argument"); - options.links.push((lib, LinkType::Default)); - } - "--static-link" => { - let lib = iter.next().expect("--static-link needs an argument"); - options.links.push((lib, LinkType::Static)); - } - "--framework-link" => { - let lib = iter.next() - .expect("--framework-link needs an argument"); - options.links.push((lib, LinkType::Framework)); - } - "--raw-line" => { - let line = iter.next().expect("--raw-line needs an argument"); - options.raw_lines.push(line); - } - "--opaque-type" => { - let ty_canonical_name = iter.next() - .expect("--opaque-type expects a type"); - options.opaque_types.insert(ty_canonical_name); - } - "--blacklist-type" => { - let ty_canonical_name = iter.next() - .expect("--blacklist-type expects a type"); - options.hidden_types.insert(ty_canonical_name); - } - "--whitelist-type" => { - let ty_pat = iter.next() - .expect("--whitelist-type expects a type pattern"); - options.whitelisted_types.insert(&ty_pat); - } - "--whitelist-function" => { - let function_pat = iter.next() - .expect("--whitelist-function expects a pattern"); - options.whitelisted_functions.insert(&function_pat); - } - "--whitelist-var" => { - let var_pat = iter.next() - .expect("--whitelist-var expects a pattern"); - options.whitelisted_vars.insert(&var_pat); - } - "--bitfield-enum" => { - let enum_pat = iter.next() - .expect("--bitfield-enum expects a pattern"); - options.bitfield_enums.insert(&enum_pat); - } - "--" => { - while let Some(clang_arg) = iter.next() { - options.clang_args.push(clang_arg); - } - } - "--output" | "-o" => { - let out_name = iter.next().expect("-o expects a file name"); - dest_file = Some(out_name); - } - "--builtins" => { - options.builtins = true; - } - "--ignore-functions" => { - options.ignore_functions = true; - } - "--ignore-methods" => { - options.ignore_methods = true; - } - "--enable-cxx-namespaces" => { - options.enable_cxx_namespaces = true; - } - "--no-unstable-rust" => { - options.unstable_rust = false; - } - "--use-core" => { - options.use_core = true; - } - "--ctypes-prefix" => { - let prefix = iter.next() - .expect("--ctypes-prefix expects a prefix after it"); - options.ctypes_prefix = Some(prefix); - } - "--emit-clang-ast" => { - options.emit_ast = true; - } - "--no-convert-floats" => { - options.convert_floats = false; - } - "--use-msvc-mangling" => { - options.msvc_mangling = true; - } - "--dummy-uses" => { - let dummy_path = iter.next() - .expect("--dummy-uses expects a file path"); - options.dummy_uses = Some(dummy_path); - } - other if source_file.is_none() => { - source_file = Some(other.into()); - } - other => { - panic!("Unknown option: \"{}\"", other); - } - } - } - - if let Some(source_file) = source_file.take() { - options.clang_args.push(source_file); - options.input_header = options.clang_args.last().cloned(); - } else { - options.input_header = options.clang_args - .iter() - .find(|arg| arg.ends_with(".h") || arg.ends_with(".hpp")) - .cloned(); - } - - let out = if let Some(ref path_name) = dest_file { - let path = path::Path::new(path_name); - let file = fs::File::create(path).expect("Opening out file failed"); - Box::new(io::BufWriter::new(file)) as Box<io::Write> - } else { - Box::new(io::BufWriter::new(io::stdout())) as Box<io::Write> - }; - - (options, out) -} +mod options; +use options::builder_from_flags; pub fn main() { log::set_logger(|max_log_level| { @@ -304,14 +70,18 @@ pub fn main() { } } - let (options, out) = parse_args_or_exit(bind_args); - - let mut bindings = Bindings::generate(options, None) - .expect("Unable to generate bindings"); - - bindings.write_dummy_uses() - .expect("Unable to write dummy uses to file."); - - bindings.write(out) - .expect("Unable to write bindings to file."); + match builder_from_flags(env::args()) { + Ok((builder, output)) => { + let mut bindings = builder.generate() + .expect("Unable to generate bindings"); + bindings.write(output) + .expect("Unable to write output"); + bindings.write_dummy_uses() + .expect("Unable to write dummy uses to file."); + } + Err(error) => { + println!("{}", error); + std::process::exit(1); + } + }; } diff --git a/src/bin/options.rs b/src/bin/options.rs new file mode 100644 index 00000000..9f746fd0 --- /dev/null +++ b/src/bin/options.rs @@ -0,0 +1,269 @@ + + +use bindgen::{Builder, builder}; + +use clap::{App, Arg}; +use std::fs::File; +use std::io::{self, Error, ErrorKind}; + +/// Construct a new [`Builder`](./struct.Builder.html) from command line flags. +pub fn builder_from_flags<I>(args: I) + -> Result<(Builder, Box<io::Write>), io::Error> + where I: Iterator<Item = String>, +{ + let matches = App::new("bindgen") + .version(env!("CARGO_PKG_VERSION")) + .about("Generates Rust bindings from C/C++ headers.") + .usage("bindgen [FLAGS] [OPTIONS] <header> -- <clang-args>...") + .args(&[ + Arg::with_name("header") + .help("C or C++ header file") + .required(true), + Arg::with_name("bitfield-enum") + .long("bitfield-enum") + .help("Mark any enum whose name matches <regex> as a set of \ + bitfield flags instead of an enumeration.") + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("blacklist-type") + .long("blacklist-type") + .help("Mark a type as hidden.") + .value_name("type") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("builtins") + .long("builtins") + .help("Output bindings for builtin definitions, e.g. \ + __builtin_va_list."), + Arg::with_name("ctypes-prefix") + .long("ctypes-prefix") + .help("Use the given prefix before raw types instead of \ + ::std::os::raw.") + .value_name("prefix") + .takes_value(true), + // All positional arguments after the end of options marker, `--` + Arg::with_name("clang-args") + .multiple(true), + Arg::with_name("dummy-uses") + .long("dummy-uses") + .help("For testing purposes, generate a C/C++ file containing \ + dummy uses of all types defined in the input header.") + .takes_value(true), + Arg::with_name("emit-clang-ast") + .long("emit-clang-ast") + .help("Output the Clang AST for debugging purposes."), + Arg::with_name("enable-cxx-namespaces") + .long("enable-cxx-namespaces") + .help("Enable support for C++ namespaces."), + Arg::with_name("framework") + .long("framework-link") + .help("Link to framework.") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("ignore-functions") + .long("ignore-functions") + .help("Do not generate bindings for functions or methods. This \ + is useful when you only care about struct layouts."), + Arg::with_name("ignore-methods") + .long("ignore-methods") + .help("Do not generate bindings for methods."), + Arg::with_name("dynamic") + .short("l") + .long("link") + .help("Link to dynamic library.") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("no-convert-floats") + .long("no-convert-floats") + .help("Don't automatically convert floats to f32/f64."), + Arg::with_name("no-unstable-rust") + .long("no-unstable-rust") + .help("Do not generate unstable Rust code.") + .multiple(true), // FIXME: Pass legacy test suite + Arg::with_name("opaque-type") + .long("opaque-type") + .help("Mark a type as opaque.") + .value_name("type") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("output") + .short("o") + .long("output") + .help("Write Rust bindings to <output>.") + .takes_value(true), + Arg::with_name("raw-line") + .long("raw-line") + .help("Add a raw line of Rust code at the beginning of output.") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("static") + .long("static-link") + .help("Link to static library.") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("use-core") + .long("use-core") + .help("Use types from Rust core instead of std."), + Arg::with_name("use-msvc-mangling") + .long("use-msvc-mangling") + .help("MSVC C++ ABI mangling. DEPRECATED: Has no effect."), + Arg::with_name("whitelist-function") + .long("whitelist-function") + .help("Whitelist all the free-standing functions matching \ + <regex>. Other non-whitelisted functions will not be \ + generated.") + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("whitelist-type") + .long("whitelist-type") + .help("Whitelist the type. Other non-whitelisted types will \ + not be generated.") + .value_name("type") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("whitelist-var") + .long("whitelist-var") + .help("Whitelist all the free-standing variables matching \ + <regex>. Other non-whitelisted variables will not be \ + generated.") + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), + ]) // .args() + .get_matches_from(args); + + let mut builder = builder(); + + if let Some(header) = matches.value_of("header") { + builder = builder.header(header); + } else { + return Err(Error::new(ErrorKind::Other, "Header not found")); + } + + if let Some(bitfields) = matches.values_of("bitfield-enum") { + for regex in bitfields { + builder = builder.bitfield_enum(regex); + } + } + + if let Some(hidden_types) = matches.values_of("blacklist-type") { + for ty in hidden_types { + builder = builder.hide_type(ty); + } + } + + if matches.is_present("builtins") { + builder = builder.emit_builtins(); + } + + if let Some(prefix) = matches.value_of("ctypes-prefix") { + builder = builder.ctypes_prefix(prefix); + } + + if let Some(dummy) = matches.value_of("dummy-uses") { + builder = builder.dummy_uses(dummy); + } + + if let Some(links) = matches.values_of("dynamic") { + for library in links { + builder = builder.link(library); + } + } + + if matches.is_present("emit-clang-ast") { + builder = builder.emit_clang_ast(); + } + + if matches.is_present("enable-cxx-namespaces") { + builder = builder.enable_cxx_namespaces(); + } + + if let Some(links) = matches.values_of("framework") { + for framework in links { + builder = builder.link_framework(framework); + } + } + + if matches.is_present("ignore-functions") { + builder = builder.ignore_functions(); + } + + if matches.is_present("ignore-methods") { + builder = builder.ignore_methods(); + } + + if matches.is_present("no-unstable-rust") { + builder = builder.no_unstable_rust(); + } + + if matches.is_present("no-convert-floats") { + builder = builder.no_convert_floats(); + } + + if let Some(opaque_types) = matches.values_of("opaque-type") { + for ty in opaque_types { + builder = builder.opaque_type(ty); + } + } + + if let Some(lines) = matches.values_of("raw-line") { + for line in lines { + builder = builder.raw_line(line); + } + } + + if let Some(links) = matches.values_of("static") { + for library in links { + builder = builder.link_static(library); + } + } + + if matches.is_present("use-core") { + builder = builder.use_core(); + } + + if let Some(whitelist) = matches.values_of("whitelist-function") { + for regex in whitelist { + builder = builder.whitelisted_function(regex); + } + } + + if let Some(whitelist) = matches.values_of("whitelist-type") { + for regex in whitelist { + builder = builder.whitelisted_type(regex); + } + } + + if let Some(whitelist) = matches.values_of("whitelist-var") { + for regex in whitelist { + builder = builder.whitelisted_var(regex); + } + } + + if let Some(args) = matches.values_of("clang-args") { + for arg in args { + builder = builder.clang_arg(arg); + } + } + + let output = if let Some(path) = matches.value_of("output") { + let file = try!(File::create(path)); + Box::new(io::BufWriter::new(file)) as Box<io::Write> + } else { + Box::new(io::BufWriter::new(io::stdout())) as Box<io::Write> + }; + + Ok((builder, output)) +} diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 41e5c3d3..bd794a98 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -635,7 +635,7 @@ impl CompInfo { ci.has_vtable = cur.is_virtual_base(); } let type_id = - Item::from_ty(&cur.cur_type(), None, None, ctx) + Item::from_ty(&cur.cur_type(), Some(cur), None, ctx) .expect("BaseSpecifier"); ci.base_members.push(type_id); } @@ -763,6 +763,7 @@ impl CompInfo { CXCursor_UnionDecl => CompKind::Union, CXCursor_ClassDecl | CXCursor_StructDecl => CompKind::Struct, + CXCursor_CXXBaseSpecifier | CXCursor_ClassTemplatePartialSpecialization | CXCursor_ClassTemplate => { match cursor.template_kind() { diff --git a/src/ir/item.rs b/src/ir/item.rs index c6d80a08..691cfec2 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -1043,8 +1043,7 @@ impl ClangItemParser for Item { } } // If we have recursed into the AST all we know, and we still - // haven't found what we've got, let's - // just make a named type. + // haven't found what we've got, let's just make a named type. // // This is what happens with some template members, for example. // diff --git a/src/ir/ty.rs b/src/ir/ty.rs index 5a72eb22..4c2ef5d8 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -542,8 +542,56 @@ impl Type { } else if let Some(location) = location { match location.kind() { CXCursor_ClassTemplatePartialSpecialization | + CXCursor_CXXBaseSpecifier | CXCursor_ClassTemplate => { - name = location.spelling(); + if location.kind() == CXCursor_CXXBaseSpecifier { + // In the case we're parsing a base specifier + // inside an unexposed or invalid type, it means + // that we're parsing one of two things: + // + // * A template parameter. + // * A complex class that isn't exposed. + // + // This means, unfortunately, that there's no + // good way to differentiate between them. + // + // Probably we could try to look at the + // declaration and complicate more this logic, + // but we'll keep it simple... if it's a valid + // C++ identifier, we'll consider it as a + // template parameter. + // + // This is because: + // + // * We expect every other base that is a + // proper identifier (that is, a simple + // struct/union declaration), to be exposed, + // so this path can't be reached in that + // case. + // + // * Quite conveniently, complex base + // specifiers preserve their full names (that + // is: Foo<T> instead of Foo). We can take + // advantage of this. + // + // If we find some edge case where this doesn't + // work (which I guess is unlikely, see the + // different test cases[1][2][3][4]), we'd need + // to find more creative ways of differentiating + // these two cases. + // + // [1]: inherit_named.hpp + // [2]: forward-inherit-struct-with-fields.hpp + // [3]: forward-inherit-struct.hpp + // [4]: inherit-namespaced.hpp + if location.spelling() + .chars() + .all(|c| c.is_alphanumeric() || c == '_') { + return Err(ParseError::Recurse); + } + } else { + name = location.spelling(); + } let complex = CompInfo::from_ty(potential_id, ty, Some(location), diff --git a/src/ir/var.rs b/src/ir/var.rs index 047e8642..0e7df618 100644 --- a/src/ir/var.rs +++ b/src/ir/var.rs @@ -111,7 +111,9 @@ impl ClangSubItemParser for Var { EvalResult::Invalid => return Err(ParseError::Continue), EvalResult::Int(Wrapping(value)) => { - let kind = ctx.options().type_chooser.as_ref() + let kind = ctx.options() + .type_chooser + .as_ref() .and_then(|c| c.int_macro(&name, value)) .unwrap_or_else(|| { if value < 0 { @@ -222,6 +222,30 @@ impl Builder { self } + /// Emit Clang AST. + pub fn emit_clang_ast(mut self) -> Builder { + self.options.emit_ast = true; + self + } + + /// Enable C++ namespaces. + pub fn enable_cxx_namespaces(mut self) -> Builder { + self.options.enable_cxx_namespaces = true; + self + } + + /// Ignore functions. + pub fn ignore_functions(mut self) -> Builder { + self.options.ignore_functions = true; + self + } + + /// Ignore methods. + pub fn ignore_methods(mut self) -> Builder { + self.options.ignore_methods = true; + self + } + /// Avoid generating any unstable Rust in the generated bindings. pub fn no_unstable_rust(mut self) -> Builder { self.options.unstable_rust = false; diff --git a/tests/expectations/tests/forward-inherit-struct-with-fields.rs b/tests/expectations/tests/forward-inherit-struct-with-fields.rs new file mode 100644 index 00000000..84104971 --- /dev/null +++ b/tests/expectations/tests/forward-inherit-struct-with-fields.rs @@ -0,0 +1,17 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Rooted<T> { + pub _base: RootedBase<T>, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct RootedBase<T> { + pub foo: *mut T, + pub next: *mut Rooted<T>, +} diff --git a/tests/expectations/tests/forward-inherit-struct.rs b/tests/expectations/tests/forward-inherit-struct.rs new file mode 100644 index 00000000..e053adcd --- /dev/null +++ b/tests/expectations/tests/forward-inherit-struct.rs @@ -0,0 +1,18 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Rooted<T> { + pub _address: u8, + pub _phantom_0: ::std::marker::PhantomData<T>, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct RootedBase<T> { + pub _address: u8, + pub _phantom_0: ::std::marker::PhantomData<T>, +} diff --git a/tests/expectations/tests/inherit-namespaced.rs b/tests/expectations/tests/inherit-namespaced.rs new file mode 100644 index 00000000..a58058b0 --- /dev/null +++ b/tests/expectations/tests/inherit-namespaced.rs @@ -0,0 +1,18 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct RootedBase<T> { + pub _address: u8, + pub _phantom_0: ::std::marker::PhantomData<T>, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Rooted<T> { + pub _address: u8, + pub _phantom_0: ::std::marker::PhantomData<T>, +} diff --git a/tests/expectations/tests/multiple-inherit-empty-correct-layout.rs b/tests/expectations/tests/multiple-inherit-empty-correct-layout.rs new file mode 100644 index 00000000..5e9cf522 --- /dev/null +++ b/tests/expectations/tests/multiple-inherit-empty-correct-layout.rs @@ -0,0 +1,45 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Foo { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_Foo() { + assert_eq!(::std::mem::size_of::<Foo>() , 1usize); + assert_eq!(::std::mem::align_of::<Foo>() , 1usize); +} +impl Clone for Foo { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Bar { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_Bar() { + assert_eq!(::std::mem::size_of::<Bar>() , 1usize); + assert_eq!(::std::mem::align_of::<Bar>() , 1usize); +} +impl Clone for Bar { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Baz { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_Baz() { + assert_eq!(::std::mem::size_of::<Baz>() , 1usize); + assert_eq!(::std::mem::align_of::<Baz>() , 1usize); +} +impl Clone for Baz { + fn clone(&self) -> Self { *self } +} diff --git a/tests/headers/forward-inherit-struct-with-fields.hpp b/tests/headers/forward-inherit-struct-with-fields.hpp new file mode 100644 index 00000000..437fff5d --- /dev/null +++ b/tests/headers/forward-inherit-struct-with-fields.hpp @@ -0,0 +1,8 @@ +template <typename> class Rooted; +namespace js { + template <typename T> class RootedBase { + T* foo; + Rooted<T>* next; + }; +} +template <typename T> class Rooted : js::RootedBase<T> {}; diff --git a/tests/headers/forward-inherit-struct.hpp b/tests/headers/forward-inherit-struct.hpp new file mode 100644 index 00000000..ac7aef5e --- /dev/null +++ b/tests/headers/forward-inherit-struct.hpp @@ -0,0 +1,5 @@ +template <typename> class Rooted; +namespace js { + template <typename T> class RootedBase {}; +} +template <typename T> class Rooted : js::RootedBase<T> {}; diff --git a/tests/headers/inherit-namespaced.hpp b/tests/headers/inherit-namespaced.hpp new file mode 100644 index 00000000..61eafd5a --- /dev/null +++ b/tests/headers/inherit-namespaced.hpp @@ -0,0 +1,4 @@ +namespace js { + template <typename T> class RootedBase {}; +} +template <typename T> class Rooted : js::RootedBase<T> {}; diff --git a/tests/headers/multiple-inherit-empty-correct-layout.hpp b/tests/headers/multiple-inherit-empty-correct-layout.hpp new file mode 100644 index 00000000..1e2b133a --- /dev/null +++ b/tests/headers/multiple-inherit-empty-correct-layout.hpp @@ -0,0 +1,3 @@ +struct Foo {}; +struct Bar {}; +struct Baz : public Foo, public Bar {}; |