From 0ac02118b14da32f12ec088745d3b970481580f0 Mon Sep 17 00:00:00 2001 From: coekjan Date: Sun, 27 Oct 2024 21:56:11 +0800 Subject: [PATCH] Parse `unsafe` attributes --- Cargo.lock | 4 +- src/bindgen/utilities.rs | 72 ++++++++++++++++++++++--- tests/expectations/export_name.c | 2 + tests/expectations/export_name.compat.c | 2 + tests/expectations/export_name.cpp | 2 + tests/expectations/export_name.pyx | 2 + tests/expectations/mangle.c | 2 + tests/expectations/mangle.compat.c | 2 + tests/expectations/mangle.cpp | 2 + tests/expectations/mangle.pyx | 2 + tests/expectations/mangle_both.c | 2 + tests/expectations/mangle_both.compat.c | 2 + tests/expectations/mangle_tag.c | 2 + tests/expectations/mangle_tag.compat.c | 2 + tests/expectations/mangle_tag.pyx | 2 + tests/rust/export_name.rs | 7 ++- tests/rust/mangle.rs | 6 +++ 17 files changed, 105 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e95120c..8e3f757 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -405,9 +405,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.72" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", diff --git a/src/bindgen/utilities.rs b/src/bindgen/utilities.rs index 5cab65b..223acdc 100644 --- a/src/bindgen/utilities.rs +++ b/src/bindgen/utilities.rs @@ -4,7 +4,7 @@ #![allow(clippy::redundant_closure_call)] -use syn::ext::IdentExt; +use syn::{ext::IdentExt, parse::Parser}; pub trait IterHelpers: Iterator { fn try_skip_map(&mut self, f: F) -> Result, E> @@ -38,12 +38,10 @@ impl SynItemHelpers for syn::ItemFn { fn exported_name(&self) -> Option { self.attrs .attr_name_value_lookup("export_name") + .or_else(|| self.unsafe_attr_name_value_lookup("export_name")) .or_else(|| { - if self.is_no_mangle() { - Some(self.sig.ident.unraw().to_string()) - } else { - None - } + self.is_no_mangle() + .then(|| self.sig.ident.unraw().to_string()) }) } } @@ -150,6 +148,29 @@ pub trait SynAttributeHelpers { }) } + /// Searches for attributes like `#[unsafe(test)]`. + /// Example: + /// - `item.has_unsafe_attr_word("test")` => `#[unsafe(test)]` + fn has_unsafe_attr_word(&self, name: &str) -> bool { + self.attrs().iter().filter_map(|attr| { + match &attr.meta { + syn::Meta::List(list) if list.path.is_ident("unsafe") => Some(list.tokens.clone()), + _ => None, + } + }).any(|tokens| { + let parser = syn::punctuated::Punctuated::::parse_terminated; + let Ok(args) = parser.parse2(tokens) else { + return false; + }; + args.into_iter().any(|arg| { + match syn::parse2::(arg) { + Ok(path) => path.is_ident(name), + Err(_) => false, + } + }) + }) + } + fn find_deprecated_note(&self) -> Option { let attrs = self.attrs(); // #[deprecated = ""] @@ -194,7 +215,7 @@ pub trait SynAttributeHelpers { } fn is_no_mangle(&self) -> bool { - self.has_attr_word("no_mangle") + self.has_attr_word("no_mangle") || self.has_unsafe_attr_word("no_mangle") } /// Sees whether we should skip parsing a given item. @@ -231,6 +252,43 @@ pub trait SynAttributeHelpers { .next() } + fn unsafe_attr_name_value_lookup(&self, name: &str) -> Option { + self.attrs() + .iter() + .filter_map(|attr| { + let syn::Meta::List(syn::MetaList { path, tokens, .. }) = &attr.meta else { + return None; + }; + if path.is_ident("unsafe") { + let parser = syn::punctuated::Punctuated::< + proc_macro2::TokenStream, + syn::Token![,], + >::parse_terminated; + let Ok(args) = parser.parse2(tokens.clone()) else { + return None; + }; + for arg in args { + match syn::parse2::(arg) { + Ok(syn::MetaNameValue { + path, + value: + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }), + .. + }) if path.is_ident(name) => { + return Some(lit.value()); + } + _ => {} + } + } + } + None + }) + .next() + } + fn get_comment_lines(&self) -> Vec { let mut comment = Vec::new(); diff --git a/tests/expectations/export_name.c b/tests/expectations/export_name.c index 5ec561e..e3c1dcb 100644 --- a/tests/expectations/export_name.c +++ b/tests/expectations/export_name.c @@ -4,3 +4,5 @@ #include void do_the_thing_with_export_name(void); + +void do_the_thing_with_unsafe_export_name(void); diff --git a/tests/expectations/export_name.compat.c b/tests/expectations/export_name.compat.c index 67d6638..714ecc2 100644 --- a/tests/expectations/export_name.compat.c +++ b/tests/expectations/export_name.compat.c @@ -9,6 +9,8 @@ extern "C" { void do_the_thing_with_export_name(void); +void do_the_thing_with_unsafe_export_name(void); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/tests/expectations/export_name.cpp b/tests/expectations/export_name.cpp index e657310..0168826 100644 --- a/tests/expectations/export_name.cpp +++ b/tests/expectations/export_name.cpp @@ -8,4 +8,6 @@ extern "C" { void do_the_thing_with_export_name(); +void do_the_thing_with_unsafe_export_name(); + } // extern "C" diff --git a/tests/expectations/export_name.pyx b/tests/expectations/export_name.pyx index 5fb9644..08b5e8c 100644 --- a/tests/expectations/export_name.pyx +++ b/tests/expectations/export_name.pyx @@ -7,3 +7,5 @@ cdef extern from *: cdef extern from *: void do_the_thing_with_export_name(); + + void do_the_thing_with_unsafe_export_name(); diff --git a/tests/expectations/mangle.c b/tests/expectations/mangle.c index ccd8117..255f7c1 100644 --- a/tests/expectations/mangle.c +++ b/tests/expectations/mangle.c @@ -15,3 +15,5 @@ typedef struct { typedef FooU8 Boo; void root(Boo x, Bar y); + +void unsafe_root(Boo x, Bar y); diff --git a/tests/expectations/mangle.compat.c b/tests/expectations/mangle.compat.c index 83f816e..00b2d16 100644 --- a/tests/expectations/mangle.compat.c +++ b/tests/expectations/mangle.compat.c @@ -20,6 +20,8 @@ extern "C" { void root(Boo x, Bar y); +void unsafe_root(Boo x, Bar y); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/tests/expectations/mangle.cpp b/tests/expectations/mangle.cpp index 30b959c..0220d4c 100644 --- a/tests/expectations/mangle.cpp +++ b/tests/expectations/mangle.cpp @@ -20,4 +20,6 @@ extern "C" { void root(Boo x, Bar y); +void unsafe_root(Boo x, Bar y); + } // extern "C" diff --git a/tests/expectations/mangle.pyx b/tests/expectations/mangle.pyx index ddb1977..8ab50d7 100644 --- a/tests/expectations/mangle.pyx +++ b/tests/expectations/mangle.pyx @@ -16,3 +16,5 @@ cdef extern from *: ctypedef FooU8 Boo; void root(Boo x, Bar y); + + void unsafe_root(Boo x, Bar y); diff --git a/tests/expectations/mangle_both.c b/tests/expectations/mangle_both.c index 71408c8..7e17c21 100644 --- a/tests/expectations/mangle_both.c +++ b/tests/expectations/mangle_both.c @@ -15,3 +15,5 @@ typedef struct FooU8 { typedef struct FooU8 Boo; void root(Boo x, enum Bar y); + +void unsafe_root(Boo x, enum Bar y); diff --git a/tests/expectations/mangle_both.compat.c b/tests/expectations/mangle_both.compat.c index db849dc..053f1ac 100644 --- a/tests/expectations/mangle_both.compat.c +++ b/tests/expectations/mangle_both.compat.c @@ -20,6 +20,8 @@ extern "C" { void root(Boo x, enum Bar y); +void unsafe_root(Boo x, enum Bar y); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/tests/expectations/mangle_tag.c b/tests/expectations/mangle_tag.c index caa759c..37eec1a 100644 --- a/tests/expectations/mangle_tag.c +++ b/tests/expectations/mangle_tag.c @@ -15,3 +15,5 @@ struct FooU8 { typedef struct FooU8 Boo; void root(Boo x, enum Bar y); + +void unsafe_root(Boo x, enum Bar y); diff --git a/tests/expectations/mangle_tag.compat.c b/tests/expectations/mangle_tag.compat.c index a7b9231..fe0518f 100644 --- a/tests/expectations/mangle_tag.compat.c +++ b/tests/expectations/mangle_tag.compat.c @@ -20,6 +20,8 @@ extern "C" { void root(Boo x, enum Bar y); +void unsafe_root(Boo x, enum Bar y); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/tests/expectations/mangle_tag.pyx b/tests/expectations/mangle_tag.pyx index 4a54cc8..8a83d2d 100644 --- a/tests/expectations/mangle_tag.pyx +++ b/tests/expectations/mangle_tag.pyx @@ -16,3 +16,5 @@ cdef extern from *: ctypedef FooU8 Boo; void root(Boo x, Bar y); + + void unsafe_root(Boo x, Bar y); diff --git a/tests/rust/export_name.rs b/tests/rust/export_name.rs index 5d17ee1..d8683d0 100644 --- a/tests/rust/export_name.rs +++ b/tests/rust/export_name.rs @@ -1,4 +1,9 @@ #[export_name = "do_the_thing_with_export_name"] pub extern "C" fn do_the_thing() { println!("doing the thing!"); -} \ No newline at end of file +} + +#[unsafe(export_name = "do_the_thing_with_unsafe_export_name")] +pub extern "C" fn unsafe_do_the_thing() { + println!("doing the thing!"); +} diff --git a/tests/rust/mangle.rs b/tests/rust/mangle.rs index 1ac7e13..01d7a75 100644 --- a/tests/rust/mangle.rs +++ b/tests/rust/mangle.rs @@ -17,3 +17,9 @@ pub extern "C" fn root( x: Boo, y: Bar, ) { } + +#[unsafe(no_mangle)] +pub extern "C" fn unsafe_root( + x: Boo, + y: Bar, +) { }