Skip to content

Commit a673a6b

Browse files
authored
Allow callback composition (#2330)
* Allow callback composition Store all the callbacks added to the builder in a `Vec` so bindgen invokes each one of them in a last-to-first manner.
1 parent 8342689 commit a673a6b

File tree

8 files changed

+69
-50
lines changed

8 files changed

+69
-50
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@
154154
* Regex inputs are sanitized so alternation (`a|b`) is handled correctly but
155155
wildcard patterns (`*`) are now considered invalid.
156156
* the `ParseCallbacks`trait does not require to implement `UnwindSafe`.
157+
* the `Builder::parse_callbacks` method no longer overwrites previously added
158+
callbacks and composes them in a last-to-first manner.
157159

158160
## Removed
159161

bindgen/codegen/mod.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -2112,12 +2112,11 @@ impl CodeGenerator for CompInfo {
21122112

21132113
// The custom derives callback may return a list of derive attributes;
21142114
// add them to the end of the list.
2115-
let custom_derives;
2116-
if let Some(cb) = &ctx.options().parse_callbacks {
2117-
custom_derives = cb.add_derives(&canonical_name);
2118-
// In most cases this will be a no-op, since custom_derives will be empty.
2119-
derives.extend(custom_derives.iter().map(|s| s.as_str()));
2120-
};
2115+
let custom_derives = ctx
2116+
.options()
2117+
.all_callbacks(|cb| cb.add_derives(&canonical_name));
2118+
// In most cases this will be a no-op, since custom_derives will be empty.
2119+
derives.extend(custom_derives.iter().map(|s| s.as_str()));
21212120

21222121
if !derives.is_empty() {
21232122
attributes.push(attributes::derives(&derives))
@@ -3152,12 +3151,10 @@ impl CodeGenerator for Enum {
31523151

31533152
// The custom derives callback may return a list of derive attributes;
31543153
// add them to the end of the list.
3155-
let custom_derives;
3156-
if let Some(cb) = &ctx.options().parse_callbacks {
3157-
custom_derives = cb.add_derives(&name);
3158-
// In most cases this will be a no-op, since custom_derives will be empty.
3159-
derives.extend(custom_derives.iter().map(|s| s.as_str()));
3160-
};
3154+
let custom_derives =
3155+
ctx.options().all_callbacks(|cb| cb.add_derives(&name));
3156+
// In most cases this will be a no-op, since custom_derives will be empty.
3157+
derives.extend(custom_derives.iter().map(|s| s.as_str()));
31613158

31623159
attrs.push(attributes::derives(&derives));
31633160
}

bindgen/ir/context.rs

+19-20
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use super::module::{Module, ModuleKind};
1919
use super::template::{TemplateInstantiation, TemplateParameters};
2020
use super::traversal::{self, Edge, ItemTraversal};
2121
use super::ty::{FloatKind, Type, TypeKind};
22-
use crate::callbacks::ParseCallbacks;
2322
use crate::clang::{self, Cursor};
2423
use crate::parse::ClangItemParser;
2524
use crate::BindgenOptions;
@@ -619,15 +618,10 @@ If you encounter an error missing from this list, please file an issue or a PR!"
619618
)
620619
}
621620

622-
/// Get the user-provided callbacks by reference, if any.
623-
pub fn parse_callbacks(&self) -> Option<&dyn ParseCallbacks> {
624-
self.options().parse_callbacks.as_deref()
625-
}
626-
627621
/// Add another path to the set of included files.
628622
pub fn include_file(&mut self, filename: String) {
629-
if let Some(cbs) = self.parse_callbacks() {
630-
cbs.include_file(&filename);
623+
for cb in &self.options().parse_callbacks {
624+
cb.include_file(&filename);
631625
}
632626
self.deps.insert(filename);
633627
}
@@ -2240,19 +2234,24 @@ If you encounter an error missing from this list, please file an issue or a PR!"
22402234
.or_insert_with(|| {
22412235
item.expect_type()
22422236
.name()
2243-
.and_then(|name| match self.options.parse_callbacks {
2244-
Some(ref cb) => cb.blocklisted_type_implements_trait(
2245-
name,
2246-
derive_trait,
2247-
),
2248-
// Sized integer types from <stdint.h> get mapped to Rust primitive
2249-
// types regardless of whether they are blocklisted, so ensure that
2250-
// standard traits are considered derivable for them too.
2251-
None => Some(if self.is_stdint_type(name) {
2252-
CanDerive::Yes
2237+
.and_then(|name| {
2238+
if self.options.parse_callbacks.is_empty() {
2239+
// Sized integer types from <stdint.h> get mapped to Rust primitive
2240+
// types regardless of whether they are blocklisted, so ensure that
2241+
// standard traits are considered derivable for them too.
2242+
if self.is_stdint_type(name) {
2243+
Some(CanDerive::Yes)
2244+
} else {
2245+
Some(CanDerive::No)
2246+
}
22532247
} else {
2254-
CanDerive::No
2255-
}),
2248+
self.options.last_callback(|cb| {
2249+
cb.blocklisted_type_implements_trait(
2250+
name,
2251+
derive_trait,
2252+
)
2253+
})
2254+
}
22562255
})
22572256
.unwrap_or(CanDerive::No)
22582257
})

bindgen/ir/enum_ty.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ impl Enum {
102102
let name = cursor.spelling();
103103
let annotations = Annotations::new(&cursor);
104104
let custom_behavior = ctx
105-
.parse_callbacks()
106-
.and_then(|callbacks| {
105+
.options()
106+
.last_callback(|callbacks| {
107107
callbacks
108108
.enum_variant_behavior(type_name, &name, val)
109109
})
@@ -119,8 +119,8 @@ impl Enum {
119119
});
120120

121121
let new_name = ctx
122-
.parse_callbacks()
123-
.and_then(|callbacks| {
122+
.options()
123+
.last_callback(|callbacks| {
124124
callbacks.enum_variant_name(type_name, &name, val)
125125
})
126126
.or_else(|| {

bindgen/ir/function.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,11 @@ impl ClangSubItemParser for Function {
664664
// but seems easy enough to handle it here.
665665
name.push_str("_destructor");
666666
}
667-
if let Some(callbacks) = context.parse_callbacks() {
668-
if let Some(nm) = callbacks.generated_name_override(&name) {
669-
name = nm;
670-
}
667+
if let Some(nm) = context
668+
.options()
669+
.last_callback(|callbacks| callbacks.generated_name_override(&name))
670+
{
671+
name = nm;
671672
}
672673
assert!(!name.is_empty(), "Empty function name.");
673674

bindgen/ir/item.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -932,8 +932,8 @@ impl Item {
932932
let name = names.join("_");
933933

934934
let name = if opt.user_mangled == UserMangled::Yes {
935-
ctx.parse_callbacks()
936-
.and_then(|callbacks| callbacks.item_name(&name))
935+
ctx.options()
936+
.last_callback(|callbacks| callbacks.item_name(&name))
937937
.unwrap_or(name)
938938
} else {
939939
name

bindgen/ir/var.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl ClangSubItemParser for Var {
182182
use clang_sys::*;
183183
match cursor.kind() {
184184
CXCursor_MacroDefinition => {
185-
if let Some(callbacks) = ctx.parse_callbacks() {
185+
for callbacks in &ctx.options().parse_callbacks {
186186
match callbacks.will_parse_macro(&cursor.spelling()) {
187187
MacroParsingBehavior::Ignore => {
188188
return Err(ParseError::Continue);
@@ -191,7 +191,7 @@ impl ClangSubItemParser for Var {
191191
}
192192

193193
if cursor.is_macro_function_like() {
194-
handle_function_macro(&cursor, callbacks);
194+
handle_function_macro(&cursor, callbacks.as_ref());
195195
// We handled the macro, skip macro processing below.
196196
return Err(ParseError::Continue);
197197
}
@@ -249,15 +249,15 @@ impl ClangSubItemParser for Var {
249249
true,
250250
ctx,
251251
);
252-
if let Some(callbacks) = ctx.parse_callbacks() {
252+
for callbacks in &ctx.options().parse_callbacks {
253253
callbacks.str_macro(&name, &val);
254254
}
255255
(TypeKind::Pointer(char_ty), VarType::String(val))
256256
}
257257
EvalResult::Int(Wrapping(value)) => {
258258
let kind = ctx
259-
.parse_callbacks()
260-
.and_then(|c| c.int_macro(&name, value))
259+
.options()
260+
.last_callback(|c| c.int_macro(&name, value))
261261
.unwrap_or_else(|| {
262262
default_macro_constant_type(ctx, value)
263263
});

bindgen/lib.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,7 @@ impl Builder {
14641464
mut self,
14651465
cb: Box<dyn callbacks::ParseCallbacks>,
14661466
) -> Self {
1467-
self.options.parse_callbacks = Some(Rc::from(cb));
1467+
self.options.parse_callbacks.push(Rc::from(cb));
14681468
self
14691469
}
14701470

@@ -1982,7 +1982,7 @@ struct BindgenOptions {
19821982

19831983
/// A user-provided visitor to allow customizing different kinds of
19841984
/// situations.
1985-
parse_callbacks: Option<Rc<dyn callbacks::ParseCallbacks>>,
1985+
parse_callbacks: Vec<Rc<dyn callbacks::ParseCallbacks>>,
19861986

19871987
/// Which kind of items should we generate? By default, we'll generate all
19881988
/// of them.
@@ -2160,6 +2160,26 @@ impl BindgenOptions {
21602160
pub fn rust_features(&self) -> RustFeatures {
21612161
self.rust_features
21622162
}
2163+
2164+
fn last_callback<T>(
2165+
&self,
2166+
f: impl Fn(&dyn callbacks::ParseCallbacks) -> Option<T>,
2167+
) -> Option<T> {
2168+
self.parse_callbacks
2169+
.iter()
2170+
.filter_map(|cb| f(cb.as_ref()))
2171+
.last()
2172+
}
2173+
2174+
fn all_callbacks<T>(
2175+
&self,
2176+
f: impl Fn(&dyn callbacks::ParseCallbacks) -> Vec<T>,
2177+
) -> Vec<T> {
2178+
self.parse_callbacks
2179+
.iter()
2180+
.flat_map(|cb| f(cb.as_ref()))
2181+
.collect()
2182+
}
21632183
}
21642184

21652185
impl Default for BindgenOptions {
@@ -2225,7 +2245,7 @@ impl Default for BindgenOptions {
22252245
clang_args: vec![],
22262246
input_headers: vec![],
22272247
input_header_contents: Default::default(),
2228-
parse_callbacks: None,
2248+
parse_callbacks: Default::default(),
22292249
codegen_config: CodegenConfig::all(),
22302250
conservative_inline_namespaces: false,
22312251
generate_comments: true,

0 commit comments

Comments
 (0)