use core::panic;
use std::marker::PhantomData;
use internal::QuotedOutput;
use proc_macro_crate::FoundCrate;
use proc_macro2::Span;
use quote::quote;
pub use stageleft_macro::{entry, export, q, quse_fn};
pub use type_name::{add_private_reexport, quote_type};
#[doc(hidden)]
pub mod internal {
use crate::runtime_support::QuoteTokens;
pub use ctor;
pub use proc_macro2::{Span, TokenStream};
pub use quote::quote;
pub use crate::type_name::{add_crate_with_staged, add_deps_reexport};
pub use {proc_macro_crate, proc_macro2, syn};
pub struct Capture {
pub ident: &'static str,
pub tokens: QuoteTokens,
}
pub struct QuotedOutput {
pub module_path: &'static str,
pub crate_name: &'static str,
pub tokens: &'static str,
pub captures: Vec<Capture>,
}
}
pub(crate) mod attempt_transform_macro;
mod rewrite_paths;
pub mod runtime_support;
mod type_name;
pub mod properties;
use runtime_support::{FreeVariableWithContextWithProps, QuoteTokens, get_final_crate_name};
use syn::visit_mut::VisitMut;
#[cfg(windows)]
#[macro_export]
macro_rules! PATH_SEPARATOR {
() => {
r"\"
};
}
#[cfg(not(windows))]
#[macro_export]
macro_rules! PATH_SEPARATOR {
() => {
r"/"
};
}
#[macro_export]
macro_rules! stageleft_crate {
($macro_crate:ident) => {
#[cfg(not(stageleft_macro))]
#[doc(hidden)]
pub use $macro_crate as __macro;
#[cfg(stageleft_macro)]
include!(concat!(
env!("OUT_DIR"),
$crate::PATH_SEPARATOR!(),
"lib_macro.rs"
));
#[cfg(not(stageleft_macro))]
$crate::stageleft_no_entry_crate!();
};
}
#[macro_export]
macro_rules! stageleft_no_entry_crate {
() => {
#[doc(hidden)]
#[allow(
ambiguous_glob_reexports,
mismatched_lifetime_syntaxes,
unexpected_cfgs,
unfulfilled_lint_expectations,
unused,
clippy::suspicious_else_formatting,
clippy::type_complexity,
reason = "generated code"
)]
pub mod __staged {
#[cfg(any(feature = "stageleft_macro_entrypoint", stageleft_trybuild))]
include!(concat!(
env!("OUT_DIR"),
$crate::PATH_SEPARATOR!(),
"lib_pub.rs"
));
include!(concat!(
env!("OUT_DIR"),
$crate::PATH_SEPARATOR!(),
"staged_deps.rs"
));
}
};
}
pub trait QuotedContext {
fn create() -> Self;
}
pub struct BorrowBounds<'a> {
_marker: PhantomData<fn(&'a ()) -> &'a ()>,
}
impl QuotedContext for BorrowBounds<'_> {
fn create() -> Self {
BorrowBounds {
_marker: PhantomData,
}
}
}
pub trait QuotedWithContextWithProps<'a, T, Ctx, Props>:
FreeVariableWithContextWithProps<Ctx, Props, O = T>
{
fn splice_untyped_ctx_props(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
{
let (res, props) = FreeVariableWithContextWithProps::to_tokens(self, ctx);
if res.prelude.is_some() {
panic!("Quoted value should not have prelude");
}
(syn::parse2(res.expr.unwrap()).unwrap(), props)
}
fn splice_typed_ctx_props(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let out_type = quote_type::<T>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::type_hint::<#out_type>(#inner_expr)
},
props,
)
}
fn splice_fn0_ctx_props<O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: Fn() -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fn0_type_hint::<#out_type>(#inner_expr)
},
props,
)
}
fn splice_fn1_ctx_props<I, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: Fn(I) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fn1_type_hint::<#in_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fn1_borrow_ctx_props<I, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: Fn(&I) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fn1_borrow_type_hint::<#in_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fn2_ctx_props<I1, I2, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: Fn(I1, I2) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fn2_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fn2_borrow_ctx_props<I1, I2, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: Fn(&I1, &I2) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fn2_borrow_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fn2_borrow_mut_ctx_props<I1, I2, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: Fn(&mut I1, I2) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fn2_borrow_mut_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fnmut0_ctx_props<O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: FnMut() -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut0_type_hint::<#out_type>(#inner_expr)
},
props,
)
}
fn splice_fnmut1_ctx_props<I, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: FnMut(I) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut1_type_hint::<#in_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fnmut1_borrow_ctx_props<I, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: FnMut(&I) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut1_borrow_type_hint::<#in_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fnmut2_ctx_props<I1, I2, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: FnMut(I1, I2) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut2_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
},
props,
)
}
fn splice_fnmut2_borrow_ctx_props<I1, I2, O>(self, ctx: &Ctx) -> (syn::Expr, Props)
where
Self: Sized,
T: FnMut(&I1, &I2) -> O,
{
let (inner_expr, props) = self.splice_untyped_ctx_props(ctx);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
(
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut2_borrow_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
},
props,
)
}
}
pub trait QuotedWithContext<'a, T, Ctx>: QuotedWithContextWithProps<'a, T, Ctx, ()> {
fn splice_untyped_ctx(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
{
QuotedWithContextWithProps::splice_untyped_ctx_props(self, ctx).0
}
fn splice_typed_ctx(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
{
QuotedWithContextWithProps::splice_typed_ctx_props(self, ctx).0
}
fn splice_fn0_ctx<O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: Fn() -> O,
{
QuotedWithContextWithProps::splice_fn0_ctx_props(self, ctx).0
}
fn splice_fn1_ctx<I, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: Fn(I) -> O,
{
QuotedWithContextWithProps::splice_fn1_ctx_props(self, ctx).0
}
fn splice_fn1_borrow_ctx<I, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: Fn(&I) -> O,
{
QuotedWithContextWithProps::splice_fn1_borrow_ctx_props(self, ctx).0
}
fn splice_fn2_ctx<I1, I2, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: Fn(I1, I2) -> O,
{
QuotedWithContextWithProps::splice_fn2_ctx_props(self, ctx).0
}
fn splice_fn2_borrow_ctx<I1, I2, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: Fn(&I1, &I2) -> O,
{
QuotedWithContextWithProps::splice_fn2_borrow_ctx_props(self, ctx).0
}
fn splice_fn2_borrow_mut_ctx<I1, I2, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: Fn(&mut I1, I2) -> O,
{
QuotedWithContextWithProps::splice_fn2_borrow_mut_ctx_props(self, ctx).0
}
fn splice_fnmut0_ctx<O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: FnMut() -> O,
{
QuotedWithContextWithProps::splice_fnmut0_ctx_props(self, ctx).0
}
fn splice_fnmut1_ctx<I, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: FnMut(I) -> O,
{
QuotedWithContextWithProps::splice_fnmut1_ctx_props(self, ctx).0
}
fn splice_fnmut1_borrow_ctx<I, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: FnMut(&I) -> O,
{
QuotedWithContextWithProps::splice_fnmut1_borrow_ctx_props(self, ctx).0
}
fn splice_fnmut2_ctx<I1, I2, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: FnMut(I1, I2) -> O,
{
QuotedWithContextWithProps::splice_fnmut2_ctx_props(self, ctx).0
}
fn splice_fnmut2_borrow_ctx<I1, I2, O>(self, ctx: &Ctx) -> syn::Expr
where
Self: Sized,
T: FnMut(&I1, &I2) -> O,
{
QuotedWithContextWithProps::splice_fnmut2_borrow_ctx_props(self, ctx).0
}
fn splice_untyped(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
{
QuotedWithContext::splice_untyped_ctx(self, &Default::default())
}
fn splice_typed(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let out_type = quote_type::<T>();
syn::parse_quote! {
#stageleft_root::runtime_support::type_hint::<#out_type>(#inner_expr)
}
}
fn splice_fn0<O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: Fn() -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fn0_type_hint::<#out_type>(#inner_expr)
}
}
fn splice_fn1<I, O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: Fn(I) -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fn1_type_hint::<#in_type, #out_type>(#inner_expr)
}
}
fn splice_fn1_borrow<I, O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: Fn(&I) -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fn1_borrow_type_hint::<#in_type, #out_type>(#inner_expr)
}
}
fn splice_fn2_borrow_mut<I1, I2, O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: Fn(&mut I1, I2) -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fn2_borrow_mut_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
}
}
fn splice_fnmut0<O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: FnMut() -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut0_type_hint::<#out_type>(#inner_expr)
}
}
fn splice_fnmut1<I, O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: FnMut(I) -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut1_type_hint::<#in_type, #out_type>(#inner_expr)
}
}
fn splice_fnmut1_borrow<I, O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: FnMut(&I) -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let in_type = quote_type::<I>();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut1_borrow_type_hint::<#in_type, #out_type>(#inner_expr)
}
}
fn splice_fnmut2<I1, I2, O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: FnMut(I1, I2) -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut2_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
}
}
fn splice_fnmut2_borrow<I1, I2, O>(self) -> syn::Expr
where
Self: Sized,
Ctx: Default,
T: FnMut(&I1, &I2) -> O,
{
let inner_expr = QuotedWithContext::splice_untyped(self);
let stageleft_root = stageleft_root();
let in1_type = quote_type::<I1>();
let in2_type = quote_type::<I2>();
let out_type = quote_type::<O>();
syn::parse_quote! {
#stageleft_root::runtime_support::fnmut2_borrow_type_hint::<#in1_type, #in2_type, #out_type>(#inner_expr)
}
}
}
impl<'a, T, Ctx, F: QuotedWithContextWithProps<'a, T, Ctx, ()>> QuotedWithContext<'a, T, Ctx>
for F
{
}
pub trait Quoted<'a, T>: QuotedWithContext<'a, T, ()> {}
impl<'a, T, F: QuotedWithContext<'a, T, ()>> Quoted<'a, T> for F {}
fn stageleft_root() -> syn::Ident {
let pkg_name = env!("CARGO_PKG_NAME");
let stageleft_crate = proc_macro_crate::crate_name(pkg_name).unwrap_or_else(|_| {
panic!("Expected stageleft `{pkg_name}` package to be present in `Cargo.toml`")
});
match stageleft_crate {
FoundCrate::Name(name) => syn::Ident::new(&name, Span::call_site()),
FoundCrate::Itself => syn::Ident::new("crate", Span::call_site()),
}
}
pub trait IntoQuotedOnce<'a, T: 'a, Ctx, Props = ()>:
FnOnce(&Ctx, &mut QuotedOutput, &mut Option<Props>) -> T
+ QuotedWithContextWithProps<'a, T, Ctx, Props>
{
fn boxed(self) -> Box<dyn 'a + IntoQuotedOnce<'a, T, Ctx, Props>>
where
Self: 'a + Sized,
{
Box::new(self)
}
}
impl<'a, T: 'a, Ctx, Props, F: FnOnce(&Ctx, &mut QuotedOutput, &mut Option<Props>) -> T>
QuotedWithContextWithProps<'a, T, Ctx, Props> for F
{
}
impl<'a, T: 'a, Ctx, Props, F: FnOnce(&Ctx, &mut QuotedOutput, &mut Option<Props>) -> T>
IntoQuotedOnce<'a, T, Ctx, Props> for F
{
}
impl<T, Ctx, Props, F: for<'b> FnOnce(&'b Ctx, &mut QuotedOutput, &mut Option<Props>) -> T>
FreeVariableWithContextWithProps<Ctx, Props> for F
{
type O = T;
fn to_tokens(self, ctx: &Ctx) -> (QuoteTokens, Props) {
static GLOBAL_HOOK_MUTEX: parking_lot::ReentrantMutex<()> =
parking_lot::ReentrantMutex::new(());
let mut output = QuotedOutput {
module_path: "",
crate_name: "",
tokens: "",
captures: Vec::new(),
};
let mut props = None;
let output_ref = std::panic::AssertUnwindSafe(&mut output);
let props_ref = std::panic::AssertUnwindSafe(&mut props);
let _guard = GLOBAL_HOOK_MUTEX.lock();
let prev_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
let output_ref = output_ref;
let props_ref = props_ref;
std::mem::forget(self(ctx, output_ref.0, props_ref.0));
}));
std::panic::set_hook(prev_hook);
drop(_guard);
let instantiated_free_variables = output.captures.iter().flat_map(|capture| {
let ident = syn::Ident::new(capture.ident, Span::call_site());
capture
.tokens
.prelude
.iter()
.map(|prelude| quote!(#prelude))
.chain(
capture
.tokens
.expr
.iter()
.map(move |value| quote!(let #ident = #value;)),
)
});
let final_crate_root = get_final_crate_name(output.crate_name);
let module_path: syn::Path = syn::parse_str(output.module_path).unwrap();
let module_path_segments = module_path
.segments
.iter()
.skip(1)
.cloned()
.collect::<Vec<_>>();
let module_path = if module_path_segments.is_empty()
|| module_path_segments
.iter()
.any(|segment| segment.ident.to_string().starts_with("__doctest"))
{
None
} else {
Some(syn::Path {
leading_colon: None,
segments: syn::punctuated::Punctuated::from_iter(module_path_segments),
})
};
let mut expr: syn::Expr = syn::parse_str(output.tokens).unwrap();
rewrite_paths::RewritePaths {
crate_root_path: syn::parse_quote!(#final_crate_root::__staged),
module_path: module_path
.clone()
.map(|module_path| syn::parse_quote!(#final_crate_root::__staged::#module_path)),
}
.visit_expr_mut(&mut expr);
let with_env = if let Some(module_path) = module_path {
quote!({
use #final_crate_root::__staged::__deps::*;
use #final_crate_root::__staged::#module_path::*;
#(#instantiated_free_variables)*
#expr
})
} else {
quote!({
use #final_crate_root::__staged::__deps::*;
use #final_crate_root::__staged::*;
#(#instantiated_free_variables)*
#expr
})
};
(
QuoteTokens {
prelude: None,
expr: Some(with_env),
},
props.unwrap(),
)
}
}
pub trait IntoQuotedMut<'a, T: 'a, Ctx, Props = ()>:
FnMut(&Ctx, &mut QuotedOutput, &mut Option<Props>) -> T
{
}
impl<'a, T: 'a, Ctx, Props, F: FnMut(&Ctx, &mut QuotedOutput, &mut Option<Props>) -> T>
IntoQuotedMut<'a, T, Ctx, Props> for F
{
}
pub struct RuntimeData<T> {
ident: &'static str,
_phantom: PhantomData<T>,
}
impl<T: Copy> Copy for RuntimeData<T> {}
impl<T: Copy> Clone for RuntimeData<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> RuntimeData<T> {
pub fn new(ident: &'static str) -> RuntimeData<T> {
RuntimeData {
ident,
_phantom: PhantomData,
}
}
}
impl<T, Ctx> FreeVariableWithContextWithProps<Ctx, ()> for RuntimeData<T> {
type O = T;
fn to_tokens(self, _ctx: &Ctx) -> (QuoteTokens, ()) {
let ident = syn::Ident::new(self.ident, Span::call_site());
(
QuoteTokens {
prelude: None,
expr: Some(quote!(#ident)),
},
(),
)
}
}