mirror of
https://github.com/BlueTheDuck/nds-rs.git
synced 2025-06-18 11:05:51 -04:00
Improve #[entry] macro
This commit is contained in:
parent
dd5ae49909
commit
4b58ae07ed
@ -11,4 +11,4 @@ proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "2.0", features = ["full"]}
|
||||
quote = "1.0"
|
||||
quote = "1.0"
|
||||
|
@ -1,21 +1,109 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, ItemFn, Meta, MetaNameValue, ReturnType, Type};
|
||||
use syn::{
|
||||
parse_macro_input, spanned::Spanned, Attribute, FnArg, ItemFn, Meta, MetaNameValue, PatType,
|
||||
ReturnType, Type, Visibility,
|
||||
};
|
||||
|
||||
/// Use it to mark the ROM entry point.
|
||||
/// Allows only `fn(Hw) -> !`
|
||||
fn check_sig(signature: &syn::Signature) -> Result<(), syn::Error> {
|
||||
let span = signature.span();
|
||||
if let ReturnType::Type(_, ref t) = signature.output {
|
||||
match **t {
|
||||
Type::Never(_) => {}
|
||||
Type::Infer(_) => {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Add `-> !`. To avoid ambiguity, please specify the return type as `!`",
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Change to `-> !`. The entry point must be diverging. E.g. `fn() -> !`",
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Add `-> !`. To avoid ambiguity, please specify the return type as `!`.",
|
||||
));
|
||||
}
|
||||
|
||||
if signature.inputs.len() != 1 {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Incorrect arguments. The entry point must take exactly one argument of type `Hw`",
|
||||
));
|
||||
} else {
|
||||
let arg = signature.inputs.first().unwrap();
|
||||
if let FnArg::Typed(PatType { ty, .. }) = arg {
|
||||
if let Type::Path(ref path) = **ty {
|
||||
if path.path.segments.last().unwrap().ident != "Hw" {
|
||||
return Err(syn::Error::new(
|
||||
arg.span(),
|
||||
"Change type. The entry point must take exactly one argument of type `Hw`",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
arg.span(),
|
||||
"Change type. The entry point must take exactly one argument of type `Hw`",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
arg.span(),
|
||||
"Change type. The entry point can not be a method.",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allow only `pub` visibility
|
||||
fn check_vis(vis: &Visibility) -> Result<(), syn::Error> {
|
||||
if let Visibility::Public(..) = vis {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(syn::Error::new(
|
||||
vis.span(),
|
||||
"Add `pub`. The entry point must be public and without restriction.",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Forbids `#[no_mangle]` and `#[link_name = ...]`
|
||||
fn check_attr(attrs: &Vec<Attribute>) -> Result<(), syn::Error> {
|
||||
for attr in attrs {
|
||||
match &attr.meta {
|
||||
Meta::NameValue(MetaNameValue { path, .. }) if path.is_ident("export_name") => {
|
||||
return Err(syn::Error::new(path.span(), "Remove this `link_name`. The entry point requires a specific symbol name that it's set by the `entry` attribute."))
|
||||
}
|
||||
Meta::Path(path) if path.is_ident("no_mangle") => {
|
||||
return Err(syn::Error::new(path.span(), "Remove this `no_mangle`. The entry point requires a specific symbol name that it's set by the `entry` attribute."))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Use it to mark the entry point of your program.
|
||||
///
|
||||
/// Example:
|
||||
/// # Example:
|
||||
/// ```rust,no_run
|
||||
/// #![no_main]
|
||||
///
|
||||
/// #[macro_use]
|
||||
/// extern crate nds:
|
||||
/// extern crate nds_proc_macros:
|
||||
///
|
||||
/// #[entry]
|
||||
/// fn main(mut hw: nds::Hw) -> ! {
|
||||
/// loop {
|
||||
/// nds::interrupts::swi_wait_for_v_blank();
|
||||
/// }
|
||||
/// pub fn main(_: nds::Hw) -> ! {
|
||||
/// loop { }
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
@ -24,37 +112,24 @@ pub fn entry(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
|
||||
// Check the signature is correct:
|
||||
// - Must be `fn() -> !`
|
||||
// - Must be `pub`
|
||||
// - Must have 1 argument
|
||||
// - Must not have `link_name` or `no_mangle` attributes
|
||||
// - Must be `pub`
|
||||
// - Must not have `export_name` or `no_mangle` attributes
|
||||
|
||||
if let ReturnType::Type(_, t) = &input.sig.output {
|
||||
assert!(
|
||||
matches!(**t, Type::Never(_)),
|
||||
"The function must return `!`"
|
||||
);
|
||||
} else {
|
||||
panic!("The function must return `!`");
|
||||
if let Err(e) = check_sig(&input.sig) {
|
||||
return e.to_compile_error().into();
|
||||
}
|
||||
|
||||
assert!(matches!(input.vis, syn::Visibility::Public(_)));
|
||||
if let Err(e) = check_vis(&input.vis) {
|
||||
return e.to_compile_error().into();
|
||||
}
|
||||
|
||||
assert_eq!(input.sig.inputs.len(), 1);
|
||||
|
||||
for attr in &input.attrs {
|
||||
match &attr.meta {
|
||||
Meta::NameValue(MetaNameValue { path, .. }) if path.is_ident("link_name") => {
|
||||
panic!("The `link_name` attribute is not allowed here");
|
||||
}
|
||||
Meta::Path(path) if path.is_ident("no_mangle") => {
|
||||
panic!("The `no_mangle` attribute is not allowed here");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Err(e) = check_attr(&input.attrs) {
|
||||
return e.to_compile_error().into();
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[link_name = "rust_main"]
|
||||
#[export_name = "__rust_user_main"]
|
||||
#input
|
||||
}
|
||||
.into()
|
||||
|
@ -2,14 +2,19 @@
|
||||
|
||||
use nds_rs::Hw;
|
||||
|
||||
/// Entry point called from the C runtime
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must never be called by user code.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn main() -> ! {
|
||||
pub unsafe extern "C" fn main() -> ! {
|
||||
extern "Rust" {
|
||||
#[link_name = "rust_main"]
|
||||
#[link_name = "__rust_user_main"]
|
||||
fn main(hw: Hw) -> !;
|
||||
}
|
||||
|
||||
let peripherals = Hw::take().unwrap();
|
||||
let peripherals = Hw::take().unwrap_unchecked();
|
||||
unsafe {
|
||||
main(peripherals);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user