| //! Bindgen's core intermediate representation type. |
| |
| use super::super::codegen::{EnumVariation, CONSTIFIED_ENUM_MODULE_REPR_NAME}; |
| use super::analysis::{HasVtable, HasVtableResult, Sizedness, SizednessResult}; |
| use super::annotations::Annotations; |
| use super::comp::{CompKind, MethodKind}; |
| use super::context::{BindgenContext, ItemId, PartialType, TypeId}; |
| use super::derive::{ |
| CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, |
| CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd, |
| }; |
| use super::dot::DotAttributes; |
| use super::function::{Function, FunctionKind}; |
| use super::item_kind::ItemKind; |
| use super::layout::Opaque; |
| use super::module::Module; |
| use super::template::{AsTemplateParam, TemplateParameters}; |
| use super::traversal::{EdgeKind, Trace, Tracer}; |
| use super::ty::{Type, TypeKind}; |
| use crate::clang; |
| use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; |
| |
| use lazycell::LazyCell; |
| |
| use std::cell::Cell; |
| use std::collections::BTreeSet; |
| use std::fmt::Write; |
| use std::io; |
| use std::iter; |
| |
| /// A trait to get the canonical name from an item. |
| /// |
| /// This is the trait that will eventually isolate all the logic related to name |
| /// mangling and that kind of stuff. |
| /// |
| /// This assumes no nested paths, at some point I'll have to make it a more |
| /// complex thing. |
| /// |
| /// This name is required to be safe for Rust, that is, is not expected to |
| /// return any rust keyword from here. |
| pub(crate) trait ItemCanonicalName { |
| /// Get the canonical name for this item. |
| fn canonical_name(&self, ctx: &BindgenContext) -> String; |
| } |
| |
| /// The same, but specifies the path that needs to be followed to reach an item. |
| /// |
| /// To contrast with canonical_name, here's an example: |
| /// |
| /// ```c++ |
| /// namespace foo { |
| /// const BAR = 3; |
| /// } |
| /// ``` |
| /// |
| /// For bar, the canonical path is `vec!["foo", "BAR"]`, while the canonical |
| /// name is just `"BAR"`. |
| pub(crate) trait ItemCanonicalPath { |
| /// Get the namespace-aware canonical path for this item. This means that if |
| /// namespaces are disabled, you'll get a single item, and otherwise you get |
| /// the whole path. |
| fn namespace_aware_canonical_path( |
| &self, |
| ctx: &BindgenContext, |
| ) -> Vec<String>; |
| |
| /// Get the canonical path for this item. |
| fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String>; |
| } |
| |
| /// A trait for determining if some IR thing is opaque or not. |
| pub(crate) trait IsOpaque { |
| /// Extra context the IR thing needs to determine if it is opaque or not. |
| type Extra; |
| |
| /// Returns `true` if the thing is opaque, and `false` otherwise. |
| /// |
| /// May only be called when `ctx` is in the codegen phase. |
| fn is_opaque(&self, ctx: &BindgenContext, extra: &Self::Extra) -> bool; |
| } |
| |
| /// A trait for determining if some IR thing has type parameter in array or not. |
| pub(crate) trait HasTypeParamInArray { |
| /// Returns `true` if the thing has Array, and `false` otherwise. |
| fn has_type_param_in_array(&self, ctx: &BindgenContext) -> bool; |
| } |
| |
| /// A trait for determining if some IR thing has float or not. |
| pub(crate) trait HasFloat { |
| /// Returns `true` if the thing has float, and `false` otherwise. |
| fn has_float(&self, ctx: &BindgenContext) -> bool; |
| } |
| |
| /// A trait for iterating over an item and its parents and up its ancestor chain |
| /// up to (but not including) the implicit root module. |
| pub(crate) trait ItemAncestors { |
| /// Get an iterable over this item's ancestors. |
| fn ancestors<'a>(&self, ctx: &'a BindgenContext) -> ItemAncestorsIter<'a>; |
| } |
| |
| #[cfg(feature = "__testing_only_extra_assertions")] |
| type DebugOnlyItemSet = ItemSet; |
| |
| #[cfg(not(feature = "__testing_only_extra_assertions"))] |
| struct DebugOnlyItemSet; |
| |
| #[cfg(not(feature = "__testing_only_extra_assertions"))] |
| impl DebugOnlyItemSet { |
| fn new() -> Self { |
| DebugOnlyItemSet |
| } |
| |
| fn contains(&self, _id: &ItemId) -> bool { |
| false |
| } |
| |
| fn insert(&mut self, _id: ItemId) {} |
| } |
| |
| /// An iterator over an item and its ancestors. |
| pub(crate) struct ItemAncestorsIter<'a> { |
| item: ItemId, |
| ctx: &'a BindgenContext, |
| seen: DebugOnlyItemSet, |
| } |
| |
| impl<'a> ItemAncestorsIter<'a> { |
| fn new<Id: Into<ItemId>>(ctx: &'a BindgenContext, id: Id) -> Self { |
| ItemAncestorsIter { |
| item: id.into(), |
| ctx, |
| seen: DebugOnlyItemSet::new(), |
| } |
| } |
| } |
| |
| impl<'a> Iterator for ItemAncestorsIter<'a> { |
| type Item = ItemId; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| let item = self.ctx.resolve_item(self.item); |
| |
| if item.parent_id() == self.item { |
| None |
| } else { |
| self.item = item.parent_id(); |
| |
| extra_assert!(!self.seen.contains(&item.id())); |
| self.seen.insert(item.id()); |
| |
| Some(item.id()) |
| } |
| } |
| } |
| |
| impl<T> AsTemplateParam for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| type Extra = (); |
| |
| fn as_template_param( |
| &self, |
| ctx: &BindgenContext, |
| _: &(), |
| ) -> Option<TypeId> { |
| ctx.resolve_item((*self).into()).as_template_param(ctx, &()) |
| } |
| } |
| |
| impl AsTemplateParam for Item { |
| type Extra = (); |
| |
| fn as_template_param( |
| &self, |
| ctx: &BindgenContext, |
| _: &(), |
| ) -> Option<TypeId> { |
| self.kind.as_template_param(ctx, self) |
| } |
| } |
| |
| impl AsTemplateParam for ItemKind { |
| type Extra = Item; |
| |
| fn as_template_param( |
| &self, |
| ctx: &BindgenContext, |
| item: &Item, |
| ) -> Option<TypeId> { |
| match *self { |
| ItemKind::Type(ref ty) => ty.as_template_param(ctx, item), |
| ItemKind::Module(..) | |
| ItemKind::Function(..) | |
| ItemKind::Var(..) => None, |
| } |
| } |
| } |
| |
| impl<T> ItemCanonicalName for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn canonical_name(&self, ctx: &BindgenContext) -> String { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.resolve_item(*self).canonical_name(ctx) |
| } |
| } |
| |
| impl<T> ItemCanonicalPath for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn namespace_aware_canonical_path( |
| &self, |
| ctx: &BindgenContext, |
| ) -> Vec<String> { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.resolve_item(*self).namespace_aware_canonical_path(ctx) |
| } |
| |
| fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String> { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.resolve_item(*self).canonical_path(ctx) |
| } |
| } |
| |
| impl<T> ItemAncestors for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn ancestors<'a>(&self, ctx: &'a BindgenContext) -> ItemAncestorsIter<'a> { |
| ItemAncestorsIter::new(ctx, *self) |
| } |
| } |
| |
| impl ItemAncestors for Item { |
| fn ancestors<'a>(&self, ctx: &'a BindgenContext) -> ItemAncestorsIter<'a> { |
| self.id().ancestors(ctx) |
| } |
| } |
| |
| impl<Id> Trace for Id |
| where |
| Id: Copy + Into<ItemId>, |
| { |
| type Extra = (); |
| |
| fn trace<T>(&self, ctx: &BindgenContext, tracer: &mut T, extra: &()) |
| where |
| T: Tracer, |
| { |
| ctx.resolve_item(*self).trace(ctx, tracer, extra); |
| } |
| } |
| |
| impl Trace for Item { |
| type Extra = (); |
| |
| fn trace<T>(&self, ctx: &BindgenContext, tracer: &mut T, _extra: &()) |
| where |
| T: Tracer, |
| { |
| // Even if this item is blocklisted/hidden, we want to trace it. It is |
| // traversal iterators' consumers' responsibility to filter items as |
| // needed. Generally, this filtering happens in the implementation of |
| // `Iterator` for `allowlistedItems`. Fully tracing blocklisted items is |
| // necessary for things like the template parameter usage analysis to |
| // function correctly. |
| |
| match *self.kind() { |
| ItemKind::Type(ref ty) => { |
| // There are some types, like resolved type references, where we |
| // don't want to stop collecting types even though they may be |
| // opaque. |
| if ty.should_be_traced_unconditionally() || |
| !self.is_opaque(ctx, &()) |
| { |
| ty.trace(ctx, tracer, self); |
| } |
| } |
| ItemKind::Function(ref fun) => { |
| // Just the same way, it has not real meaning for a function to |
| // be opaque, so we trace across it. |
| tracer.visit(fun.signature().into()); |
| } |
| ItemKind::Var(ref var) => { |
| tracer.visit_kind(var.ty().into(), EdgeKind::VarType); |
| } |
| ItemKind::Module(_) => { |
| // Module -> children edges are "weak", and we do not want to |
| // trace them. If we did, then allowlisting wouldn't work as |
| // expected: everything in every module would end up |
| // allowlisted. |
| // |
| // TODO: make a new edge kind for module -> children edges and |
| // filter them during allowlisting traversals. |
| } |
| } |
| } |
| } |
| |
| impl CanDeriveDebug for Item { |
| fn can_derive_debug(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_debug(ctx) |
| } |
| } |
| |
| impl CanDeriveDefault for Item { |
| fn can_derive_default(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_default(ctx) |
| } |
| } |
| |
| impl CanDeriveCopy for Item { |
| fn can_derive_copy(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_copy(ctx) |
| } |
| } |
| |
| impl CanDeriveHash for Item { |
| fn can_derive_hash(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_hash(ctx) |
| } |
| } |
| |
| impl CanDerivePartialOrd for Item { |
| fn can_derive_partialord(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_partialord(ctx) |
| } |
| } |
| |
| impl CanDerivePartialEq for Item { |
| fn can_derive_partialeq(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_partialeq(ctx) |
| } |
| } |
| |
| impl CanDeriveEq for Item { |
| fn can_derive_eq(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_eq(ctx) |
| } |
| } |
| |
| impl CanDeriveOrd for Item { |
| fn can_derive_ord(&self, ctx: &BindgenContext) -> bool { |
| self.id().can_derive_ord(ctx) |
| } |
| } |
| |
| /// An item is the base of the bindgen representation, it can be either a |
| /// module, a type, a function, or a variable (see `ItemKind` for more |
| /// information). |
| /// |
| /// Items refer to each other by `ItemId`. Every item has its parent's |
| /// ID. Depending on the kind of item this is, it may also refer to other items, |
| /// such as a compound type item referring to other types. Collectively, these |
| /// references form a graph. |
| /// |
| /// The entry-point to this graph is the "root module": a meta-item used to hold |
| /// all top-level items. |
| /// |
| /// An item may have a comment, and annotations (see the `annotations` module). |
| /// |
| /// Note that even though we parse all the types of annotations in comments, not |
| /// all of them apply to every item. Those rules are described in the |
| /// `annotations` module. |
| #[derive(Debug)] |
| pub(crate) struct Item { |
| /// This item's ID. |
| id: ItemId, |
| |
| /// The item's local ID, unique only amongst its siblings. Only used for |
| /// anonymous items. |
| /// |
| /// Lazily initialized in local_id(). |
| /// |
| /// Note that only structs, unions, and enums get a local type ID. In any |
| /// case this is an implementation detail. |
| local_id: LazyCell<usize>, |
| |
| /// The next local ID to use for a child or template instantiation. |
| next_child_local_id: Cell<usize>, |
| |
| /// A cached copy of the canonical name, as returned by `canonical_name`. |
| /// |
| /// This is a fairly used operation during codegen so this makes bindgen |
| /// considerably faster in those cases. |
| canonical_name: LazyCell<String>, |
| |
| /// The path to use for allowlisting and other name-based checks, as |
| /// returned by `path_for_allowlisting`, lazily constructed. |
| path_for_allowlisting: LazyCell<Vec<String>>, |
| |
| /// A doc comment over the item, if any. |
| comment: Option<String>, |
| /// Annotations extracted from the doc comment, or the default ones |
| /// otherwise. |
| annotations: Annotations, |
| /// An item's parent ID. This will most likely be a class where this item |
| /// was declared, or a module, etc. |
| /// |
| /// All the items have a parent, except the root module, in which case the |
| /// parent ID is its own ID. |
| parent_id: ItemId, |
| /// The item kind. |
| kind: ItemKind, |
| /// The source location of the item. |
| location: Option<clang::SourceLocation>, |
| } |
| |
| impl AsRef<ItemId> for Item { |
| fn as_ref(&self) -> &ItemId { |
| &self.id |
| } |
| } |
| |
| impl Item { |
| /// Construct a new `Item`. |
| pub(crate) fn new( |
| id: ItemId, |
| comment: Option<String>, |
| annotations: Option<Annotations>, |
| parent_id: ItemId, |
| kind: ItemKind, |
| location: Option<clang::SourceLocation>, |
| ) -> Self { |
| debug_assert!(id != parent_id || kind.is_module()); |
| Item { |
| id, |
| local_id: LazyCell::new(), |
| next_child_local_id: Cell::new(1), |
| canonical_name: LazyCell::new(), |
| path_for_allowlisting: LazyCell::new(), |
| parent_id, |
| comment, |
| annotations: annotations.unwrap_or_default(), |
| kind, |
| location, |
| } |
| } |
| |
| /// Construct a new opaque item type. |
| pub(crate) fn new_opaque_type( |
| with_id: ItemId, |
| ty: &clang::Type, |
| ctx: &mut BindgenContext, |
| ) -> TypeId { |
| let location = ty.declaration().location(); |
| let ty = Opaque::from_clang_ty(ty, ctx); |
| let kind = ItemKind::Type(ty); |
| let parent = ctx.root_module().into(); |
| ctx.add_item( |
| Item::new(with_id, None, None, parent, kind, Some(location)), |
| None, |
| None, |
| ); |
| with_id.as_type_id_unchecked() |
| } |
| |
| /// Get this `Item`'s identifier. |
| pub(crate) fn id(&self) -> ItemId { |
| self.id |
| } |
| |
| /// Get this `Item`'s parent's identifier. |
| /// |
| /// For the root module, the parent's ID is its own ID. |
| pub(crate) fn parent_id(&self) -> ItemId { |
| self.parent_id |
| } |
| |
| /// Set this item's parent ID. |
| /// |
| /// This is only used so replacements get generated in the proper module. |
| pub(crate) fn set_parent_for_replacement<Id: Into<ItemId>>( |
| &mut self, |
| id: Id, |
| ) { |
| self.parent_id = id.into(); |
| } |
| |
| /// Returns the depth this item is indented to. |
| /// |
| /// FIXME(emilio): This may need fixes for the enums within modules stuff. |
| pub(crate) fn codegen_depth(&self, ctx: &BindgenContext) -> usize { |
| if !ctx.options().enable_cxx_namespaces { |
| return 0; |
| } |
| |
| self.ancestors(ctx) |
| .filter(|id| { |
| ctx.resolve_item(*id).as_module().map_or(false, |module| { |
| !module.is_inline() || |
| ctx.options().conservative_inline_namespaces |
| }) |
| }) |
| .count() + |
| 1 |
| } |
| |
| /// Get this `Item`'s comment, if it has any, already preprocessed and with |
| /// the right indentation. |
| pub(crate) fn comment(&self, ctx: &BindgenContext) -> Option<String> { |
| if !ctx.options().generate_comments { |
| return None; |
| } |
| |
| self.comment |
| .as_ref() |
| .map(|comment| ctx.options().process_comment(comment)) |
| } |
| |
| /// What kind of item is this? |
| pub(crate) fn kind(&self) -> &ItemKind { |
| &self.kind |
| } |
| |
| /// Get a mutable reference to this item's kind. |
| pub(crate) fn kind_mut(&mut self) -> &mut ItemKind { |
| &mut self.kind |
| } |
| |
| /// Where in the source is this item located? |
| pub(crate) fn location(&self) -> Option<&clang::SourceLocation> { |
| self.location.as_ref() |
| } |
| |
| /// Get an identifier that differentiates this item from its siblings. |
| /// |
| /// This should stay relatively stable in the face of code motion outside or |
| /// below this item's lexical scope, meaning that this can be useful for |
| /// generating relatively stable identifiers within a scope. |
| pub(crate) fn local_id(&self, ctx: &BindgenContext) -> usize { |
| *self.local_id.borrow_with(|| { |
| let parent = ctx.resolve_item(self.parent_id); |
| parent.next_child_local_id() |
| }) |
| } |
| |
| /// Get an identifier that differentiates a child of this item of other |
| /// related items. |
| /// |
| /// This is currently used for anonymous items, and template instantiation |
| /// tests, in both cases in order to reduce noise when system headers are at |
| /// place. |
| pub(crate) fn next_child_local_id(&self) -> usize { |
| let local_id = self.next_child_local_id.get(); |
| self.next_child_local_id.set(local_id + 1); |
| local_id |
| } |
| |
| /// Returns whether this item is a top-level item, from the point of view of |
| /// bindgen. |
| /// |
| /// This point of view changes depending on whether namespaces are enabled |
| /// or not. That way, in the following example: |
| /// |
| /// ```c++ |
| /// namespace foo { |
| /// static int var; |
| /// } |
| /// ``` |
| /// |
| /// `var` would be a toplevel item if namespaces are disabled, but won't if |
| /// they aren't. |
| /// |
| /// This function is used to determine when the codegen phase should call |
| /// `codegen` on an item, since any item that is not top-level will be |
| /// generated by its parent. |
| pub(crate) fn is_toplevel(&self, ctx: &BindgenContext) -> bool { |
| // FIXME: Workaround for some types falling behind when parsing weird |
| // stl classes, for example. |
| if ctx.options().enable_cxx_namespaces && |
| self.kind().is_module() && |
| self.id() != ctx.root_module() |
| { |
| return false; |
| } |
| |
| let mut parent = self.parent_id; |
| loop { |
| let parent_item = match ctx.resolve_item_fallible(parent) { |
| Some(item) => item, |
| None => return false, |
| }; |
| |
| if parent_item.id() == ctx.root_module() { |
| return true; |
| } else if ctx.options().enable_cxx_namespaces || |
| !parent_item.kind().is_module() |
| { |
| return false; |
| } |
| |
| parent = parent_item.parent_id(); |
| } |
| } |
| |
| /// Get a reference to this item's underlying `Type`. Panic if this is some |
| /// other kind of item. |
| pub(crate) fn expect_type(&self) -> &Type { |
| self.kind().expect_type() |
| } |
| |
| /// Get a reference to this item's underlying `Type`, or `None` if this is |
| /// some other kind of item. |
| pub(crate) fn as_type(&self) -> Option<&Type> { |
| self.kind().as_type() |
| } |
| |
| /// Get a reference to this item's underlying `Function`. Panic if this is |
| /// some other kind of item. |
| pub(crate) fn expect_function(&self) -> &Function { |
| self.kind().expect_function() |
| } |
| |
| /// Is this item a module? |
| pub(crate) fn is_module(&self) -> bool { |
| matches!(self.kind, ItemKind::Module(..)) |
| } |
| |
| /// Get this item's annotations. |
| pub(crate) fn annotations(&self) -> &Annotations { |
| &self.annotations |
| } |
| |
| /// Whether this item should be blocklisted. |
| /// |
| /// This may be due to either annotations or to other kind of configuration. |
| pub(crate) fn is_blocklisted(&self, ctx: &BindgenContext) -> bool { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| if self.annotations.hide() { |
| return true; |
| } |
| |
| if !ctx.options().blocklisted_files.is_empty() { |
| if let Some(location) = &self.location { |
| let (file, _, _, _) = location.location(); |
| if let Some(filename) = file.name() { |
| if ctx.options().blocklisted_files.matches(filename) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| let path = self.path_for_allowlisting(ctx); |
| let name = path[1..].join("::"); |
| ctx.options().blocklisted_items.matches(&name) || |
| match self.kind { |
| ItemKind::Type(..) => { |
| ctx.options().blocklisted_types.matches(&name) || |
| ctx.is_replaced_type(path, self.id) |
| } |
| ItemKind::Function(..) => { |
| ctx.options().blocklisted_functions.matches(&name) |
| } |
| ItemKind::Var(..) => { |
| ctx.options().blocklisted_vars.matches(&name) |
| } |
| // TODO: Add namespace blocklisting? |
| ItemKind::Module(..) => false, |
| } |
| } |
| |
| /// Take out item NameOptions |
| pub(crate) fn name<'a>( |
| &'a self, |
| ctx: &'a BindgenContext, |
| ) -> NameOptions<'a> { |
| NameOptions::new(self, ctx) |
| } |
| |
| /// Get the target item ID for name generation. |
| fn name_target(&self, ctx: &BindgenContext) -> ItemId { |
| let mut targets_seen = DebugOnlyItemSet::new(); |
| let mut item = self; |
| |
| loop { |
| extra_assert!(!targets_seen.contains(&item.id())); |
| targets_seen.insert(item.id()); |
| |
| if self.annotations().use_instead_of().is_some() { |
| return self.id(); |
| } |
| |
| match *item.kind() { |
| ItemKind::Type(ref ty) => match *ty.kind() { |
| TypeKind::ResolvedTypeRef(inner) => { |
| item = ctx.resolve_item(inner); |
| } |
| TypeKind::TemplateInstantiation(ref inst) => { |
| item = ctx.resolve_item(inst.template_definition()); |
| } |
| _ => return item.id(), |
| }, |
| _ => return item.id(), |
| } |
| } |
| } |
| |
| /// Create a fully disambiguated name for an item, including template |
| /// parameters if it is a type |
| pub(crate) fn full_disambiguated_name( |
| &self, |
| ctx: &BindgenContext, |
| ) -> String { |
| let mut s = String::new(); |
| let level = 0; |
| self.push_disambiguated_name(ctx, &mut s, level); |
| s |
| } |
| |
| /// Helper function for full_disambiguated_name |
| fn push_disambiguated_name( |
| &self, |
| ctx: &BindgenContext, |
| to: &mut String, |
| level: u8, |
| ) { |
| to.push_str(&self.canonical_name(ctx)); |
| if let ItemKind::Type(ref ty) = *self.kind() { |
| if let TypeKind::TemplateInstantiation(ref inst) = *ty.kind() { |
| to.push_str(&format!("_open{}_", level)); |
| for arg in inst.template_arguments() { |
| arg.into_resolver() |
| .through_type_refs() |
| .resolve(ctx) |
| .push_disambiguated_name(ctx, to, level + 1); |
| to.push('_'); |
| } |
| to.push_str(&format!("close{}", level)); |
| } |
| } |
| } |
| |
| /// Get this function item's name, or `None` if this item is not a function. |
| fn func_name(&self) -> Option<&str> { |
| match *self.kind() { |
| ItemKind::Function(ref func) => Some(func.name()), |
| _ => None, |
| } |
| } |
| |
| /// Get the overload index for this method. If this is not a method, return |
| /// `None`. |
| fn overload_index(&self, ctx: &BindgenContext) -> Option<usize> { |
| self.func_name().and_then(|func_name| { |
| let parent = ctx.resolve_item(self.parent_id()); |
| if let ItemKind::Type(ref ty) = *parent.kind() { |
| if let TypeKind::Comp(ref ci) = *ty.kind() { |
| // All the constructors have the same name, so no need to |
| // resolve and check. |
| return ci |
| .constructors() |
| .iter() |
| .position(|c| *c == self.id()) |
| .or_else(|| { |
| ci.methods() |
| .iter() |
| .filter(|m| { |
| let item = ctx.resolve_item(m.signature()); |
| let func = item.expect_function(); |
| func.name() == func_name |
| }) |
| .position(|m| m.signature() == self.id()) |
| }); |
| } |
| } |
| |
| None |
| }) |
| } |
| |
| /// Get this item's base name (aka non-namespaced name). |
| fn base_name(&self, ctx: &BindgenContext) -> String { |
| if let Some(path) = self.annotations().use_instead_of() { |
| return path.last().unwrap().clone(); |
| } |
| |
| match *self.kind() { |
| ItemKind::Var(ref var) => var.name().to_owned(), |
| ItemKind::Module(ref module) => { |
| module.name().map(ToOwned::to_owned).unwrap_or_else(|| { |
| format!("_bindgen_mod_{}", self.exposed_id(ctx)) |
| }) |
| } |
| ItemKind::Type(ref ty) => { |
| ty.sanitized_name(ctx).map(Into::into).unwrap_or_else(|| { |
| format!("_bindgen_ty_{}", self.exposed_id(ctx)) |
| }) |
| } |
| ItemKind::Function(ref fun) => { |
| let mut name = fun.name().to_owned(); |
| |
| if let Some(idx) = self.overload_index(ctx) { |
| if idx > 0 { |
| write!(&mut name, "{}", idx).unwrap(); |
| } |
| } |
| |
| name |
| } |
| } |
| } |
| |
| fn is_anon(&self) -> bool { |
| match self.kind() { |
| ItemKind::Module(module) => module.name().is_none(), |
| ItemKind::Type(ty) => ty.name().is_none(), |
| ItemKind::Function(_) => false, |
| ItemKind::Var(_) => false, |
| } |
| } |
| |
| /// Get the canonical name without taking into account the replaces |
| /// annotation. |
| /// |
| /// This is the base logic used to implement hiding and replacing via |
| /// annotations, and also to implement proper name mangling. |
| /// |
| /// The idea is that each generated type in the same "level" (read: module |
| /// or namespace) has a unique canonical name. |
| /// |
| /// This name should be derived from the immutable state contained in the |
| /// type and the parent chain, since it should be consistent. |
| /// |
| /// If `BindgenOptions::disable_nested_struct_naming` is true then returned |
| /// name is the inner most non-anonymous name plus all the anonymous base names |
| /// that follows. |
| pub(crate) fn real_canonical_name( |
| &self, |
| ctx: &BindgenContext, |
| opt: &NameOptions, |
| ) -> String { |
| let target = ctx.resolve_item(self.name_target(ctx)); |
| |
| // Short-circuit if the target has an override, and just use that. |
| if let Some(path) = target.annotations.use_instead_of() { |
| if ctx.options().enable_cxx_namespaces { |
| return path.last().unwrap().clone(); |
| } |
| return path.join("_"); |
| } |
| |
| let base_name = target.base_name(ctx); |
| |
| // Named template type arguments are never namespaced, and never |
| // mangled. |
| if target.is_template_param(ctx, &()) { |
| return base_name; |
| } |
| |
| // Ancestors' ID iter |
| let mut ids_iter = target |
| .parent_id() |
| .ancestors(ctx) |
| .filter(|id| *id != ctx.root_module()) |
| .take_while(|id| { |
| // Stop iterating ancestors once we reach a non-inline namespace |
| // when opt.within_namespaces is set. |
| !opt.within_namespaces || !ctx.resolve_item(*id).is_module() |
| }) |
| .filter(|id| { |
| if !ctx.options().conservative_inline_namespaces { |
| if let ItemKind::Module(ref module) = |
| *ctx.resolve_item(*id).kind() |
| { |
| return !module.is_inline(); |
| } |
| } |
| |
| true |
| }); |
| |
| let ids: Vec<_> = if ctx.options().disable_nested_struct_naming { |
| let mut ids = Vec::new(); |
| |
| // If target is anonymous we need find its first named ancestor. |
| if target.is_anon() { |
| for id in ids_iter.by_ref() { |
| ids.push(id); |
| |
| if !ctx.resolve_item(id).is_anon() { |
| break; |
| } |
| } |
| } |
| |
| ids |
| } else { |
| ids_iter.collect() |
| }; |
| |
| // Concatenate this item's ancestors' names together. |
| let mut names: Vec<_> = ids |
| .into_iter() |
| .map(|id| { |
| let item = ctx.resolve_item(id); |
| let target = ctx.resolve_item(item.name_target(ctx)); |
| target.base_name(ctx) |
| }) |
| .filter(|name| !name.is_empty()) |
| .collect(); |
| |
| names.reverse(); |
| |
| if !base_name.is_empty() { |
| names.push(base_name); |
| } |
| |
| if ctx.options().c_naming { |
| if let Some(prefix) = self.c_naming_prefix() { |
| names.insert(0, prefix.to_string()); |
| } |
| } |
| |
| let name = names.join("_"); |
| |
| let name = if opt.user_mangled == UserMangled::Yes { |
| ctx.options() |
| .last_callback(|callbacks| callbacks.item_name(&name)) |
| .unwrap_or(name) |
| } else { |
| name |
| }; |
| |
| ctx.rust_mangle(&name).into_owned() |
| } |
| |
| /// The exposed ID that represents an unique ID among the siblings of a |
| /// given item. |
| pub(crate) fn exposed_id(&self, ctx: &BindgenContext) -> String { |
| // Only use local ids for enums, classes, structs and union types. All |
| // other items use their global ID. |
| let ty_kind = self.kind().as_type().map(|t| t.kind()); |
| if let Some(ty_kind) = ty_kind { |
| match *ty_kind { |
| TypeKind::Comp(..) | |
| TypeKind::TemplateInstantiation(..) | |
| TypeKind::Enum(..) => return self.local_id(ctx).to_string(), |
| _ => {} |
| } |
| } |
| |
| // Note that this `id_` prefix prevents (really unlikely) collisions |
| // between the global ID and the local ID of an item with the same |
| // parent. |
| format!("id_{}", self.id().as_usize()) |
| } |
| |
| /// Get a reference to this item's `Module`, or `None` if this is not a |
| /// `Module` item. |
| pub(crate) fn as_module(&self) -> Option<&Module> { |
| match self.kind { |
| ItemKind::Module(ref module) => Some(module), |
| _ => None, |
| } |
| } |
| |
| /// Get a mutable reference to this item's `Module`, or `None` if this is |
| /// not a `Module` item. |
| pub(crate) fn as_module_mut(&mut self) -> Option<&mut Module> { |
| match self.kind { |
| ItemKind::Module(ref mut module) => Some(module), |
| _ => None, |
| } |
| } |
| |
| /// Returns whether the item is a constified module enum |
| fn is_constified_enum_module(&self, ctx: &BindgenContext) -> bool { |
| // Do not jump through aliases, except for aliases that point to a type |
| // with the same name, since we dont generate coe for them. |
| let item = self.id.into_resolver().through_type_refs().resolve(ctx); |
| let type_ = match *item.kind() { |
| ItemKind::Type(ref type_) => type_, |
| _ => return false, |
| }; |
| |
| match *type_.kind() { |
| TypeKind::Enum(ref enum_) => { |
| enum_.computed_enum_variation(ctx, self) == |
| EnumVariation::ModuleConsts |
| } |
| TypeKind::Alias(inner_id) => { |
| // TODO(emilio): Make this "hop through type aliases that aren't |
| // really generated" an option in `ItemResolver`? |
| let inner_item = ctx.resolve_item(inner_id); |
| let name = item.canonical_name(ctx); |
| |
| if inner_item.canonical_name(ctx) == name { |
| inner_item.is_constified_enum_module(ctx) |
| } else { |
| false |
| } |
| } |
| _ => false, |
| } |
| } |
| |
| /// Is this item of a kind that is enabled for code generation? |
| pub(crate) fn is_enabled_for_codegen(&self, ctx: &BindgenContext) -> bool { |
| let cc = &ctx.options().codegen_config; |
| match *self.kind() { |
| ItemKind::Module(..) => true, |
| ItemKind::Var(_) => cc.vars(), |
| ItemKind::Type(_) => cc.types(), |
| ItemKind::Function(ref f) => match f.kind() { |
| FunctionKind::Function => cc.functions(), |
| FunctionKind::Method(MethodKind::Constructor) => { |
| cc.constructors() |
| } |
| FunctionKind::Method(MethodKind::Destructor) | |
| FunctionKind::Method(MethodKind::VirtualDestructor { |
| .. |
| }) => cc.destructors(), |
| FunctionKind::Method(MethodKind::Static) | |
| FunctionKind::Method(MethodKind::Normal) | |
| FunctionKind::Method(MethodKind::Virtual { .. }) => { |
| cc.methods() |
| } |
| }, |
| } |
| } |
| |
| /// Returns the path we should use for allowlisting / blocklisting, which |
| /// doesn't include user-mangling. |
| pub(crate) fn path_for_allowlisting( |
| &self, |
| ctx: &BindgenContext, |
| ) -> &Vec<String> { |
| self.path_for_allowlisting |
| .borrow_with(|| self.compute_path(ctx, UserMangled::No)) |
| } |
| |
| fn compute_path( |
| &self, |
| ctx: &BindgenContext, |
| mangled: UserMangled, |
| ) -> Vec<String> { |
| if let Some(path) = self.annotations().use_instead_of() { |
| let mut ret = |
| vec![ctx.resolve_item(ctx.root_module()).name(ctx).get()]; |
| ret.extend_from_slice(path); |
| return ret; |
| } |
| |
| let target = ctx.resolve_item(self.name_target(ctx)); |
| let mut path: Vec<_> = target |
| .ancestors(ctx) |
| .chain(iter::once(ctx.root_module().into())) |
| .map(|id| ctx.resolve_item(id)) |
| .filter(|item| { |
| item.id() == target.id() || |
| item.as_module().map_or(false, |module| { |
| !module.is_inline() || |
| ctx.options().conservative_inline_namespaces |
| }) |
| }) |
| .map(|item| { |
| ctx.resolve_item(item.name_target(ctx)) |
| .name(ctx) |
| .within_namespaces() |
| .user_mangled(mangled) |
| .get() |
| }) |
| .collect(); |
| path.reverse(); |
| path |
| } |
| |
| /// Returns a prefix for the canonical name when C naming is enabled. |
| fn c_naming_prefix(&self) -> Option<&str> { |
| let ty = match self.kind { |
| ItemKind::Type(ref ty) => ty, |
| _ => return None, |
| }; |
| |
| Some(match ty.kind() { |
| TypeKind::Comp(ref ci) => match ci.kind() { |
| CompKind::Struct => "struct", |
| CompKind::Union => "union", |
| }, |
| TypeKind::Enum(..) => "enum", |
| _ => return None, |
| }) |
| } |
| |
| /// Whether this is a `#[must_use]` type. |
| pub(crate) fn must_use(&self, ctx: &BindgenContext) -> bool { |
| self.annotations().must_use_type() || ctx.must_use_type_by_name(self) |
| } |
| } |
| |
| impl<T> IsOpaque for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| type Extra = (); |
| |
| fn is_opaque(&self, ctx: &BindgenContext, _: &()) -> bool { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.resolve_item((*self).into()).is_opaque(ctx, &()) |
| } |
| } |
| |
| impl IsOpaque for Item { |
| type Extra = (); |
| |
| fn is_opaque(&self, ctx: &BindgenContext, _: &()) -> bool { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| self.annotations.opaque() || |
| self.as_type().map_or(false, |ty| ty.is_opaque(ctx, self)) || |
| ctx.opaque_by_name(self.path_for_allowlisting(ctx)) |
| } |
| } |
| |
| impl<T> HasVtable for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn has_vtable(&self, ctx: &BindgenContext) -> bool { |
| let id: ItemId = (*self).into(); |
| id.as_type_id(ctx).map_or(false, |id| { |
| !matches!(ctx.lookup_has_vtable(id), HasVtableResult::No) |
| }) |
| } |
| |
| fn has_vtable_ptr(&self, ctx: &BindgenContext) -> bool { |
| let id: ItemId = (*self).into(); |
| id.as_type_id(ctx).map_or(false, |id| { |
| matches!(ctx.lookup_has_vtable(id), HasVtableResult::SelfHasVtable) |
| }) |
| } |
| } |
| |
| impl HasVtable for Item { |
| fn has_vtable(&self, ctx: &BindgenContext) -> bool { |
| self.id().has_vtable(ctx) |
| } |
| |
| fn has_vtable_ptr(&self, ctx: &BindgenContext) -> bool { |
| self.id().has_vtable_ptr(ctx) |
| } |
| } |
| |
| impl<T> Sizedness for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn sizedness(&self, ctx: &BindgenContext) -> SizednessResult { |
| let id: ItemId = (*self).into(); |
| id.as_type_id(ctx) |
| .map_or(SizednessResult::default(), |id| ctx.lookup_sizedness(id)) |
| } |
| } |
| |
| impl Sizedness for Item { |
| fn sizedness(&self, ctx: &BindgenContext) -> SizednessResult { |
| self.id().sizedness(ctx) |
| } |
| } |
| |
| impl<T> HasTypeParamInArray for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn has_type_param_in_array(&self, ctx: &BindgenContext) -> bool { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.lookup_has_type_param_in_array(*self) |
| } |
| } |
| |
| impl HasTypeParamInArray for Item { |
| fn has_type_param_in_array(&self, ctx: &BindgenContext) -> bool { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.lookup_has_type_param_in_array(self.id()) |
| } |
| } |
| |
| impl<T> HasFloat for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn has_float(&self, ctx: &BindgenContext) -> bool { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.lookup_has_float(*self) |
| } |
| } |
| |
| impl HasFloat for Item { |
| fn has_float(&self, ctx: &BindgenContext) -> bool { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| ctx.lookup_has_float(self.id()) |
| } |
| } |
| |
| /// A set of items. |
| pub(crate) type ItemSet = BTreeSet<ItemId>; |
| |
| impl DotAttributes for Item { |
| fn dot_attributes<W>( |
| &self, |
| ctx: &BindgenContext, |
| out: &mut W, |
| ) -> io::Result<()> |
| where |
| W: io::Write, |
| { |
| writeln!( |
| out, |
| "<tr><td>{:?}</td></tr> |
| <tr><td>name</td><td>{}</td></tr>", |
| self.id, |
| self.name(ctx).get() |
| )?; |
| |
| if self.is_opaque(ctx, &()) { |
| writeln!(out, "<tr><td>opaque</td><td>true</td></tr>")?; |
| } |
| |
| self.kind.dot_attributes(ctx, out) |
| } |
| } |
| |
| impl<T> TemplateParameters for T |
| where |
| T: Copy + Into<ItemId>, |
| { |
| fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> { |
| ctx.resolve_item_fallible(*self) |
| .map_or(vec![], |item| item.self_template_params(ctx)) |
| } |
| } |
| |
| impl TemplateParameters for Item { |
| fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> { |
| self.kind.self_template_params(ctx) |
| } |
| } |
| |
| impl TemplateParameters for ItemKind { |
| fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> { |
| match *self { |
| ItemKind::Type(ref ty) => ty.self_template_params(ctx), |
| // If we start emitting bindings to explicitly instantiated |
| // functions, then we'll need to check ItemKind::Function for |
| // template params. |
| ItemKind::Function(_) | ItemKind::Module(_) | ItemKind::Var(_) => { |
| vec![] |
| } |
| } |
| } |
| } |
| |
| // An utility function to handle recursing inside nested types. |
| fn visit_child( |
| cur: clang::Cursor, |
| id: ItemId, |
| ty: &clang::Type, |
| parent_id: Option<ItemId>, |
| ctx: &mut BindgenContext, |
| result: &mut Result<TypeId, ParseError>, |
| ) -> clang_sys::CXChildVisitResult { |
| use clang_sys::*; |
| if result.is_ok() { |
| return CXChildVisit_Break; |
| } |
| |
| *result = Item::from_ty_with_id(id, ty, cur, parent_id, ctx); |
| |
| match *result { |
| Ok(..) => CXChildVisit_Break, |
| Err(ParseError::Recurse) => { |
| cur.visit(|c| visit_child(c, id, ty, parent_id, ctx, result)); |
| CXChildVisit_Continue |
| } |
| Err(ParseError::Continue) => CXChildVisit_Continue, |
| } |
| } |
| |
| impl Item { |
| /// Create a builtin type. |
| pub(crate) fn builtin_type( |
| kind: TypeKind, |
| is_const: bool, |
| ctx: &mut BindgenContext, |
| ) -> TypeId { |
| // Feel free to add more here, I'm just lazy. |
| match kind { |
| TypeKind::Void | |
| TypeKind::Int(..) | |
| TypeKind::Pointer(..) | |
| TypeKind::Float(..) => {} |
| _ => panic!("Unsupported builtin type"), |
| } |
| |
| let ty = Type::new(None, None, kind, is_const); |
| let id = ctx.next_item_id(); |
| let module = ctx.root_module().into(); |
| ctx.add_item( |
| Item::new(id, None, None, module, ItemKind::Type(ty), None), |
| None, |
| None, |
| ); |
| id.as_type_id_unchecked() |
| } |
| |
| /// Parse this item from the given Clang cursor. |
| pub(crate) fn parse( |
| cursor: clang::Cursor, |
| parent_id: Option<ItemId>, |
| ctx: &mut BindgenContext, |
| ) -> Result<ItemId, ParseError> { |
| use crate::ir::var::Var; |
| use clang_sys::*; |
| |
| if !cursor.is_valid() { |
| return Err(ParseError::Continue); |
| } |
| |
| let comment = cursor.raw_comment(); |
| let annotations = Annotations::new(&cursor); |
| |
| let current_module = ctx.current_module().into(); |
| let relevant_parent_id = parent_id.unwrap_or(current_module); |
| |
| #[allow(clippy::missing_docs_in_private_items)] |
| macro_rules! try_parse { |
| ($what:ident) => { |
| match $what::parse(cursor, ctx) { |
| Ok(ParseResult::New(item, declaration)) => { |
| let id = ctx.next_item_id(); |
| |
| ctx.add_item( |
| Item::new( |
| id, |
| comment, |
| annotations, |
| relevant_parent_id, |
| ItemKind::$what(item), |
| Some(cursor.location()), |
| ), |
| declaration, |
| Some(cursor), |
| ); |
| return Ok(id); |
| } |
| Ok(ParseResult::AlreadyResolved(id)) => { |
| return Ok(id); |
| } |
| Err(ParseError::Recurse) => return Err(ParseError::Recurse), |
| Err(ParseError::Continue) => {} |
| } |
| }; |
| } |
| |
| try_parse!(Module); |
| |
| // NOTE: Is extremely important to parse functions and vars **before** |
| // types. Otherwise we can parse a function declaration as a type |
| // (which is legal), and lose functions to generate. |
| // |
| // In general, I'm not totally confident this split between |
| // ItemKind::Function and TypeKind::FunctionSig is totally worth it, but |
| // I guess we can try. |
| try_parse!(Function); |
| try_parse!(Var); |
| |
| // Types are sort of special, so to avoid parsing template classes |
| // twice, handle them separately. |
| { |
| let definition = cursor.definition(); |
| let applicable_cursor = definition.unwrap_or(cursor); |
| |
| let relevant_parent_id = match definition { |
| Some(definition) => { |
| if definition != cursor { |
| ctx.add_semantic_parent(definition, relevant_parent_id); |
| return Ok(Item::from_ty_or_ref( |
| applicable_cursor.cur_type(), |
| cursor, |
| parent_id, |
| ctx, |
| ) |
| .into()); |
| } |
| ctx.known_semantic_parent(definition) |
| .or(parent_id) |
| .unwrap_or_else(|| ctx.current_module().into()) |
| } |
| None => relevant_parent_id, |
| }; |
| |
| match Item::from_ty( |
| &applicable_cursor.cur_type(), |
| applicable_cursor, |
| Some(relevant_parent_id), |
| ctx, |
| ) { |
| Ok(ty) => return Ok(ty.into()), |
| Err(ParseError::Recurse) => return Err(ParseError::Recurse), |
| Err(ParseError::Continue) => {} |
| } |
| } |
| |
| match cursor.kind() { |
| // On Clang 18+, extern "C" is reported accurately as a LinkageSpec. |
| // Older LLVM treat it as UnexposedDecl. |
| CXCursor_LinkageSpec | CXCursor_UnexposedDecl => { |
| Err(ParseError::Recurse) |
| } |
| |
| // We allowlist cursors here known to be unhandled, to prevent being |
| // too noisy about this. |
| CXCursor_MacroDefinition | |
| CXCursor_MacroExpansion | |
| CXCursor_UsingDeclaration | |
| CXCursor_UsingDirective | |
| CXCursor_StaticAssert | |
| CXCursor_FunctionTemplate => { |
| debug!( |
| "Unhandled cursor kind {:?}: {:?}", |
| cursor.kind(), |
| cursor |
| ); |
| Err(ParseError::Continue) |
| } |
| |
| CXCursor_InclusionDirective => { |
| let file = cursor.get_included_file_name(); |
| match file { |
| None => { |
| warn!("Inclusion of a nameless file in {:?}", cursor); |
| } |
| Some(included_file) => { |
| for cb in &ctx.options().parse_callbacks { |
| cb.include_file(&included_file); |
| } |
| |
| ctx.add_dep(included_file.into_boxed_str()); |
| } |
| } |
| Err(ParseError::Continue) |
| } |
| |
| _ => { |
| // ignore toplevel operator overloads |
| let spelling = cursor.spelling(); |
| if !spelling.starts_with("operator") { |
| warn!( |
| "Unhandled cursor kind {:?}: {:?}", |
| cursor.kind(), |
| cursor |
| ); |
| } |
| Err(ParseError::Continue) |
| } |
| } |
| } |
| |
| /// Parse this item from the given Clang type, or if we haven't resolved all |
| /// the other items this one depends on, an unresolved reference. |
| pub(crate) fn from_ty_or_ref( |
| ty: clang::Type, |
| location: clang::Cursor, |
| parent_id: Option<ItemId>, |
| ctx: &mut BindgenContext, |
| ) -> TypeId { |
| let id = ctx.next_item_id(); |
| Self::from_ty_or_ref_with_id(id, ty, location, parent_id, ctx) |
| } |
| |
| /// Parse a C++ type. If we find a reference to a type that has not been |
| /// defined yet, use `UnresolvedTypeRef` as a placeholder. |
| /// |
| /// This logic is needed to avoid parsing items with the incorrect parent |
| /// and it's sort of complex to explain, so I'll just point to |
| /// `tests/headers/typeref.hpp` to see the kind of constructs that forced |
| /// this. |
| /// |
| /// Typerefs are resolved once parsing is completely done, see |
| /// `BindgenContext::resolve_typerefs`. |
| pub(crate) fn from_ty_or_ref_with_id( |
| potential_id: ItemId, |
| ty: clang::Type, |
| location: clang::Cursor, |
| parent_id: Option<ItemId>, |
| ctx: &mut BindgenContext, |
| ) -> TypeId { |
| debug!( |
| "from_ty_or_ref_with_id: {:?} {:?}, {:?}, {:?}", |
| potential_id, ty, location, parent_id |
| ); |
| |
| if ctx.collected_typerefs() { |
| debug!("refs already collected, resolving directly"); |
| return Item::from_ty_with_id( |
| potential_id, |
| &ty, |
| location, |
| parent_id, |
| ctx, |
| ) |
| .unwrap_or_else(|_| Item::new_opaque_type(potential_id, &ty, ctx)); |
| } |
| |
| if let Some(ty) = ctx.builtin_or_resolved_ty( |
| potential_id, |
| parent_id, |
| &ty, |
| Some(location), |
| ) { |
| debug!("{:?} already resolved: {:?}", ty, location); |
| return ty; |
| } |
| |
| debug!("New unresolved type reference: {:?}, {:?}", ty, location); |
| |
| let is_const = ty.is_const(); |
| let kind = TypeKind::UnresolvedTypeRef(ty, location, parent_id); |
| let current_module = ctx.current_module(); |
| |
| ctx.add_item( |
| Item::new( |
| potential_id, |
| None, |
| None, |
| parent_id.unwrap_or_else(|| current_module.into()), |
| ItemKind::Type(Type::new(None, None, kind, is_const)), |
| Some(location.location()), |
| ), |
| None, |
| None, |
| ); |
| potential_id.as_type_id_unchecked() |
| } |
| |
| /// Parse this item from the given Clang type. See [`Item::from_ty_with_id`]. |
| pub(crate) fn from_ty( |
| ty: &clang::Type, |
| location: clang::Cursor, |
| parent_id: Option<ItemId>, |
| ctx: &mut BindgenContext, |
| ) -> Result<TypeId, ParseError> { |
| let id = ctx.next_item_id(); |
| Item::from_ty_with_id(id, ty, location, parent_id, ctx) |
| } |
| |
| /// This is one of the trickiest methods you'll find (probably along with |
| /// some of the ones that handle templates in `BindgenContext`). |
| /// |
| /// This method parses a type, given the potential ID of that type (if |
| /// parsing it was correct), an optional location we're scanning, which is |
| /// critical some times to obtain information, an optional parent item ID, |
| /// that will, if it's `None`, become the current module ID, and the |
| /// context. |
| pub(crate) fn from_ty_with_id( |
| id: ItemId, |
| ty: &clang::Type, |
| location: clang::Cursor, |
| parent_id: Option<ItemId>, |
| ctx: &mut BindgenContext, |
| ) -> Result<TypeId, ParseError> { |
| use clang_sys::*; |
| |
| debug!( |
| "Item::from_ty_with_id: {:?}\n\ |
| \tty = {:?},\n\ |
| \tlocation = {:?}", |
| id, ty, location |
| ); |
| |
| if ty.kind() == clang_sys::CXType_Unexposed || |
| location.cur_type().kind() == clang_sys::CXType_Unexposed |
| { |
| if ty.is_associated_type() || |
| location.cur_type().is_associated_type() |
| { |
| return Ok(Item::new_opaque_type(id, ty, ctx)); |
| } |
| |
| if let Some(param_id) = Item::type_param(None, location, ctx) { |
| return Ok(ctx.build_ty_wrapper(id, param_id, None, ty)); |
| } |
| } |
| |
| // Treat all types that are declared inside functions as opaque. The Rust binding |
| // won't be able to do anything with them anyway. |
| // |
| // (If we don't do this check here, we can have subtle logic bugs because we generally |
| // ignore function bodies. See issue #2036.) |
| if let Some(ref parent) = ty.declaration().fallible_semantic_parent() { |
| if FunctionKind::from_cursor(parent).is_some() { |
| debug!("Skipping type declared inside function: {:?}", ty); |
| return Ok(Item::new_opaque_type(id, ty, ctx)); |
| } |
| } |
| |
| let decl = { |
| let canonical_def = ty.canonical_type().declaration().definition(); |
| canonical_def.unwrap_or_else(|| ty.declaration()) |
| }; |
| |
| let comment = location |
| .raw_comment() |
| .or_else(|| decl.raw_comment()) |
| .or_else(|| location.raw_comment()); |
| |
| let annotations = |
| Annotations::new(&decl).or_else(|| Annotations::new(&location)); |
| |
| if let Some(ref annotations) = annotations { |
| if let Some(replaced) = annotations.use_instead_of() { |
| ctx.replace(replaced, id); |
| } |
| } |
| |
| if let Some(ty) = |
| ctx.builtin_or_resolved_ty(id, parent_id, ty, Some(location)) |
| { |
| return Ok(ty); |
| } |
| |
| // First, check we're not recursing. |
| let mut valid_decl = decl.kind() != CXCursor_NoDeclFound; |
| let declaration_to_look_for = if valid_decl { |
| decl.canonical() |
| } else if location.kind() == CXCursor_ClassTemplate { |
| valid_decl = true; |
| location |
| } else { |
| decl |
| }; |
| |
| if valid_decl { |
| if let Some(partial) = ctx |
| .currently_parsed_types() |
| .iter() |
| .find(|ty| *ty.decl() == declaration_to_look_for) |
| { |
| debug!("Avoiding recursion parsing type: {:?}", ty); |
| // Unchecked because we haven't finished this type yet. |
| return Ok(partial.id().as_type_id_unchecked()); |
| } |
| } |
| |
| let current_module = ctx.current_module().into(); |
| let partial_ty = PartialType::new(declaration_to_look_for, id); |
| if valid_decl { |
| ctx.begin_parsing(partial_ty); |
| } |
| |
| let result = Type::from_clang_ty(id, ty, location, parent_id, ctx); |
| let relevant_parent_id = parent_id.unwrap_or(current_module); |
| let ret = match result { |
| Ok(ParseResult::AlreadyResolved(ty)) => { |
| Ok(ty.as_type_id_unchecked()) |
| } |
| Ok(ParseResult::New(item, declaration)) => { |
| ctx.add_item( |
| Item::new( |
| id, |
| comment, |
| annotations, |
| relevant_parent_id, |
| ItemKind::Type(item), |
| Some(location.location()), |
| ), |
| declaration, |
| Some(location), |
| ); |
| Ok(id.as_type_id_unchecked()) |
| } |
| Err(ParseError::Continue) => Err(ParseError::Continue), |
| Err(ParseError::Recurse) => { |
| debug!("Item::from_ty recursing in the ast"); |
| let mut result = Err(ParseError::Recurse); |
| |
| // Need to pop here, otherwise we'll get stuck. |
| // |
| // TODO: Find a nicer interface, really. Also, the |
| // declaration_to_look_for suspiciously shares a lot of |
| // logic with ir::context, so we should refactor that. |
| if valid_decl { |
| let finished = ctx.finish_parsing(); |
| assert_eq!(*finished.decl(), declaration_to_look_for); |
| } |
| |
| location.visit(|cur| { |
| visit_child(cur, id, ty, parent_id, ctx, &mut result) |
| }); |
| |
| if valid_decl { |
| let partial_ty = |
| PartialType::new(declaration_to_look_for, id); |
| ctx.begin_parsing(partial_ty); |
| } |
| |
| // If we have recursed into the AST all we know, and we still |
| // haven't found what we've got, let's just try and make a named |
| // type. |
| // |
| // This is what happens with some template members, for example. |
| if let Err(ParseError::Recurse) = result { |
| warn!( |
| "Unknown type, assuming named template type: \ |
| id = {:?}; spelling = {}", |
| id, |
| ty.spelling() |
| ); |
| Item::type_param(Some(id), location, ctx) |
| .map(Ok) |
| .unwrap_or(Err(ParseError::Recurse)) |
| } else { |
| result |
| } |
| } |
| }; |
| |
| if valid_decl { |
| let partial_ty = ctx.finish_parsing(); |
| assert_eq!(*partial_ty.decl(), declaration_to_look_for); |
| } |
| |
| ret |
| } |
| |
| /// A named type is a template parameter, e.g., the `T` in `Foo<T>`. They're always local so |
| /// it's the only exception when there's no declaration for a type. |
| pub(crate) fn type_param( |
| with_id: Option<ItemId>, |
| location: clang::Cursor, |
| ctx: &mut BindgenContext, |
| ) -> Option<TypeId> { |
| let ty = location.cur_type(); |
| |
| debug!( |
| "Item::type_param:\n\ |
| \twith_id = {:?},\n\ |
| \tty = {} {:?},\n\ |
| \tlocation: {:?}", |
| with_id, |
| ty.spelling(), |
| ty, |
| location |
| ); |
| |
| if ty.kind() != clang_sys::CXType_Unexposed { |
| // If the given cursor's type's kind is not Unexposed, then we |
| // aren't looking at a template parameter. This check may need to be |
| // updated in the future if they start properly exposing template |
| // type parameters. |
| return None; |
| } |
| |
| let ty_spelling = ty.spelling(); |
| |
| // Clang does not expose any information about template type parameters |
| // via their clang::Type, nor does it give us their canonical cursors |
| // the straightforward way. However, there are three situations from |
| // which we can find the definition of the template type parameter, if |
| // the cursor is indeed looking at some kind of a template type |
| // parameter or use of one: |
| // |
| // 1. The cursor is pointing at the template type parameter's |
| // definition. This is the trivial case. |
| // |
| // (kind = TemplateTypeParameter, ...) |
| // |
| // 2. The cursor is pointing at a TypeRef whose referenced() cursor is |
| // situation (1). |
| // |
| // (kind = TypeRef, |
| // referenced = (kind = TemplateTypeParameter, ...), |
| // ...) |
| // |
| // 3. The cursor is pointing at some use of a template type parameter |
| // (for example, in a FieldDecl), and this cursor has a child cursor |
| // whose spelling is the same as the parent's type's spelling, and whose |
| // kind is a TypeRef of the situation (2) variety. |
| // |
| // (kind = FieldDecl, |
| // type = (kind = Unexposed, |
| // spelling = "T", |
| // ...), |
| // children = |
| // (kind = TypeRef, |
| // spelling = "T", |
| // referenced = (kind = TemplateTypeParameter, |
| // spelling = "T", |
| // ...), |
| // ...) |
| // ...) |
| // |
| // TODO: The alternative to this hacky pattern matching would be to |
| // maintain proper scopes of template parameters while parsing and use |
| // de Brujin indices to access template parameters, which clang exposes |
| // in the cursor's type's canonical type's spelling: |
| // "type-parameter-x-y". That is probably a better approach long-term, |
| // but maintaining these scopes properly would require more changes to |
| // the whole libclang -> IR parsing code. |
| |
| fn is_template_with_spelling( |
| refd: &clang::Cursor, |
| spelling: &str, |
| ) -> bool { |
| lazy_static! { |
| static ref ANON_TYPE_PARAM_RE: regex::Regex = |
| regex::Regex::new(r"^type\-parameter\-\d+\-\d+$").unwrap(); |
| } |
| |
| if refd.kind() != clang_sys::CXCursor_TemplateTypeParameter { |
| return false; |
| } |
| |
| let refd_spelling = refd.spelling(); |
| refd_spelling == spelling || |
| // Allow for anonymous template parameters. |
| (refd_spelling.is_empty() && ANON_TYPE_PARAM_RE.is_match(spelling.as_ref())) |
| } |
| |
| let definition = if is_template_with_spelling(&location, &ty_spelling) { |
| // Situation (1) |
| location |
| } else if location.kind() == clang_sys::CXCursor_TypeRef { |
| // Situation (2) |
| match location.referenced() { |
| Some(refd) |
| if is_template_with_spelling(&refd, &ty_spelling) => |
| { |
| refd |
| } |
| _ => return None, |
| } |
| } else { |
| // Situation (3) |
| let mut definition = None; |
| |
| location.visit(|child| { |
| let child_ty = child.cur_type(); |
| if child_ty.kind() == clang_sys::CXCursor_TypeRef && |
| child_ty.spelling() == ty_spelling |
| { |
| match child.referenced() { |
| Some(refd) |
| if is_template_with_spelling( |
| &refd, |
| &ty_spelling, |
| ) => |
| { |
| definition = Some(refd); |
| return clang_sys::CXChildVisit_Break; |
| } |
| _ => {} |
| } |
| } |
| |
| clang_sys::CXChildVisit_Continue |
| }); |
| |
| definition? |
| }; |
| assert!(is_template_with_spelling(&definition, &ty_spelling)); |
| |
| // Named types are always parented to the root module. They are never |
| // referenced with namespace prefixes, and they can't inherit anything |
| // from their parent either, so it is simplest to just hang them off |
| // something we know will always exist. |
| let parent = ctx.root_module().into(); |
| |
| if let Some(id) = ctx.get_type_param(&definition) { |
| if let Some(with_id) = with_id { |
| return Some(ctx.build_ty_wrapper( |
| with_id, |
| id, |
| Some(parent), |
| &ty, |
| )); |
| } else { |
| return Some(id); |
| } |
| } |
| |
| // See tests/headers/const_tparam.hpp and |
| // tests/headers/variadic_tname.hpp. |
| let name = ty_spelling.replace("const ", "").replace('.', ""); |
| |
| let id = with_id.unwrap_or_else(|| ctx.next_item_id()); |
| let item = Item::new( |
| id, |
| None, |
| None, |
| parent, |
| ItemKind::Type(Type::named(name)), |
| Some(location.location()), |
| ); |
| ctx.add_type_param(item, definition); |
| Some(id.as_type_id_unchecked()) |
| } |
| } |
| |
| impl ItemCanonicalName for Item { |
| fn canonical_name(&self, ctx: &BindgenContext) -> String { |
| debug_assert!( |
| ctx.in_codegen_phase(), |
| "You're not supposed to call this yet" |
| ); |
| self.canonical_name |
| .borrow_with(|| { |
| let in_namespace = ctx.options().enable_cxx_namespaces || |
| ctx.options().disable_name_namespacing; |
| |
| if in_namespace { |
| self.name(ctx).within_namespaces().get() |
| } else { |
| self.name(ctx).get() |
| } |
| }) |
| .clone() |
| } |
| } |
| |
| impl ItemCanonicalPath for Item { |
| fn namespace_aware_canonical_path( |
| &self, |
| ctx: &BindgenContext, |
| ) -> Vec<String> { |
| let mut path = self.canonical_path(ctx); |
| |
| // ASSUMPTION: (disable_name_namespacing && cxx_namespaces) |
| // is equivalent to |
| // disable_name_namespacing |
| if ctx.options().disable_name_namespacing { |
| // Only keep the last item in path |
| let split_idx = path.len() - 1; |
| path = path.split_off(split_idx); |
| } else if !ctx.options().enable_cxx_namespaces { |
| // Ignore first item "root" |
| path = vec![path[1..].join("_")]; |
| } |
| |
| if self.is_constified_enum_module(ctx) { |
| path.push(CONSTIFIED_ENUM_MODULE_REPR_NAME.into()); |
| } |
| |
| path |
| } |
| |
| fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String> { |
| self.compute_path(ctx, UserMangled::Yes) |
| } |
| } |
| |
| /// Whether to use the user-mangled name (mangled by the `item_name` callback or |
| /// not. |
| /// |
| /// Most of the callers probably want just yes, but the ones dealing with |
| /// allowlisting and blocklisting don't. |
| #[derive(Copy, Clone, Debug, PartialEq)] |
| enum UserMangled { |
| No, |
| Yes, |
| } |
| |
| /// Builder struct for naming variations, which hold inside different |
| /// flags for naming options. |
| #[derive(Debug)] |
| pub(crate) struct NameOptions<'a> { |
| item: &'a Item, |
| ctx: &'a BindgenContext, |
| within_namespaces: bool, |
| user_mangled: UserMangled, |
| } |
| |
| impl<'a> NameOptions<'a> { |
| /// Construct a new `NameOptions` |
| pub(crate) fn new(item: &'a Item, ctx: &'a BindgenContext) -> Self { |
| NameOptions { |
| item, |
| ctx, |
| within_namespaces: false, |
| user_mangled: UserMangled::Yes, |
| } |
| } |
| |
| /// Construct the name without the item's containing C++ namespaces mangled |
| /// into it. In other words, the item's name within the item's namespace. |
| pub(crate) fn within_namespaces(&mut self) -> &mut Self { |
| self.within_namespaces = true; |
| self |
| } |
| |
| fn user_mangled(&mut self, user_mangled: UserMangled) -> &mut Self { |
| self.user_mangled = user_mangled; |
| self |
| } |
| |
| /// Construct a name `String` |
| pub(crate) fn get(&self) -> String { |
| self.item.real_canonical_name(self.ctx, self) |
| } |
| } |