lazing/
lib.rs

1#![feature(proc_macro_diagnostic)]
2#![feature(allow_internal_unstable)]
3
4//! A macro like lazy_static can initialize static variables.
5//!
6//! # Usage
7//!
8//! ```
9//! # use std::ops::Deref;
10//! #[lazy]
11//! static NAME: String = "Hello".to_string();
12//!
13//! fn main() {
14//!    println!("{}",NAME.deref());
15//! }
16//!  
17//! ```
18
19use proc_macro::TokenStream;
20use quote::{quote, quote_spanned};
21use syn::parse::{Parse, ParseStream, Result};
22use syn::spanned::Spanned;
23use syn::{parse_macro_input, Expr, Ident, Token, Type, Visibility};
24
25struct LazyStatic {
26    visibility: Visibility,
27    name: Ident,
28    ty: Type,
29    init: Expr,
30}
31
32impl Parse for LazyStatic {
33    fn parse(input: ParseStream) -> Result<Self> {
34        let visibility: Visibility = input.parse()?;
35        input.parse::<Token![static]>()?;
36        let name: Ident = input.parse()?;
37        input.parse::<Token![:]>()?;
38        let ty: Type = input.parse()?;
39        input.parse::<Token![=]>()?;
40        let init: Expr = input.parse()?;
41        input.parse::<Token![;]>()?;
42        Ok(LazyStatic {
43            visibility,
44            name,
45            ty,
46            init,
47        })
48    }
49}
50
51/// Parses the following syntax
52/// ```
53/// # const IGNORE_TOKENS: &str = stringify! {
54/// #[lazy]
55/// $Visibility static $NAME: $Type = $EXPRESS;
56/// # };
57/// ```
58/// # Example
59/// ```
60/// # const IGNORE_TOKENS: &str = stringify! {
61/// #[lazy]
62/// pub static foo: String = "Hello".to_string();
63/// # };
64/// ```
65#[proc_macro_attribute]
66pub fn lazy(attr: TokenStream, item: TokenStream) -> TokenStream {
67    if !attr.is_empty() {
68        proc_macro2::TokenStream::from(attr)
69            .span()
70            .unwrap()
71            .error("no parameter should be at here.")
72            .emit();
73        return TokenStream::new();
74    }
75    let LazyStatic {
76        visibility,
77        name,
78        ty,
79        init,
80    } = parse_macro_input!(item as LazyStatic);
81
82    // Assert that the static type implements Sync. If not, user sees an error
83    // message like the following. We span this assertion with the field type's
84    // line/column so that the error message appears in the correct place.
85    //
86    //     error[E0277]: the trait bound `*const (): std::marker::Sync` is not satisfied
87    //       --> src/main.rs:10:21
88    //        |
89    //     10 |     static ref PTR: *const () = &();
90    //        |                     ^^^^^^^^^ `*const ()` cannot be shared between threads safely
91    let assert_sync = quote_spanned! {ty.span()=>
92        struct _AssertSync where #ty: std::marker::Sync;
93    };
94
95    // Check for Sized. Not vital to check here, but the error message is less
96    // confusing this way than if they get a Sized error in one of our
97    // implementation details where it assumes Sized.
98    //
99    //     error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
100    //       --> src/main.rs:10:19
101    //        |
102    //     10 |     static ref A: str = "";
103    //        |                   ^^^ `str` does not have a constant size known at compile-time
104    let assert_sized = quote_spanned! {ty.span()=>
105        struct _AssertSized where #ty: std::marker::Sized;
106    };
107
108    let init_ptr = quote_spanned! {init.span()=>
109        Box::into_raw(Box::new(#init))
110    };
111
112    let expanded = quote! {
113        #[allow(non_camel_case_types)]
114        #visibility struct #name;
115
116        impl std::ops::Deref for #name {
117            type Target = #ty;
118
119            fn deref(&self) -> &#ty {
120                #assert_sync
121                #assert_sized
122
123                static ONCE: std::sync::Once = std::sync::Once::new();
124                static mut VALUE: *mut #ty = 0 as *mut #ty;
125
126                unsafe {
127                    ONCE.call_once(|| VALUE = #init_ptr);
128                    &*VALUE
129                }
130            }
131        }
132    };
133
134    TokenStream::from(expanded)
135}