summaryrefslogtreecommitdiff
path: root/src/uses.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/uses.rs')
-rw-r--r--src/uses.rs102
1 files changed, 102 insertions, 0 deletions
diff --git a/src/uses.rs b/src/uses.rs
new file mode 100644
index 00000000..47f72da6
--- /dev/null
+++ b/src/uses.rs
@@ -0,0 +1,102 @@
+//! Take in our IR and output a C/C++ file with dummy uses of each IR type.
+//!
+//! Say that we had this C++ header, `header.hpp`:
+//!
+//! ```c++
+//! class Point {
+//! int x;
+//! int y;
+//! }
+//!
+//! enum Bar {
+//! THIS,
+//! THAT,
+//! OTHER
+//! }
+//! ```
+//!
+//! If we generated dummy uses for this header, we would get a `.cpp` file like
+//! this:
+//!
+//! ```c++
+//! #include "header.hpp"
+//!
+//! void dummy(Point*) {}
+//! void dummy(Bar*) {}
+//! ```
+//!
+//! This is useful because we can compile this `.cpp` file into an object file,
+//! and then compare its debugging information to the debugging information
+//! generated for our Rust bindings. These two sets of debugging information had
+//! better agree on the C/C++ types' physical layout, or else our bindings are
+//! incorrect!
+//!
+//! "But you still haven't explained why we have to generate the dummy uses" you
+//! complain. Well if the types are never used, then they are elided when the
+//! C/C++ compiler generates debugging information.
+
+use ir::context::BindgenContext;
+use ir::item::{Item, ItemAncestors, ItemCanonicalName};
+use std::io;
+
+// Like `canonical_path`, except we always take namespaces into account, ignore
+// the generated names of anonymous items, and return a `String`.
+//
+// TODO: Would it be easier to try and demangle the USR?
+fn namespaced_name(ctx: &BindgenContext, item: &Item) -> String {
+ let mut names: Vec<_> = item.ancestors(ctx)
+ .map(|id| ctx.resolve_item(id).canonical_name(ctx))
+ .filter(|name| !name.starts_with("_bindgen_"))
+ .collect();
+ names.reverse();
+ names.join("::")
+}
+
+/// Generate the dummy uses for all the items in the given context, and write
+/// the dummy uses to `dest`.
+pub fn generate_dummy_uses<W>(ctx: &mut BindgenContext,
+ mut dest: W)
+ -> io::Result<()>
+ where W: io::Write,
+{
+ ctx.gen(|ctx| {
+ let input_header = ctx.options()
+ .input_header
+ .as_ref()
+ .expect("Should not generate dummy uses without an input header");
+
+ try!(writeln!(dest, "/* automatically generated by rust-bindgen */"));
+ try!(writeln!(dest, ""));
+ try!(writeln!(dest, "#include \"{}\"", input_header));
+ try!(writeln!(dest, ""));
+
+ let type_items = ctx.whitelisted_items()
+ .map(|id| ctx.resolve_item(id))
+ .filter(|item| {
+ // We only want type items.
+ if let Some(ty) = item.kind().as_type() {
+ // However, we don't want anonymous types, as we can't
+ // generate dummy uses for them.
+ ty.name().is_some() &&
+ // Nor do we want builtin types or named template type
+ // arguments. Again, we can't generate dummy uses for
+ // these.
+ !ty.is_builtin_or_named() &&
+ // And finally, we won't be creating any dummy
+ // specializations, so ignore template declarations and
+ // partial specializations.
+ item.applicable_template_args(ctx).is_empty()
+ } else {
+ false
+ }
+ })
+ .map(|item| namespaced_name(ctx, item))
+ .enumerate();
+
+ for (idx, name) in type_items {
+ try!(writeln!(dest, "void dummy{}({}*) {{ }}", idx, name));
+ }
+
+ Ok(())
+ })
+}