Skip to content

Commit f5a8364

Browse files
committed
Add callback to declare functions/statics safe
1 parent ccd5637 commit f5a8364

File tree

6 files changed

+154
-3
lines changed

6 files changed

+154
-3
lines changed

bindgen-tests/tests/expectations/tests/parsecb-declare-safe.rs

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// bindgen-parse-callbacks: declare-safe
2+
3+
const int my_safe_var[3] = {1,2,3};
4+
5+
int my_safe_func();

bindgen-tests/tests/parse_callbacks/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,28 @@ impl ParseCallbacks for OperatorRename {
160160
}
161161
}
162162

163+
#[derive(Debug)]
164+
struct DeclareSafe;
165+
166+
impl ParseCallbacks for DeclareSafe {
167+
fn declare_safe(&self, item_info: ItemInfo<'_>) -> Option<String> {
168+
match item_info.kind {
169+
ItemKind::Function => {
170+
if item_info.name == "my_safe_func" {
171+
return Some("safe to call".to_owned());
172+
}
173+
}
174+
ItemKind::Var => {
175+
if item_info.name == "my_safe_var" {
176+
return Some("safe to access".to_owned());
177+
}
178+
}
179+
_ => todo!(),
180+
}
181+
None
182+
}
183+
}
184+
163185
pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
164186
match cb {
165187
"enum-variant-rename" => Box::new(EnumVariantRename),
@@ -169,6 +191,7 @@ pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
169191
"wrap-as-variadic-fn" => Box::new(WrapAsVariadicFn),
170192
"type-visibility" => Box::new(TypeVisibility),
171193
"operator-rename" => Box::new(OperatorRename),
194+
"declare-safe" => Box::new(DeclareSafe),
172195
call_back => {
173196
if let Some(prefix) =
174197
call_back.strip_prefix("remove-function-prefix-")

bindgen-tests/tests/tests.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use bindgen::{clang_version, Builder};
1+
use bindgen::callbacks::{ItemInfo, ItemKind, ParseCallbacks};
2+
use bindgen::{clang_version, Builder, Formatter};
23
use owo_colors::{OwoColorize, Style};
34
use similar::{ChangeTag, TextDiff};
45
use std::env;
@@ -427,6 +428,58 @@ fn test_header_contents() {
427428
assert_eq!(expected, actual);
428429
}
429430

431+
#[test]
432+
fn test_declare_safe() {
433+
// prettyplease does not retain non-doc comments: https://github.com/dtolnay/prettyplease/issues/50
434+
435+
#[derive(Debug)]
436+
struct DeclareSafe;
437+
438+
impl ParseCallbacks for DeclareSafe {
439+
fn declare_safe(&self, item_info: ItemInfo<'_>) -> Option<String> {
440+
match item_info.kind {
441+
ItemKind::Function => {
442+
if item_info.name == "my_safe_func" {
443+
return Some("safe to call".to_owned());
444+
}
445+
}
446+
ItemKind::Var => {
447+
if item_info.name == "my_safe_var" {
448+
return Some("safe to access".to_owned());
449+
}
450+
}
451+
_ => todo!(),
452+
}
453+
None
454+
}
455+
}
456+
457+
let actual = builder()
458+
.disable_header_comment()
459+
.header_contents(
460+
"safe.h",
461+
"const int my_safe_var[3] = {1,2,3};\
462+
int my_safe_func();",
463+
)
464+
.formatter(Formatter::Rustfmt)
465+
.parse_callbacks(Box::new(DeclareSafe))
466+
.generate()
467+
.unwrap()
468+
.to_string();
469+
470+
let expected = r#"unsafe extern "C" {
471+
/* Safety : "safe to access" */
472+
pub safe static my_safe_var: [::std::os::raw::c_int; 3usize];
473+
}
474+
unsafe extern "C" {
475+
/* Safety : "safe to call" */
476+
pub safe fn my_safe_func() -> ::std::os::raw::c_int;
477+
}
478+
"#;
479+
480+
assert_eq!(expected, actual);
481+
}
482+
430483
#[test]
431484
fn test_multiple_header_calls_in_builder() {
432485
let actual = builder()

bindgen/callbacks.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ pub trait ParseCallbacks: fmt::Debug {
134134
vec![]
135135
}
136136

137+
/// Allows declaring items as `safe`.
138+
///
139+
/// The returned string will be prepended to the item as `Safety: ...` comment.
140+
///
141+
/// When using [`Formatter::Prettyplease`][crate::Formatter::Prettyplease] to format code, non-doc comments are removed ([issue][doc_removal]).
142+
///
143+
/// [doc_removal]: https://github.com/dtolnay/prettyplease/issues/50
144+
fn declare_safe(&self, _item_info: ItemInfo<'_>) -> Option<String> {
145+
None
146+
}
147+
137148
/// Provide a list of custom attributes.
138149
///
139150
/// If no additional attributes are wanted, this function should return an

bindgen/codegen/mod.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -856,10 +856,17 @@ impl CodeGenerator for Var {
856856
.unsafe_extern_blocks
857857
.then(|| quote!(unsafe));
858858

859+
let (safety_comment, var_safety) = utils::declare_safe(
860+
&canonical_ident,
861+
crate::callbacks::ItemKind::Var,
862+
ctx,
863+
);
864+
859865
let tokens = quote!(
860866
#safety extern "C" {
861867
#(#attrs)*
862-
pub static #maybe_mut #canonical_ident: #ty;
868+
#safety_comment
869+
pub #var_safety static #maybe_mut #canonical_ident: #ty;
863870
}
864871
);
865872

@@ -4870,11 +4877,18 @@ impl CodeGenerator for Function {
48704877
.unsafe_extern_blocks
48714878
.then(|| quote!(unsafe));
48724879

4880+
let (safety_comment, fn_safety) = utils::declare_safe(
4881+
&ident,
4882+
crate::callbacks::ItemKind::Function,
4883+
ctx,
4884+
);
4885+
48734886
let tokens = quote! {
48744887
#block_attributes
48754888
#safety extern #abi {
48764889
#(#attributes)*
4877-
pub fn #ident ( #( #args ),* ) #ret;
4890+
#safety_comment
4891+
pub #fn_safety fn #ident ( #( #args ),* ) #ret;
48784892
}
48794893
};
48804894

@@ -5346,6 +5360,44 @@ pub(crate) mod utils {
53465360
use std::path::PathBuf;
53475361
use std::str::FromStr;
53485362

5363+
pub(super) fn declare_safe(
5364+
item_ident: &proc_macro2::Ident,
5365+
item_kind: crate::callbacks::ItemKind,
5366+
context: &BindgenContext,
5367+
) -> (
5368+
Option<proc_macro2::TokenStream>,
5369+
Option<proc_macro2::TokenStream>,
5370+
) {
5371+
let safety_comment = context
5372+
.options()
5373+
.rust_features
5374+
.unsafe_extern_blocks
5375+
.then( || {
5376+
context.options().last_callback(|cb| {
5377+
cb.declare_safe(ItemInfo {
5378+
name: &item_ident.to_string(),
5379+
kind: item_kind,
5380+
})
5381+
})
5382+
})
5383+
.flatten()
5384+
.map(|safety_comment| {
5385+
let comment =
5386+
proc_macro2::Punct::new('/', proc_macro2::Spacing::Joint);
5387+
let comment2 =
5388+
proc_macro2::Punct::new('*', proc_macro2::Spacing::Alone);
5389+
let comment3 =
5390+
proc_macro2::Punct::new('*', proc_macro2::Spacing::Joint);
5391+
let comment4 =
5392+
proc_macro2::Punct::new('/', proc_macro2::Spacing::Alone);
5393+
5394+
quote!(#comment #comment2 Safety: #safety_comment #comment3 #comment4)
5395+
});
5396+
5397+
let item_safety = safety_comment.is_some().then_some(quote!(safe));
5398+
(safety_comment, item_safety)
5399+
}
5400+
53495401
pub(super) fn serialize_items(
53505402
result: &CodegenResult,
53515403
context: &BindgenContext,

0 commit comments

Comments
 (0)