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}