summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2021-03-22 20:07:03 +0100
committerGitHub <noreply@github.com>2021-03-22 20:07:03 +0100
commitde94b4875659fb76b4ae21ee4f677a344ff8a1ca (patch)
tree3ed6a06594c2639ef17ac963800cc939dd797147
parent5a055fde1d4b8937396914c4089a3f9883380786 (diff)
parent0780f804a89309417b6c99ccfc5d5941bdd38bab (diff)
Merge pull request #2007 from jethrogb/jb/issue-1454-alt
Add callback to check derives for blocklisted types
-rw-r--r--src/callbacks.rs19
-rw-r--r--src/ir/analysis/derive.rs25
-rw-r--r--src/ir/context.rs38
-rw-r--r--tests/expectations/tests/issue-1454.rs41
-rw-r--r--tests/headers/issue-1454.h10
-rw-r--r--tests/parse_callbacks/mod.rs24
6 files changed, 148 insertions, 9 deletions
diff --git a/src/callbacks.rs b/src/callbacks.rs
index 1cd7f37b..e288af43 100644
--- a/src/callbacks.rs
+++ b/src/callbacks.rs
@@ -1,5 +1,7 @@
//! A public API for more fine-grained customization of bindgen behavior.
+pub use crate::ir::analysis::DeriveTrait;
+pub use crate::ir::derive::CanDerive as ImplementsTrait;
pub use crate::ir::enum_ty::{EnumVariantCustomBehavior, EnumVariantValue};
pub use crate::ir::int::IntKind;
use std::fmt;
@@ -76,4 +78,21 @@ pub trait ParseCallbacks: fmt::Debug + UnwindSafe {
/// This will be called on every file inclusion, with the full path of the included file.
fn include_file(&self, _filename: &str) {}
+
+ /// This will be called to determine whether a particular blocklisted type
+ /// implements a trait or not. This will be used to implement traits on
+ /// other types containing the blocklisted type.
+ ///
+ /// * `None`: use the default behavior
+ /// * `Some(ImplementsTrait::Yes)`: `_name` implements `_derive_trait`
+ /// * `Some(ImplementsTrait::Manually)`: any type including `_name` can't
+ /// derive `_derive_trait` but can implemented it manually
+ /// * `Some(ImplementsTrait::No)`: `_name` doesn't implement `_derive_trait`
+ fn blocklisted_type_implements_trait(
+ &self,
+ _name: &str,
+ _derive_trait: DeriveTrait,
+ ) -> Option<ImplementsTrait> {
+ None
+ }
}
diff --git a/src/ir/analysis/derive.rs b/src/ir/analysis/derive.rs
index fa4ce795..be626661 100644
--- a/src/ir/analysis/derive.rs
+++ b/src/ir/analysis/derive.rs
@@ -16,7 +16,7 @@ use crate::ir::ty::{Type, TypeKind};
use crate::{Entry, HashMap, HashSet};
/// Which trait to consider when doing the `CannotDerive` analysis.
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum DeriveTrait {
/// The `Copy` trait.
Copy,
@@ -139,11 +139,24 @@ impl<'ctx> CannotDerive<'ctx> {
fn constrain_type(&mut self, item: &Item, ty: &Type) -> CanDerive {
if !self.ctx.allowlisted_items().contains(&item.id()) {
- trace!(
- " cannot derive {} for blocklisted type",
- self.derive_trait
- );
- return CanDerive::No;
+ let can_derive = self
+ .ctx
+ .blocklisted_type_implements_trait(item, self.derive_trait);
+ match can_derive {
+ CanDerive::Yes => trace!(
+ " blocklisted type explicitly implements {}",
+ self.derive_trait
+ ),
+ CanDerive::Manually => trace!(
+ " blocklisted type requires manual implementation of {}",
+ self.derive_trait
+ ),
+ CanDerive::No => trace!(
+ " cannot derive {} for blocklisted type",
+ self.derive_trait
+ ),
+ }
+ return can_derive;
}
if self.derive_trait.not_by_name(self.ctx, &item) {
diff --git a/src/ir/context.rs b/src/ir/context.rs
index a0d4de1e..ccb05e75 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -28,7 +28,7 @@ use cexpr;
use clang_sys;
use proc_macro2::{Ident, Span};
use std::borrow::Cow;
-use std::cell::Cell;
+use std::cell::{Cell, RefCell};
use std::collections::HashMap as StdHashMap;
use std::iter::IntoIterator;
use std::mem;
@@ -380,6 +380,10 @@ pub struct BindgenContext {
/// 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.
///
@@ -560,6 +564,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
options,
generated_bindgen_complex: Cell::new(false),
allowlisted: None,
+ blocklisted_types_implement_traits: Default::default(),
codegen_items: None,
used_template_parameters: None,
need_bitfield_allocation: Default::default(),
@@ -2205,6 +2210,37 @@ If you encounter an error missing from this list, please file an issue or a PR!"
self.allowlisted.as_ref().unwrap()
}
+ /// Check whether a particular blocklisted type implements a trait or not.
+ /// Results may be cached.
+ pub fn blocklisted_type_implements_trait(
+ &self,
+ item: &Item,
+ derive_trait: DeriveTrait,
+ ) -> CanDerive {
+ assert!(self.in_codegen_phase());
+ assert!(self.current_module == self.root_module);
+
+ let cb = match self.options.parse_callbacks {
+ Some(ref cb) => cb,
+ None => return CanDerive::No,
+ };
+
+ *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| {
+ cb.blocklisted_type_implements_trait(name, derive_trait)
+ })
+ .unwrap_or(CanDerive::No)
+ })
+ }
+
/// Get a reference to the set of items we should generate.
pub fn codegen_items(&self) -> &ItemSet {
assert!(self.in_codegen_phase());
diff --git a/tests/expectations/tests/issue-1454.rs b/tests/expectations/tests/issue-1454.rs
new file mode 100644
index 00000000..e88e4697
--- /dev/null
+++ b/tests/expectations/tests/issue-1454.rs
@@ -0,0 +1,41 @@
+#![allow(
+ dead_code,
+ non_snake_case,
+ non_camel_case_types,
+ non_upper_case_globals
+)]
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct extern_type;
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct local_type {
+ pub inner: extern_type,
+}
+#[test]
+fn bindgen_test_layout_local_type() {
+ assert_eq!(
+ ::std::mem::size_of::<local_type>(),
+ 0usize,
+ concat!("Size of: ", stringify!(local_type))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<local_type>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(local_type))
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<local_type>())).inner as *const _ as usize
+ },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(local_type),
+ "::",
+ stringify!(inner)
+ )
+ );
+}
diff --git a/tests/headers/issue-1454.h b/tests/headers/issue-1454.h
new file mode 100644
index 00000000..96645dac
--- /dev/null
+++ b/tests/headers/issue-1454.h
@@ -0,0 +1,10 @@
+// bindgen-flags: --no-recursive-allowlist --allowlist-type "local_type" --with-derive-hash --no-derive-copy --no-derive-default --raw-line "#[repr(C)] #[derive(Debug)] pub struct extern_type;"
+// bindgen-parse-callbacks: blocklisted-type-implements-trait
+
+struct extern_type {};
+
+typedef struct
+{
+ struct extern_type inner;
+}
+local_type;
diff --git a/tests/parse_callbacks/mod.rs b/tests/parse_callbacks/mod.rs
index 01993cfc..b94b54de 100644
--- a/tests/parse_callbacks/mod.rs
+++ b/tests/parse_callbacks/mod.rs
@@ -1,4 +1,4 @@
-use bindgen::callbacks::ParseCallbacks;
+use bindgen::callbacks::*;
#[derive(Debug)]
struct EnumVariantRename;
@@ -8,15 +8,35 @@ impl ParseCallbacks for EnumVariantRename {
&self,
_enum_name: Option<&str>,
original_variant_name: &str,
- _variant_value: bindgen::callbacks::EnumVariantValue,
+ _variant_value: EnumVariantValue,
) -> Option<String> {
Some(format!("RENAMED_{}", original_variant_name))
}
}
+#[derive(Debug)]
+struct BlocklistedTypeImplementsTrait;
+
+impl ParseCallbacks for BlocklistedTypeImplementsTrait {
+ fn blocklisted_type_implements_trait(
+ &self,
+ _name: &str,
+ derive_trait: DeriveTrait,
+ ) -> Option<ImplementsTrait> {
+ if derive_trait == DeriveTrait::Hash {
+ Some(ImplementsTrait::No)
+ } else {
+ Some(ImplementsTrait::Yes)
+ }
+ }
+}
+
pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
match cb {
"enum-variant-rename" => Box::new(EnumVariantRename),
+ "blocklisted-type-implements-trait" => {
+ Box::new(BlocklistedTypeImplementsTrait)
+ }
_ => panic!("Couldn't find name ParseCallbacks: {}", cb),
}
}