blob: 9be8acc0932b28a7a045a877ab28260db85a2e3b (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
//! The `#[renderify]` extension — transforms `expr?` into `render_route!(expr)`.
//!
//! Designed as an extension for the Mingling attribute macro system, intended
//! to be used with `#[renderer(renderify)]`, `#[help(renderify)]`,
//! or standalone as `#[renderify]`.
//!
//! # How it works
//!
//! The macro parses the function AST and replaces every `Expr::Try` node with an
//! equivalent `render_route!(expr)` invocation, which routes errors to the
//! rendering pipeline via `crate::ThisProgram::render(AnyOutput::new(e))`.
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::spanned::Spanned;
use syn::visit_mut::VisitMut;
use syn::{Expr, ItemFn, parse_macro_input};
struct RenderifyTransform;
impl VisitMut for RenderifyTransform {
fn visit_expr_mut(&mut self, expr: &mut Expr) {
syn::visit_mut::visit_expr_mut(self, expr);
if let Expr::Try(try_expr) = expr {
let inner = &*try_expr.expr;
let inner_tokens = inner.to_token_stream();
// Set the span of the generated `render_route` ident to the `?` token's span,
// so that rust-analyzer resolves the `?` position to the `render_route!` macro
// instead of the standard Try trait, showing the render_route macro's docs on hover.
let q_span = try_expr.question_token.span();
let route_ident = proc_macro2::Ident::new("render_route", q_span);
if let Ok(macro_expr) = syn::parse2::<Expr>(quote::quote! {
::mingling::macros::#route_ident!(#inner_tokens)
}) {
*expr = macro_expr;
}
}
}
}
pub(crate) fn renderify_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input_fn = parse_macro_input!(item as ItemFn);
RenderifyTransform.visit_item_fn_mut(&mut input_fn);
input_fn.to_token_stream().into()
}
|