Skip to content
This repository has been archived by the owner on Jun 8, 2024. It is now read-only.

Commit

Permalink
report errors when hooks aren't applied
Browse files Browse the repository at this point in the history
  • Loading branch information
KodrAus committed Nov 16, 2023
1 parent daf81c6 commit e1ece0b
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 36 deletions.
31 changes: 25 additions & 6 deletions macros/src/capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ impl Parse for Args {
}
}

pub fn key_value_with_hook(attrs: &[Attribute], fv: &FieldValue) -> TokenStream {
pub fn key_value_with_hook(
attrs: &[Attribute],
fv: &FieldValue,
interpolated: bool,
) -> TokenStream {
let fn_name = match &*fv.key_name() {
emit_core::well_known::LVL_KEY => quote_spanned!(fv.span()=> __private_capture_as_level),
emit_core::well_known::ERR_KEY => quote_spanned!(fv.span()=> __private_capture_as_error),
Expand All @@ -50,10 +54,16 @@ pub fn key_value_with_hook(attrs: &[Attribute], fv: &FieldValue) -> TokenStream
let key_expr = fv.key_expr();
let expr = &fv.expr;

let interpolated_expr = if interpolated {
quote!(.__private_interpolated())
} else {
quote!(.__private_uninterpolated())
};

let key_tokens = key::key_with_hook(&[], &key_expr);
let value_tokens = quote_spanned!(fv.span()=> {
use emit::__private::{__PrivateCaptureHook, __PrivateOptionalCaptureHook, __PrivateOptionalMapHook};
(#expr).__private_optional_capture_some().__private_optional_map_some(|v| v.#fn_name())
use emit::__private::{__PrivateCaptureHook as _, __PrivateOptionalCaptureHook as _, __PrivateOptionalMapHook as _, __PrivateInterpolatedHook as _};
(#expr).__private_optional_capture_some().__private_optional_map_some(|v| v.#fn_name())#interpolated_expr
});

quote_spanned!(fv.span()=>
Expand All @@ -65,6 +75,7 @@ pub fn key_value_with_hook(attrs: &[Attribute], fv: &FieldValue) -> TokenStream
}

pub struct RenameHookTokens<T> {
pub name: &'static str,
pub args: TokenStream,
pub expr: TokenStream,
pub to: T,
Expand All @@ -74,13 +85,21 @@ pub fn rename_hook_tokens(
opts: RenameHookTokens<impl Fn(&Args) -> TokenStream>,
) -> Result<TokenStream, syn::Error> {
hook::rename_hook_tokens(hook::RenameHookTokens {
name: opts.name,
target: "values in `emit` macros",
args: opts.args,
expr: opts.expr,
predicate: |ident: &str| ident.starts_with("__private_capture"),
to: move |args: &Args, _: &Ident, _: &Punctuated<Expr, Comma>| {
predicate: |ident: &str| {
ident.starts_with("__private_capture") || ident.starts_with("__private_interpolated")
},
to: move |args: &Args, ident: &Ident, _: &Punctuated<Expr, Comma>| {
if ident.to_string().starts_with("__private_interpolated") {
return None;
}

let to_ident = (opts.to)(args);

(to_ident, quote!())
Some((to_ident, quote!()))
},
})
}
Expand Down
31 changes: 24 additions & 7 deletions macros/src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@ use crate::{
hook,
};

pub fn template_hole_with_hook(attrs: &[Attribute], hole: &ExprLit) -> TokenStream {
pub fn template_hole_with_hook(
attrs: &[Attribute],
hole: &ExprLit,
interpolated: bool,
) -> TokenStream {
let interpolated_expr = if interpolated {
quote!(.__private_interpolated())
} else {
quote!(.__private_uninterpolated())
};

quote_spanned!(hole.span()=>
#(#attrs)*
{
use emit::__private::__PrivateFmtHook;
emit::template::Part::hole(#hole).__private_fmt_as_default()
use emit::__private::{__PrivateFmtHook as _, __PrivateInterpolatedHook as _};
emit::template::Part::hole(#hole).__private_fmt_as_default()#interpolated_expr
}
)
}
Expand Down Expand Up @@ -65,19 +75,26 @@ pub struct RenameHookTokens {

pub fn rename_hook_tokens(opts: RenameHookTokens) -> Result<TokenStream, syn::Error> {
hook::rename_hook_tokens(hook::RenameHookTokens {
name: "fmt",
target: "values in templates or event macros",
args: opts.args,
expr: opts.expr,
predicate: |ident: &str| ident.starts_with("__private_fmt"),
to: move |args: &Args, _: &Ident, _: &Punctuated<Expr, Comma>| {
predicate: |ident: &str| {
ident.starts_with("__private_fmt") || ident.starts_with("__private_interpolated")
},
to: move |args: &Args, ident: &Ident, _: &Punctuated<Expr, Comma>| {
if ident.to_string().starts_with("__private_interpolated") {
return None;
}

let fmt = args.to_format_args();

let to_ident = quote!(__private_fmt_as);
let to_arg = quote!(emit::template::Formatter::new(|v, f| {
use emit::__private::core::fmt;
emit::__private::core::write!(f, #fmt, v)
}));

(to_ident, to_arg)
Some((to_ident, to_arg))
},
})
}
Expand Down
37 changes: 27 additions & 10 deletions macros/src/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use quote::ToTokens;
use syn::{
parse::Parse,
punctuated::Punctuated,
spanned::Spanned,
token::Comma,
visit_mut::{self, VisitMut},
Expr, ExprMethodCall, Ident,
Expand All @@ -17,38 +18,52 @@ pub struct RenameHookTokens<P, T> {
pub expr: TokenStream,
pub predicate: P,
pub to: T,
pub name: &'static str,
pub target: &'static str,
}

pub fn rename_hook_tokens<A: Parse>(
opts: RenameHookTokens<
impl Fn(&str) -> bool,
impl Fn(&A, &Ident, &Punctuated<Expr, Comma>) -> (TokenStream, TokenStream),
impl Fn(&A, &Ident, &Punctuated<Expr, Comma>) -> Option<(TokenStream, TokenStream)>,
>,
) -> Result<TokenStream, syn::Error> {
let mut hook = syn::parse2::<Hook>(opts.expr)?;

RenameVisitor {
let mut visitor = RenameVisitor {
scratch: String::new(),
predicate: opts.predicate,
to: opts.to,
args: syn::parse2::<A>(opts.args)?,
}
.visit_expr_mut(&mut hook.expr);
applied: false,
};

visitor.visit_expr_mut(&mut hook.expr);

Ok(hook.to_token_stream())
if !visitor.applied {
Err(syn::Error::new(
hook.expr.span(),
format_args!(
"`{}` isn't valid here; it can only be applied to {}",
opts.name, opts.target
),
))
} else {
Ok(hook.to_token_stream())
}
}

struct RenameVisitor<P, A, T> {
scratch: String,
predicate: P,
args: A,
to: T,
applied: bool,
}

impl<P, A, T> VisitMut for RenameVisitor<P, A, T>
where
P: Fn(&str) -> bool,
T: Fn(&A, &Ident, &Punctuated<Expr, Comma>) -> (TokenStream, TokenStream),
T: Fn(&A, &Ident, &Punctuated<Expr, Comma>) -> Option<(TokenStream, TokenStream)>,
{
fn visit_expr_method_call_mut(&mut self, i: &mut ExprMethodCall) {
let ExprMethodCall { method, args, .. } = i;
Expand All @@ -57,10 +72,12 @@ where
write!(&mut self.scratch, "{}", method).expect("infallible write to string");

if (self.predicate)(&self.scratch) {
let (to_ident_tokens, to_arg_tokens) = (self.to)(&self.args, &method, &args);
self.applied = true;

*method = syn::parse2(to_ident_tokens).expect("invalid ident");
*args = parse_comma_separated2(to_arg_tokens).expect("invalid args");
if let Some((to_ident_tokens, to_arg_tokens)) = (self.to)(&self.args, &method, &args) {
*method = syn::parse2(to_ident_tokens).expect("invalid ident");
*args = parse_comma_separated2(to_arg_tokens).expect("invalid args");
}
}

visit_mut::visit_expr_method_call_mut(self, i)
Expand Down
6 changes: 4 additions & 2 deletions macros/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub fn key_with_hook(attrs: &[Attribute], key_expr: &ExprLit) -> TokenStream {
quote_spanned!(key_expr.span()=>
#(#attrs)*
{
use emit::__private::__PrivateKeyHook;
use emit::__private::__PrivateKeyHook as _;
emit::Key::new(#key_expr).__private_key_as_default()
}
)
Expand Down Expand Up @@ -70,6 +70,8 @@ pub struct RenameHookTokens {

pub fn rename_hook_tokens(opts: RenameHookTokens) -> Result<TokenStream, syn::Error> {
hook::rename_hook_tokens(hook::RenameHookTokens {
name: "key",
target: "values in templates or event macros",
args: opts.args,
expr: opts.expr,
predicate: |ident: &str| ident.starts_with("__private_key"),
Expand All @@ -79,7 +81,7 @@ pub fn rename_hook_tokens(opts: RenameHookTokens) -> Result<TokenStream, syn::Er
Name::Any(ref name) => (quote!(__private_key_as), name.clone()),
};

(to_ident, to_arg)
Some((to_ident, to_arg))
},
})
}
7 changes: 7 additions & 0 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ pub fn as_debug(
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
capture_as(
"as_debug",
TokenStream::from(args),
TokenStream::from(item),
quote!(__private_capture_as_debug),
Expand All @@ -172,6 +173,7 @@ pub fn as_display(
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
capture_as(
"as_display",
TokenStream::from(args),
TokenStream::from(item),
quote!(__private_capture_as_display),
Expand All @@ -190,6 +192,7 @@ pub fn as_sval(
#[cfg(feature = "sval")]
{
capture_as(
"as_sval",
TokenStream::from(args),
TokenStream::from(item),
quote!(__private_capture_as_sval),
Expand Down Expand Up @@ -218,6 +221,7 @@ pub fn as_serde(
#[cfg(feature = "serde")]
{
capture_as(
"as_serde",
TokenStream::from(args),
TokenStream::from(item),
quote!(__private_capture_as_serde),
Expand Down Expand Up @@ -246,6 +250,7 @@ pub fn as_error(
#[cfg(feature = "std")]
{
capture_as(
"as_error",
TokenStream::from(args),
TokenStream::from(item),
quote!(__private_capture_as_error),
Expand Down Expand Up @@ -277,12 +282,14 @@ fn emit(level: TokenStream, item: TokenStream) -> proc_macro::TokenStream {
}

fn capture_as(
name: &'static str,
args: TokenStream,
expr: TokenStream,
as_fn: TokenStream,
as_anon_fn: TokenStream,
) -> proc_macro::TokenStream {
capture::rename_hook_tokens(capture::RenameHookTokens {
name,
args: TokenStream::from(args),
expr: TokenStream::from(expr),
to: |args: &capture::Args| {
Expand Down
4 changes: 3 additions & 1 deletion macros/src/optional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ pub struct RenameHookTokens {

pub fn rename_hook_tokens(opts: RenameHookTokens) -> Result<TokenStream, syn::Error> {
hook::rename_hook_tokens(hook::RenameHookTokens {
name: "optional",
target: "values in `emit` macros",
args: opts.args,
expr: opts.expr,
predicate: |ident: &str| ident.starts_with("__private_optional"),
to: move |_: &Args, ident: &Ident, args: &Punctuated<Expr, Comma>| {
let ident = Ident::new(&ident.to_string().replace("some", "option"), ident.span());

(quote!(#ident), quote!(#args))
Some((quote!(#ident), quote!(#args)))
},
})
}
16 changes: 11 additions & 5 deletions macros/src/props.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::{
util::{AttributeCfg, FieldValueKey},
};

#[derive(Default)]
pub struct Props {
interpolated: bool,
match_value_tokens: Vec<TokenStream>,
match_binding_tokens: Vec<TokenStream>,
key_values: BTreeMap<String, KeyValue>,
Expand All @@ -20,7 +20,7 @@ impl Parse for Props {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let fv = input.parse_terminated(FieldValue::parse, Token![,])?;

let mut props = Props::new();
let mut props = Props::new(false);

for fv in fv {
props.push(&fv)?;
Expand All @@ -47,8 +47,14 @@ impl KeyValue {
}

impl Props {
pub fn new() -> Self {
Self::default()
pub fn new(interpolated: bool) -> Self {
Props {
interpolated,
match_value_tokens: Vec::new(),
match_binding_tokens: Vec::new(),
key_values: BTreeMap::new(),
key_value_index: 0,
}
}

pub fn match_input_tokens(&self) -> impl Iterator<Item = &TokenStream> {
Expand Down Expand Up @@ -110,7 +116,7 @@ impl Props {
let match_bound_ident = self.next_match_binding_ident(fv.span());

let key_value_tokens = {
let key_value_tokens = capture::key_value_with_hook(&attrs, &fv);
let key_value_tokens = capture::key_value_with_hook(&attrs, &fv, self.interpolated);

match cfg_attr {
Some(ref cfg_attr) => quote_spanned!(fv.span()=>
Expand Down
4 changes: 2 additions & 2 deletions macros/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn parse2<A: Parse>(input: TokenStream) -> Result<(A, Template, Props), syn:
.map(|fv| Ok((fv.key_name(), fv)))
.collect::<Result<_, syn::Error>>()?;

let mut props = Props::new();
let mut props = Props::new(true);

// Push the field-values that appear in the template
for fv in template.literal_field_values() {
Expand Down Expand Up @@ -108,7 +108,7 @@ impl<'a> fv_template::LiteralVisitor for TemplateVisitor<'a> {

let field = self.props.get(&label).expect("missing prop");

let hole_tokens = fmt::template_hole_with_hook(&field.attrs, &hole);
let hole_tokens = fmt::template_hole_with_hook(&field.attrs, &hole, true);

match field.cfg_attr {
Some(ref cfg_attr) => self.parts.push(quote!(#cfg_attr { #hole_tokens })),
Expand Down
4 changes: 2 additions & 2 deletions macros/src/with.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ mod tests {
let mut __link = emit::ctxt::Link::new(
emit::ambient_with(),
emit::props::SortedSlice::new_ref(&[{
use emit::__private::__PrivateCaptureHook;
use emit::__private::__PrivateCaptureHook as _;
(emit::Key::new("a"), (a).__private_capture_as_default())
}]),
);
Expand Down Expand Up @@ -133,7 +133,7 @@ mod tests {
emit::ctxt::LinkFuture::new(
emit::ambient_with(),
emit::props::SortedSlice::new_ref(&[{
use emit::__private::__PrivateCaptureHook;
use emit::__private::__PrivateCaptureHook as _;
(emit::Key::new("a"), (a).__private_capture_as_default())
}]),
async {
Expand Down
Loading

0 comments on commit e1ece0b

Please sign in to comment.