tracers_codegen/proc_macros.rs
1//! This module is where the *implementation* of the probe-related proc macros are. The actual
2//! proc macro is in the `tracers-macros` crate because proc macro crates can _only_ export proc
3//! macros and nothing else. That's an inconvenient restriction, especially since there's quite a
4//! lot of overlap between the macro code and the build-time probe code generation logic. Hence,
5//! this bifurcation.
6use crate::gen;
7use crate::spec::ProbeCallSpecification;
8use crate::spec::ProviderInitSpecification;
9use crate::spec::ProviderSpecification;
10use crate::spec::TracerAttributeArgs;
11use crate::TracersResult;
12use proc_macro2::TokenStream;
13use std::fmt::Display;
14
15/// Uses the `syn` library's `Error` struct to report an error in the form of a `TokenStream`, so
16/// that a proc macro can insert this token stream into its output and thereby report a detailed
17/// error message to the user.
18///
19/// The span of this error corresponds to the `tokens` parameter, so the user gets the relevant
20/// context for the error
21pub fn report_error<T: quote::ToTokens, U: Display>(tokens: &T, message: U) -> TokenStream {
22 syn::Error::new_spanned(tokens, message).to_compile_error()
23}
24
25/// Translates what looks to be an explicit call to the associated function corresponding to a
26/// probe on a provider trait, into something which at runtime will most efficiently attempt to
27/// access the global static instance of the probe and, if it's enabled, evaluate the args and fire
28/// the probe.
29///
30/// It translates something like this:
31///
32/// ```noexecute
33/// probe!(MyProvider::myprobe(1, 5, "this is a string", compute_something()));
34/// ```
35///
36/// into:
37///
38/// ```noexecute
39/// {
40/// if let Some(probe) = MyProvider::get_myprobe_probe() {
41/// if probe.is_enabled() {
42/// probe.fire((1, 5, "this is a string", compute_something(),)));
43/// }
44/// }
45/// }
46/// ```
47///
48/// In particular, note that the probe's parameters are not evaluated unless the provider
49/// initialized successfully and the probe is enabled.
50pub fn probe_impl(tokens: TokenStream) -> TracersResult<TokenStream> {
51 gen::code_generator()?.handle_probe_call(ProbeCallSpecification::from_token_stream(tokens)?)
52}
53
54pub fn init_provider_impl(tokens: TokenStream) -> TracersResult<TokenStream> {
55 gen::code_generator()?
56 .handle_init_provider(ProviderInitSpecification::from_token_stream(tokens)?)
57}
58
59/// Actual implementation of the macro logic, factored out of the proc macro itself so that it's
60/// more testable
61pub fn tracer_impl(attr_tokens: TokenStream, tokens: TokenStream) -> TracersResult<TokenStream> {
62 gen::code_generator()?.handle_provider_trait(ProviderSpecification::from_token_stream(
63 &std::env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME"),
64 TracerAttributeArgs::from_token_stream(attr_tokens)?,
65 tokens,
66 )?)
67}