difftreelog
feature: non-static builtins
in: master
1 file changed
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth1use quote::quote;1use quote::{quote, quote_spanned};2use syn::{2use syn::{3 parse_macro_input, FnArg, GenericArgument, ItemFn, Pat, PatType, Path, PathArguments, Type,3 parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned,4 token::Comma, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments, Token,5 Type,4};6};576fn is_location_arg(t: &PatType) -> bool {8fn is_location_arg(t: &PatType) -> bool {7 t.attrs.iter().any(|a| a.path.is_ident("location"))9 t.attrs.iter().any(|a| a.path.is_ident("location"))8}10}11fn is_self_arg(t: &PatType) -> bool {12 t.attrs.iter().any(|a| a.path.is_ident("self"))13}91410trait RetainHad<T> {15trait RetainHad<T> {11 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool;16 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool;45 }50 }46}51}5253struct Field {54 name: Ident,55 _colon: Token![:],56 ty: Type,57}58impl Parse for Field {59 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {60 Ok(Self {61 name: input.parse()?,62 _colon: input.parse()?,63 ty: input.parse()?,64 })65 }66}6768mod kw {69 syn::custom_keyword!(fields);70}7172struct BuiltinAttrs {73 fields: Vec<Field>,74}75impl Parse for BuiltinAttrs {76 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {77 if input.is_empty() {78 return Ok(Self { fields: Vec::new() });79 }80 input.parse::<kw::fields>()?;81 let fields;82 parenthesized!(fields in input);83 let p = Punctuated::<Field, Comma>::parse_terminated(&fields)?;84 Ok(Self {85 fields: p.into_iter().collect(),86 })87 }88}478948#[proc_macro_attribute]90#[proc_macro_attribute]49pub fn builtin(91pub fn builtin(50 _attr: proc_macro::TokenStream,92 attr: proc_macro::TokenStream,51 item: proc_macro::TokenStream,93 item: proc_macro::TokenStream,52) -> proc_macro::TokenStream {94) -> proc_macro::TokenStream {53 // syn::ItemFn::parse(input)95 let attrs = parse_macro_input!(attr as BuiltinAttrs);54 let mut fun: ItemFn = parse_macro_input!(item);96 let mut fun: ItemFn = parse_macro_input!(item);559756 let result = match fun.sig.output {98 let result = match fun.sig.output {57 syn::ReturnType::Default => panic!("builtin should return something"),99 syn::ReturnType::Default => {100 return quote_spanned! { fun.sig.span() =>101 compile_error!("builtins should return something");102 }103 .into()104 }58 syn::ReturnType::Type(_, ref ty) => ty.clone(),105 syn::ReturnType::Type(_, ref ty) => ty.clone(),59 };106 };6010766 FnArg::Receiver(_) => unreachable!(),113 FnArg::Receiver(_) => unreachable!(),67 FnArg::Typed(t) => t,114 FnArg::Typed(t) => t,68 })115 })69 .filter(|a| !is_location_arg(a))116 .filter(|a| !is_location_arg(a) && !is_self_arg(a))70 .map(|t| {117 .map(|t| {71 let ident = match &t.pat as &Pat {118 let ident = match &t.pat as &Pat {72 Pat::Ident(i) => i.ident.to_string(),119 Pat::Ident(i) => i.ident.to_string(),73 _ => panic!("only idents supported yet"),120 _ => {121 return quote_spanned! { t.pat.span() =>122 compile_error!("args should be plain identifiers")123 }124 .into()125 }74 };126 };75 let optional = extract_type_from_option(&t.ty).is_some();127 let optional = extract_type_from_option(&t.ty).is_some();76 quote! {128 quote! {91 FnArg::Typed(t) => t,143 FnArg::Typed(t) => t,92 })144 })93 .map(|t| {145 .map(|t| {94 let is_location = t.attrs.retain_had(|a| !a.path.is_ident("location"));146 if t.attrs.retain_had(|a| !a.path.is_ident("location")) {147 quote! {{148 loc149 }}95 if is_location {150 } else if t.attrs.retain_had(|a| !a.path.is_ident("self")) {96 quote! {{151 quote! {{97 loc152 self98 }}153 }}99 } else {154 } else {100 let ident = match &t.pat as &Pat {155 let ident = match &t.pat as &Pat {101 Pat::Ident(i) => i.ident.to_string(),156 Pat::Ident(i) => i.ident.to_string(),102 _ => panic!("only idents supported yet"),157 _ => {158 return quote_spanned! { t.pat.span() =>159 compile_error!("args should be plain identifiers")160 }161 .into()162 }103 };163 };104 let ty = &t.ty;164 let ty = &t.ty;105 if let Some(opt_ty) = extract_type_from_option(&t.ty) {165 if let Some(opt_ty) = extract_type_from_option(&t.ty) {127 })187 })128 .collect::<Vec<_>>();188 .collect::<Vec<_>>();189190 let fields = attrs.fields.iter().map(|field| {191 let name = &field.name;192 let ty = &field.ty;193 quote! {194 pub #name: #ty,195 }196 });129197130 let name = &fun.sig.ident;198 let name = &fun.sig.ident;131 let vis = &fun.vis;199 let vis = &fun.vis;200 let static_ext = if attrs.fields.is_empty() {201 quote! {202 impl #name {203 pub const INST: &'static dyn StaticBuiltin = &#name {};204 }205 impl StaticBuiltin for #name {}206 }207 } else {208 quote! {}209 };210 let static_derive_copy = if attrs.fields.is_empty() {211 quote! {, Copy}212 } else {213 quote! {}214 };215132 (quote! {216 (quote! {133 #fun217 #fun134 #[doc(hidden)]218 #[doc(hidden)]135 #[allow(non_camel_case_types)]219 #[allow(non_camel_case_types)]136 #[derive(Clone, Copy, gcmodule::Trace)]220 #[derive(Clone, gcmodule::Trace #static_derive_copy)]137 #vis struct #name {}221 #vis struct #name {222 #(#fields)*223 }138 const _: () = {224 const _: () = {139 use ::jrsonnet_evaluator::{225 use ::jrsonnet_evaluator::{145 #(#params),*231 #(#params),*146 ];232 ];147233148 impl #name {234 #static_ext149 pub const INST: &'static dyn StaticBuiltin = &#name {};150 }151 impl StaticBuiltin for #name {}152 impl Builtin for #name235 impl Builtin for #name153 where236 where154 Self: 'static237 Self: 'static