Skip to main content

no_alloc_check/
lib.rs

1//! Proc-macro attribute expanding to a cfg-gated tool-attribute marker.
2//!
3//! On a normal build `cfg_attr` is evaluated in the *caller's* crate and the
4//! `no_alloc_check` cfg is unset, so the whole attribute evaluates away:
5//! no nightly features, no marker residue, no codegen impact. Under the
6//! checker driver (`--cfg no_alloc_check`) it expands to
7//! `#[no_alloc_tool::root]`, legible only because the driver also passes
8//! `-Zcrate-attr=feature(register_tool)` and
9//! `-Zcrate-attr=register_tool(no_alloc_tool)`.
10
11use proc_macro::TokenStream;
12use quote::quote;
13use syn::{ImplItemFn, ItemFn, TraitItemFn};
14
15#[proc_macro_attribute]
16pub fn no_alloc(args: TokenStream, item: TokenStream) -> TokenStream {
17    if !args.is_empty() {
18        let args = proc_macro2::TokenStream::from(args);
19        return syn::Error::new_spanned(args, "#[no_alloc] takes no arguments")
20            .to_compile_error()
21            .into();
22    }
23
24    let tokens = proc_macro2::TokenStream::from(item);
25    let (is_async, is_trait_declaration) =
26        if let Ok(function) = syn::parse2::<ItemFn>(tokens.clone()) {
27            (function.sig.asyncness.is_some(), false)
28        } else if let Ok(function) = syn::parse2::<ImplItemFn>(tokens.clone()) {
29            (function.sig.asyncness.is_some(), false)
30        } else if let Ok(function) = syn::parse2::<TraitItemFn>(tokens.clone()) {
31            (function.sig.asyncness.is_some(), function.default.is_none())
32        } else {
33            return syn::Error::new_spanned(
34                tokens,
35                "#[no_alloc] can only be applied to functions and methods",
36            )
37            .to_compile_error()
38            .into();
39        };
40
41    if is_async {
42        return syn::Error::new_spanned(tokens, "#[no_alloc] does not yet support async functions")
43            .to_compile_error()
44            .into();
45    }
46    if is_trait_declaration {
47        return syn::Error::new_spanned(
48            tokens,
49            "#[no_alloc] cannot be applied to a trait method without a body",
50        )
51        .to_compile_error()
52        .into();
53    }
54
55    quote! {
56        #[cfg_attr(no_alloc_check, no_alloc_tool::root)]
57        #tokens
58    }
59    .into()
60}