summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md86
-rw-r--r--Cargo.toml1
-rwxr-xr-xsrc/bin/bindgen.rs266
-rw-r--r--src/bin/options.rs269
-rw-r--r--src/ir/comp.rs3
-rw-r--r--src/ir/item.rs3
-rw-r--r--src/ir/ty.rs50
-rw-r--r--src/ir/var.rs4
-rwxr-xr-xsrc/lib.rs24
-rw-r--r--tests/expectations/tests/forward-inherit-struct-with-fields.rs17
-rw-r--r--tests/expectations/tests/forward-inherit-struct.rs18
-rw-r--r--tests/expectations/tests/inherit-namespaced.rs18
-rw-r--r--tests/expectations/tests/multiple-inherit-empty-correct-layout.rs45
-rw-r--r--tests/headers/forward-inherit-struct-with-fields.hpp8
-rw-r--r--tests/headers/forward-inherit-struct.hpp5
-rw-r--r--tests/headers/inherit-namespaced.hpp4
-rw-r--r--tests/headers/multiple-inherit-empty-correct-layout.hpp3
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/)
diff --git a/Cargo.toml b/Cargo.toml
index 97bcb82f..92090226 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 {
diff --git a/src/lib.rs b/src/lib.rs
index 8914d72d..db87eb22 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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 {};