#![deny(warnings)] extern crate proc_macro; #[macro_use] extern crate quote; extern crate core; extern crate proc_macro2; #[macro_use] extern crate syn; use proc_macro2::Span; use syn::{parse, spanned::Spanned, ItemFn, ReturnType, Type, Visibility}; use proc_macro::TokenStream; /// Attribute to declare the entry point of the program /// /// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you /// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no /// private modules between the item and the root of the crate); if the item is in the root of the /// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases. /// /// The specified function will be called by the reset handler *after* RAM has been initialized. /// If present, the FPU will also be enabled before the function is called. /// /// The type of the specified function must be `[unsafe] fn() -> !` (never ending function) /// /// # Properties /// /// The entry point will be called by the reset handler. The program can't reference to the entry /// point, much less invoke it. /// /// # Examples /// /// - Simple entry point /// /// ``` no_run /// # #![no_main] /// # use riscv_rt_macros::entry; /// #[entry] /// fn main() -> ! { /// loop { /// /* .. */ /// } /// } /// ``` #[proc_macro_attribute] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature let valid_signature = f.sig.constness.is_none() && f.sig.asyncness.is_none() && f.vis == Visibility::Inherited && f.sig.abi.is_none() && f.sig.inputs.is_empty() && f.sig.generics.params.is_empty() && f.sig.generics.where_clause.is_none() && f.sig.variadic.is_none() && match f.sig.output { ReturnType::Default => false, ReturnType::Type(_, ref ty) => match **ty { Type::Never(_) => true, _ => false, }, }; if !valid_signature { return parse::Error::new( f.span(), "`#[entry]` function must have signature `[unsafe] fn() -> !`", ) .to_compile_error() .into(); } if !args.is_empty() { return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") .to_compile_error() .into(); } // XXX should we blacklist other attributes? let attrs = f.attrs; let unsafety = f.sig.unsafety; let stmts = f.block.stmts; quote!( #[export_name = "main"] #(#attrs)* pub #unsafety fn __risc_v_rt__main() -> ! { #(#stmts)* } ) .into() } /// Attribute to mark which function will be called at the beginning of the reset handler. /// /// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you /// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no /// private modules between the item and the root of the crate); if the item is in the root of the /// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer /// releases. /// /// The function must have the signature of `unsafe fn()`. /// /// The function passed will be called before static variables are initialized. Any access of static /// variables will result in undefined behavior. /// /// # Examples /// /// ``` /// # use riscv_rt_macros::pre_init; /// #[pre_init] /// unsafe fn before_main() { /// // do something here /// } /// /// # fn main() {} /// ``` #[proc_macro_attribute] pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature let valid_signature = f.sig.constness.is_none() && f.sig.asyncness.is_none() && f.vis == Visibility::Inherited && f.sig.unsafety.is_some() && f.sig.abi.is_none() && f.sig.inputs.is_empty() && f.sig.generics.params.is_empty() && f.sig.generics.where_clause.is_none() && f.sig.variadic.is_none() && match f.sig.output { ReturnType::Default => true, ReturnType::Type(_, ref ty) => match **ty { Type::Tuple(ref tuple) => tuple.elems.is_empty(), _ => false, }, }; if !valid_signature { return parse::Error::new( f.span(), "`#[pre_init]` function must have signature `unsafe fn()`", ) .to_compile_error() .into(); } if !args.is_empty() { return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") .to_compile_error() .into(); } // XXX should we blacklist other attributes? let attrs = f.attrs; let ident = f.sig.ident; let block = f.block; quote!( #[export_name = "__pre_init"] #(#attrs)* pub unsafe fn #ident() #block ) .into() }