| //! Common context that is passed around during parsing and codegen. | |
| use super::super::time::Timer; | |
| use super::analysis::{ | |
| analyze, as_cannot_derive_set, CannotDerive, DeriveTrait, | |
| HasDestructorAnalysis, HasFloat, HasTypeParameterInArray, | |
| HasVtableAnalysis, HasVtableResult, SizednessAnalysis, SizednessResult, | |
| UsedTemplateParameters, | |
| }; | |
| use super::derive::{ | |
| CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, | |
| CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd, | |
| }; | |
| use super::function::Function; | |
| use super::int::IntKind; | |
| use super::item::{IsOpaque, Item, ItemAncestors, ItemSet}; | |
| use super::item_kind::ItemKind; | |
| use super::module::{Module, ModuleKind}; | |
| use super::template::{TemplateInstantiation, TemplateParameters}; | |
| use super::traversal::{self, Edge, ItemTraversal}; | |
| use super::ty::{FloatKind, Type, TypeKind}; | |
| use crate::clang::{self, ABIKind, Cursor}; | |
| use crate::codegen::CodegenError; | |
| use crate::ir::item::ItemCanonicalName; | |
| use crate::BindgenOptions; | |
| use crate::{Entry, HashMap, HashSet}; | |
| use proc_macro2::{Ident, Span, TokenStream}; | |
| use quote::ToTokens; | |
| use std::borrow::Cow; | |
| use std::cell::{Cell, RefCell}; | |
| use std::collections::{BTreeSet, HashMap as StdHashMap}; | |
| use std::mem; | |
| use std::path::Path; | |
| /// An identifier for some kind of IR item. | |
| #[derive(Debug, Copy, Clone, Eq, PartialOrd, Ord, Hash)] | |
| pub(crate) struct ItemId(usize); | |
| /// Declare a newtype around `ItemId` with conversion methods. | |
| macro_rules! item_id_newtype { | |
| ( | |
| $( #[$attr:meta] )* | |
| pub(crate) struct $name:ident(ItemId) | |
| where | |
| $( #[$checked_attr:meta] )* | |
| checked = $checked:ident with $check_method:ident, | |
| $( #[$expected_attr:meta] )* | |
| expected = $expected:ident, | |
| $( #[$unchecked_attr:meta] )* | |
| unchecked = $unchecked:ident; | |
| ) => { | |
| $( #[$attr] )* | |
| #[derive(Debug, Copy, Clone, Eq, PartialOrd, Ord, Hash)] | |
| pub(crate) struct $name(ItemId); | |
| impl $name { | |
| /// Create an `ItemResolver` from this ID. | |
| #[allow(dead_code)] | |
| pub(crate) fn into_resolver(self) -> ItemResolver { | |
| let id: ItemId = self.into(); | |
| id.into() | |
| } | |
| } | |
| impl<T> ::std::cmp::PartialEq<T> for $name | |
| where | |
| T: Copy + Into<ItemId> | |
| { | |
| fn eq(&self, rhs: &T) -> bool { | |
| let rhs: ItemId = (*rhs).into(); | |
| self.0 == rhs | |
| } | |
| } | |
| impl From<$name> for ItemId { | |
| fn from(id: $name) -> ItemId { | |
| id.0 | |
| } | |
| } | |
| impl<'a> From<&'a $name> for ItemId { | |
| fn from(id: &'a $name) -> ItemId { | |
| id.0 | |
| } | |
| } | |
| #[allow(dead_code)] | |
| impl ItemId { | |
| $( #[$checked_attr] )* | |
| pub(crate) fn $checked(&self, ctx: &BindgenContext) -> Option<$name> { | |
| if ctx.resolve_item(*self).kind().$check_method() { | |
| Some($name(*self)) | |
| } else { | |
| None | |
| } | |
| } | |
| $( #[$expected_attr] )* | |
| pub(crate) fn $expected(&self, ctx: &BindgenContext) -> $name { | |
| self.$checked(ctx) | |
| .expect(concat!( | |
| stringify!($expected), | |
| " called with ItemId that points to the wrong ItemKind" | |
| )) | |
| } | |
| $( #[$unchecked_attr] )* | |
| pub(crate) fn $unchecked(&self) -> $name { | |
| $name(*self) | |
| } | |
| } | |
| } | |
| } | |
| item_id_newtype! { | |
| /// An identifier for an `Item` whose `ItemKind` is known to be | |
| /// `ItemKind::Type`. | |
| pub(crate) struct TypeId(ItemId) | |
| where | |
| /// Convert this `ItemId` into a `TypeId` if its associated item is a type, | |
| /// otherwise return `None`. | |
| checked = as_type_id with is_type, | |
| /// Convert this `ItemId` into a `TypeId`. | |
| /// | |
| /// If this `ItemId` does not point to a type, then panic. | |
| expected = expect_type_id, | |
| /// Convert this `ItemId` into a `TypeId` without actually checking whether | |
| /// this ID actually points to a `Type`. | |
| unchecked = as_type_id_unchecked; | |
| } | |
| item_id_newtype! { | |
| /// An identifier for an `Item` whose `ItemKind` is known to be | |
| /// `ItemKind::Module`. | |
| pub(crate) struct ModuleId(ItemId) | |
| where | |
| /// Convert this `ItemId` into a `ModuleId` if its associated item is a | |
| /// module, otherwise return `None`. | |
| checked = as_module_id with is_module, | |
| /// Convert this `ItemId` into a `ModuleId`. | |
| /// | |
| /// If this `ItemId` does not point to a module, then panic. | |
| expected = expect_module_id, | |
| /// Convert this `ItemId` into a `ModuleId` without actually checking | |
| /// whether this ID actually points to a `Module`. | |
| unchecked = as_module_id_unchecked; | |
| } | |
| item_id_newtype! { | |
| /// An identifier for an `Item` whose `ItemKind` is known to be | |
| /// `ItemKind::Var`. | |
| pub(crate) struct VarId(ItemId) | |
| where | |
| /// Convert this `ItemId` into a `VarId` if its associated item is a var, | |
| /// otherwise return `None`. | |
| checked = as_var_id with is_var, | |
| /// Convert this `ItemId` into a `VarId`. | |
| /// | |
| /// If this `ItemId` does not point to a var, then panic. | |
| expected = expect_var_id, | |
| /// Convert this `ItemId` into a `VarId` without actually checking whether | |
| /// this ID actually points to a `Var`. | |
| unchecked = as_var_id_unchecked; | |
| } | |
| item_id_newtype! { | |
| /// An identifier for an `Item` whose `ItemKind` is known to be | |
| /// `ItemKind::Function`. | |
| pub(crate) struct FunctionId(ItemId) | |
| where | |
| /// Convert this `ItemId` into a `FunctionId` if its associated item is a function, | |
| /// otherwise return `None`. | |
| checked = as_function_id with is_function, | |
| /// Convert this `ItemId` into a `FunctionId`. | |
| /// | |
| /// If this `ItemId` does not point to a function, then panic. | |
| expected = expect_function_id, | |
| /// Convert this `ItemId` into a `FunctionId` without actually checking whether | |
| /// this ID actually points to a `Function`. | |
| unchecked = as_function_id_unchecked; | |
| } | |
| impl From<ItemId> for usize { | |
| fn from(id: ItemId) -> usize { | |
| id.0 | |
| } | |
| } | |
| impl ItemId { | |
| /// Get a numeric representation of this ID. | |
| pub(crate) fn as_usize(self) -> usize { | |
| self.into() | |
| } | |
| } | |
| impl<T> ::std::cmp::PartialEq<T> for ItemId | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn eq(&self, rhs: &T) -> bool { | |
| let rhs: ItemId = (*rhs).into(); | |
| self.0 == rhs.0 | |
| } | |
| } | |
| impl<T> CanDeriveDebug for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_debug(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_debug && ctx.lookup_can_derive_debug(*self) | |
| } | |
| } | |
| impl<T> CanDeriveDefault for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_default(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_default && ctx.lookup_can_derive_default(*self) | |
| } | |
| } | |
| impl<T> CanDeriveCopy for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_copy(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_copy && ctx.lookup_can_derive_copy(*self) | |
| } | |
| } | |
| impl<T> CanDeriveHash for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_hash(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_hash && ctx.lookup_can_derive_hash(*self) | |
| } | |
| } | |
| impl<T> CanDerivePartialOrd for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_partialord(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_partialord && | |
| ctx.lookup_can_derive_partialeq_or_partialord(*self) == | |
| CanDerive::Yes | |
| } | |
| } | |
| impl<T> CanDerivePartialEq for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_partialeq(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_partialeq && | |
| ctx.lookup_can_derive_partialeq_or_partialord(*self) == | |
| CanDerive::Yes | |
| } | |
| } | |
| impl<T> CanDeriveEq for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_eq(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_eq && | |
| ctx.lookup_can_derive_partialeq_or_partialord(*self) == | |
| CanDerive::Yes && | |
| !ctx.lookup_has_float(*self) | |
| } | |
| } | |
| impl<T> CanDeriveOrd for T | |
| where | |
| T: Copy + Into<ItemId>, | |
| { | |
| fn can_derive_ord(&self, ctx: &BindgenContext) -> bool { | |
| ctx.options().derive_ord && | |
| ctx.lookup_can_derive_partialeq_or_partialord(*self) == | |
| CanDerive::Yes && | |
| !ctx.lookup_has_float(*self) | |
| } | |
| } | |
| /// A key used to index a resolved type, so we only process it once. | |
| /// | |
| /// This is almost always a USR string (an unique identifier generated by | |
| /// clang), but it can also be the canonical declaration if the type is unnamed, | |
| /// in which case clang may generate the same USR for multiple nested unnamed | |
| /// types. | |
| #[derive(Eq, PartialEq, Hash, Debug)] | |
| enum TypeKey { | |
| Usr(String), | |
| Declaration(Cursor), | |
| } | |
| /// A context used during parsing and generation of structs. | |
| #[derive(Debug)] | |
| pub(crate) struct BindgenContext { | |
| /// The map of all the items parsed so far, keyed off `ItemId`. | |
| items: Vec<Option<Item>>, | |
| /// Clang USR to type map. This is needed to be able to associate types with | |
| /// item ids during parsing. | |
| types: HashMap<TypeKey, TypeId>, | |
| /// Maps from a cursor to the item ID of the named template type parameter | |
| /// for that cursor. | |
| type_params: HashMap<Cursor, TypeId>, | |
| /// A cursor to module map. Similar reason than above. | |
| modules: HashMap<Cursor, ModuleId>, | |
| /// The root module, this is guaranteed to be an item of kind Module. | |
| root_module: ModuleId, | |
| /// Current module being traversed. | |
| current_module: ModuleId, | |
| /// A `HashMap` keyed on a type definition, and whose value is the parent ID | |
| /// of the declaration. | |
| /// | |
| /// This is used to handle the cases where the semantic and the lexical | |
| /// parents of the cursor differ, like when a nested class is defined | |
| /// outside of the parent class. | |
| semantic_parents: HashMap<Cursor, ItemId>, | |
| /// A stack with the current type declarations and types we're parsing. This | |
| /// is needed to avoid infinite recursion when parsing a type like: | |
| /// | |
| /// struct c { struct c* next; }; | |
| /// | |
| /// This means effectively, that a type has a potential ID before knowing if | |
| /// it's a correct type. But that's not important in practice. | |
| /// | |
| /// We could also use the `types` `HashMap`, but my intention with it is that | |
| /// only valid types and declarations end up there, and this could | |
| /// potentially break that assumption. | |
| currently_parsed_types: Vec<PartialType>, | |
| /// A map with all the already parsed macro names. This is done to avoid | |
| /// hard errors while parsing duplicated macros, as well to allow macro | |
| /// expression parsing. | |
| /// | |
| /// This needs to be an `std::HashMap` because the [`cexpr`] API requires it. | |
| parsed_macros: StdHashMap<Vec<u8>, cexpr::expr::EvalResult>, | |
| /// A map with all include locations. | |
| /// | |
| /// This is needed so that items are created in the order they are defined in. | |
| /// | |
| /// The key is the included file, the value is a pair of the source file and | |
| /// the position of the `#include` directive in the source file. | |
| includes: StdHashMap<String, (String, usize)>, | |
| /// A set of all the included filenames. | |
| deps: BTreeSet<Box<str>>, | |
| /// The active replacements collected from replaces="xxx" annotations. | |
| replacements: HashMap<Vec<String>, ItemId>, | |
| collected_typerefs: bool, | |
| in_codegen: bool, | |
| /// The translation unit for parsing. | |
| translation_unit: clang::TranslationUnit, | |
| /// The translation unit for macro fallback parsing. | |
| fallback_tu: Option<clang::FallbackTranslationUnit>, | |
| /// Target information that can be useful for some stuff. | |
| target_info: clang::TargetInfo, | |
| /// The options given by the user via cli or other medium. | |
| options: BindgenOptions, | |
| /// Whether an opaque array was generated | |
| generated_opaque_array: RefCell<HashSet<usize>>, | |
| /// Whether a bindgen complex was generated | |
| generated_bindgen_complex: Cell<bool>, | |
| /// Whether a bindgen float16 was generated | |
| generated_bindgen_float16: Cell<bool>, | |
| /// The set of `ItemId`s that are allowlisted. This the very first thing | |
| /// computed after parsing our IR, and before running any of our analyses. | |
| allowlisted: Option<ItemSet>, | |
| /// Cache for calls to `ParseCallbacks::blocklisted_type_implements_trait` | |
| blocklisted_types_implement_traits: | |
| RefCell<HashMap<DeriveTrait, HashMap<ItemId, CanDerive>>>, | |
| /// The set of `ItemId`s that are allowlisted for code generation _and_ that | |
| /// we should generate accounting for the codegen options. | |
| /// | |
| /// It's computed right after computing the allowlisted items. | |
| codegen_items: Option<ItemSet>, | |
| /// Map from an item's ID to the set of template parameter items that it | |
| /// uses. See `ir::named` for more details. Always `Some` during the codegen | |
| /// phase. | |
| used_template_parameters: Option<HashMap<ItemId, ItemSet>>, | |
| /// The set of `TypeKind::Comp` items found during parsing that need their | |
| /// bitfield allocation units computed. Drained in `compute_bitfield_units`. | |
| need_bitfield_allocation: Vec<ItemId>, | |
| /// The set of enums that are defined by a pair of `enum` and `typedef`, | |
| /// which is legal in C (but not C++). | |
| /// | |
| /// ```c++ | |
| /// // in either order | |
| /// enum Enum { Variants... }; | |
| /// typedef int16_t Enum; | |
| /// ``` | |
| /// | |
| /// The stored `ItemId` is that of the `TypeKind::Enum`, not of the | |
| /// `TypeKind::Alias`. | |
| /// | |
| /// This is populated when we enter codegen by `compute_enum_typedef_combos` | |
| /// and is always `None` before that and `Some` after. | |
| enum_typedef_combos: Option<HashSet<ItemId>>, | |
| /// The set of (`ItemId`s of) types that can't derive debug. | |
| /// | |
| /// This is populated when we enter codegen by `compute_cannot_derive_debug` | |
| /// and is always `None` before that and `Some` after. | |
| cannot_derive_debug: Option<HashSet<ItemId>>, | |
| /// The set of (`ItemId`s of) types that can't derive default. | |
| /// | |
| /// This is populated when we enter codegen by `compute_cannot_derive_default` | |
| /// and is always `None` before that and `Some` after. | |
| cannot_derive_default: Option<HashSet<ItemId>>, | |
| /// The set of (`ItemId`s of) types that can't derive copy. | |
| /// | |
| /// This is populated when we enter codegen by `compute_cannot_derive_copy` | |
| /// and is always `None` before that and `Some` after. | |
| cannot_derive_copy: Option<HashSet<ItemId>>, | |
| /// The set of (`ItemId`s of) types that can't derive hash. | |
| /// | |
| /// This is populated when we enter codegen by `compute_can_derive_hash` | |
| /// and is always `None` before that and `Some` after. | |
| cannot_derive_hash: Option<HashSet<ItemId>>, | |
| /// The map why specified `ItemId`s of) types that can't derive hash. | |
| /// | |
| /// This is populated when we enter codegen by | |
| /// `compute_cannot_derive_partialord_partialeq_or_eq` and is always `None` | |
| /// before that and `Some` after. | |
| cannot_derive_partialeq_or_partialord: Option<HashMap<ItemId, CanDerive>>, | |
| /// The sizedness of types. | |
| /// | |
| /// This is populated by `compute_sizedness` and is always `None` before | |
| /// that function is invoked and `Some` afterwards. | |
| sizedness: Option<HashMap<TypeId, SizednessResult>>, | |
| /// The set of (`ItemId's of`) types that has vtable. | |
| /// | |
| /// Populated when we enter codegen by `compute_has_vtable`; always `None` | |
| /// before that and `Some` after. | |
| have_vtable: Option<HashMap<ItemId, HasVtableResult>>, | |
| /// The set of (`ItemId's of`) types that has destructor. | |
| /// | |
| /// Populated when we enter codegen by `compute_has_destructor`; always `None` | |
| /// before that and `Some` after. | |
| have_destructor: Option<HashSet<ItemId>>, | |
| /// The set of (`ItemId's of`) types that has array. | |
| /// | |
| /// Populated when we enter codegen by `compute_has_type_param_in_array`; always `None` | |
| /// before that and `Some` after. | |
| has_type_param_in_array: Option<HashSet<ItemId>>, | |
| /// The set of (`ItemId's of`) types that has float. | |
| /// | |
| /// Populated when we enter codegen by `compute_has_float`; always `None` | |
| /// before that and `Some` after. | |
| has_float: Option<HashSet<ItemId>>, | |
| } | |
| /// A traversal of allowlisted items. | |
| struct AllowlistedItemsTraversal<'ctx> { | |
| ctx: &'ctx BindgenContext, | |
| traversal: ItemTraversal<'ctx, ItemSet, Vec<ItemId>>, | |
| } | |
| impl Iterator for AllowlistedItemsTraversal<'_> { | |
| type Item = ItemId; | |
| fn next(&mut self) -> Option<ItemId> { | |
| loop { | |
| let id = self.traversal.next()?; | |
| if self.ctx.resolve_item(id).is_blocklisted(self.ctx) { | |
| continue; | |
| } | |
| return Some(id); | |
| } | |
| } | |
| } | |
| impl<'ctx> AllowlistedItemsTraversal<'ctx> { | |
| /// Construct a new allowlisted items traversal. | |
| pub(crate) fn new<R>( | |
| ctx: &'ctx BindgenContext, | |
| roots: R, | |
| predicate: for<'a> fn(&'a BindgenContext, Edge) -> bool, | |
| ) -> Self | |
| where | |
| R: IntoIterator<Item = ItemId>, | |
| { | |
| AllowlistedItemsTraversal { | |
| ctx, | |
| traversal: ItemTraversal::new(ctx, roots, predicate), | |
| } | |
| } | |
| } | |
| impl BindgenContext { | |
| /// Construct the context for the given `options`. | |
| pub(crate) fn new( | |
| options: BindgenOptions, | |
| input_unsaved_files: &[clang::UnsavedFile], | |
| ) -> Self { | |
| // TODO(emilio): Use the CXTargetInfo here when available. | |
| // | |
| // see: https://reviews.llvm.org/D32389 | |
| let index = clang::Index::new(false, true); | |
| let parse_options = | |
| clang_sys::CXTranslationUnit_DetailedPreprocessingRecord; | |
| let translation_unit = { | |
| let _t = | |
| Timer::new("translation_unit").with_output(options.time_phases); | |
| clang::TranslationUnit::parse( | |
| &index, | |
| "", | |
| &options.clang_args, | |
| input_unsaved_files, | |
| parse_options, | |
| ).expect("libclang error; possible causes include: | |
| - Invalid flag syntax | |
| - Unrecognized flags | |
| - Invalid flag arguments | |
| - File I/O errors | |
| - Host vs. target architecture mismatch | |
| If you encounter an error missing from this list, please file an issue or a PR!") | |
| }; | |
| let target_info = clang::TargetInfo::new(&translation_unit); | |
| let root_module = Self::build_root_module(ItemId(0)); | |
| let root_module_id = root_module.id().as_module_id_unchecked(); | |
| // depfiles need to include the explicitly listed headers too | |
| let deps = options.input_headers.iter().cloned().collect(); | |
| BindgenContext { | |
| items: vec![Some(root_module)], | |
| includes: Default::default(), | |
| deps, | |
| types: Default::default(), | |
| type_params: Default::default(), | |
| modules: Default::default(), | |
| root_module: root_module_id, | |
| current_module: root_module_id, | |
| semantic_parents: Default::default(), | |
| currently_parsed_types: vec![], | |
| parsed_macros: Default::default(), | |
| replacements: Default::default(), | |
| collected_typerefs: false, | |
| in_codegen: false, | |
| translation_unit, | |
| fallback_tu: None, | |
| target_info, | |
| options, | |
| generated_bindgen_complex: Cell::new(false), | |
| generated_bindgen_float16: Cell::new(false), | |
| generated_opaque_array: Default::default(), | |
| allowlisted: None, | |
| blocklisted_types_implement_traits: Default::default(), | |
| codegen_items: None, | |
| used_template_parameters: None, | |
| need_bitfield_allocation: Default::default(), | |
| enum_typedef_combos: None, | |
| cannot_derive_debug: None, | |
| cannot_derive_default: None, | |
| cannot_derive_copy: None, | |
| cannot_derive_hash: None, | |
| cannot_derive_partialeq_or_partialord: None, | |
| sizedness: None, | |
| have_vtable: None, | |
| have_destructor: None, | |
| has_type_param_in_array: None, | |
| has_float: None, | |
| } | |
| } | |
| /// Returns `true` if the target architecture is wasm32 | |
| pub(crate) fn is_target_wasm32(&self) -> bool { | |
| self.target_info.triple.starts_with("wasm32-") | |
| } | |
| /// Creates a timer for the current bindgen phase. If `time_phases` is `true`, | |
| /// the timer will print to stderr when it is dropped, otherwise it will do | |
| /// nothing. | |
| pub(crate) fn timer<'a>(&self, name: &'a str) -> Timer<'a> { | |
| Timer::new(name).with_output(self.options.time_phases) | |
| } | |
| /// Returns the pointer width to use for the target for the current | |
| /// translation. | |
| pub(crate) fn target_pointer_size(&self) -> usize { | |
| self.target_info.pointer_width / 8 | |
| } | |
| /// Returns the ABI, which is mostly useful for determining the mangling kind. | |
| pub(crate) fn abi_kind(&self) -> ABIKind { | |
| self.target_info.abi | |
| } | |
| /// Get the stack of partially parsed types that we are in the middle of | |
| /// parsing. | |
| pub(crate) fn currently_parsed_types(&self) -> &[PartialType] { | |
| &self.currently_parsed_types[..] | |
| } | |
| /// Begin parsing the given partial type, and push it onto the | |
| /// `currently_parsed_types` stack so that we won't infinite recurse if we | |
| /// run into a reference to it while parsing it. | |
| pub(crate) fn begin_parsing(&mut self, partial_ty: PartialType) { | |
| self.currently_parsed_types.push(partial_ty); | |
| } | |
| /// Finish parsing the current partial type, pop it off the | |
| /// `currently_parsed_types` stack, and return it. | |
| pub(crate) fn finish_parsing(&mut self) -> PartialType { | |
| self.currently_parsed_types.pop().expect( | |
| "should have been parsing a type, if we finished parsing a type", | |
| ) | |
| } | |
| /// Add the location of the `#include` directive for the `included_file`. | |
| pub(crate) fn add_include( | |
| &mut self, | |
| source_file: String, | |
| included_file: String, | |
| offset: usize, | |
| ) { | |
| self.includes | |
| .entry(included_file) | |
| .or_insert((source_file, offset)); | |
| } | |
| /// Get the location of the first `#include` directive for the `included_file`. | |
| pub(crate) fn included_file_location( | |
| &self, | |
| included_file: &str, | |
| ) -> Option<(String, usize)> { | |
| self.includes.get(included_file).cloned() | |
| } | |
| /// Add an included file. | |
| pub(crate) fn add_dep(&mut self, dep: Box<str>) { | |
| self.deps.insert(dep); | |
| } | |
| /// Get any included files. | |
| pub(crate) fn deps(&self) -> &BTreeSet<Box<str>> { | |
| &self.deps | |
| } | |
| /// Define a new item. | |
| /// | |
| /// This inserts it into the internal items set, and its type into the | |
| /// internal types set. | |
| pub(crate) fn add_item( | |
| &mut self, | |
| item: Item, | |
| declaration: Option<Cursor>, | |
| location: Option<Cursor>, | |
| ) { | |
| debug!("BindgenContext::add_item({item:?}, declaration: {declaration:?}, loc: {location:?}"); | |
| debug_assert!( | |
| declaration.is_some() || | |
| !item.kind().is_type() || | |
| item.kind().expect_type().is_builtin_or_type_param() || | |
| item.kind().expect_type().is_opaque(self, &item) || | |
| item.kind().expect_type().is_unresolved_ref(), | |
| "Adding a type without declaration?" | |
| ); | |
| let id = item.id(); | |
| let is_type = item.kind().is_type(); | |
| let is_unnamed = is_type && item.expect_type().name().is_none(); | |
| let is_template_instantiation = | |
| is_type && item.expect_type().is_template_instantiation(); | |
| if item.id() != self.root_module { | |
| self.add_item_to_module(&item); | |
| } | |
| if is_type && item.expect_type().is_comp() { | |
| self.need_bitfield_allocation.push(id); | |
| } | |
| let old_item = self.items[id.0].replace(item); | |
| assert!( | |
| old_item.is_none(), | |
| "should not have already associated an item with the given id" | |
| ); | |
| // Unnamed items can have an USR, but they can't be referenced from | |
| // other sites explicitly and the USR can match if the unnamed items are | |
| // nested, so don't bother tracking them. | |
| if !is_type || is_template_instantiation { | |
| return; | |
| } | |
| if let Some(mut declaration) = declaration { | |
| if !declaration.is_valid() { | |
| if let Some(location) = location { | |
| if location.is_template_like() { | |
| declaration = location; | |
| } | |
| } | |
| } | |
| declaration = declaration.canonical(); | |
| if !declaration.is_valid() { | |
| // This could happen, for example, with types like `int*` or | |
| // similar. | |
| // | |
| // Fortunately, we don't care about those types being | |
| // duplicated, so we can just ignore them. | |
| debug!( | |
| "Invalid declaration {declaration:?} found for type {:?}", | |
| self.resolve_item_fallible(id) | |
| .unwrap() | |
| .kind() | |
| .expect_type() | |
| ); | |
| return; | |
| } | |
| let key = if is_unnamed { | |
| TypeKey::Declaration(declaration) | |
| } else if let Some(usr) = declaration.usr() { | |
| TypeKey::Usr(usr) | |
| } else { | |
| warn!("Valid declaration with no USR: {declaration:?}, {location:?}"); | |
| TypeKey::Declaration(declaration) | |
| }; | |
| let old = self.types.insert(key, id.as_type_id_unchecked()); | |
| debug_assert_eq!(old, None); | |
| } | |
| } | |
| /// Ensure that every item (other than the root module) is in a module's | |
| /// children list. This is to make sure that every allowlisted item get's | |
| /// codegen'd, even if its parent is not allowlisted. See issue #769 for | |
| /// details. | |
| fn add_item_to_module(&mut self, item: &Item) { | |
| assert_ne!(item.id(), self.root_module); | |
| assert!(self.resolve_item_fallible(item.id()).is_none()); | |
| if let Some(ref mut parent) = self.items[item.parent_id().0] { | |
| if let Some(module) = parent.as_module_mut() { | |
| debug!( | |
| "add_item_to_module: adding {:?} as child of parent module {:?}", | |
| item.id(), | |
| item.parent_id() | |
| ); | |
| module.children_mut().insert(item.id()); | |
| return; | |
| } | |
| } | |
| debug!( | |
| "add_item_to_module: adding {:?} as child of current module {:?}", | |
| item.id(), | |
| self.current_module | |
| ); | |
| self.items[self.current_module.0 .0] | |
| .as_mut() | |
| .expect("Should always have an item for self.current_module") | |
| .as_module_mut() | |
| .expect("self.current_module should always be a module") | |
| .children_mut() | |
| .insert(item.id()); | |
| } | |
| /// Add a new named template type parameter to this context's item set. | |
| pub(crate) fn add_type_param(&mut self, item: Item, definition: Cursor) { | |
| debug!("BindgenContext::add_type_param: item = {item:?}; definition = {definition:?}"); | |
| assert!( | |
| item.expect_type().is_type_param(), | |
| "Should directly be a named type, not a resolved reference or anything" | |
| ); | |
| assert_eq!( | |
| definition.kind(), | |
| clang_sys::CXCursor_TemplateTypeParameter | |
| ); | |
| self.add_item_to_module(&item); | |
| let id = item.id(); | |
| let old_item = self.items[id.0].replace(item); | |
| assert!( | |
| old_item.is_none(), | |
| "should not have already associated an item with the given id" | |
| ); | |
| let old_named_ty = self | |
| .type_params | |
| .insert(definition, id.as_type_id_unchecked()); | |
| assert!( | |
| old_named_ty.is_none(), | |
| "should not have already associated a named type with this id" | |
| ); | |
| } | |
| /// Get the named type defined at the given cursor location, if we've | |
| /// already added one. | |
| pub(crate) fn get_type_param(&self, definition: &Cursor) -> Option<TypeId> { | |
| assert_eq!( | |
| definition.kind(), | |
| clang_sys::CXCursor_TemplateTypeParameter | |
| ); | |
| self.type_params.get(definition).copied() | |
| } | |
| // TODO: Move all this syntax crap to other part of the code. | |
| /// Mangles a name so it doesn't conflict with any keyword. | |
| #[rustfmt::skip] | |
| pub(crate) fn rust_mangle<'a>(&self, name: &'a str) -> Cow<'a, str> { | |
| if name.contains('@') || | |
| name.contains('?') || | |
| name.contains('$') || | |
| matches!( | |
| name, | |
| "abstract" | "alignof" | "as" | "async" | "await" | "become" | | |
| "box" | "break" | "const" | "continue" | "crate" | "do" | | |
| "dyn" | "else" | "enum" | "extern" | "false" | "final" | | |
| "fn" | "for" | "gen" | "if" | "impl" | "in" | "let" | "loop" | | |
| "macro" | "match" | "mod" | "move" | "mut" | "offsetof" | | |
| "override" | "priv" | "proc" | "pub" | "pure" | "ref" | | |
| "return" | "Self" | "self" | "sizeof" | "static" | | |
| "struct" | "super" | "trait" | "true" | "try" | "type" | "typeof" | | |
| "unsafe" | "unsized" | "use" | "virtual" | "where" | | |
| "while" | "yield" | "str" | "bool" | "f32" | "f64" | | |
| "usize" | "isize" | "u128" | "i128" | "u64" | "i64" | | |
| "u32" | "i32" | "u16" | "i16" | "u8" | "i8" | "_" | |
| ) | |
| { | |
| let mut s = name.to_owned(); | |
| s = s.replace('@', "_"); | |
| s = s.replace('?', "_"); | |
| s = s.replace('$', "_"); | |
| s.push('_'); | |
| return Cow::Owned(s); | |
| } | |
| Cow::Borrowed(name) | |
| } | |
| /// Returns a mangled name as a rust identifier. | |
| pub(crate) fn rust_ident<S>(&self, name: S) -> Ident | |
| where | |
| S: AsRef<str>, | |
| { | |
| self.rust_ident_raw(self.rust_mangle(name.as_ref())) | |
| } | |
| /// Returns a mangled name as a rust identifier. | |
| pub(crate) fn rust_ident_raw<T>(&self, name: T) -> Ident | |
| where | |
| T: AsRef<str>, | |
| { | |
| Ident::new(name.as_ref(), Span::call_site()) | |
| } | |
| /// Iterate over all items that have been defined. | |
| pub(crate) fn items(&self) -> impl Iterator<Item = (ItemId, &Item)> { | |
| self.items.iter().enumerate().filter_map(|(index, item)| { | |
| let item = item.as_ref()?; | |
| Some((ItemId(index), item)) | |
| }) | |
| } | |
| /// Have we collected all unresolved type references yet? | |
| pub(crate) fn collected_typerefs(&self) -> bool { | |
| self.collected_typerefs | |
| } | |
| /// Gather all the unresolved type references. | |
| fn collect_typerefs( | |
| &mut self, | |
| ) -> Vec<(ItemId, clang::Type, Cursor, Option<ItemId>)> { | |
| debug_assert!(!self.collected_typerefs); | |
| self.collected_typerefs = true; | |
| let mut typerefs = vec![]; | |
| for (id, item) in self.items() { | |
| let kind = item.kind(); | |
| let Some(ty) = kind.as_type() else { continue }; | |
| if let TypeKind::UnresolvedTypeRef(ref ty, loc, parent_id) = | |
| *ty.kind() | |
| { | |
| typerefs.push((id, *ty, loc, parent_id)); | |
| } | |
| } | |
| typerefs | |
| } | |
| /// Collect all of our unresolved type references and resolve them. | |
| fn resolve_typerefs(&mut self) { | |
| let _t = self.timer("resolve_typerefs"); | |
| let typerefs = self.collect_typerefs(); | |
| for (id, ty, loc, parent_id) in typerefs { | |
| let _resolved = | |
| { | |
| let resolved = Item::from_ty(&ty, loc, parent_id, self) | |
| .unwrap_or_else(|_| { | |
| warn!("Could not resolve type reference, falling back \ | |
| to opaque blob"); | |
| Item::new_opaque_type(self.next_item_id(), &ty, self) | |
| }); | |
| let item = self.items[id.0].as_mut().unwrap(); | |
| *item.kind_mut().as_type_mut().unwrap().kind_mut() = | |
| TypeKind::ResolvedTypeRef(resolved); | |
| resolved | |
| }; | |
| // Something in the STL is trolling me. I don't need this assertion | |
| // right now, but worth investigating properly once this lands. | |
| // | |
| // debug_assert!(self.items.get(&resolved).is_some(), "How?"); | |
| // | |
| // if let Some(parent_id) = parent_id { | |
| // assert_eq!(self.items[&resolved].parent_id(), parent_id); | |
| // } | |
| } | |
| } | |
| /// Temporarily loan `Item` with the given `ItemId`. This provides means to | |
| /// mutably borrow `Item` while having a reference to `BindgenContext`. | |
| /// | |
| /// `Item` with the given `ItemId` is removed from the context, given | |
| /// closure is executed and then `Item` is placed back. | |
| /// | |
| /// # Panics | |
| /// | |
| /// Panics if attempt to resolve given `ItemId` inside the given | |
| /// closure is made. | |
| fn with_loaned_item<F, T>(&mut self, id: ItemId, f: F) -> T | |
| where | |
| F: (FnOnce(&BindgenContext, &mut Item) -> T), | |
| { | |
| let mut item = self.items[id.0].take().unwrap(); | |
| let result = f(self, &mut item); | |
| let existing = self.items[id.0].replace(item); | |
| assert!(existing.is_none()); | |
| result | |
| } | |
| /// Compute the bitfield allocation units for all `TypeKind::Comp` items we | |
| /// parsed. | |
| fn compute_bitfield_units(&mut self) { | |
| let _t = self.timer("compute_bitfield_units"); | |
| assert!(self.collected_typerefs()); | |
| let need_bitfield_allocation = | |
| mem::take(&mut self.need_bitfield_allocation); | |
| for id in need_bitfield_allocation { | |
| self.with_loaned_item(id, |ctx, item| { | |
| let ty = item.kind_mut().as_type_mut().unwrap(); | |
| let layout = ty.layout(ctx); | |
| ty.as_comp_mut() | |
| .unwrap() | |
| .compute_bitfield_units(ctx, layout.as_ref()); | |
| }); | |
| } | |
| } | |
| /// Assign a new generated name for each anonymous field. | |
| fn assign_field_names(&mut self) { | |
| let _t = self.timer("assign_field_names"); | |
| let comp_item_ids: Vec<ItemId> = self | |
| .items() | |
| .filter_map(|(id, item)| { | |
| if item.kind().as_type()?.is_comp() { | |
| return Some(id); | |
| } | |
| None | |
| }) | |
| .collect(); | |
| for id in comp_item_ids { | |
| let canonical_type_name = | |
| self.resolve_item(id).canonical_name(self); | |
| self.with_loaned_item(id, |ctx, item| { | |
| item.kind_mut() | |
| .as_type_mut() | |
| .unwrap() | |
| .as_comp_mut() | |
| .unwrap() | |
| .assign_field_names(ctx, &canonical_type_name); | |
| }); | |
| } | |
| } | |
| /// Iterate over all items and replace any item that has been named in a | |
| /// `replaces="SomeType"` annotation with the replacement type. | |
| fn process_replacements(&mut self) { | |
| let _t = self.timer("process_replacements"); | |
| if self.replacements.is_empty() { | |
| debug!("No replacements to process"); | |
| return; | |
| } | |
| // FIXME: This is linear, but the replaces="xxx" annotation was already | |
| // there, and for better or worse it's useful, sigh... | |
| // | |
| // We leverage the ResolvedTypeRef thing, though, which is cool :P. | |
| let mut replacements = vec![]; | |
| for (id, item) in self.items() { | |
| if item.annotations().use_instead_of().is_some() { | |
| continue; | |
| } | |
| // Calls to `canonical_name` are expensive, so eagerly filter out | |
| // items that cannot be replaced. | |
| let Some(ty) = item.kind().as_type() else { | |
| continue; | |
| }; | |
| match *ty.kind() { | |
| TypeKind::Comp(..) | | |
| TypeKind::TemplateAlias(..) | | |
| TypeKind::Enum(..) | | |
| TypeKind::Alias(..) => {} | |
| _ => continue, | |
| } | |
| let path = item.path_for_allowlisting(self); | |
| let replacement = self.replacements.get(&path[1..]); | |
| if let Some(replacement) = replacement { | |
| if *replacement != id { | |
| // We set this just after parsing the annotation. It's | |
| // very unlikely, but this can happen. | |
| if self.resolve_item_fallible(*replacement).is_some() { | |
| replacements.push(( | |
| id.expect_type_id(self), | |
| replacement.expect_type_id(self), | |
| )); | |
| } | |
| } | |
| } | |
| } | |
| for (id, replacement_id) in replacements { | |
| debug!("Replacing {id:?} with {replacement_id:?}"); | |
| let new_parent = { | |
| let item_id: ItemId = id.into(); | |
| let item = self.items[item_id.0].as_mut().unwrap(); | |
| *item.kind_mut().as_type_mut().unwrap().kind_mut() = | |
| TypeKind::ResolvedTypeRef(replacement_id); | |
| item.parent_id() | |
| }; | |
| // Relocate the replacement item from where it was declared, to | |
| // where the thing it is replacing was declared. | |
| // | |
| // First, we'll make sure that its parent ID is correct. | |
| let old_parent = self.resolve_item(replacement_id).parent_id(); | |
| if new_parent == old_parent { | |
| // Same parent and therefore also same containing | |
| // module. Nothing to do here. | |
| continue; | |
| } | |
| let replacement_item_id: ItemId = replacement_id.into(); | |
| self.items[replacement_item_id.0] | |
| .as_mut() | |
| .unwrap() | |
| .set_parent_for_replacement(new_parent); | |
| // Second, make sure that it is in the correct module's children | |
| // set. | |
| let old_module = { | |
| let immut_self = &*self; | |
| old_parent | |
| .ancestors(immut_self) | |
| .chain(Some(immut_self.root_module.into())) | |
| .find(|id| { | |
| let item = immut_self.resolve_item(*id); | |
| item.as_module().is_some_and(|m| { | |
| m.children().contains(&replacement_id.into()) | |
| }) | |
| }) | |
| }; | |
| let old_module = old_module | |
| .expect("Every replacement item should be in a module"); | |
| let new_module = { | |
| let immut_self = &*self; | |
| new_parent | |
| .ancestors(immut_self) | |
| .find(|id| immut_self.resolve_item(*id).is_module()) | |
| }; | |
| let new_module = | |
| new_module.unwrap_or_else(|| self.root_module.into()); | |
| if new_module == old_module { | |
| // Already in the correct module. | |
| continue; | |
| } | |
| self.items[old_module.0] | |
| .as_mut() | |
| .unwrap() | |
| .as_module_mut() | |
| .unwrap() | |
| .children_mut() | |
| .remove(&replacement_id.into()); | |
| self.items[new_module.0] | |
| .as_mut() | |
| .unwrap() | |
| .as_module_mut() | |
| .unwrap() | |
| .children_mut() | |
| .insert(replacement_id.into()); | |
| } | |
| } | |
| /// Enter the code generation phase, invoke the given callback `cb`, and | |
| /// leave the code generation phase. | |
| pub(crate) fn gen<F, Out>( | |
| mut self, | |
| cb: F, | |
| ) -> Result<(Out, BindgenOptions), CodegenError> | |
| where | |
| F: FnOnce(&Self) -> Result<Out, CodegenError>, | |
| { | |
| self.in_codegen = true; | |
| self.resolve_typerefs(); | |
| self.compute_bitfield_units(); | |
| self.process_replacements(); | |
| self.assign_field_names(); | |
| self.assert_no_dangling_references(); | |
| // Compute the allowlisted set after processing replacements and | |
| // resolving type refs, as those are the final mutations of the IR | |
| // graph, and their completion means that the IR graph is now frozen. | |
| self.compute_allowlisted_and_codegen_items(); | |
| // Make sure to do this after processing replacements, since that messes | |
| // with the parentage and module children, and we want to assert that it | |
| // messes with them correctly. | |
| self.assert_every_item_in_a_module(); | |
| self.compute_has_vtable(); | |
| self.compute_sizedness(); | |
| self.compute_has_destructor(); | |
| self.find_used_template_parameters(); | |
| self.compute_enum_typedef_combos(); | |
| self.compute_cannot_derive_debug(); | |
| self.compute_cannot_derive_default(); | |
| self.compute_cannot_derive_copy(); | |
| self.compute_has_type_param_in_array(); | |
| self.compute_has_float(); | |
| self.compute_cannot_derive_hash(); | |
| self.compute_cannot_derive_partialord_partialeq_or_eq(); | |
| let ret = cb(&self)?; | |
| Ok((ret, self.options)) | |
| } | |
| /// When the `__testing_only_extra_assertions` feature is enabled, this | |
| /// function walks the IR graph and asserts that we do not have any edges | |
| /// referencing an `ItemId` for which we do not have an associated IR item. | |
| fn assert_no_dangling_references(&self) { | |
| if cfg!(feature = "__testing_only_extra_assertions") { | |
| for _ in self.assert_no_dangling_item_traversal() { | |
| // The iterator's next method does the asserting for us. | |
| } | |
| } | |
| } | |
| fn assert_no_dangling_item_traversal( | |
| &self, | |
| ) -> traversal::AssertNoDanglingItemsTraversal<'_> { | |
| assert!(self.in_codegen_phase()); | |
| assert_eq!(self.current_module, self.root_module); | |
| let roots = self.items().map(|(id, _)| id); | |
| traversal::AssertNoDanglingItemsTraversal::new( | |
| self, | |
| roots, | |
| traversal::all_edges, | |
| ) | |
| } | |
| /// When the `__testing_only_extra_assertions` feature is enabled, walk over | |
| /// every item and ensure that it is in the children set of one of its | |
| /// module ancestors. | |
| fn assert_every_item_in_a_module(&self) { | |
| if cfg!(feature = "__testing_only_extra_assertions") { | |
| assert!(self.in_codegen_phase()); | |
| assert_eq!(self.current_module, self.root_module); | |
| for (id, _item) in self.items() { | |
| if id == self.root_module { | |
| continue; | |
| } | |
| assert!( | |
| { | |
| let id = id | |
| .into_resolver() | |
| .through_type_refs() | |
| .through_type_aliases() | |
| .resolve(self) | |
| .id(); | |
| id.ancestors(self) | |
| .chain(Some(self.root_module.into())) | |
| .any(|ancestor| { | |
| debug!("Checking if {id:?} is a child of {ancestor:?}"); | |
| self.resolve_item(ancestor) | |
| .as_module() | |
| .is_some_and(|m| m.children().contains(&id)) | |
| }) | |
| }, | |
| "{id:?} should be in some ancestor module's children set" | |
| ); | |
| } | |
| } | |
| } | |
| /// Compute for every type whether it is sized or not, and whether it is | |
| /// sized or not as a base class. | |
| fn compute_sizedness(&mut self) { | |
| let _t = self.timer("compute_sizedness"); | |
| assert!(self.sizedness.is_none()); | |
| self.sizedness = Some(analyze::<SizednessAnalysis>(self)); | |
| } | |
| /// Look up whether the type with the given ID is sized or not. | |
| pub(crate) fn lookup_sizedness(&self, id: TypeId) -> SizednessResult { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute sizedness after we've entered codegen" | |
| ); | |
| self.sizedness | |
| .as_ref() | |
| .unwrap() | |
| .get(&id) | |
| .copied() | |
| .unwrap_or(SizednessResult::ZeroSized) | |
| } | |
| /// Compute whether the type has vtable. | |
| fn compute_has_vtable(&mut self) { | |
| let _t = self.timer("compute_has_vtable"); | |
| assert!(self.have_vtable.is_none()); | |
| self.have_vtable = Some(analyze::<HasVtableAnalysis>(self)); | |
| } | |
| /// Look up whether the item with `id` has vtable or not. | |
| pub(crate) fn lookup_has_vtable(&self, id: TypeId) -> HasVtableResult { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute vtables when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` has a | |
| // vtable or not. | |
| self.have_vtable | |
| .as_ref() | |
| .unwrap() | |
| .get(&id.into()) | |
| .copied() | |
| .unwrap_or(HasVtableResult::No) | |
| } | |
| /// Compute whether the type has a destructor. | |
| fn compute_has_destructor(&mut self) { | |
| let _t = self.timer("compute_has_destructor"); | |
| assert!(self.have_destructor.is_none()); | |
| self.have_destructor = Some(analyze::<HasDestructorAnalysis>(self)); | |
| } | |
| /// Look up whether the item with `id` has a destructor. | |
| pub(crate) fn lookup_has_destructor(&self, id: TypeId) -> bool { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute destructors when we enter codegen" | |
| ); | |
| self.have_destructor.as_ref().unwrap().contains(&id.into()) | |
| } | |
| fn find_used_template_parameters(&mut self) { | |
| let _t = self.timer("find_used_template_parameters"); | |
| if self.options.allowlist_recursively { | |
| let used_params = analyze::<UsedTemplateParameters>(self); | |
| self.used_template_parameters = Some(used_params); | |
| } else { | |
| // If you aren't recursively allowlisting, then we can't really make | |
| // any sense of template parameter usage, and you're on your own. | |
| let mut used_params = HashMap::default(); | |
| for &id in self.allowlisted_items() { | |
| used_params.entry(id).or_insert_with(|| { | |
| id.self_template_params(self) | |
| .into_iter() | |
| .map(|p| p.into()) | |
| .collect() | |
| }); | |
| } | |
| self.used_template_parameters = Some(used_params); | |
| } | |
| } | |
| /// Return `true` if `item` uses the given `template_param`, `false` | |
| /// otherwise. | |
| /// | |
| /// This method may only be called during the codegen phase, because the | |
| /// template usage information is only computed as we enter the codegen | |
| /// phase. | |
| /// | |
| /// If the item is blocklisted, then we say that it always uses the template | |
| /// parameter. This is a little subtle. The template parameter usage | |
| /// analysis only considers allowlisted items, and if any blocklisted item | |
| /// shows up in the generated bindings, it is the user's responsibility to | |
| /// manually provide a definition for them. To give them the most | |
| /// flexibility when doing that, we assume that they use every template | |
| /// parameter and always pass template arguments through in instantiations. | |
| pub(crate) fn uses_template_parameter( | |
| &self, | |
| item: ItemId, | |
| template_param: TypeId, | |
| ) -> bool { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute template parameter usage as we enter codegen" | |
| ); | |
| if self.resolve_item(item).is_blocklisted(self) { | |
| return true; | |
| } | |
| let template_param = template_param | |
| .into_resolver() | |
| .through_type_refs() | |
| .through_type_aliases() | |
| .resolve(self) | |
| .id(); | |
| self.used_template_parameters | |
| .as_ref() | |
| .expect("should have found template parameter usage if we're in codegen") | |
| .get(&item).is_some_and(|items_used_params| items_used_params.contains(&template_param)) | |
| } | |
| /// Return `true` if `item` uses any unbound, generic template parameters, | |
| /// `false` otherwise. | |
| /// | |
| /// Has the same restrictions that `uses_template_parameter` has. | |
| pub(crate) fn uses_any_template_parameters(&self, item: ItemId) -> bool { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute template parameter usage as we enter codegen" | |
| ); | |
| self.used_template_parameters | |
| .as_ref() | |
| .expect( | |
| "should have template parameter usage info in codegen phase", | |
| ) | |
| .get(&item) | |
| .is_some_and(|used| !used.is_empty()) | |
| } | |
| // This deserves a comment. Builtin types don't get a valid declaration, so | |
| // we can't add it to the cursor->type map. | |
| // | |
| // That being said, they're not generated anyway, and are few, so the | |
| // duplication and special-casing is fine. | |
| // | |
| // If at some point we care about the memory here, probably a map TypeKind | |
| // -> builtin type ItemId would be the best to improve that. | |
| fn add_builtin_item(&mut self, item: Item) { | |
| debug!("add_builtin_item: item = {item:?}"); | |
| debug_assert!(item.kind().is_type()); | |
| self.add_item_to_module(&item); | |
| let id = item.id(); | |
| let old_item = self.items[id.0].replace(item); | |
| assert!(old_item.is_none(), "Inserted type twice?"); | |
| } | |
| fn build_root_module(id: ItemId) -> Item { | |
| let module = Module::new(Some("root".into()), ModuleKind::Normal); | |
| Item::new(id, None, None, id, ItemKind::Module(module), None) | |
| } | |
| /// Get the root module. | |
| pub(crate) fn root_module(&self) -> ModuleId { | |
| self.root_module | |
| } | |
| /// Resolve a type with the given ID. | |
| /// | |
| /// Panics if there is no item for the given `TypeId` or if the resolved | |
| /// item is not a `Type`. | |
| pub(crate) fn resolve_type(&self, type_id: TypeId) -> &Type { | |
| self.resolve_item(type_id).kind().expect_type() | |
| } | |
| /// Resolve a function with the given ID. | |
| /// | |
| /// Panics if there is no item for the given `FunctionId` or if the resolved | |
| /// item is not a `Function`. | |
| pub(crate) fn resolve_func(&self, func_id: FunctionId) -> &Function { | |
| self.resolve_item(func_id).kind().expect_function() | |
| } | |
| /// Resolve the given `ItemId` as a type, or `None` if there is no item with | |
| /// the given ID. | |
| /// | |
| /// Panics if the ID resolves to an item that is not a type. | |
| pub(crate) fn safe_resolve_type(&self, type_id: TypeId) -> Option<&Type> { | |
| self.resolve_item_fallible(type_id) | |
| .map(|t| t.kind().expect_type()) | |
| } | |
| /// Resolve the given `ItemId` into an `Item`, or `None` if no such item | |
| /// exists. | |
| pub(crate) fn resolve_item_fallible<Id: Into<ItemId>>( | |
| &self, | |
| id: Id, | |
| ) -> Option<&Item> { | |
| self.items.get(id.into().0)?.as_ref() | |
| } | |
| /// Resolve the given `ItemId` into an `Item`. | |
| /// | |
| /// Panics if the given ID does not resolve to any item. | |
| pub(crate) fn resolve_item<Id: Into<ItemId>>(&self, item_id: Id) -> &Item { | |
| let item_id = item_id.into(); | |
| match self.resolve_item_fallible(item_id) { | |
| Some(item) => item, | |
| None => panic!("Not an item: {item_id:?}"), | |
| } | |
| } | |
| /// Get the current module. | |
| pub(crate) fn current_module(&self) -> ModuleId { | |
| self.current_module | |
| } | |
| /// Add a semantic parent for a given type definition. | |
| /// | |
| /// We do this from the type declaration, in order to be able to find the | |
| /// correct type definition afterwards. | |
| /// | |
| /// TODO(emilio): We could consider doing this only when | |
| /// `declaration.lexical_parent() != definition.lexical_parent()`, but it's | |
| /// not sure it's worth it. | |
| pub(crate) fn add_semantic_parent( | |
| &mut self, | |
| definition: Cursor, | |
| parent_id: ItemId, | |
| ) { | |
| self.semantic_parents.insert(definition, parent_id); | |
| } | |
| /// Returns a known semantic parent for a given definition. | |
| pub(crate) fn known_semantic_parent( | |
| &self, | |
| definition: Cursor, | |
| ) -> Option<ItemId> { | |
| self.semantic_parents.get(&definition).copied() | |
| } | |
| /// Given a cursor pointing to the location of a template instantiation, | |
| /// return a tuple of the form `(declaration_cursor, declaration_id, | |
| /// num_expected_template_args)`. | |
| /// | |
| /// Note that `declaration_id` is not guaranteed to be in the context's item | |
| /// set! It is possible that it is a partial type that we are still in the | |
| /// middle of parsing. | |
| fn get_declaration_info_for_template_instantiation( | |
| &self, | |
| instantiation: &Cursor, | |
| ) -> Option<(Cursor, ItemId, usize)> { | |
| instantiation | |
| .cur_type() | |
| .canonical_declaration(Some(instantiation)) | |
| .and_then(|canon_decl| { | |
| self.get_resolved_type(&canon_decl).and_then( | |
| |template_decl_id| { | |
| let num_params = | |
| template_decl_id.num_self_template_params(self); | |
| if num_params == 0 { | |
| None | |
| } else { | |
| Some(( | |
| *canon_decl.cursor(), | |
| template_decl_id.into(), | |
| num_params, | |
| )) | |
| } | |
| }, | |
| ) | |
| }) | |
| .or_else(|| { | |
| // If we haven't already parsed the declaration of | |
| // the template being instantiated, then it *must* | |
| // be on the stack of types we are currently | |
| // parsing. If it wasn't then clang would have | |
| // already errored out before we started | |
| // constructing our IR because you can't instantiate | |
| // a template until it is fully defined. | |
| instantiation | |
| .referenced() | |
| .and_then(|referenced| { | |
| self.currently_parsed_types() | |
| .iter() | |
| .find(|partial_ty| *partial_ty.decl() == referenced) | |
| }) | |
| .and_then(|template_decl| { | |
| let num_template_params = | |
| template_decl.num_self_template_params(self); | |
| if num_template_params == 0 { | |
| None | |
| } else { | |
| Some(( | |
| *template_decl.decl(), | |
| template_decl.id(), | |
| num_template_params, | |
| )) | |
| } | |
| }) | |
| }) | |
| } | |
| /// Parse a template instantiation, eg `Foo<int>`. | |
| /// | |
| /// This is surprisingly difficult to do with libclang, due to the fact that | |
| /// it doesn't provide explicit template argument information, except for | |
| /// function template declarations(!?!??!). | |
| /// | |
| /// The only way to do this is manually inspecting the AST and looking for | |
| /// `TypeRefs` and `TemplateRefs` inside. This, unfortunately, doesn't work for | |
| /// more complex cases, see the comment on the assertion below. | |
| /// | |
| /// To add insult to injury, the AST itself has structure that doesn't make | |
| /// sense. Sometimes `Foo<Bar<int>>` has an AST with nesting like you might | |
| /// expect: `(Foo (Bar (int)))`. Other times, the AST we get is completely | |
| /// flat: `(Foo Bar int)`. | |
| /// | |
| /// To see an example of what this method handles: | |
| /// | |
| /// ```c++ | |
| /// template<typename T> | |
| /// class Incomplete { | |
| /// T p; | |
| /// }; | |
| /// | |
| /// template<typename U> | |
| /// class Foo { | |
| /// Incomplete<U> bar; | |
| /// }; | |
| /// ``` | |
| /// | |
| /// Finally, template instantiations are always children of the current | |
| /// module. They use their template's definition for their name, so the | |
| /// parent is only useful for ensuring that their layout tests get | |
| /// codegen'd. | |
| fn instantiate_template( | |
| &mut self, | |
| with_id: ItemId, | |
| template: TypeId, | |
| ty: &clang::Type, | |
| location: Cursor, | |
| ) -> Option<TypeId> { | |
| let num_expected_args = | |
| self.resolve_type(template).num_self_template_params(self); | |
| if num_expected_args == 0 { | |
| warn!( | |
| "Tried to instantiate a template for which we could not \ | |
| determine any template parameters" | |
| ); | |
| return None; | |
| } | |
| let mut args = vec![]; | |
| let mut found_const_arg = false; | |
| let mut children = location.collect_children(); | |
| if children.iter().all(|c| !c.has_children()) { | |
| // This is insanity... If clang isn't giving us a properly nested | |
| // AST for which template arguments belong to which template we are | |
| // instantiating, we'll need to construct it ourselves. However, | |
| // there is an extra `NamespaceRef, NamespaceRef, ..., TemplateRef` | |
| // representing a reference to the outermost template declaration | |
| // that we need to filter out of the children. We need to do this | |
| // filtering because we already know which template declaration is | |
| // being specialized via the `location`'s type, and if we do not | |
| // filter it out, we'll add an extra layer of template instantiation | |
| // on accident. | |
| let idx = children | |
| .iter() | |
| .position(|c| c.kind() == clang_sys::CXCursor_TemplateRef); | |
| if let Some(idx) = idx { | |
| if children | |
| .iter() | |
| .take(idx) | |
| .all(|c| c.kind() == clang_sys::CXCursor_NamespaceRef) | |
| { | |
| children = children.into_iter().skip(idx + 1).collect(); | |
| } | |
| } | |
| } | |
| for child in children.iter().rev() { | |
| match child.kind() { | |
| clang_sys::CXCursor_TypeRef | | |
| clang_sys::CXCursor_TypedefDecl | | |
| clang_sys::CXCursor_TypeAliasDecl => { | |
| // The `with_id` ID will potentially end up unused if we give up | |
| // on this type (for example, because it has const value | |
| // template args), 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 ty = Item::from_ty_or_ref( | |
| child.cur_type(), | |
| *child, | |
| Some(template.into()), | |
| self, | |
| ); | |
| args.push(ty); | |
| } | |
| clang_sys::CXCursor_TemplateRef => { | |
| let ( | |
| template_decl_cursor, | |
| template_decl_id, | |
| num_expected_template_args, | |
| ) = self.get_declaration_info_for_template_instantiation( | |
| child, | |
| )?; | |
| if num_expected_template_args == 0 || | |
| child.has_at_least_num_children( | |
| num_expected_template_args, | |
| ) | |
| { | |
| // Do a happy little parse. See comment in the TypeRef | |
| // match arm about parent IDs. | |
| let ty = Item::from_ty_or_ref( | |
| child.cur_type(), | |
| *child, | |
| Some(template.into()), | |
| self, | |
| ); | |
| args.push(ty); | |
| } else { | |
| // This is the case mentioned in the doc comment where | |
| // clang gives us a flattened AST and we have to | |
| // reconstruct which template arguments go to which | |
| // instantiation :( | |
| let args_len = args.len(); | |
| if args_len < num_expected_template_args { | |
| warn!( | |
| "Found a template instantiation without \ | |
| enough template arguments" | |
| ); | |
| return None; | |
| } | |
| let mut sub_args: Vec<_> = args | |
| .drain(args_len - num_expected_template_args..) | |
| .collect(); | |
| sub_args.reverse(); | |
| let sub_name = Some(template_decl_cursor.spelling()); | |
| let sub_inst = TemplateInstantiation::new( | |
| // This isn't guaranteed to be a type that we've | |
| // already finished parsing yet. | |
| template_decl_id.as_type_id_unchecked(), | |
| sub_args, | |
| ); | |
| let sub_kind = | |
| TypeKind::TemplateInstantiation(sub_inst); | |
| let sub_ty = Type::new( | |
| sub_name, | |
| template_decl_cursor | |
| .cur_type() | |
| .fallible_layout(self) | |
| .ok(), | |
| sub_kind, | |
| false, | |
| ); | |
| let sub_id = self.next_item_id(); | |
| let sub_item = Item::new( | |
| sub_id, | |
| None, | |
| None, | |
| self.current_module.into(), | |
| ItemKind::Type(sub_ty), | |
| Some(child.location()), | |
| ); | |
| // Bypass all the validations in add_item explicitly. | |
| debug!( | |
| "instantiate_template: inserting nested \ | |
| instantiation item: {sub_item:?}" | |
| ); | |
| self.add_item_to_module(&sub_item); | |
| debug_assert_eq!(sub_id, sub_item.id()); | |
| self.items[sub_id.0] = Some(sub_item); | |
| args.push(sub_id.as_type_id_unchecked()); | |
| } | |
| } | |
| _ => { | |
| warn!( | |
| "Found template arg cursor we can't handle: {child:?}" | |
| ); | |
| found_const_arg = true; | |
| } | |
| } | |
| } | |
| if found_const_arg { | |
| // This is a dependently typed template instantiation. That is, an | |
| // instantiation of a template with one or more const values as | |
| // template arguments, rather than only types as template | |
| // arguments. For example, `Foo<true, 5>` versus `Bar<bool, int>`. | |
| // We can't handle these instantiations, so just punt in this | |
| // situation... | |
| warn!( | |
| "Found template instantiated with a const value; \ | |
| bindgen can't handle this kind of template instantiation!" | |
| ); | |
| return None; | |
| } | |
| if args.len() != num_expected_args { | |
| warn!( | |
| "Found a template with an unexpected number of template \ | |
| arguments" | |
| ); | |
| return None; | |
| } | |
| args.reverse(); | |
| let type_kind = TypeKind::TemplateInstantiation( | |
| TemplateInstantiation::new(template, args), | |
| ); | |
| let name = ty.spelling(); | |
| let name = if name.is_empty() { None } else { Some(name) }; | |
| let ty = Type::new( | |
| name, | |
| ty.fallible_layout(self).ok(), | |
| type_kind, | |
| ty.is_const(), | |
| ); | |
| let item = Item::new( | |
| with_id, | |
| None, | |
| None, | |
| self.current_module.into(), | |
| ItemKind::Type(ty), | |
| Some(location.location()), | |
| ); | |
| // Bypass all the validations in add_item explicitly. | |
| debug!("instantiate_template: inserting item: {item:?}"); | |
| self.add_item_to_module(&item); | |
| debug_assert_eq!(with_id, item.id()); | |
| self.items[with_id.0] = Some(item); | |
| Some(with_id.as_type_id_unchecked()) | |
| } | |
| /// If we have already resolved the type for the given type declaration, | |
| /// return its `ItemId`. Otherwise, return `None`. | |
| pub(crate) fn get_resolved_type( | |
| &self, | |
| decl: &clang::CanonicalTypeDeclaration, | |
| ) -> Option<TypeId> { | |
| self.types | |
| .get(&TypeKey::Declaration(*decl.cursor())) | |
| .or_else(|| { | |
| decl.cursor() | |
| .usr() | |
| .and_then(|usr| self.types.get(&TypeKey::Usr(usr))) | |
| }) | |
| .copied() | |
| } | |
| /// Looks up for an already resolved type, either because it's builtin, or | |
| /// because we already have it in the map. | |
| pub(crate) fn builtin_or_resolved_ty( | |
| &mut self, | |
| with_id: ItemId, | |
| parent_id: Option<ItemId>, | |
| ty: &clang::Type, | |
| location: Option<Cursor>, | |
| ) -> Option<TypeId> { | |
| use clang_sys::{ | |
| CXCursor_TypeAliasTemplateDecl, CXCursor_TypeRef, | |
| CXCursor_TypedefDecl, | |
| }; | |
| debug!("builtin_or_resolved_ty: {ty:?}, {location:?}, {with_id:?}, {parent_id:?}"); | |
| if let Some(decl) = ty.canonical_declaration(location.as_ref()) { | |
| if let Some(id) = self.get_resolved_type(&decl) { | |
| debug!( | |
| "Already resolved ty {id:?}, {decl:?}, {ty:?} {location:?}" | |
| ); | |
| // If the declaration already exists, then either: | |
| // | |
| // * the declaration is a template declaration of some sort, | |
| // and we are looking at an instantiation or specialization | |
| // of it, or | |
| // * we have already parsed and resolved this type, and | |
| // there's nothing left to do. | |
| if let Some(location) = location { | |
| // When a hidden `typedef struct Foo Foo;` carries docs, the | |
| // alias is not emitted in codegen and those docs would | |
| // otherwise be lost. Attach the docs to the already-resolved | |
| // target type here. | |
| if location.kind() == CXCursor_TypedefDecl { | |
| self.inherit_typedef_comment( | |
| id, | |
| *decl.cursor(), | |
| location, | |
| ); | |
| } | |
| if decl.cursor().is_template_like() && | |
| *ty != decl.cursor().cur_type() | |
| { | |
| // For specialized type aliases, there's no way to get the | |
| // template parameters as of this writing (for a struct | |
| // specialization we wouldn't be in this branch anyway). | |
| // | |
| // Explicitly return `None` if there aren't any | |
| // unspecialized parameters (contains any `TypeRef`) so we | |
| // resolve the canonical type if there is one and it's | |
| // exposed. | |
| // | |
| // This is _tricky_, I know :( | |
| if decl.cursor().kind() == | |
| CXCursor_TypeAliasTemplateDecl && | |
| !location.contains_cursor(CXCursor_TypeRef) && | |
| ty.canonical_type().is_valid_and_exposed() | |
| { | |
| return None; | |
| } | |
| return self | |
| .instantiate_template(with_id, id, ty, location) | |
| .or(Some(id)); | |
| } | |
| } | |
| return Some(self.build_ty_wrapper(with_id, id, parent_id, ty)); | |
| } | |
| } | |
| debug!("Not resolved, maybe builtin?"); | |
| self.build_builtin_ty(ty) | |
| } | |
| fn inherit_typedef_comment( | |
| &mut self, | |
| resolved_type: TypeId, | |
| decl: Cursor, | |
| typedef_cursor: Cursor, | |
| ) { | |
| let Some(comment) = typedef_cursor.raw_comment() else { | |
| return; | |
| }; | |
| // Only inherit docs for hidden overlapping aliases, e.g. | |
| // `typedef struct Foo Foo;`. | |
| if typedef_cursor.spelling() != decl.spelling() { | |
| return; | |
| } | |
| let item_id: ItemId = resolved_type.into(); | |
| if let Some(item) = self.items[item_id.0].as_mut() { | |
| item.set_comment_if_none(comment); | |
| } | |
| } | |
| /// Make a new item that is a resolved type reference to the `wrapped_id`. | |
| /// | |
| /// This is unfortunately a lot of bloat, but is needed to properly track | |
| /// constness et al. | |
| /// | |
| /// We should probably make the constness tracking separate, so it doesn't | |
| /// bloat that much, but hey, we already bloat the heck out of builtin | |
| /// types. | |
| pub(crate) fn build_ty_wrapper( | |
| &mut self, | |
| with_id: ItemId, | |
| wrapped_id: TypeId, | |
| parent_id: Option<ItemId>, | |
| ty: &clang::Type, | |
| ) -> TypeId { | |
| self.build_wrapper(with_id, wrapped_id, parent_id, ty, ty.is_const()) | |
| } | |
| /// A wrapper over a type that adds a const qualifier explicitly. | |
| /// | |
| /// Needed to handle const methods in C++, wrapping the type . | |
| pub(crate) fn build_const_wrapper( | |
| &mut self, | |
| with_id: ItemId, | |
| wrapped_id: TypeId, | |
| parent_id: Option<ItemId>, | |
| ty: &clang::Type, | |
| ) -> TypeId { | |
| self.build_wrapper( | |
| with_id, wrapped_id, parent_id, ty, /* is_const = */ true, | |
| ) | |
| } | |
| fn build_wrapper( | |
| &mut self, | |
| with_id: ItemId, | |
| wrapped_id: TypeId, | |
| parent_id: Option<ItemId>, | |
| ty: &clang::Type, | |
| is_const: bool, | |
| ) -> TypeId { | |
| let spelling = ty.spelling(); | |
| let layout = ty.fallible_layout(self).ok(); | |
| let location = ty.declaration().location(); | |
| let type_kind = TypeKind::ResolvedTypeRef(wrapped_id); | |
| let ty = Type::new(Some(spelling), layout, type_kind, is_const); | |
| let item = Item::new( | |
| with_id, | |
| None, | |
| None, | |
| parent_id.unwrap_or_else(|| self.current_module.into()), | |
| ItemKind::Type(ty), | |
| Some(location), | |
| ); | |
| self.add_builtin_item(item); | |
| with_id.as_type_id_unchecked() | |
| } | |
| /// Returns the next item ID to be used for an item. | |
| pub(crate) fn next_item_id(&mut self) -> ItemId { | |
| let ret = ItemId(self.items.len()); | |
| self.items.push(None); | |
| ret | |
| } | |
| fn build_builtin_ty(&mut self, ty: &clang::Type) -> Option<TypeId> { | |
| use clang_sys::*; | |
| let type_kind = match ty.kind() { | |
| CXType_NullPtr => TypeKind::NullPtr, | |
| CXType_Void => TypeKind::Void, | |
| CXType_Bool => TypeKind::Int(IntKind::Bool), | |
| CXType_Int => TypeKind::Int(IntKind::Int), | |
| CXType_UInt => TypeKind::Int(IntKind::UInt), | |
| CXType_Char_S => TypeKind::Int(IntKind::Char { is_signed: true }), | |
| CXType_Char_U => TypeKind::Int(IntKind::Char { is_signed: false }), | |
| CXType_SChar => TypeKind::Int(IntKind::SChar), | |
| CXType_UChar => TypeKind::Int(IntKind::UChar), | |
| CXType_Short => TypeKind::Int(IntKind::Short), | |
| CXType_UShort => TypeKind::Int(IntKind::UShort), | |
| CXType_WChar => TypeKind::Int(IntKind::WChar), | |
| CXType_Char16 if self.options().use_distinct_char16_t => { | |
| TypeKind::Int(IntKind::Char16) | |
| } | |
| CXType_Char16 => TypeKind::Int(IntKind::U16), | |
| CXType_Char32 => TypeKind::Int(IntKind::U32), | |
| CXType_Long => TypeKind::Int(IntKind::Long), | |
| CXType_ULong => TypeKind::Int(IntKind::ULong), | |
| CXType_LongLong => TypeKind::Int(IntKind::LongLong), | |
| CXType_ULongLong => TypeKind::Int(IntKind::ULongLong), | |
| CXType_Int128 => TypeKind::Int(IntKind::I128), | |
| CXType_UInt128 => TypeKind::Int(IntKind::U128), | |
| CXType_Float16 | CXType_Half => TypeKind::Float(FloatKind::Float16), | |
| CXType_Float => TypeKind::Float(FloatKind::Float), | |
| CXType_Double => TypeKind::Float(FloatKind::Double), | |
| CXType_LongDouble => TypeKind::Float(FloatKind::LongDouble), | |
| CXType_Float128 => TypeKind::Float(FloatKind::Float128), | |
| CXType_Complex => { | |
| let float_type = | |
| ty.elem_type().expect("Not able to resolve complex type?"); | |
| let float_kind = match float_type.kind() { | |
| CXType_Float16 | CXType_Half => FloatKind::Float16, | |
| CXType_Float => FloatKind::Float, | |
| CXType_Double => FloatKind::Double, | |
| CXType_LongDouble => FloatKind::LongDouble, | |
| CXType_Float128 => FloatKind::Float128, | |
| _ => panic!( | |
| "Non floating-type complex? {ty:?}, {float_type:?}", | |
| ), | |
| }; | |
| TypeKind::Complex(float_kind) | |
| } | |
| _ => return None, | |
| }; | |
| let spelling = ty.spelling(); | |
| let is_const = ty.is_const(); | |
| let layout = ty.fallible_layout(self).ok(); | |
| let location = ty.declaration().location(); | |
| let ty = Type::new(Some(spelling), layout, type_kind, is_const); | |
| let id = self.next_item_id(); | |
| let item = Item::new( | |
| id, | |
| None, | |
| None, | |
| self.root_module.into(), | |
| ItemKind::Type(ty), | |
| Some(location), | |
| ); | |
| self.add_builtin_item(item); | |
| Some(id.as_type_id_unchecked()) | |
| } | |
| /// Get the current Clang translation unit that is being processed. | |
| pub(crate) fn translation_unit(&self) -> &clang::TranslationUnit { | |
| &self.translation_unit | |
| } | |
| /// Initialize fallback translation unit if it does not exist and | |
| /// then return a mutable reference to the fallback translation unit. | |
| pub(crate) fn try_ensure_fallback_translation_unit( | |
| &mut self, | |
| ) -> Option<&mut clang::FallbackTranslationUnit> { | |
| if self.fallback_tu.is_none() { | |
| let file = format!( | |
| "{}/.macro_eval.c", | |
| match self.options().clang_macro_fallback_build_dir { | |
| Some(ref path) => path.as_os_str().to_str()?, | |
| None => ".", | |
| } | |
| ); | |
| let index = clang::Index::new(false, false); | |
| let mut header_names_to_compile = Vec::new(); | |
| let mut header_paths = Vec::new(); | |
| let mut header_includes = Vec::new(); | |
| let [input_headers @ .., single_header] = | |
| &self.options().input_headers[..] | |
| else { | |
| return None; | |
| }; | |
| for input_header in input_headers { | |
| let path = Path::new(input_header.as_ref()); | |
| if let Some(header_path) = path.parent() { | |
| if header_path == Path::new("") { | |
| header_paths.push("."); | |
| } else { | |
| header_paths.push(header_path.as_os_str().to_str()?); | |
| } | |
| } else { | |
| header_paths.push("."); | |
| } | |
| let header_name = path.file_name()?.to_str()?; | |
| header_includes.push(header_name.to_string()); | |
| header_names_to_compile | |
| .push(header_name.split(".h").next()?.to_string()); | |
| } | |
| let pch = format!( | |
| "{}/{}", | |
| match self.options().clang_macro_fallback_build_dir { | |
| Some(ref path) => path.as_os_str().to_str()?, | |
| None => ".", | |
| }, | |
| header_names_to_compile.join("-") + "-precompile.h.pch" | |
| ); | |
| let mut c_args = self.options.fallback_clang_args.clone(); | |
| c_args.push("-x".to_string().into_boxed_str()); | |
| c_args.push("c-header".to_string().into_boxed_str()); | |
| for header_path in header_paths { | |
| c_args.push(format!("-I{header_path}").into_boxed_str()); | |
| } | |
| for header_include in header_includes { | |
| c_args.push("-include".to_string().into_boxed_str()); | |
| c_args.push(header_include.into_boxed_str()); | |
| } | |
| let mut tu = clang::TranslationUnit::parse( | |
| &index, | |
| single_header, | |
| &c_args, | |
| &[], | |
| clang_sys::CXTranslationUnit_ForSerialization, | |
| )?; | |
| tu.save(&pch).ok()?; | |
| let mut c_args = vec![ | |
| "-include-pch".to_string().into_boxed_str(), | |
| pch.clone().into_boxed_str(), | |
| ]; | |
| let mut skip_next = false; | |
| for arg in &self.options.fallback_clang_args { | |
| if arg.as_ref() == "-include" { | |
| skip_next = true; | |
| } else if skip_next { | |
| skip_next = false; | |
| } else { | |
| c_args.push(arg.clone()); | |
| } | |
| } | |
| self.fallback_tu = | |
| Some(clang::FallbackTranslationUnit::new(file, pch, &c_args)?); | |
| } | |
| self.fallback_tu.as_mut() | |
| } | |
| /// Have we parsed the macro named `macro_name` already? | |
| pub(crate) fn parsed_macro(&self, macro_name: &[u8]) -> bool { | |
| self.parsed_macros.contains_key(macro_name) | |
| } | |
| /// Get the currently parsed macros. | |
| /// This map only contains macros accepted by [`cexpr`] | |
| pub(crate) fn parsed_macros( | |
| &self, | |
| ) -> &StdHashMap<Vec<u8>, cexpr::expr::EvalResult> { | |
| debug_assert!(!self.in_codegen_phase()); | |
| &self.parsed_macros | |
| } | |
| /// Mark the macro named `macro_name` as parsed. | |
| pub(crate) fn note_parsed_macro( | |
| &mut self, | |
| id: Vec<u8>, | |
| value: cexpr::expr::EvalResult, | |
| ) { | |
| self.parsed_macros.insert(id, value); | |
| } | |
| /// Are we in the codegen phase? | |
| pub(crate) fn in_codegen_phase(&self) -> bool { | |
| self.in_codegen | |
| } | |
| /// Mark the type with the given `name` as replaced by the type with ID | |
| /// `potential_ty`. | |
| /// | |
| /// Replacement types are declared using the `replaces="xxx"` annotation, | |
| /// and implies that the original type is hidden. | |
| pub(crate) fn replace(&mut self, name: &[String], potential_ty: ItemId) { | |
| match self.replacements.entry(name.into()) { | |
| Entry::Vacant(entry) => { | |
| debug!("Defining replacement for {name:?} as {potential_ty:?}"); | |
| entry.insert(potential_ty); | |
| } | |
| Entry::Occupied(occupied) => { | |
| warn!( | |
| "Replacement for {name:?} already defined as {:?}; \ | |
| ignoring duplicate replacement definition as {potential_ty:?}", | |
| occupied.get(), | |
| ); | |
| } | |
| } | |
| } | |
| /// Has the item with the given `name` and `id` been replaced by another | |
| /// type? | |
| pub(crate) fn is_replaced_type<Id: Into<ItemId>>( | |
| &self, | |
| path: &[String], | |
| id: Id, | |
| ) -> bool { | |
| let id = id.into(); | |
| matches!(self.replacements.get(path), Some(replaced_by) if *replaced_by != id) | |
| } | |
| /// Is the type with the given `name` marked as opaque? | |
| pub(crate) fn opaque_by_name(&self, path: &[String]) -> bool { | |
| debug_assert!( | |
| self.in_codegen_phase(), | |
| "You're not supposed to call this yet" | |
| ); | |
| self.options.opaque_types.matches(path[1..].join("::")) | |
| } | |
| /// Get the options used to configure this bindgen context. | |
| pub(crate) fn options(&self) -> &BindgenOptions { | |
| &self.options | |
| } | |
| /// Tokenizes a namespace cursor in order to get the name and kind of the | |
| /// namespace. | |
| fn tokenize_namespace( | |
| &self, | |
| cursor: &Cursor, | |
| ) -> (Option<String>, ModuleKind) { | |
| assert_eq!( | |
| cursor.kind(), | |
| ::clang_sys::CXCursor_Namespace, | |
| "Be a nice person" | |
| ); | |
| let mut module_name = None; | |
| let spelling = cursor.spelling(); | |
| if !spelling.is_empty() { | |
| module_name = Some(spelling); | |
| } | |
| let mut kind = ModuleKind::Normal; | |
| let mut looking_for_name = false; | |
| for token in cursor.tokens().iter() { | |
| match token.spelling() { | |
| b"inline" => { | |
| debug_assert!( | |
| kind != ModuleKind::Inline, | |
| "Multiple inline keywords?" | |
| ); | |
| kind = ModuleKind::Inline; | |
| // When hitting a nested inline namespace we get a spelling | |
| // that looks like ["inline", "foo"]. Deal with it properly. | |
| looking_for_name = true; | |
| } | |
| // The double colon allows us to handle nested namespaces like | |
| // namespace foo::bar { } | |
| // | |
| // libclang still gives us two namespace cursors, which is cool, | |
| // but the tokenization of the second begins with the double | |
| // colon. That's ok, so we only need to handle the weird | |
| // tokenization here. | |
| b"namespace" | b"::" => { | |
| looking_for_name = true; | |
| } | |
| b"{" => { | |
| // This should be an anonymous namespace. | |
| assert!(looking_for_name); | |
| break; | |
| } | |
| name => { | |
| if looking_for_name { | |
| if module_name.is_none() { | |
| module_name = Some( | |
| String::from_utf8_lossy(name).into_owned(), | |
| ); | |
| } | |
| break; | |
| } | |
| // This is _likely_, but not certainly, a macro that's | |
| // been placed just before the namespace keyword. | |
| // Unfortunately, clang tokens don't let us easily see | |
| // through the ifdef tokens, so we don't know what this | |
| // token should really be. Instead of panicking though, | |
| // we warn the user that we assumed the token was blank, | |
| // and then move on. | |
| // | |
| // See also https://github.com/rust-lang/rust-bindgen/issues/1676. | |
| warn!("Ignored unknown namespace prefix '{}' at {token:?} in {cursor:?}", String::from_utf8_lossy(name)); | |
| } | |
| } | |
| } | |
| if cursor.is_inline_namespace() { | |
| kind = ModuleKind::Inline; | |
| } | |
| (module_name, kind) | |
| } | |
| /// Given a `CXCursor_Namespace` cursor, return the item ID of the | |
| /// corresponding module, or create one on the fly. | |
| pub(crate) fn module(&mut self, cursor: Cursor) -> ModuleId { | |
| use clang_sys::*; | |
| assert_eq!(cursor.kind(), CXCursor_Namespace, "Be a nice person"); | |
| let cursor = cursor.canonical(); | |
| if let Some(id) = self.modules.get(&cursor) { | |
| return *id; | |
| } | |
| let (module_name, kind) = self.tokenize_namespace(&cursor); | |
| let module_id = self.next_item_id(); | |
| let module = Module::new(module_name, kind); | |
| let module = Item::new( | |
| module_id, | |
| None, | |
| None, | |
| self.current_module.into(), | |
| ItemKind::Module(module), | |
| Some(cursor.location()), | |
| ); | |
| let module_id = module.id().as_module_id_unchecked(); | |
| self.modules.insert(cursor, module_id); | |
| self.add_item(module, None, None); | |
| module_id | |
| } | |
| /// Start traversing the module with the given `module_id`, invoke the | |
| /// callback `cb`, and then return to traversing the original module. | |
| pub(crate) fn with_module<F>(&mut self, module_id: ModuleId, cb: F) | |
| where | |
| F: FnOnce(&mut Self), | |
| { | |
| debug_assert!(self.resolve_item(module_id).kind().is_module(), "Wat"); | |
| let previous_id = self.current_module; | |
| self.current_module = module_id; | |
| cb(self); | |
| self.current_module = previous_id; | |
| } | |
| /// Iterate over all (explicitly or transitively) allowlisted items. | |
| /// | |
| /// If no items are explicitly allowlisted, then all items are considered | |
| /// allowlisted. | |
| pub(crate) fn allowlisted_items(&self) -> &ItemSet { | |
| assert!(self.in_codegen_phase()); | |
| assert_eq!(self.current_module, self.root_module); | |
| self.allowlisted.as_ref().unwrap() | |
| } | |
| /// Check whether a particular blocklisted type implements a trait or not. | |
| /// Results may be cached. | |
| pub(crate) fn blocklisted_type_implements_trait( | |
| &self, | |
| item: &Item, | |
| derive_trait: DeriveTrait, | |
| ) -> CanDerive { | |
| assert!(self.in_codegen_phase()); | |
| assert_eq!(self.current_module, self.root_module); | |
| *self | |
| .blocklisted_types_implement_traits | |
| .borrow_mut() | |
| .entry(derive_trait) | |
| .or_default() | |
| .entry(item.id()) | |
| .or_insert_with(|| { | |
| item.expect_type() | |
| .name() | |
| .and_then(|name| { | |
| if self.options.parse_callbacks.is_empty() { | |
| // Sized integer types from <stdint.h> get mapped to Rust primitive | |
| // types regardless of whether they are blocklisted, so ensure that | |
| // standard traits are considered derivable for them too. | |
| if self.is_stdint_type(name) { | |
| Some(CanDerive::Yes) | |
| } else { | |
| Some(CanDerive::No) | |
| } | |
| } else { | |
| self.options.last_callback(|cb| { | |
| cb.blocklisted_type_implements_trait( | |
| name, | |
| derive_trait, | |
| ) | |
| }) | |
| } | |
| }) | |
| .unwrap_or(CanDerive::No) | |
| }) | |
| } | |
| /// Is the given type a type from <stdint.h> that corresponds to a Rust primitive type? | |
| pub(crate) fn is_stdint_type(&self, name: &str) -> bool { | |
| match name { | |
| "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | "int32_t" | | |
| "uint32_t" | "int64_t" | "uint64_t" | "uintptr_t" | | |
| "intptr_t" | "ptrdiff_t" => true, | |
| "size_t" | "ssize_t" => self.options.size_t_is_usize, | |
| _ => false, | |
| } | |
| } | |
| /// Get a reference to the set of items we should generate. | |
| pub(crate) fn codegen_items(&self) -> &ItemSet { | |
| assert!(self.in_codegen_phase()); | |
| assert_eq!(self.current_module, self.root_module); | |
| self.codegen_items.as_ref().unwrap() | |
| } | |
| /// Compute the allowlisted items set and populate `self.allowlisted`. | |
| fn compute_allowlisted_and_codegen_items(&mut self) { | |
| assert!(self.in_codegen_phase()); | |
| assert_eq!(self.current_module, self.root_module); | |
| assert!(self.allowlisted.is_none()); | |
| let _t = self.timer("compute_allowlisted_and_codegen_items"); | |
| let roots = { | |
| let mut roots = self | |
| .items() | |
| // Only consider roots that are enabled for codegen. | |
| .filter(|&(_, item)| item.is_enabled_for_codegen(self)) | |
| .filter(|&(_, item)| { | |
| // If nothing is explicitly allowlisted, then everything is fair | |
| // game. | |
| if self.options().allowlisted_types.is_empty() && | |
| self.options().allowlisted_functions.is_empty() && | |
| self.options().allowlisted_vars.is_empty() && | |
| self.options().allowlisted_files.is_empty() && | |
| self.options().allowlisted_items.is_empty() | |
| { | |
| return true; | |
| } | |
| // If this is a type that explicitly replaces another, we assume | |
| // you know what you're doing. | |
| if item.annotations().use_instead_of().is_some() { | |
| return true; | |
| } | |
| // Items with a source location in an explicitly allowlisted file | |
| // are always included. | |
| if !self.options().allowlisted_files.is_empty() { | |
| if let Some(location) = item.location() { | |
| let (file, _, _, _) = location.location(); | |
| if let Some(filename) = file.name() { | |
| if self | |
| .options() | |
| .allowlisted_files | |
| .matches(filename) | |
| { | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| let name = item.path_for_allowlisting(self)[1..].join("::"); | |
| debug!("allowlisted_items: testing {name:?}"); | |
| if self.options().allowlisted_items.matches(&name) { | |
| return true; | |
| } | |
| match *item.kind() { | |
| ItemKind::Module(..) => true, | |
| ItemKind::Function(_) => { | |
| self.options().allowlisted_functions.matches(&name) | |
| } | |
| ItemKind::Var(_) => { | |
| self.options().allowlisted_vars.matches(&name) | |
| } | |
| ItemKind::Type(ref ty) => { | |
| if self.options().allowlisted_types.matches(&name) { | |
| return true; | |
| } | |
| // Auto-allowlist types that don't need code | |
| // generation if not allowlisting recursively, to | |
| // make the #[derive] analysis not be lame. | |
| if !self.options().allowlist_recursively { | |
| match *ty.kind() { | |
| TypeKind::Void | | |
| TypeKind::NullPtr | | |
| TypeKind::Int(..) | | |
| TypeKind::Float(..) | | |
| TypeKind::Complex(..) | | |
| TypeKind::Array(..) | | |
| TypeKind::Vector(..) | | |
| TypeKind::Pointer(..) | | |
| TypeKind::Reference(..) | | |
| TypeKind::Function(..) | | |
| TypeKind::ResolvedTypeRef(..) | | |
| TypeKind::Opaque | | |
| TypeKind::TypeParam => return true, | |
| _ => {} | |
| } | |
| if self.is_stdint_type(&name) { | |
| return true; | |
| } | |
| } | |
| // Unnamed top-level enums are special and we | |
| // allowlist them via the `allowlisted_vars` filter, | |
| // since they're effectively top-level constants, | |
| // and there's no way for them to be referenced | |
| // consistently. | |
| let parent = self.resolve_item(item.parent_id()); | |
| if !parent.is_module() { | |
| return false; | |
| } | |
| let TypeKind::Enum(ref enum_) = *ty.kind() else { | |
| return false; | |
| }; | |
| if ty.name().is_some() { | |
| return false; | |
| } | |
| let mut prefix_path = | |
| parent.path_for_allowlisting(self).clone(); | |
| enum_.variants().iter().any(|variant| { | |
| prefix_path.push( | |
| variant.name_for_allowlisting().into(), | |
| ); | |
| let name = prefix_path[1..].join("::"); | |
| prefix_path.pop().unwrap(); | |
| self.options().allowlisted_vars.matches(&name) | |
| || self | |
| .options() | |
| .allowlisted_items | |
| .matches(name) | |
| }) | |
| } | |
| } | |
| }) | |
| .map(|(id, _)| id) | |
| .collect::<Vec<_>>(); | |
| // The reversal preserves the expected ordering of traversal, | |
| // resulting in more stable-ish bindgen-generated names for | |
| // anonymous types (like unions). | |
| roots.reverse(); | |
| roots | |
| }; | |
| let allowlisted_items_predicate = | |
| if self.options().allowlist_recursively { | |
| traversal::all_edges | |
| } else { | |
| // Only follow InnerType edges from the allowlisted roots. | |
| // Such inner types (e.g. anonymous structs/unions) are | |
| // always emitted by codegen, and they need to be allowlisted | |
| // to make sure they are processed by e.g. the derive analysis. | |
| traversal::only_inner_type_edges | |
| }; | |
| let allowlisted = AllowlistedItemsTraversal::new( | |
| self, | |
| roots.clone(), | |
| allowlisted_items_predicate, | |
| ) | |
| .collect::<ItemSet>(); | |
| let codegen_items = if self.options().allowlist_recursively { | |
| AllowlistedItemsTraversal::new( | |
| self, | |
| roots, | |
| traversal::codegen_edges, | |
| ) | |
| .collect::<ItemSet>() | |
| } else { | |
| allowlisted.clone() | |
| }; | |
| self.allowlisted = Some(allowlisted); | |
| self.codegen_items = Some(codegen_items); | |
| for item in self.options().allowlisted_functions.unmatched_items() { | |
| unused_regex_diagnostic(item, "--allowlist-function", self); | |
| } | |
| for item in self.options().allowlisted_vars.unmatched_items() { | |
| unused_regex_diagnostic(item, "--allowlist-var", self); | |
| } | |
| for item in self.options().allowlisted_types.unmatched_items() { | |
| unused_regex_diagnostic(item, "--allowlist-type", self); | |
| } | |
| for item in self.options().allowlisted_items.unmatched_items() { | |
| unused_regex_diagnostic(item, "--allowlist-items", self); | |
| } | |
| } | |
| /// Convenient method for getting the prefix to use for most traits in | |
| /// codegen depending on the `use_core` option. | |
| pub(crate) fn trait_prefix(&self) -> Ident { | |
| if self.options().use_core { | |
| self.rust_ident_raw("core") | |
| } else { | |
| self.rust_ident_raw("std") | |
| } | |
| } | |
| /// Call if an opaque array is generated | |
| pub(crate) fn generated_opaque_array(&self, align: usize) { | |
| self.generated_opaque_array.borrow_mut().insert(align); | |
| } | |
| /// Whether we need to generate the opaque array type | |
| pub(crate) fn opaque_array_types_needed(&self) -> Vec<usize> { | |
| let mut alignments = self | |
| .generated_opaque_array | |
| .borrow() | |
| .iter() | |
| .copied() | |
| .collect::<Vec<_>>(); | |
| alignments.sort_unstable(); | |
| alignments | |
| } | |
| /// Call if a bindgen complex is generated | |
| pub(crate) fn generated_bindgen_complex(&self) { | |
| self.generated_bindgen_complex.set(true); | |
| } | |
| /// Whether we need to generate the bindgen complex type | |
| pub(crate) fn need_bindgen_complex_type(&self) -> bool { | |
| self.generated_bindgen_complex.get() | |
| } | |
| /// Call if a bindgen float16 is generated | |
| pub(crate) fn generated_bindgen_float16(&self) { | |
| self.generated_bindgen_float16.set(true); | |
| } | |
| /// Whether we need to generate the bindgen float16 type | |
| pub(crate) fn need_bindgen_float16_type(&self) -> bool { | |
| self.generated_bindgen_float16.get() | |
| } | |
| /// Compute which `enum`s have an associated `typedef` definition. | |
| fn compute_enum_typedef_combos(&mut self) { | |
| let _t = self.timer("compute_enum_typedef_combos"); | |
| assert!(self.enum_typedef_combos.is_none()); | |
| let mut enum_typedef_combos = HashSet::default(); | |
| for item in &self.items { | |
| if let Some(ItemKind::Module(module)) = | |
| item.as_ref().map(Item::kind) | |
| { | |
| // Find typedefs in this module, and build set of their names. | |
| let mut names_of_typedefs = HashSet::default(); | |
| for child_id in module.children() { | |
| if let Some(ItemKind::Type(ty)) = | |
| self.items[child_id.0].as_ref().map(Item::kind) | |
| { | |
| if let (Some(name), TypeKind::Alias(type_id)) = | |
| (ty.name(), ty.kind()) | |
| { | |
| // We disregard aliases that refer to the enum | |
| // itself, such as in `typedef enum { ... } Enum;`. | |
| if type_id | |
| .into_resolver() | |
| .through_type_refs() | |
| .through_type_aliases() | |
| .resolve(self) | |
| .expect_type() | |
| .is_int() | |
| { | |
| names_of_typedefs.insert(name); | |
| } | |
| } | |
| } | |
| } | |
| // Find enums in this module, and record the ID of each one that | |
| // has a typedef. | |
| for child_id in module.children() { | |
| if let Some(ItemKind::Type(ty)) = | |
| self.items[child_id.0].as_ref().map(Item::kind) | |
| { | |
| if let (Some(name), true) = (ty.name(), ty.is_enum()) { | |
| if names_of_typedefs.contains(name) { | |
| enum_typedef_combos.insert(*child_id); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| self.enum_typedef_combos = Some(enum_typedef_combos); | |
| } | |
| /// Look up whether `id` refers to an `enum` whose underlying type is | |
| /// defined by a `typedef`. | |
| pub(crate) fn is_enum_typedef_combo(&self, id: ItemId) -> bool { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute enum_typedef_combos when we enter codegen", | |
| ); | |
| self.enum_typedef_combos.as_ref().unwrap().contains(&id) | |
| } | |
| /// Compute whether we can derive debug. | |
| fn compute_cannot_derive_debug(&mut self) { | |
| let _t = self.timer("compute_cannot_derive_debug"); | |
| assert!(self.cannot_derive_debug.is_none()); | |
| if self.options.derive_debug { | |
| self.cannot_derive_debug = | |
| Some(as_cannot_derive_set(analyze::<CannotDerive>(( | |
| self, | |
| DeriveTrait::Debug, | |
| )))); | |
| } | |
| } | |
| /// Look up whether the item with `id` can | |
| /// derive debug or not. | |
| pub(crate) fn lookup_can_derive_debug<Id: Into<ItemId>>( | |
| &self, | |
| id: Id, | |
| ) -> bool { | |
| let id = id.into(); | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute can_derive_debug when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` can | |
| // derive debug or not. | |
| !self.cannot_derive_debug.as_ref().unwrap().contains(&id) | |
| } | |
| /// Compute whether we can derive default. | |
| fn compute_cannot_derive_default(&mut self) { | |
| let _t = self.timer("compute_cannot_derive_default"); | |
| assert!(self.cannot_derive_default.is_none()); | |
| if self.options.derive_default { | |
| self.cannot_derive_default = | |
| Some(as_cannot_derive_set(analyze::<CannotDerive>(( | |
| self, | |
| DeriveTrait::Default, | |
| )))); | |
| } | |
| } | |
| /// Look up whether the item with `id` can | |
| /// derive default or not. | |
| pub(crate) fn lookup_can_derive_default<Id: Into<ItemId>>( | |
| &self, | |
| id: Id, | |
| ) -> bool { | |
| let id = id.into(); | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute can_derive_default when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` can | |
| // derive default or not. | |
| !self.cannot_derive_default.as_ref().unwrap().contains(&id) | |
| } | |
| /// Compute whether we can derive copy. | |
| fn compute_cannot_derive_copy(&mut self) { | |
| let _t = self.timer("compute_cannot_derive_copy"); | |
| assert!(self.cannot_derive_copy.is_none()); | |
| self.cannot_derive_copy = | |
| Some(as_cannot_derive_set(analyze::<CannotDerive>(( | |
| self, | |
| DeriveTrait::Copy, | |
| )))); | |
| } | |
| /// Compute whether we can derive hash. | |
| fn compute_cannot_derive_hash(&mut self) { | |
| let _t = self.timer("compute_cannot_derive_hash"); | |
| assert!(self.cannot_derive_hash.is_none()); | |
| if self.options.derive_hash { | |
| self.cannot_derive_hash = | |
| Some(as_cannot_derive_set(analyze::<CannotDerive>(( | |
| self, | |
| DeriveTrait::Hash, | |
| )))); | |
| } | |
| } | |
| /// Look up whether the item with `id` can | |
| /// derive hash or not. | |
| pub(crate) fn lookup_can_derive_hash<Id: Into<ItemId>>( | |
| &self, | |
| id: Id, | |
| ) -> bool { | |
| let id = id.into(); | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute can_derive_debug when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` can | |
| // derive hash or not. | |
| !self.cannot_derive_hash.as_ref().unwrap().contains(&id) | |
| } | |
| /// Compute whether we can derive `PartialOrd`, `PartialEq` or `Eq`. | |
| fn compute_cannot_derive_partialord_partialeq_or_eq(&mut self) { | |
| let _t = self.timer("compute_cannot_derive_partialord_partialeq_or_eq"); | |
| assert!(self.cannot_derive_partialeq_or_partialord.is_none()); | |
| if self.options.derive_partialord || | |
| self.options.derive_partialeq || | |
| self.options.derive_eq | |
| { | |
| self.cannot_derive_partialeq_or_partialord = | |
| Some(analyze::<CannotDerive>(( | |
| self, | |
| DeriveTrait::PartialEqOrPartialOrd, | |
| ))); | |
| } | |
| } | |
| /// Look up whether the item with `id` can derive `Partial{Eq,Ord}`. | |
| pub(crate) fn lookup_can_derive_partialeq_or_partialord< | |
| Id: Into<ItemId>, | |
| >( | |
| &self, | |
| id: Id, | |
| ) -> CanDerive { | |
| let id = id.into(); | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute can_derive_partialeq_or_partialord when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` can | |
| // derive partialeq or not. | |
| self.cannot_derive_partialeq_or_partialord | |
| .as_ref() | |
| .unwrap() | |
| .get(&id) | |
| .copied() | |
| .unwrap_or(CanDerive::Yes) | |
| } | |
| /// Look up whether the item with `id` can derive `Copy` or not. | |
| pub(crate) fn lookup_can_derive_copy<Id: Into<ItemId>>( | |
| &self, | |
| id: Id, | |
| ) -> bool { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute can_derive_debug when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` can | |
| // derive `Copy` or not. | |
| let id = id.into(); | |
| !self.lookup_has_type_param_in_array(id) && | |
| !self.cannot_derive_copy.as_ref().unwrap().contains(&id) | |
| } | |
| /// Compute whether the type has type parameter in array. | |
| fn compute_has_type_param_in_array(&mut self) { | |
| let _t = self.timer("compute_has_type_param_in_array"); | |
| assert!(self.has_type_param_in_array.is_none()); | |
| self.has_type_param_in_array = | |
| Some(analyze::<HasTypeParameterInArray>(self)); | |
| } | |
| /// Look up whether the item with `id` has type parameter in array or not. | |
| pub(crate) fn lookup_has_type_param_in_array<Id: Into<ItemId>>( | |
| &self, | |
| id: Id, | |
| ) -> bool { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute has array when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` has | |
| // type parameter in array or not. | |
| self.has_type_param_in_array | |
| .as_ref() | |
| .unwrap() | |
| .contains(&id.into()) | |
| } | |
| /// Compute whether the type has float. | |
| fn compute_has_float(&mut self) { | |
| let _t = self.timer("compute_has_float"); | |
| assert!(self.has_float.is_none()); | |
| if self.options.derive_eq || self.options.derive_ord { | |
| self.has_float = Some(analyze::<HasFloat>(self)); | |
| } | |
| } | |
| /// Look up whether the item with `id` has array or not. | |
| pub(crate) fn lookup_has_float<Id: Into<ItemId>>(&self, id: Id) -> bool { | |
| assert!( | |
| self.in_codegen_phase(), | |
| "We only compute has float when we enter codegen" | |
| ); | |
| // Look up the computed value for whether the item with `id` has | |
| // float or not. | |
| self.has_float.as_ref().unwrap().contains(&id.into()) | |
| } | |
| /// Check if `--no-partialeq` flag is enabled for this item. | |
| pub(crate) fn no_partialeq_by_name(&self, item: &Item) -> bool { | |
| let name = item.path_for_allowlisting(self)[1..].join("::"); | |
| self.options().no_partialeq_types.matches(name) | |
| } | |
| /// Check if `--no-copy` flag is enabled for this item. | |
| pub(crate) fn no_copy_by_name(&self, item: &Item) -> bool { | |
| let name = item.path_for_allowlisting(self)[1..].join("::"); | |
| self.options().no_copy_types.matches(name) | |
| } | |
| /// Check if `--no-debug` flag is enabled for this item. | |
| pub(crate) fn no_debug_by_name(&self, item: &Item) -> bool { | |
| let name = item.path_for_allowlisting(self)[1..].join("::"); | |
| self.options().no_debug_types.matches(name) | |
| } | |
| /// Check if `--no-default` flag is enabled for this item. | |
| pub(crate) fn no_default_by_name(&self, item: &Item) -> bool { | |
| let name = item.path_for_allowlisting(self)[1..].join("::"); | |
| self.options().no_default_types.matches(name) | |
| } | |
| /// Check if `--no-hash` flag is enabled for this item. | |
| pub(crate) fn no_hash_by_name(&self, item: &Item) -> bool { | |
| let name = item.path_for_allowlisting(self)[1..].join("::"); | |
| self.options().no_hash_types.matches(name) | |
| } | |
| /// Check if `--must-use-type` flag is enabled for this item. | |
| pub(crate) fn must_use_type_by_name(&self, item: &Item) -> bool { | |
| let name = item.path_for_allowlisting(self)[1..].join("::"); | |
| self.options().must_use_types.matches(name) | |
| } | |
| /// Wrap some tokens in an `unsafe` block if the `--wrap-unsafe-ops` option is enabled. | |
| pub(crate) fn wrap_unsafe_ops(&self, tokens: impl ToTokens) -> TokenStream { | |
| if self.options.wrap_unsafe_ops { | |
| quote!(unsafe { #tokens }) | |
| } else { | |
| tokens.into_token_stream() | |
| } | |
| } | |
| /// Get the suffix to be added to `static` functions if the `--wrap-static-fns` option is | |
| /// enabled. | |
| pub(crate) fn wrap_static_fns_suffix(&self) -> &str { | |
| self.options() | |
| .wrap_static_fns_suffix | |
| .as_deref() | |
| .unwrap_or(crate::DEFAULT_NON_EXTERN_FNS_SUFFIX) | |
| } | |
| } | |
| /// A builder struct for configuring item resolution options. | |
| #[derive(Debug, Copy, Clone)] | |
| pub(crate) struct ItemResolver { | |
| id: ItemId, | |
| through_type_refs: bool, | |
| through_type_aliases: bool, | |
| } | |
| impl ItemId { | |
| /// Create an `ItemResolver` from this item ID. | |
| pub(crate) fn into_resolver(self) -> ItemResolver { | |
| self.into() | |
| } | |
| } | |
| impl<T> From<T> for ItemResolver | |
| where | |
| T: Into<ItemId>, | |
| { | |
| fn from(id: T) -> ItemResolver { | |
| ItemResolver::new(id) | |
| } | |
| } | |
| impl ItemResolver { | |
| /// Construct a new `ItemResolver` from the given ID. | |
| pub(crate) fn new<Id: Into<ItemId>>(id: Id) -> ItemResolver { | |
| let id = id.into(); | |
| ItemResolver { | |
| id, | |
| through_type_refs: false, | |
| through_type_aliases: false, | |
| } | |
| } | |
| /// Keep resolving through `Type::TypeRef` items. | |
| pub(crate) fn through_type_refs(mut self) -> ItemResolver { | |
| self.through_type_refs = true; | |
| self | |
| } | |
| /// Keep resolving through `Type::Alias` items. | |
| pub(crate) fn through_type_aliases(mut self) -> ItemResolver { | |
| self.through_type_aliases = true; | |
| self | |
| } | |
| /// Finish configuring and perform the actual item resolution. | |
| pub(crate) fn resolve(self, ctx: &BindgenContext) -> &Item { | |
| assert!(ctx.collected_typerefs()); | |
| let mut id = self.id; | |
| let mut seen_ids = HashSet::default(); | |
| loop { | |
| let item = ctx.resolve_item(id); | |
| // Detect cycles and bail out. These can happen in certain cases | |
| // involving incomplete qualified dependent types (#2085). | |
| if !seen_ids.insert(id) { | |
| return item; | |
| } | |
| let ty_kind = item.as_type().map(|t| t.kind()); | |
| match ty_kind { | |
| Some(&TypeKind::ResolvedTypeRef(next_id)) | |
| if self.through_type_refs => | |
| { | |
| id = next_id.into(); | |
| } | |
| // We intentionally ignore template aliases here, as they are | |
| // more complicated, and don't represent a simple renaming of | |
| // some type. | |
| Some(&TypeKind::Alias(next_id)) | |
| if self.through_type_aliases => | |
| { | |
| id = next_id.into(); | |
| } | |
| _ => return item, | |
| } | |
| } | |
| } | |
| } | |
| /// A type that we are in the middle of parsing. | |
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] | |
| pub(crate) struct PartialType { | |
| decl: Cursor, | |
| // Just an ItemId, and not a TypeId, because we haven't finished this type | |
| // yet, so there's still time for things to go wrong. | |
| id: ItemId, | |
| } | |
| impl PartialType { | |
| /// Construct a new `PartialType`. | |
| pub(crate) fn new(decl: Cursor, id: ItemId) -> PartialType { | |
| // assert!(decl == decl.canonical()); | |
| PartialType { decl, id } | |
| } | |
| /// The cursor pointing to this partial type's declaration location. | |
| pub(crate) fn decl(&self) -> &Cursor { | |
| &self.decl | |
| } | |
| /// The item ID allocated for this type. This is *NOT* a key for an entry in | |
| /// the context's item set yet! | |
| pub(crate) fn id(&self) -> ItemId { | |
| self.id | |
| } | |
| } | |
| impl TemplateParameters for PartialType { | |
| fn self_template_params(&self, _ctx: &BindgenContext) -> Vec<TypeId> { | |
| // Maybe at some point we will eagerly parse named types, but for now we | |
| // don't and this information is unavailable. | |
| vec![] | |
| } | |
| fn num_self_template_params(&self, _ctx: &BindgenContext) -> usize { | |
| // Wouldn't it be nice if libclang would reliably give us this | |
| // information‽ | |
| match self.decl().kind() { | |
| clang_sys::CXCursor_ClassTemplate | | |
| clang_sys::CXCursor_FunctionTemplate | | |
| clang_sys::CXCursor_TypeAliasTemplateDecl => { | |
| let mut num_params = 0; | |
| self.decl().visit(|c| { | |
| match c.kind() { | |
| clang_sys::CXCursor_TemplateTypeParameter | | |
| clang_sys::CXCursor_TemplateTemplateParameter | | |
| clang_sys::CXCursor_NonTypeTemplateParameter => { | |
| num_params += 1; | |
| } | |
| _ => {} | |
| } | |
| clang_sys::CXChildVisit_Continue | |
| }); | |
| num_params | |
| } | |
| _ => 0, | |
| } | |
| } | |
| } | |
| fn unused_regex_diagnostic(item: &str, name: &str, _ctx: &BindgenContext) { | |
| warn!("unused option: {name} {item}"); | |
| #[cfg(feature = "experimental")] | |
| if _ctx.options().emit_diagnostics { | |
| use crate::diagnostics::{Diagnostic, Level}; | |
| Diagnostic::default() | |
| .with_title( | |
| format!("Unused regular expression: `{item}`."), | |
| Level::Warning, | |
| ) | |
| .add_annotation( | |
| format!("This regular expression was passed to `{name}`."), | |
| Level::Note, | |
| ) | |
| .display(); | |
| } | |
| } |