diff options
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | src/bin/bindgen.rs | 21 | ||||
-rwxr-xr-x | src/clang.rs | 84 | ||||
-rw-r--r-- | src/ir/comp.rs | 21 | ||||
-rw-r--r-- | src/ir/context.rs | 57 | ||||
-rw-r--r-- | src/ir/enum_ty.rs | 2 | ||||
-rw-r--r-- | src/ir/function.rs | 4 | ||||
-rw-r--r-- | src/ir/item.rs | 38 | ||||
-rw-r--r-- | src/ir/module.rs | 4 | ||||
-rw-r--r-- | src/ir/ty.rs | 6 | ||||
-rw-r--r-- | src/ir/var.rs | 2 | ||||
-rwxr-xr-x | src/lib.rs | 45 | ||||
-rw-r--r-- | tests/expectations/replace_template_alias.rs | 15 | ||||
-rw-r--r-- | tests/headers/replace_template_alias.hpp | 23 |
14 files changed, 214 insertions, 110 deletions
@@ -149,4 +149,4 @@ This mode isn't actively maintained, so no promises are made around it. Check out the upstream documentation for info about how it *should* work. [sm-script]: https://github.com/servo/rust-mozjs/blob/master/etc/bindings.sh -[stylo-scripts]: https://github.com/servo/servo/tree/master/ports/geckolib/gecko_bindings/tools +[stylo-scripts]: https://github.com/servo/servo/tree/master/components/style/binding_tools diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs index 17385d85..c906efee 100755 --- a/src/bin/bindgen.rs +++ b/src/bin/bindgen.rs @@ -8,7 +8,7 @@ extern crate log; extern crate clang_sys; extern crate rustc_serialize; -use bindgen::{BindgenOptions, Bindings, LinkType}; +use bindgen::{BindgenOptions, Bindings, ClangVersion, LinkType, clang_version}; use std::default::Default; use std::env; use std::fs; @@ -232,6 +232,25 @@ pub fn main() { let mut bind_args: Vec<_> = env::args().collect(); + let version = clang_version(); + let expected_version = if cfg!(feature = "llvm_stable") { + (3, 8) + } else { + (3, 9) + }; + + info!("Clang Version: {}", version.full); + + match version.parsed { + None => warn!("Couldn't parse libclang version"), + Some(version) if version != expected_version => { + error!("Using clang {:?}, expected {:?}", + version, + expected_version); + } + _ => {} + } + if let Some(clang) = clang_sys::support::Clang::find(None) { let has_clang_args = bind_args.iter().rposition(|arg| *arg == "--").is_some(); diff --git a/src/clang.rs b/src/clang.rs index 7f81c379..94e74d87 100755 --- a/src/clang.rs +++ b/src/clang.rs @@ -31,14 +31,6 @@ impl fmt::Debug for Cursor { } } -/// A cursor visitor function. -/// -/// The first argument is the AST node currently being visited. The second -/// argument is the parent of the AST node currently being visited. The return -/// value informs how traversal should proceed. -pub type CursorVisitor<'s> = for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) - -> Enum_CXChildVisitResult + 's; - impl Cursor { /// Get the Unified Symbol Resolution for this cursor's referent, if /// available. @@ -190,7 +182,7 @@ impl Cursor { /// Is the referent a template specialization? pub fn is_template(&self) -> bool { - self.specialized().is_valid() + self.specialized().is_some() } /// Is the referent a fully specialized template specialization without any @@ -257,11 +249,13 @@ impl Cursor { /// Given that this cursor's referent is a reference to another type, or is /// a declaration, get the cursor pointing to the referenced type or type of /// the declared thing. - pub fn definition(&self) -> Cursor { + pub fn definition(&self) -> Option<Cursor> { unsafe { - Cursor { + let ret = Cursor { x: clang_getCursorDefinition(self.x), - } + }; + + if ret.is_valid() { Some(ret) } else { None } } } @@ -290,11 +284,12 @@ impl Cursor { /// Given that this cursor points to a template specialization, get a cursor /// pointing to the template definition that is being specialized. - pub fn specialized(&self) -> Cursor { + pub fn specialized(&self) -> Option<Cursor> { unsafe { - Cursor { + let ret = Cursor { x: clang_getSpecializedCursorTemplate(self.x), - } + }; + if ret.is_valid() { Some(ret) } else { None } } } @@ -306,21 +301,14 @@ impl Cursor { /// Traverse this cursor's referent and its children. /// - /// Call the given function on each AST node traversed. See `CursorVisitor` - /// for details on arguments passed to the function and how its return value - /// is interpreted. - pub fn visit<F>(&self, func: F) - where F: for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) - -> Enum_CXChildVisitResult, + /// Call the given function on each AST node traversed. + pub fn visit<Visitor>(&self, mut visitor: Visitor) + where Visitor: FnMut(Cursor) -> Enum_CXChildVisitResult, { - let mut data: Box<CursorVisitor> = Box::new(func); - let opt_visit = - Some(visit_children as extern "C" fn(CXCursor, - CXCursor, - CXClientData) - -> Enum_CXChildVisitResult); unsafe { - clang_visitChildren(self.x, opt_visit, mem::transmute(&mut data)); + clang_visitChildren(self.x, + Some(visit_children::<Visitor>), + mem::transmute(&mut visitor)); } } @@ -415,16 +403,6 @@ impl Cursor { } /// Given that this cursor's referent is a function/method call or - /// declaration, return a cursor to its return type. - pub fn ret_type(&self) -> Type { - unsafe { - Type { - x: clang_getCursorResultType(self.x), - } - } - } - - /// Given that this cursor's referent is a function/method call or /// declaration, return the number of arguments it takes. /// /// Returns -1 if the cursor's referent is not a function/method call or @@ -484,17 +462,18 @@ impl Cursor { } } -extern "C" fn visit_children(cur: CXCursor, - parent: CXCursor, - data: CXClientData) - -> Enum_CXChildVisitResult { - let func: &mut Box<CursorVisitor> = unsafe { mem::transmute(data) }; - (*func)(&Cursor { - x: cur, - }, - &Cursor { - x: parent, - }) +extern "C" fn visit_children<Visitor>(cur: CXCursor, + _parent: CXCursor, + data: CXClientData) + -> Enum_CXChildVisitResult + where Visitor: FnMut(Cursor) -> Enum_CXChildVisitResult, +{ + let func: &mut Visitor = unsafe { mem::transmute(data) }; + let child = Cursor { + x: cur, + }; + + (*func)(child) } impl PartialEq for Cursor { @@ -1184,7 +1163,12 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> Enum_CXVisitorResult { kind_to_str(c.kind()), c.spelling(), type_to_str(ct))); - c.visit(|s, _: &Cursor| ast_dump(s, depth + 1)); + c.visit(|s| ast_dump(&s, depth + 1)); print_indent(depth, ")"); CXChildVisit_Continue } + +/// Try to extract the clang version to a string +pub fn extract_clang_version() -> String { + unsafe { clang_getClangVersion().into() } +} diff --git a/src/ir/comp.rs b/src/ir/comp.rs index d55c24ca..41e5c3d3 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -512,10 +512,11 @@ impl CompInfo { } }; - ci.ref_template = Item::parse(cursor.specialized(), None, ctx).ok(); + ci.ref_template = cursor.specialized() + .and_then(|c| Item::parse(c, None, ctx).ok()); let mut maybe_anonymous_struct_field = None; - cursor.visit(|cur, _other| { + cursor.visit(|cur| { if cur.kind() != CXCursor_FieldDecl { if let Some((ty, _)) = maybe_anonymous_struct_field { let field = Field::new(None, ty, None, None, None, false); @@ -529,7 +530,7 @@ impl CompInfo { match maybe_anonymous_struct_field.take() { Some((ty, clang_ty)) => { let mut used = false; - cur.visit(|child, _| { + cur.visit(|child| { if child.cur_type() == clang_ty { used = true; } @@ -550,12 +551,12 @@ impl CompInfo { let bit_width = cur.bit_width(); let field_type = Item::from_ty_or_ref(cur.cur_type(), - Some(*cur), + Some(cur), Some(potential_id), ctx); let comment = cur.raw_comment(); - let annotations = Annotations::new(cur); + let annotations = Annotations::new(&cur); let name = cur.spelling(); let is_mutable = cursor.is_mutable_field(); @@ -575,7 +576,7 @@ impl CompInfo { ci.fields.push(field); // No we look for things like attributes and stuff. - cur.visit(|cur, _| { + cur.visit(|cur| { if cur.kind() == CXCursor_UnexposedAttr { ci.found_unknown_attr = true; } @@ -593,7 +594,7 @@ impl CompInfo { CXCursor_UnionDecl | CXCursor_ClassTemplate | CXCursor_ClassDecl => { - let inner = Item::parse(*cur, Some(potential_id), ctx) + let inner = Item::parse(cur, Some(potential_id), ctx) .expect("Inner ClassDecl"); if !ci.inner_types.contains(&inner) { ci.inner_types.push(inner); @@ -619,7 +620,7 @@ impl CompInfo { } let default_type = Item::from_ty(&cur.cur_type(), - Some(*cur), + Some(cur), Some(potential_id), ctx) .ok(); @@ -687,7 +688,7 @@ impl CompInfo { // NB: This gets us an owned `Function`, not a // `FunctionSig`. let method_signature = - Item::parse(*cur, Some(potential_id), ctx) + Item::parse(cur, Some(potential_id), ctx) .expect("CXXMethod"); let is_const = cur.method_is_const(); @@ -726,7 +727,7 @@ impl CompInfo { return CXChildVisit_Continue; } - let item = Item::parse(*cur, Some(potential_id), ctx) + let item = Item::parse(cur, Some(potential_id), ctx) .expect("VarDecl"); ci.inner_vars.push(item); } diff --git a/src/ir/context.rs b/src/ir/context.rs index beccc514..fc06375c 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -4,7 +4,7 @@ use BindgenOptions; use clang::{self, Cursor}; use parse::ClangItemParser; use std::borrow::{Borrow, Cow}; -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, HashSet, hash_map}; use std::collections::btree_map::{self, BTreeMap}; use std::fmt; use super::int::IntKind; @@ -308,6 +308,7 @@ impl<'ctx> BindgenContext<'ctx> { /// `replaces="SomeType"` annotation with the replacement type. fn process_replacements(&mut self) { if self.replacements.is_empty() { + debug!("No replacements to process"); return; } @@ -319,23 +320,27 @@ impl<'ctx> BindgenContext<'ctx> { let mut replacements = vec![]; for (id, item) in self.items.iter() { + // Calls to `canonical_name` are expensive, so eagerly filter out + // items that cannot be replaced. let ty = match item.kind().as_type() { Some(ty) => ty, None => continue, }; - // canonical_name calls are expensive. - let ci = match ty.as_comp() { - Some(ci) => ci, - None => continue, - }; - - if ci.is_template_specialization() { - continue; + match *ty.kind() { + TypeKind::Comp(ref ci) if !ci.is_template_specialization() => {} + TypeKind::TemplateAlias(_, _) | + TypeKind::Alias(_, _) => {} + _ => continue, } - if let Some(replacement) = self.replacements - .get(&item.canonical_name(self)) { + let name = item.real_canonical_name(self, + self.options() + .enable_cxx_namespaces, + true); + let replacement = self.replacements.get(&name); + + if let Some(replacement) = replacement { if replacement != id { // We set this just after parsing the annotation. It's // very unlikely, but this can happen. @@ -347,6 +352,8 @@ impl<'ctx> BindgenContext<'ctx> { } for (id, replacement) in replacements { + debug!("Replacing {:?} with {:?}", id, replacement); + let mut item = self.items.get_mut(&id).unwrap(); *item.kind_mut().as_type_mut().unwrap().kind_mut() = TypeKind::ResolvedTypeRef(replacement); @@ -492,15 +499,21 @@ impl<'ctx> BindgenContext<'ctx> { use clangll::*; let mut args = vec![]; let mut found_invalid_template_ref = false; - location.visit(|c, _| { + location.visit(|c| { if c.kind() == CXCursor_TemplateRef && c.cur_type().kind() == CXType_Invalid { found_invalid_template_ref = true; } if c.kind() == CXCursor_TypeRef { + // The `with_id` id will potentially end up unused if we give up + // on this type (for example, its a tricky partial template + // specialization), so if we pass `with_id` as the parent, it is + // potentially a dangling reference. Instead, use the canonical + // template declaration as the parent. It is already parsed and + // has a known-resolvable `ItemId`. let new_ty = Item::from_ty_or_ref(c.cur_type(), - Some(*c), - Some(with_id), + Some(c), + Some(wrapping), self); args.push(new_ty); } @@ -725,7 +738,21 @@ impl<'ctx> BindgenContext<'ctx> { /// Replacement types are declared using the `replaces="xxx"` annotation, /// and implies that the original type is hidden. pub fn replace(&mut self, name: &str, potential_ty: ItemId) { - self.replacements.insert(name.into(), potential_ty); + match self.replacements.entry(name.into()) { + hash_map::Entry::Vacant(entry) => { + debug!("Defining replacement for {} as {:?}", + name, + potential_ty); + entry.insert(potential_ty); + } + hash_map::Entry::Occupied(occupied) => { + warn!("Replacement for {} already defined as {:?}; \ + ignoring duplicate replacement definition as {:?}}}", + name, + occupied.get(), + potential_ty); + } + } } /// Is the item with the given `name` hidden? Or is the item with the given diff --git a/src/ir/enum_ty.rs b/src/ir/enum_ty.rs index fd7dbb45..e78184e7 100644 --- a/src/ir/enum_ty.rs +++ b/src/ir/enum_ty.rs @@ -70,7 +70,7 @@ impl Enum { None => true, }; - declaration.visit(|cursor, _| { + declaration.visit(|cursor| { if cursor.kind() == CXCursor_EnumConstantDecl { let name = cursor.spelling(); let comment = cursor.raw_comment(); diff --git a/src/ir/function.rs b/src/ir/function.rs index c2e6ffd0..163e3039 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -167,10 +167,10 @@ impl FunctionSig { // For non-CXCursor_FunctionDecl, visiting the cursor's children // is the only reliable way to get parameter names. let mut args = vec![]; - cursor.visit(|c, _| { + cursor.visit(|c| { if c.kind() == CXCursor_ParmDecl { let ty = - Item::from_ty(&c.cur_type(), Some(*c), None, ctx) + Item::from_ty(&c.cur_type(), Some(c), None, ctx) .expect("ParmDecl?"); let name = c.spelling(); let name = diff --git a/src/ir/item.rs b/src/ir/item.rs index 690f4222..a07ee1f3 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -49,7 +49,9 @@ pub trait ItemCanonicalPath { /// up to (but not including) the implicit root module. pub trait ItemAncestors { /// Get an iterable over this item's ancestors. - fn ancestors<'a, 'b>(&self, ctx: &'a BindgenContext<'b>) -> ItemAncestorsIter<'a, 'b>; + fn ancestors<'a, 'b>(&self, + ctx: &'a BindgenContext<'b>) + -> ItemAncestorsIter<'a, 'b>; } /// An iterator over an item and its ancestors. @@ -86,7 +88,8 @@ impl ItemId { /// Allocate the next `ItemId`. pub fn next() -> Self { static NEXT_ITEM_ID: AtomicUsize = ATOMIC_USIZE_INIT; - ItemId(NEXT_ITEM_ID.fetch_add(1, Ordering::Relaxed)) + let next_id = NEXT_ITEM_ID.fetch_add(1, Ordering::Relaxed); + ItemId(next_id) } } @@ -539,11 +542,11 @@ impl Item { /// /// This name should be derived from the immutable state contained in the /// type and the parent chain, since it should be consistent. - fn real_canonical_name(&self, - ctx: &BindgenContext, - count_namespaces: bool, - for_name_checking: bool) - -> String { + pub fn real_canonical_name(&self, + ctx: &BindgenContext, + count_namespaces: bool, + for_name_checking: bool) + -> String { let base_name = match *self.kind() { ItemKind::Type(ref ty) => { match *ty.kind() { @@ -799,12 +802,7 @@ impl ClangItemParser for Item { // Types are sort of special, so to avoid parsing template classes // twice, handle them separately. { - let definition = cursor.definition(); - let applicable_cursor = if definition.is_valid() { - definition - } else { - cursor - }; + let applicable_cursor = cursor.definition().unwrap_or(cursor); match Self::from_ty(&applicable_cursor.cur_type(), Some(applicable_cursor), parent_id, @@ -823,6 +821,9 @@ impl ClangItemParser for Item { // too noisy about this. match cursor.kind() { CXCursor_MacroDefinition | + CXCursor_MacroExpansion | + CXCursor_UsingDeclaration | + CXCursor_StaticAssert | CXCursor_InclusionDirective => { debug!("Unhandled cursor kind {:?}: {:?}", cursor.kind(), @@ -935,12 +936,7 @@ impl ClangItemParser for Item { let decl = { let decl = ty.declaration(); - let definition = decl.definition(); - if definition.is_valid() { - definition - } else { - decl - } + decl.definition().unwrap_or(decl) }; let comment = decl.raw_comment() @@ -1016,11 +1012,11 @@ impl ClangItemParser for Item { assert_eq!(popped_decl, declaration_to_look_for); } - location.visit(|cur, _other| { + location.visit(|cur| { use clangll::*; result = Item::from_ty_with_id(id, ty, - Some(*cur), + Some(cur), parent_id, ctx); match result { diff --git a/src/ir/module.rs b/src/ir/module.rs index c582d3eb..42175b92 100644 --- a/src/ir/module.rs +++ b/src/ir/module.rs @@ -49,8 +49,8 @@ impl ClangSubItemParser for Module { CXCursor_Namespace => { let module_id = ctx.module(cursor); ctx.with_module(module_id, |ctx, children| { - cursor.visit(|cursor, _| { - parse_one(ctx, *cursor, Some(module_id), children) + cursor.visit(|cursor| { + parse_one(ctx, cursor, Some(module_id), children) }) }); diff --git a/src/ir/ty.rs b/src/ir/ty.rs index 4d26cdff..81212029 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -547,14 +547,14 @@ impl Type { let mut inner = Err(ParseError::Continue); let mut args = vec![]; - location.visit(|cur, _| { + location.visit(|cur| { match cur.kind() { CXCursor_TypeAliasDecl => { debug_assert!(cur.cur_type().kind() == CXType_Typedef); inner = Item::from_ty(&cur.cur_type(), - Some(*cur), + Some(cur), Some(potential_id), ctx); } @@ -567,7 +567,7 @@ impl Type { let default_type = Item::from_ty(&cur.cur_type(), - Some(*cur), + Some(cur), Some(potential_id), ctx) .ok(); diff --git a/src/ir/var.rs b/src/ir/var.rs index 216d8185..33e56242 100644 --- a/src/ir/var.rs +++ b/src/ir/var.rs @@ -203,7 +203,7 @@ fn get_integer_literal_from_cursor(cursor: &clang::Cursor, -> Option<i64> { use clangll::*; let mut value = None; - cursor.visit(|c, _| { + cursor.visit(|c| { match c.kind() { CXCursor_IntegerLiteral | CXCursor_UnaryOperator => { @@ -461,7 +461,7 @@ pub fn parse_one(ctx: &mut BindgenContext, Ok(id) => children.push(id), Err(ParseError::Continue) => {} Err(ParseError::Recurse) => { - cursor.visit(|child, _| parse_one(ctx, *child, parent, children)); + cursor.visit(|child| parse_one(ctx, child, parent, children)); } } CXChildVisit_Continue @@ -480,14 +480,53 @@ fn parse(context: &mut BindgenContext) { let cursor = context.translation_unit().cursor(); if context.options().emit_ast { - cursor.visit(|cur, _| clang::ast_dump(cur, 0)); + cursor.visit(|cur| clang::ast_dump(&cur, 0)); } let root = context.root_module(); context.with_module(root, |context, children| { - cursor.visit(|cursor, _| parse_one(context, *cursor, None, children)) + cursor.visit(|cursor| parse_one(context, cursor, None, children)) }); assert!(context.current_module() == context.root_module(), "How did this happen?"); } + +/// Extracted Clang version data +#[derive(Debug)] +pub struct ClangVersion { + /// Major and minor semvar, if parsing was successful + pub parsed: Option<(u32, u32)>, + /// full version string + pub full: String, +} + +/// Get the major and the minor semvar numbers of Clang's version +pub fn clang_version() -> ClangVersion { + let raw_v: String = clang::extract_clang_version(); + let split_v: Option<Vec<&str>> = raw_v.split_whitespace() + .nth(2) + .map(|v| v.split('.').collect()); + match split_v { + Some(v) => { + if v.len() >= 2 { + let maybe_major = v[0].parse::<u32>(); + let maybe_minor = v[1].parse::<u32>(); + match (maybe_major, maybe_minor) { + (Ok(major), Ok(minor)) => { + return ClangVersion { + parsed: Some((major, minor)), + full: raw_v.clone(), + } + } + _ => {} + } + } + } + None => {} + }; + ClangVersion { + parsed: None, + full: raw_v.clone(), + } +} diff --git a/tests/expectations/replace_template_alias.rs b/tests/expectations/replace_template_alias.rs new file mode 100644 index 00000000..61a2fbcc --- /dev/null +++ b/tests/expectations/replace_template_alias.rs @@ -0,0 +1,15 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Rooted<T> { + pub ptr: MaybeWrapped<T>, +} +/// But the replacement type does use T! +/// +/// <div rustbindgen replaces="MaybeWrapped" /> +pub type MaybeWrapped<T> = T; diff --git a/tests/headers/replace_template_alias.hpp b/tests/headers/replace_template_alias.hpp new file mode 100644 index 00000000..6ceae4e5 --- /dev/null +++ b/tests/headers/replace_template_alias.hpp @@ -0,0 +1,23 @@ +// bindgen-flags: -- --std=c++14 + +namespace JS { +namespace detail { + +/// Notice how this doesn't use T. +template <typename T> +using MaybeWrapped = int; + +} + +template <typename T> +class Rooted { + detail::MaybeWrapped<T> ptr; +}; + +} + +/// But the replacement type does use T! +/// +/// <div rustbindgen replaces="MaybeWrapped" /> +template <typename T> +using replaces_MaybeWrapped = T; |