Parse unsafe attributes

This commit is contained in:
coekjan 2024-10-27 21:56:11 +08:00 committed by Emilio Cobos Álvarez
parent 3ed9434f09
commit 0ac02118b1
17 changed files with 105 additions and 10 deletions

4
Cargo.lock generated
View File

@ -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",

View File

@ -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<F, T, E>(&mut self, f: F) -> Result<Vec<T>, E>
@ -38,12 +38,10 @@ impl SynItemHelpers for syn::ItemFn {
fn exported_name(&self) -> Option<String> {
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::<proc_macro2::TokenStream, syn::Token![,]>::parse_terminated;
let Ok(args) = parser.parse2(tokens) else {
return false;
};
args.into_iter().any(|arg| {
match syn::parse2::<syn::Path>(arg) {
Ok(path) => path.is_ident(name),
Err(_) => false,
}
})
})
}
fn find_deprecated_note(&self) -> Option<String> {
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<String> {
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::<syn::MetaNameValue>(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<String> {
let mut comment = Vec::new();

View File

@ -4,3 +4,5 @@
#include <stdlib.h>
void do_the_thing_with_export_name(void);
void do_the_thing_with_unsafe_export_name(void);

View File

@ -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

View File

@ -8,4 +8,6 @@ extern "C" {
void do_the_thing_with_export_name();
void do_the_thing_with_unsafe_export_name();
} // extern "C"

View File

@ -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();

View File

@ -15,3 +15,5 @@ typedef struct {
typedef FooU8 Boo;
void root(Boo x, Bar y);
void unsafe_root(Boo x, Bar y);

View File

@ -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

View File

@ -20,4 +20,6 @@ extern "C" {
void root(Boo x, Bar y);
void unsafe_root(Boo x, Bar y);
} // extern "C"

View File

@ -16,3 +16,5 @@ cdef extern from *:
ctypedef FooU8 Boo;
void root(Boo x, Bar y);
void unsafe_root(Boo x, Bar y);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -16,3 +16,5 @@ cdef extern from *:
ctypedef FooU8 Boo;
void root(Boo x, Bar y);
void unsafe_root(Boo x, Bar y);

View File

@ -1,4 +1,9 @@
#[export_name = "do_the_thing_with_export_name"]
pub extern "C" fn do_the_thing() {
println!("doing the thing!");
}
}
#[unsafe(export_name = "do_the_thing_with_unsafe_export_name")]
pub extern "C" fn unsafe_do_the_thing() {
println!("doing the thing!");
}

View File

@ -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,
) { }