Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

478
vendor/tracing-attributes/src/attr.rs vendored Normal file
View File

@@ -0,0 +1,478 @@
use std::collections::HashSet;
use syn::{punctuated::Punctuated, Expr, Ident, LitInt, LitStr, Path, Token};
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use syn::ext::IdentExt as _;
use syn::parse::{Parse, ParseStream};
/// Arguments to `#[instrument(err(...))]` and `#[instrument(ret(...))]` which describe how the
/// return value event should be emitted.
#[derive(Clone, Default, Debug)]
pub(crate) struct EventArgs {
level: Option<Level>,
pub(crate) mode: FormatMode,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct InstrumentArgs {
level: Option<Level>,
pub(crate) name: Option<LitStrOrIdent>,
target: Option<LitStrOrIdent>,
pub(crate) parent: Option<Expr>,
pub(crate) follows_from: Option<Expr>,
pub(crate) skips: HashSet<Ident>,
pub(crate) skip_all: bool,
pub(crate) fields: Option<Fields>,
pub(crate) err_args: Option<EventArgs>,
pub(crate) ret_args: Option<EventArgs>,
/// Errors describing any unrecognized parse inputs that we skipped.
parse_warnings: Vec<syn::Error>,
}
impl InstrumentArgs {
pub(crate) fn level(&self) -> Level {
self.level.clone().unwrap_or(Level::Info)
}
pub(crate) fn target(&self) -> impl ToTokens {
if let Some(ref target) = self.target {
quote!(#target)
} else {
quote!(module_path!())
}
}
/// Generate "deprecation" warnings for any unrecognized attribute inputs
/// that we skipped.
///
/// For backwards compatibility, we need to emit compiler warnings rather
/// than errors for unrecognized inputs. Generating a fake deprecation is
/// the only way to do this on stable Rust right now.
pub(crate) fn warnings(&self) -> impl ToTokens {
let warnings = self.parse_warnings.iter().map(|err| {
let msg = format!("found unrecognized input, {}", err);
let msg = LitStr::new(&msg, err.span());
// TODO(eliza): This is a bit of a hack, but it's just about the
// only way to emit warnings from a proc macro on stable Rust.
// Eventually, when the `proc_macro::Diagnostic` API stabilizes, we
// should definitely use that instead.
quote_spanned! {err.span()=>
#[warn(deprecated)]
{
#[deprecated(since = "not actually deprecated", note = #msg)]
const TRACING_INSTRUMENT_WARNING: () = ();
let _ = TRACING_INSTRUMENT_WARNING;
}
}
});
quote! {
{ #(#warnings)* }
}
}
}
impl Parse for InstrumentArgs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut args = Self::default();
while !input.is_empty() {
let lookahead = input.lookahead1();
if lookahead.peek(kw::name) {
if args.name.is_some() {
return Err(input.error("expected only a single `name` argument"));
}
let name = input.parse::<StrArg<kw::name>>()?.value;
args.name = Some(name);
} else if lookahead.peek(LitStr) {
// XXX: apparently we support names as either named args with an
// sign, _or_ as unnamed string literals. That's weird, but
// changing it is apparently breaking.
// This also means that when using idents for name, it must be via
// a named arg, i.e. `#[instrument(name = SOME_IDENT)]`.
if args.name.is_some() {
return Err(input.error("expected only a single `name` argument"));
}
args.name = Some(input.parse()?);
} else if lookahead.peek(kw::target) {
if args.target.is_some() {
return Err(input.error("expected only a single `target` argument"));
}
let target = input.parse::<StrArg<kw::target>>()?.value;
args.target = Some(target);
} else if lookahead.peek(kw::parent) {
if args.target.is_some() {
return Err(input.error("expected only a single `parent` argument"));
}
let parent = input.parse::<ExprArg<kw::parent>>()?;
args.parent = Some(parent.value);
} else if lookahead.peek(kw::follows_from) {
if args.target.is_some() {
return Err(input.error("expected only a single `follows_from` argument"));
}
let follows_from = input.parse::<ExprArg<kw::follows_from>>()?;
args.follows_from = Some(follows_from.value);
} else if lookahead.peek(kw::level) {
if args.level.is_some() {
return Err(input.error("expected only a single `level` argument"));
}
args.level = Some(input.parse()?);
} else if lookahead.peek(kw::skip) {
if !args.skips.is_empty() {
return Err(input.error("expected only a single `skip` argument"));
}
if args.skip_all {
return Err(input.error("expected either `skip` or `skip_all` argument"));
}
let Skips(skips) = input.parse()?;
args.skips = skips;
} else if lookahead.peek(kw::skip_all) {
if args.skip_all {
return Err(input.error("expected only a single `skip_all` argument"));
}
if !args.skips.is_empty() {
return Err(input.error("expected either `skip` or `skip_all` argument"));
}
let _ = input.parse::<kw::skip_all>()?;
args.skip_all = true;
} else if lookahead.peek(kw::fields) {
if args.fields.is_some() {
return Err(input.error("expected only a single `fields` argument"));
}
args.fields = Some(input.parse()?);
} else if lookahead.peek(kw::err) {
let _ = input.parse::<kw::err>();
let err_args = EventArgs::parse(input)?;
args.err_args = Some(err_args);
} else if lookahead.peek(kw::ret) {
let _ = input.parse::<kw::ret>()?;
let ret_args = EventArgs::parse(input)?;
args.ret_args = Some(ret_args);
} else if lookahead.peek(Token![,]) {
let _ = input.parse::<Token![,]>()?;
} else {
// We found a token that we didn't expect!
// We want to emit warnings for these, rather than errors, so
// we'll add it to the list of unrecognized inputs we've seen so
// far and keep going.
args.parse_warnings.push(lookahead.error());
// Parse the unrecognized token tree to advance the parse
// stream, and throw it away so we can keep parsing.
let _ = input.parse::<proc_macro2::TokenTree>();
}
}
Ok(args)
}
}
impl EventArgs {
pub(crate) fn level(&self, default: Level) -> Level {
self.level.clone().unwrap_or(default)
}
}
impl Parse for EventArgs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
if !input.peek(syn::token::Paren) {
return Ok(Self::default());
}
let content;
let _ = syn::parenthesized!(content in input);
let mut result = Self::default();
let mut parse_one_arg =
|| {
let lookahead = content.lookahead1();
if lookahead.peek(kw::level) {
if result.level.is_some() {
return Err(content.error("expected only a single `level` argument"));
}
result.level = Some(content.parse()?);
} else if result.mode != FormatMode::default() {
return Err(content.error("expected only a single format argument"));
} else if let Some(ident) = content.parse::<Option<Ident>>()? {
match ident.to_string().as_str() {
"Debug" => result.mode = FormatMode::Debug,
"Display" => result.mode = FormatMode::Display,
_ => return Err(syn::Error::new(
ident.span(),
"unknown event formatting mode, expected either `Debug` or `Display`",
)),
}
}
Ok(())
};
parse_one_arg()?;
if !content.is_empty() {
if content.lookahead1().peek(Token![,]) {
let _ = content.parse::<Token![,]>()?;
parse_one_arg()?;
} else {
return Err(content.error("expected `,` or `)`"));
}
}
Ok(result)
}
}
#[derive(Debug, Clone)]
pub(super) enum LitStrOrIdent {
LitStr(LitStr),
Ident(Ident),
}
impl ToTokens for LitStrOrIdent {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
LitStrOrIdent::LitStr(target) => target.to_tokens(tokens),
LitStrOrIdent::Ident(ident) => ident.to_tokens(tokens),
}
}
}
impl Parse for LitStrOrIdent {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
input
.parse::<LitStr>()
.map(LitStrOrIdent::LitStr)
.or_else(|_| input.parse::<Ident>().map(LitStrOrIdent::Ident))
}
}
struct StrArg<T> {
value: LitStrOrIdent,
_p: std::marker::PhantomData<T>,
}
impl<T: Parse> Parse for StrArg<T> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let _ = input.parse::<T>()?;
let _ = input.parse::<Token![=]>()?;
let value = input.parse()?;
Ok(Self {
value,
_p: std::marker::PhantomData,
})
}
}
struct ExprArg<T> {
value: Expr,
_p: std::marker::PhantomData<T>,
}
impl<T: Parse> Parse for ExprArg<T> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let _ = input.parse::<T>()?;
let _ = input.parse::<Token![=]>()?;
let value = input.parse()?;
Ok(Self {
value,
_p: std::marker::PhantomData,
})
}
}
struct Skips(HashSet<Ident>);
impl Parse for Skips {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let _ = input.parse::<kw::skip>();
let content;
let _ = syn::parenthesized!(content in input);
let names = content.parse_terminated(Ident::parse_any, Token![,])?;
let mut skips = HashSet::new();
for name in names {
if skips.contains(&name) {
return Err(syn::Error::new(
name.span(),
"tried to skip the same field twice",
));
} else {
skips.insert(name);
}
}
Ok(Self(skips))
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
pub(crate) enum FormatMode {
#[default]
Default,
Display,
Debug,
}
#[derive(Clone, Debug)]
pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>);
#[derive(Clone, Debug)]
pub(crate) struct Field {
pub(crate) name: Punctuated<Ident, Token![.]>,
pub(crate) value: Option<Expr>,
pub(crate) kind: FieldKind,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum FieldKind {
Debug,
Display,
Value,
}
impl Parse for Fields {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let _ = input.parse::<kw::fields>();
let content;
let _ = syn::parenthesized!(content in input);
let fields = content.parse_terminated(Field::parse, Token![,])?;
Ok(Self(fields))
}
}
impl ToTokens for Fields {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens)
}
}
impl Parse for Field {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut kind = FieldKind::Value;
if input.peek(Token![%]) {
input.parse::<Token![%]>()?;
kind = FieldKind::Display;
} else if input.peek(Token![?]) {
input.parse::<Token![?]>()?;
kind = FieldKind::Debug;
};
let name = Punctuated::parse_separated_nonempty_with(input, Ident::parse_any)?;
let value = if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
if input.peek(Token![%]) {
input.parse::<Token![%]>()?;
kind = FieldKind::Display;
} else if input.peek(Token![?]) {
input.parse::<Token![?]>()?;
kind = FieldKind::Debug;
};
Some(input.parse()?)
} else {
None
};
Ok(Self { name, value, kind })
}
}
impl ToTokens for Field {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(ref value) = self.value {
let name = &self.name;
let kind = &self.kind;
tokens.extend(quote! {
#name = #kind #value
})
} else if self.kind == FieldKind::Value {
// XXX(eliza): I don't like that fields without values produce
// empty fields rather than local variable shorthand...but,
// we've released a version where field names without values in
// `instrument` produce empty field values, so changing it now
// is a breaking change. agh.
let name = &self.name;
tokens.extend(quote!(#name = ::tracing::field::Empty))
} else {
self.kind.to_tokens(tokens);
self.name.to_tokens(tokens);
}
}
}
impl ToTokens for FieldKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
FieldKind::Debug => tokens.extend(quote! { ? }),
FieldKind::Display => tokens.extend(quote! { % }),
_ => {}
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum Level {
Trace,
Debug,
Info,
Warn,
Error,
Path(Path),
}
impl Parse for Level {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let _ = input.parse::<kw::level>()?;
let _ = input.parse::<Token![=]>()?;
let lookahead = input.lookahead1();
if lookahead.peek(LitStr) {
let str: LitStr = input.parse()?;
match str.value() {
s if s.eq_ignore_ascii_case("trace") => Ok(Level::Trace),
s if s.eq_ignore_ascii_case("debug") => Ok(Level::Debug),
s if s.eq_ignore_ascii_case("info") => Ok(Level::Info),
s if s.eq_ignore_ascii_case("warn") => Ok(Level::Warn),
s if s.eq_ignore_ascii_case("error") => Ok(Level::Error),
_ => Err(input.error(
"unknown verbosity level, expected one of \"trace\", \
\"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
)),
}
} else if lookahead.peek(LitInt) {
fn is_level(lit: &LitInt, expected: u64) -> bool {
match lit.base10_parse::<u64>() {
Ok(value) => value == expected,
Err(_) => false,
}
}
let int: LitInt = input.parse()?;
match &int {
i if is_level(i, 1) => Ok(Level::Trace),
i if is_level(i, 2) => Ok(Level::Debug),
i if is_level(i, 3) => Ok(Level::Info),
i if is_level(i, 4) => Ok(Level::Warn),
i if is_level(i, 5) => Ok(Level::Error),
_ => Err(input.error(
"unknown verbosity level, expected one of \"trace\", \
\"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
)),
}
} else if lookahead.peek(Ident) {
Ok(Self::Path(input.parse()?))
} else {
Err(lookahead.error())
}
}
}
impl ToTokens for Level {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Level::Trace => tokens.extend(quote!(::tracing::Level::TRACE)),
Level::Debug => tokens.extend(quote!(::tracing::Level::DEBUG)),
Level::Info => tokens.extend(quote!(::tracing::Level::INFO)),
Level::Warn => tokens.extend(quote!(::tracing::Level::WARN)),
Level::Error => tokens.extend(quote!(::tracing::Level::ERROR)),
Level::Path(ref pat) => tokens.extend(quote!(#pat)),
}
}
}
mod kw {
syn::custom_keyword!(fields);
syn::custom_keyword!(skip);
syn::custom_keyword!(skip_all);
syn::custom_keyword!(level);
syn::custom_keyword!(target);
syn::custom_keyword!(parent);
syn::custom_keyword!(follows_from);
syn::custom_keyword!(name);
syn::custom_keyword!(err);
syn::custom_keyword!(ret);
}

854
vendor/tracing-attributes/src/expand.rs vendored Normal file
View File

@@ -0,0 +1,854 @@
use std::iter;
use proc_macro2::TokenStream;
use quote::TokenStreamExt;
use quote::{quote, quote_spanned, ToTokens};
use syn::visit_mut::VisitMut;
use syn::{
punctuated::Punctuated, spanned::Spanned, Block, Expr, ExprAsync, ExprCall, FieldPat, FnArg,
Ident, Item, ItemFn, Pat, PatIdent, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType,
Path, ReturnType, Signature, Stmt, Token, Type, TypePath,
};
use crate::{
attr::{Field, Fields, FormatMode, InstrumentArgs, Level},
MaybeItemFn, MaybeItemFnRef,
};
/// Given an existing function, generate an instrumented version of that function
pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
input: MaybeItemFnRef<'a, B>,
args: InstrumentArgs,
instrumented_function_name: &str,
self_type: Option<&TypePath>,
) -> proc_macro2::TokenStream {
// these are needed ahead of time, as ItemFn contains the function body _and_
// isn't representable inside a quote!/quote_spanned! macro
// (Syn's ToTokens isn't implemented for ItemFn)
let MaybeItemFnRef {
outer_attrs,
inner_attrs,
vis,
sig,
brace_token,
block,
} = input;
let Signature {
output,
inputs: params,
unsafety,
asyncness,
constness,
abi,
ident,
generics:
syn::Generics {
params: gen_params,
where_clause,
lt_token,
gt_token,
},
fn_token,
paren_token,
variadic,
} = sig;
let warnings = args.warnings();
let (return_type, return_span) = if let ReturnType::Type(_, return_type) = &output {
(erase_impl_trait(return_type), return_type.span())
} else {
// Point at function name if we don't have an explicit return type
(syn::parse_quote! { () }, ident.span())
};
// Install a fake return statement as the first thing in the function
// body, so that we eagerly infer that the return type is what we
// declared in the async fn signature.
// The `#[allow(..)]` is given because the return statement is
// unreachable, but does affect inference, so it needs to be written
// exactly that way for it to do its magic.
let fake_return_edge = quote_spanned! {return_span=>
#[allow(
unknown_lints,
unreachable_code,
clippy::diverging_sub_expression,
clippy::empty_loop,
clippy::let_unit_value,
clippy::let_with_type_underscore,
clippy::needless_return,
clippy::unreachable
)]
if false {
let __tracing_attr_fake_return: #return_type = loop {};
return __tracing_attr_fake_return;
}
};
let block = quote! {
{
#fake_return_edge
{ #block }
}
};
let body = gen_block(
&block,
params,
asyncness.is_some(),
args,
instrumented_function_name,
self_type,
);
let mut result = quote!(
#(#outer_attrs) *
#vis #constness #asyncness #unsafety #abi #fn_token #ident
#lt_token #gen_params #gt_token
);
paren_token.surround(&mut result, |tokens| {
params.to_tokens(tokens);
variadic.to_tokens(tokens);
});
output.to_tokens(&mut result);
where_clause.to_tokens(&mut result);
brace_token.surround(&mut result, |tokens| {
tokens.append_all(inner_attrs);
warnings.to_tokens(tokens);
body.to_tokens(tokens);
});
result
}
/// Instrument a block
fn gen_block<B: ToTokens>(
block: &B,
params: &Punctuated<FnArg, Token![,]>,
async_context: bool,
mut args: InstrumentArgs,
instrumented_function_name: &str,
self_type: Option<&TypePath>,
) -> proc_macro2::TokenStream {
// generate the span's name
let span_name = args
// did the user override the span's name?
.name
.as_ref()
.map(|name| quote!(#name))
.unwrap_or_else(|| quote!(#instrumented_function_name));
let args_level = args.level();
let level = args_level.clone();
let follows_from = args.follows_from.iter();
let follows_from = quote! {
#(for cause in #follows_from {
__tracing_attr_span.follows_from(cause);
})*
};
// generate this inside a closure, so we can return early on errors.
let span = (|| {
// Pull out the arguments-to-be-skipped first, so we can filter results
// below.
let param_names: Vec<(Ident, (Ident, RecordType))> = params
.clone()
.into_iter()
.flat_map(|param| match param {
FnArg::Typed(PatType { pat, ty, .. }) => {
param_names(*pat, RecordType::parse_from_ty(&ty))
}
FnArg::Receiver(_) => Box::new(iter::once((
Ident::new("self", param.span()),
RecordType::Debug,
))),
})
// Little dance with new (user-exposed) names and old (internal)
// names of identifiers. That way, we could do the following
// even though async_trait (<=0.1.43) rewrites "self" as "_self":
// ```
// #[async_trait]
// impl Foo for FooImpl {
// #[instrument(skip(self))]
// async fn foo(&self, v: usize) {}
// }
// ```
.map(|(x, record_type)| {
// if we are inside a function generated by async-trait <=0.1.43, we need to
// take care to rewrite "_self" as "self" for 'user convenience'
if self_type.is_some() && x == "_self" {
(Ident::new("self", x.span()), (x, record_type))
} else {
(x.clone(), (x, record_type))
}
})
.collect();
for skip in &args.skips {
if !param_names.iter().map(|(user, _)| user).any(|y| y == skip) {
return quote_spanned! {skip.span()=>
compile_error!("attempting to skip non-existent parameter")
};
}
}
let target = args.target();
let parent = args.parent.iter();
// filter out skipped fields
let quoted_fields: Vec<_> = param_names
.iter()
.filter(|(param, _)| {
if args.skip_all || args.skips.contains(param) {
return false;
}
// If any parameters have the same name as a custom field, skip
// and allow them to be formatted by the custom field.
if let Some(ref fields) = args.fields {
fields.0.iter().all(|Field { ref name, .. }| {
let first = name.first();
first != name.last() || !first.iter().any(|name| name == &param)
})
} else {
true
}
})
.map(|(user_name, (real_name, record_type))| match record_type {
RecordType::Value => quote!(#user_name = #real_name),
RecordType::Debug => quote!(#user_name = ::tracing::field::debug(&#real_name)),
})
.collect();
// replace every use of a variable with its original name
if let Some(Fields(ref mut fields)) = args.fields {
let mut replacer = IdentAndTypesRenamer {
idents: param_names.into_iter().map(|(a, (b, _))| (a, b)).collect(),
types: Vec::new(),
};
// when async-trait <=0.1.43 is in use, replace instances
// of the "Self" type inside the fields values
if let Some(self_type) = self_type {
replacer.types.push(("Self", self_type.clone()));
}
for e in fields.iter_mut().filter_map(|f| f.value.as_mut()) {
syn::visit_mut::visit_expr_mut(&mut replacer, e);
}
}
let custom_fields = &args.fields;
quote!(::tracing::span!(
target: #target,
#(parent: #parent,)*
#level,
#span_name,
#(#quoted_fields,)*
#custom_fields
))
})();
let target = args.target();
let err_event = match args.err_args {
Some(event_args) => {
let level_tokens = event_args.level(Level::Error);
match event_args.mode {
FormatMode::Default | FormatMode::Display => Some(quote!(
::tracing::event!(target: #target, #level_tokens, error = %e)
)),
FormatMode::Debug => Some(quote!(
::tracing::event!(target: #target, #level_tokens, error = ?e)
)),
}
}
_ => None,
};
let ret_event = match args.ret_args {
Some(event_args) => {
let level_tokens = event_args.level(args_level);
match event_args.mode {
FormatMode::Display => Some(quote!(
::tracing::event!(target: #target, #level_tokens, return = %x)
)),
FormatMode::Default | FormatMode::Debug => Some(quote!(
::tracing::event!(target: #target, #level_tokens, return = ?x)
)),
}
}
_ => None,
};
// Generate the instrumented function body.
// If the function is an `async fn`, this will wrap it in an async block,
// which is `instrument`ed using `tracing-futures`. Otherwise, this will
// enter the span and then perform the rest of the body.
// If `err` is in args, instrument any resulting `Err`s.
// If `ret` is in args, instrument any resulting `Ok`s when the function
// returns `Result`s, otherwise instrument any resulting values.
if async_context {
let mk_fut = match (err_event, ret_event) {
(Some(err_event), Some(ret_event)) => quote_spanned!(block.span()=>
async move {
let __match_scrutinee = async move #block.await;
match __match_scrutinee {
#[allow(clippy::unit_arg)]
Ok(x) => {
#ret_event;
Ok(x)
},
Err(e) => {
#err_event;
Err(e)
}
}
}
),
(Some(err_event), None) => quote_spanned!(block.span()=>
async move {
match async move #block.await {
#[allow(clippy::unit_arg)]
Ok(x) => Ok(x),
Err(e) => {
#err_event;
Err(e)
}
}
}
),
(None, Some(ret_event)) => quote_spanned!(block.span()=>
async move {
let x = async move #block.await;
#ret_event;
x
}
),
(None, None) => quote_spanned!(block.span()=>
async move #block
),
};
return quote!(
let __tracing_attr_span = #span;
let __tracing_instrument_future = #mk_fut;
if !__tracing_attr_span.is_disabled() {
#follows_from
::tracing::Instrument::instrument(
__tracing_instrument_future,
__tracing_attr_span
)
.await
} else {
__tracing_instrument_future.await
}
);
}
let span = quote!(
// These variables are left uninitialized and initialized only
// if the tracing level is statically enabled at this point.
// While the tracing level is also checked at span creation
// time, that will still create a dummy span, and a dummy guard
// and drop the dummy guard later. By lazily initializing these
// variables, Rust will generate a drop flag for them and thus
// only drop the guard if it was created. This creates code that
// is very straightforward for LLVM to optimize out if the tracing
// level is statically disabled, while not causing any performance
// regression in case the level is enabled.
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::level_enabled!(#level) || ::tracing::if_log_enabled!(#level, {true} else {false}) {
__tracing_attr_span = #span;
#follows_from
__tracing_attr_guard = __tracing_attr_span.enter();
}
);
match (err_event, ret_event) {
(Some(err_event), Some(ret_event)) => quote_spanned! {block.span()=>
#span
#[allow(clippy::redundant_closure_call)]
match (move || #block)() {
#[allow(clippy::unit_arg)]
Ok(x) => {
#ret_event;
Ok(x)
},
Err(e) => {
#err_event;
Err(e)
}
}
},
(Some(err_event), None) => quote_spanned!(block.span()=>
#span
#[allow(clippy::redundant_closure_call)]
match (move || #block)() {
#[allow(clippy::unit_arg)]
Ok(x) => Ok(x),
Err(e) => {
#err_event;
Err(e)
}
}
),
(None, Some(ret_event)) => quote_spanned!(block.span()=>
#span
#[allow(clippy::redundant_closure_call)]
let x = (move || #block)();
#ret_event;
x
),
(None, None) => quote_spanned!(block.span() =>
// Because `quote` produces a stream of tokens _without_ whitespace, the
// `if` and the block will appear directly next to each other. This
// generates a clippy lint about suspicious `if/else` formatting.
// Therefore, suppress the lint inside the generated code...
#[allow(clippy::suspicious_else_formatting)]
{
#span
// ...but turn the lint back on inside the function body.
#[warn(clippy::suspicious_else_formatting)]
#block
}
),
}
}
/// Indicates whether a field should be recorded as `Value` or `Debug`.
enum RecordType {
/// The field should be recorded using its `Value` implementation.
Value,
/// The field should be recorded using `tracing::field::debug()`.
Debug,
}
impl RecordType {
/// Array of primitive types which should be recorded as [RecordType::Value].
const TYPES_FOR_VALUE: &'static [&'static str] = &[
"bool",
"str",
"u8",
"i8",
"u16",
"i16",
"u32",
"i32",
"u64",
"i64",
"u128",
"i128",
"f32",
"f64",
"usize",
"isize",
"String",
"NonZeroU8",
"NonZeroI8",
"NonZeroU16",
"NonZeroI16",
"NonZeroU32",
"NonZeroI32",
"NonZeroU64",
"NonZeroI64",
"NonZeroU128",
"NonZeroI128",
"NonZeroUsize",
"NonZeroIsize",
"Wrapping",
];
/// Parse `RecordType` from [Type] by looking up
/// the [RecordType::TYPES_FOR_VALUE] array.
fn parse_from_ty(ty: &Type) -> Self {
match ty {
Type::Path(TypePath { path, .. })
if path
.segments
.iter()
.next_back()
.map(|path_segment| {
let ident = path_segment.ident.to_string();
Self::TYPES_FOR_VALUE.iter().any(|&t| t == ident)
})
.unwrap_or(false) =>
{
RecordType::Value
}
Type::Reference(syn::TypeReference { elem, .. }) => RecordType::parse_from_ty(elem),
_ => RecordType::Debug,
}
}
}
fn param_names(pat: Pat, record_type: RecordType) -> Box<dyn Iterator<Item = (Ident, RecordType)>> {
match pat {
Pat::Ident(PatIdent { ident, .. }) => Box::new(iter::once((ident, record_type))),
Pat::Reference(PatReference { pat, .. }) => param_names(*pat, record_type),
// We can't get the concrete type of fields in the struct/tuple
// patterns by using `syn`. e.g. `fn foo(Foo { x, y }: Foo) {}`.
// Therefore, the struct/tuple patterns in the arguments will just
// always be recorded as `RecordType::Debug`.
Pat::Struct(PatStruct { fields, .. }) => Box::new(
fields
.into_iter()
.flat_map(|FieldPat { pat, .. }| param_names(*pat, RecordType::Debug)),
),
Pat::Tuple(PatTuple { elems, .. }) => Box::new(
elems
.into_iter()
.flat_map(|p| param_names(p, RecordType::Debug)),
),
Pat::TupleStruct(PatTupleStruct { elems, .. }) => Box::new(
elems
.into_iter()
.flat_map(|p| param_names(p, RecordType::Debug)),
),
// The above *should* cover all cases of irrefutable patterns,
// but we purposefully don't do any funny business here
// (such as panicking) because that would obscure rustc's
// much more informative error message.
_ => Box::new(iter::empty()),
}
}
/// The specific async code pattern that was detected
enum AsyncKind<'a> {
/// Immediately-invoked async fn, as generated by `async-trait <= 0.1.43`:
/// `async fn foo<...>(...) {...}; Box::pin(foo<...>(...))`
Function(&'a ItemFn),
/// A function returning an async (move) block, optionally `Box::pin`-ed,
/// as generated by `async-trait >= 0.1.44`:
/// `Box::pin(async move { ... })`
Async {
async_expr: &'a ExprAsync,
pinned_box: bool,
},
}
pub(crate) struct AsyncInfo<'block> {
// statement that must be patched
source_stmt: &'block Stmt,
kind: AsyncKind<'block>,
self_type: Option<TypePath>,
input: &'block ItemFn,
}
impl<'block> AsyncInfo<'block> {
/// Get the AST of the inner function we need to hook, if it looks like a
/// manual future implementation.
///
/// When we are given a function that returns a (pinned) future containing the
/// user logic, it is that (pinned) future that needs to be instrumented.
/// Were we to instrument its parent, we would only collect information
/// regarding the allocation of that future, and not its own span of execution.
///
/// We inspect the block of the function to find if it matches any of the
/// following patterns:
///
/// - Immediately-invoked async fn, as generated by `async-trait <= 0.1.43`:
/// `async fn foo<...>(...) {...}; Box::pin(foo<...>(...))`
///
/// - A function returning an async (move) block, optionally `Box::pin`-ed,
/// as generated by `async-trait >= 0.1.44`:
/// `Box::pin(async move { ... })`
///
/// We the return the statement that must be instrumented, along with some
/// other information.
/// 'gen_body' will then be able to use that information to instrument the
/// proper function/future.
///
/// (this follows the approach suggested in
/// https://github.com/dtolnay/async-trait/issues/45#issuecomment-571245673)
pub(crate) fn from_fn(input: &'block ItemFn) -> Option<Self> {
// are we in an async context? If yes, this isn't a manual async-like pattern
if input.sig.asyncness.is_some() {
return None;
}
let block = &input.block;
// list of async functions declared inside the block
let inside_funs = block.stmts.iter().filter_map(|stmt| {
if let Stmt::Item(Item::Fn(fun)) = &stmt {
// If the function is async, this is a candidate
if fun.sig.asyncness.is_some() {
return Some((stmt, fun));
}
}
None
});
// last expression of the block: it determines the return value of the
// block, this is quite likely a `Box::pin` statement or an async block
let (last_expr_stmt, last_expr) = block.stmts.iter().rev().find_map(|stmt| {
if let Stmt::Expr(expr, _semi) = stmt {
Some((stmt, expr))
} else {
None
}
})?;
// is the last expression an async block?
if let Expr::Async(async_expr) = last_expr {
return Some(AsyncInfo {
source_stmt: last_expr_stmt,
kind: AsyncKind::Async {
async_expr,
pinned_box: false,
},
self_type: None,
input,
});
}
// is the last expression a function call?
let (outside_func, outside_args) = match last_expr {
Expr::Call(ExprCall { func, args, .. }) => (func, args),
_ => return None,
};
// is it a call to `Box::pin()`?
let path = match outside_func.as_ref() {
Expr::Path(path) => &path.path,
_ => return None,
};
if !path_to_string(path).ends_with("Box::pin") {
return None;
}
// Does the call take an argument? If it doesn't,
// it's not gonna compile anyway, but that's no reason
// to (try to) perform an out of bounds access
if outside_args.is_empty() {
return None;
}
// Is the argument to Box::pin an async block that
// captures its arguments?
if let Expr::Async(async_expr) = &outside_args[0] {
return Some(AsyncInfo {
source_stmt: last_expr_stmt,
kind: AsyncKind::Async {
async_expr,
pinned_box: true,
},
self_type: None,
input,
});
}
// Is the argument to Box::pin a function call itself?
let func = match &outside_args[0] {
Expr::Call(ExprCall { func, .. }) => func,
_ => return None,
};
// "stringify" the path of the function called
let func_name = match **func {
Expr::Path(ref func_path) => path_to_string(&func_path.path),
_ => return None,
};
// Was that function defined inside of the current block?
// If so, retrieve the statement where it was declared and the function itself
let (stmt_func_declaration, func) = inside_funs
.into_iter()
.find(|(_, fun)| fun.sig.ident == func_name)?;
// If "_self" is present as an argument, we store its type to be able to rewrite "Self" (the
// parameter type) with the type of "_self"
let mut self_type = None;
for arg in &func.sig.inputs {
if let FnArg::Typed(ty) = arg {
if let Pat::Ident(PatIdent { ref ident, .. }) = *ty.pat {
if ident == "_self" {
let mut ty = *ty.ty.clone();
// extract the inner type if the argument is "&self" or "&mut self"
if let Type::Reference(syn::TypeReference { elem, .. }) = ty {
ty = *elem;
}
if let Type::Path(tp) = ty {
self_type = Some(tp);
break;
}
}
}
}
}
Some(AsyncInfo {
source_stmt: stmt_func_declaration,
kind: AsyncKind::Function(func),
self_type,
input,
})
}
pub(crate) fn gen_async(
self,
args: InstrumentArgs,
instrumented_function_name: &str,
) -> Result<proc_macro::TokenStream, syn::Error> {
// let's rewrite some statements!
let mut out_stmts: Vec<TokenStream> = self
.input
.block
.stmts
.iter()
.map(|stmt| stmt.to_token_stream())
.collect();
if let Some((iter, _stmt)) = self
.input
.block
.stmts
.iter()
.enumerate()
.find(|(_iter, stmt)| *stmt == self.source_stmt)
{
// instrument the future by rewriting the corresponding statement
out_stmts[iter] = match self.kind {
// `Box::pin(immediately_invoked_async_fn())`
AsyncKind::Function(fun) => {
let fun = MaybeItemFn::from(fun.clone());
gen_function(
fun.as_ref(),
args,
instrumented_function_name,
self.self_type.as_ref(),
)
}
// `async move { ... }`, optionally pinned
AsyncKind::Async {
async_expr,
pinned_box,
} => {
let instrumented_block = gen_block(
&async_expr.block,
&self.input.sig.inputs,
true,
args,
instrumented_function_name,
None,
);
let async_attrs = &async_expr.attrs;
if pinned_box {
quote! {
::std::boxed::Box::pin(#(#async_attrs) * async move { #instrumented_block })
}
} else {
quote! {
#(#async_attrs) * async move { #instrumented_block }
}
}
}
};
}
let vis = &self.input.vis;
let sig = &self.input.sig;
let attrs = &self.input.attrs;
Ok(quote!(
#(#attrs) *
#vis #sig {
#(#out_stmts) *
}
)
.into())
}
}
// Return a path as a String
fn path_to_string(path: &Path) -> String {
use std::fmt::Write;
// some heuristic to prevent too many allocations
let mut res = String::with_capacity(path.segments.len() * 5);
for i in 0..path.segments.len() {
write!(&mut res, "{}", path.segments[i].ident)
.expect("writing to a String should never fail");
if i < path.segments.len() - 1 {
res.push_str("::");
}
}
res
}
/// A visitor struct to replace idents and types in some piece
/// of code (e.g. the "self" and "Self" tokens in user-supplied
/// fields expressions when the function is generated by an old
/// version of async-trait).
struct IdentAndTypesRenamer<'a> {
types: Vec<(&'a str, TypePath)>,
idents: Vec<(Ident, Ident)>,
}
impl VisitMut for IdentAndTypesRenamer<'_> {
// we deliberately compare strings because we want to ignore the spans
// If we apply clippy's lint, the behavior changes
#[allow(clippy::cmp_owned)]
fn visit_ident_mut(&mut self, id: &mut Ident) {
for (old_ident, new_ident) in &self.idents {
if id.to_string() == old_ident.to_string() {
*id = new_ident.clone();
}
}
}
fn visit_type_mut(&mut self, ty: &mut Type) {
for (type_name, new_type) in &self.types {
if let Type::Path(TypePath { path, .. }) = ty {
if path_to_string(path) == *type_name {
*ty = Type::Path(new_type.clone());
}
}
}
}
}
// A visitor struct that replace an async block by its patched version
struct AsyncTraitBlockReplacer<'a> {
block: &'a Block,
patched_block: Block,
}
impl VisitMut for AsyncTraitBlockReplacer<'_> {
fn visit_block_mut(&mut self, i: &mut Block) {
if i == self.block {
*i = self.patched_block.clone();
}
}
}
// Replaces any `impl Trait` with `_` so it can be used as the type in
// a `let` statement's LHS.
struct ImplTraitEraser;
impl VisitMut for ImplTraitEraser {
fn visit_type_mut(&mut self, t: &mut Type) {
if let Type::ImplTrait(..) = t {
*t = syn::TypeInfer {
underscore_token: Token![_](t.span()),
}
.into();
} else {
syn::visit_mut::visit_type_mut(self, t);
}
}
}
fn erase_impl_trait(ty: &Type) -> Type {
let mut ty = ty.clone();
ImplTraitEraser.visit_type_mut(&mut ty);
ty
}

707
vendor/tracing-attributes/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,707 @@
//! A procedural macro attribute for instrumenting functions with [`tracing`].
//!
//! [`tracing`] is a framework for instrumenting Rust programs to collect
//! structured, event-based diagnostic information. This crate provides the
//! [`#[instrument]`][instrument] procedural macro attribute.
//!
//! Note that this macro is also re-exported by the main `tracing` crate.
//!
//! *Compiler support: [requires `rustc` 1.65+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
//! ## Usage
//!
//! In the `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! tracing-attributes = "0.1.24"
//! ```
//!
//! The [`#[instrument]`][instrument] attribute can now be added to a function
//! to automatically create and enter `tracing` [span] when that function is
//! called. For example:
//!
//! ```
//! use tracing::instrument;
//!
//! #[instrument]
//! pub fn my_function(my_arg: usize) {
//! // ...
//! }
//!
//! # fn main() {}
//! ```
//!
//! [`tracing`]: https://crates.io/crates/tracing
//! [span]: https://docs.rs/tracing/latest/tracing/span/index.html
//! [instrument]: macro@self::instrument
//!
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
//! version is 1.65. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
//! stable compiler version is 1.69, the minimum supported version will not be
//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
#![doc(
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/main/assets/logo-type.png",
issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
)]
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub,
bad_style,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
private_interfaces,
private_bounds,
unconditional_recursion,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)]
// TODO: once `tracing` bumps its MSRV to 1.42, remove this allow.
#![allow(unused)]
extern crate proc_macro;
use proc_macro2::TokenStream;
use quote::TokenStreamExt;
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::token::Brace;
use syn::{Attribute, ItemFn, Signature, Visibility};
mod attr;
mod expand;
/// Instruments a function to create and enter a `tracing` [span] every time
/// the function is called.
///
/// Unless overridden, a span with the [`INFO`] [level] will be generated.
/// The generated span's name will be the name of the function.
/// By default, all arguments to the function are included as fields on the
/// span. Arguments that are `tracing` [primitive types] implementing the
/// [`Value` trait] will be recorded as fields of that type. Types which do
/// not implement `Value` will be recorded using [`fmt::Debug`].
///
/// [primitive types]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html#foreign-impls
/// [`Value` trait]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html
///
/// # Overriding Span Attributes
///
/// To change the [name] of the generated span, add a `name` argument to the
/// `#[instrument]` macro, followed by an equals sign and a string literal. For
/// example:
///
/// ```
/// # use tracing_attributes::instrument;
///
/// // The generated span's name will be "my_span" rather than "my_function".
/// #[instrument(name = "my_span")]
/// pub fn my_function() {
/// // ... do something incredibly interesting and important ...
/// }
/// ```
///
/// To override the [target] of the generated span, add a `target` argument to
/// the `#[instrument]` macro, followed by an equals sign and a string literal
/// for the new target. The [module path] is still recorded separately. For
/// example:
///
/// ```
/// pub mod my_module {
/// # use tracing_attributes::instrument;
/// // The generated span's target will be "my_crate::some_special_target",
/// // rather than "my_crate::my_module".
/// #[instrument(target = "my_crate::some_special_target")]
/// pub fn my_function() {
/// // ... all kinds of neat code in here ...
/// }
/// }
/// ```
///
/// Finally, to override the [level] of the generated span, add a `level`
/// argument, followed by an equals sign and a string literal with the name of
/// the desired level. Level names are not case sensitive. For example:
///
/// ```
/// # use tracing_attributes::instrument;
/// // The span's level will be TRACE rather than INFO.
/// #[instrument(level = "trace")]
/// pub fn my_function() {
/// // ... I have written a truly marvelous implementation of this function,
/// // which this example is too narrow to contain ...
/// }
/// ```
///
/// # Skipping Fields
///
/// To skip recording one or more arguments to a function or method, pass
/// the argument's name inside the `skip()` argument on the `#[instrument]`
/// macro. This can be used when an argument to an instrumented function does
/// not implement [`fmt::Debug`], or to exclude an argument with a verbose or
/// costly `Debug` implementation. Note that:
///
/// - multiple argument names can be passed to `skip`.
/// - arguments passed to `skip` do _not_ need to implement `fmt::Debug`.
///
/// You can also use `skip_all` to skip all arguments.
///
/// ## Examples
///
/// ```
/// # use tracing_attributes::instrument;
/// # use std::collections::HashMap;
/// // This type doesn't implement `fmt::Debug`!
/// struct NonDebug;
///
/// // `arg` will be recorded, while `non_debug` will not.
/// #[instrument(skip(non_debug))]
/// fn my_function(arg: usize, non_debug: NonDebug) {
/// // ...
/// }
///
/// // These arguments are huge
/// #[instrument(skip_all)]
/// fn my_big_data_function(large: Vec<u8>, also_large: HashMap<String, String>) {
/// // ...
/// }
/// ```
///
/// Skipping the `self` parameter:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[derive(Debug)]
/// struct MyType {
/// data: Vec<u8>, // Suppose this buffer is often quite long...
/// }
///
/// impl MyType {
/// // Suppose we don't want to print an entire kilobyte of `data`
/// // every time this is called...
/// #[instrument(skip(self))]
/// pub fn my_method(&mut self, an_interesting_argument: usize) {
/// // ... do something (hopefully, using all that `data`!)
/// }
/// }
/// ```
///
/// # Adding Fields
///
/// Additional fields (key-value pairs with arbitrary data) can be passed to
/// to the generated span through the `fields` argument on the
/// `#[instrument]` macro. Strings, integers or boolean literals are accepted values
/// for each field. The name of the field must be a single valid Rust
/// identifier, nested (dotted) field names are not supported. Any
/// Rust expression can be used as a field value in this manner. These
/// expressions will be evaluated at the beginning of the function's body, so
/// arguments to the function may be used in these expressions. Field names may
/// also be specified *without* values. Doing so will result in an [empty field]
/// whose value may be recorded later within the function body.
///
/// Note that overlap between the names of fields and (non-skipped) arguments
/// will result in a compile error.
///
/// ## Examples
///
/// Adding a new field based on the value of an argument:
///
/// ```
/// # use tracing_attributes::instrument;
///
/// // This will record a field named "i" with the value of `i` *and* a field
/// // named "next" with the value of `i` + 1.
/// #[instrument(fields(next = i + 1))]
/// pub fn my_function(i: usize) {
/// // ...
/// }
/// ```
///
/// Recording specific properties of a struct as their own fields:
///
/// ```
/// # mod http {
/// # pub struct Error;
/// # pub struct Response<B> { pub(super) _b: std::marker::PhantomData<B> }
/// # pub struct Request<B> { _b: B }
/// # impl<B> std::fmt::Debug for Request<B> {
/// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// # f.pad("request")
/// # }
/// # }
/// # impl<B> Request<B> {
/// # pub fn uri(&self) -> &str { "fake" }
/// # pub fn method(&self) -> &str { "GET" }
/// # }
/// # }
/// # use tracing_attributes::instrument;
///
/// // This will record the request's URI and HTTP method as their own separate
/// // fields.
/// #[instrument(fields(http.uri = req.uri(), http.method = req.method()))]
/// pub fn handle_request<B>(req: http::Request<B>) -> http::Response<B> {
/// // ... handle the request ...
/// # http::Response { _b: std::marker::PhantomData }
/// }
/// ```
///
/// This can be used in conjunction with `skip` or `skip_all` to record only
/// some fields of a struct:
/// ```
/// # use tracing_attributes::instrument;
/// // Remember the struct with the very large `data` field from the earlier
/// // example? Now it also has a `name`, which we might want to include in
/// // our span.
/// #[derive(Debug)]
/// struct MyType {
/// name: &'static str,
/// data: Vec<u8>,
/// }
///
/// impl MyType {
/// // This will skip the `data` field, but will include `self.name`,
/// // formatted using `fmt::Display`.
/// #[instrument(skip(self), fields(self.name = %self.name))]
/// pub fn my_method(&mut self, an_interesting_argument: usize) {
/// // ... do something (hopefully, using all that `data`!)
/// }
/// }
/// ```
///
/// Adding an empty field to be recorded later:
///
/// ```
/// # use tracing_attributes::instrument;
///
/// // This function does a very interesting and important mathematical calculation.
/// // Suppose we want to record both the inputs to the calculation *and* its result...
/// #[instrument(fields(result))]
/// pub fn do_calculation(input_1: usize, input_2: usize) -> usize {
/// // Rerform the calculation.
/// let result = input_1 + input_2;
///
/// // Record the result as part of the current span.
/// tracing::Span::current().record("result", &result);
///
/// // Now, the result will also be included on this event!
/// tracing::info!("calculation complete!");
///
/// // ... etc ...
/// # 0
/// }
/// ```
///
/// # Examples
///
/// Instrumenting a function:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument]
/// pub fn my_function(my_arg: usize) {
/// // This event will be recorded inside a span named `my_function` with the
/// // field `my_arg`.
/// tracing::info!("inside my_function!");
/// // ...
/// }
/// ```
/// Setting the level for the generated span:
/// ```
/// # use tracing_attributes::instrument;
/// # use tracing::Level;
/// #[instrument(level = Level::DEBUG)]
/// pub fn my_function() {
/// // ...
/// }
/// ```
/// Levels can be specified either with [`Level`] constants, literal strings
/// (e.g., `"debug"`, `"info"`) or numerically (1—5, corresponding to [`Level::TRACE`]—[`Level::ERROR`]).
///
/// Overriding the generated span's name:
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(name = "my_name")]
/// pub fn my_function() {
/// // ...
/// }
/// ```
/// Overriding the generated span's target:
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(target = "my_target")]
/// pub fn my_function() {
/// // ...
/// }
/// ```
/// Overriding the generated span's parent:
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(parent = None)]
/// pub fn my_function() {
/// // ...
/// }
/// ```
/// ```
/// # use tracing_attributes::instrument;
/// // A struct which owns a span handle.
/// struct MyStruct
/// {
/// span: tracing::Span
/// }
///
/// impl MyStruct
/// {
/// // Use the struct's `span` field as the parent span
/// #[instrument(parent = &self.span, skip(self))]
/// fn my_method(&self) {}
/// }
/// ```
/// Specifying [`follows_from`] relationships:
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(follows_from = causes)]
/// pub fn my_function(causes: &[tracing::Id]) {
/// // ...
/// }
/// ```
/// Any expression of type `impl IntoIterator<Item = impl Into<Option<Id>>>`
/// may be provided to `follows_from`; e.g.:
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(follows_from = [cause])]
/// pub fn my_function(cause: &tracing::span::EnteredSpan) {
/// // ...
/// }
/// ```
///
///
/// To skip recording an argument, pass the argument's name to the `skip`:
///
/// ```
/// # use tracing_attributes::instrument;
/// struct NonDebug;
///
/// #[instrument(skip(non_debug))]
/// fn my_function(arg: usize, non_debug: NonDebug) {
/// // ...
/// }
/// ```
///
/// To add additional context to the span, pass key-value pairs to `fields`:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(fields(foo="bar", id=1, show=true))]
/// fn my_function(arg: usize) {
/// // ...
/// }
/// ```
///
/// Adding the `ret` argument to `#[instrument]` will emit an event with the function's
/// return value when the function returns:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(ret)]
/// fn my_function() -> i32 {
/// 42
/// }
/// ```
/// The return value event will have the same level as the span generated by `#[instrument]`.
/// By default, this will be [`INFO`], but if the level is overridden, the event will be at the same
/// level.
///
/// It's also possible to override the level for the `ret` event independently:
///
/// ```
/// # use tracing_attributes::instrument;
/// # use tracing::Level;
/// #[instrument(ret(level = Level::WARN))]
/// fn my_function() -> i32 {
/// 42
/// }
/// ```
///
/// **Note**: if the function returns a `Result<T, E>`, `ret` will record returned values if and
/// only if the function returns [`Result::Ok`].
///
/// By default, returned values will be recorded using their [`std::fmt::Debug`] implementations.
/// If a returned value implements [`std::fmt::Display`], it can be recorded using its `Display`
/// implementation instead, by writing `ret(Display)`:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(ret(Display))]
/// fn my_function() -> i32 {
/// 42
/// }
/// ```
///
/// If the function returns a `Result<T, E>` and `E` implements `std::fmt::Display`, adding
/// `err` or `err(Display)` will emit error events when the function returns `Err`:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(err)]
/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
/// Ok(())
/// }
/// ```
///
/// The level of the error value event defaults to `ERROR`.
///
/// Similarly, overriding the level of the `err` event :
///
/// ```
/// # use tracing_attributes::instrument;
/// # use tracing::Level;
/// #[instrument(err(level = Level::INFO))]
/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
/// Ok(())
/// }
/// ```
///
/// By default, error values will be recorded using their `std::fmt::Display` implementations.
/// If an error implements `std::fmt::Debug`, it can be recorded using its `Debug` implementation
/// instead by writing `err(Debug)`:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(err(Debug))]
/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
/// Ok(())
/// }
/// ```
///
/// If a `target` is specified, both the `ret` and `err` arguments will emit outputs to
/// the declared target (or the default channel if `target` is not specified).
///
/// The `ret` and `err` arguments can be combined in order to record an event if a
/// function returns [`Result::Ok`] or [`Result::Err`]:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument(err, ret)]
/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
/// Ok(())
/// }
/// ```
///
/// `async fn`s may also be instrumented:
///
/// ```
/// # use tracing_attributes::instrument;
/// #[instrument]
/// pub async fn my_function() -> Result<(), ()> {
/// // ...
/// # Ok(())
/// }
/// ```
///
/// It also works with [async-trait](https://crates.io/crates/async-trait)
/// (a crate that allows defining async functions in traits,
/// something not currently possible in Rust),
/// and hopefully most libraries that exhibit similar behaviors:
///
/// ```
/// # use tracing::instrument;
/// use async_trait::async_trait;
///
/// #[async_trait]
/// pub trait Foo {
/// async fn foo(&self, arg: usize);
/// }
///
/// #[derive(Debug)]
/// struct FooImpl(usize);
///
/// #[async_trait]
/// impl Foo for FooImpl {
/// #[instrument(fields(value = self.0, tmp = std::any::type_name::<Self>()))]
/// async fn foo(&self, arg: usize) {}
/// }
/// ```
///
/// `const fn` cannot be instrumented, and will result in a compilation failure:
///
/// ```compile_fail
/// # use tracing_attributes::instrument;
/// #[instrument]
/// const fn my_const_function() {}
/// ```
///
/// [span]: https://docs.rs/tracing/latest/tracing/span/index.html
/// [name]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.name
/// [target]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.target
/// [level]: https://docs.rs/tracing/latest/tracing/struct.Level.html
/// [module path]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.module_path
/// [`INFO`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.INFO
/// [empty field]: https://docs.rs/tracing/latest/tracing/field/struct.Empty.html
/// [field syntax]: https://docs.rs/tracing/latest/tracing/#recording-fields
/// [`follows_from`]: https://docs.rs/tracing/latest/tracing/struct.Span.html#method.follows_from
/// [`tracing`]: https://github.com/tokio-rs/tracing
/// [`fmt::Debug`]: std::fmt::Debug
/// [`Level`]: https://docs.rs/tracing/latest/tracing/struct.Level.html
/// [`Level::TRACE`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.TRACE
/// [`Level::ERROR`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.ERROR
#[proc_macro_attribute]
pub fn instrument(
args: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let args = syn::parse_macro_input!(args as attr::InstrumentArgs);
// Cloning a `TokenStream` is cheap since it's reference counted internally.
instrument_precise(args.clone(), item.clone())
.unwrap_or_else(|_err| instrument_speculative(args, item))
}
/// Instrument the function, without parsing the function body (instead using the raw tokens).
fn instrument_speculative(
args: attr::InstrumentArgs,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(item as MaybeItemFn);
let instrumented_function_name = input.sig.ident.to_string();
expand::gen_function(
input.as_ref(),
args,
instrumented_function_name.as_str(),
None,
)
.into()
}
/// Instrument the function, by fully parsing the function body,
/// which allows us to rewrite some statements related to async-like patterns.
fn instrument_precise(
args: attr::InstrumentArgs,
item: proc_macro::TokenStream,
) -> Result<proc_macro::TokenStream, syn::Error> {
let input = syn::parse::<ItemFn>(item)?;
let instrumented_function_name = input.sig.ident.to_string();
if input.sig.constness.is_some() {
return Ok(quote! {
compile_error!("the `#[instrument]` attribute may not be used with `const fn`s")
}
.into());
}
// check for async_trait-like patterns in the block, and instrument
// the future instead of the wrapper
if let Some(async_like) = expand::AsyncInfo::from_fn(&input) {
return async_like.gen_async(args, instrumented_function_name.as_str());
}
let input = MaybeItemFn::from(input);
Ok(expand::gen_function(
input.as_ref(),
args,
instrumented_function_name.as_str(),
None,
)
.into())
}
/// This is a more flexible/imprecise `ItemFn` type,
/// which's block is just a `TokenStream` (it may contain invalid code).
#[derive(Debug, Clone)]
struct MaybeItemFn {
outer_attrs: Vec<Attribute>,
inner_attrs: Vec<Attribute>,
vis: Visibility,
sig: Signature,
brace_token: Brace,
block: TokenStream,
}
impl MaybeItemFn {
fn as_ref(&self) -> MaybeItemFnRef<'_, TokenStream> {
MaybeItemFnRef {
outer_attrs: &self.outer_attrs,
inner_attrs: &self.inner_attrs,
vis: &self.vis,
sig: &self.sig,
brace_token: &self.brace_token,
block: &self.block,
}
}
}
/// This parses a `TokenStream` into a `MaybeItemFn`
/// (just like `ItemFn`, but skips parsing the body).
impl Parse for MaybeItemFn {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let outer_attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;
let inner_attrs = input.call(Attribute::parse_inner)?;
let block;
let brace_token = syn::braced!(block in input);
let block: TokenStream = block.call(|buffer| buffer.parse())?;
Ok(Self {
outer_attrs,
inner_attrs,
vis,
sig,
brace_token,
block,
})
}
}
impl From<ItemFn> for MaybeItemFn {
fn from(
ItemFn {
attrs,
vis,
sig,
block,
}: ItemFn,
) -> Self {
let (outer_attrs, inner_attrs) = attrs
.into_iter()
.partition(|attr| attr.style == syn::AttrStyle::Outer);
let mut block_tokens = TokenStream::new();
block_tokens.append_all(block.stmts);
Self {
outer_attrs,
inner_attrs,
vis,
sig,
brace_token: block.brace_token,
block: block_tokens,
}
}
}
/// A generic reference type for `MaybeItemFn`,
/// that takes a generic block type `B` that implements `ToTokens` (eg. `TokenStream`, `Block`).
#[derive(Debug, Clone)]
struct MaybeItemFnRef<'a, B: ToTokens> {
outer_attrs: &'a Vec<Attribute>,
inner_attrs: &'a Vec<Attribute>,
vis: &'a Visibility,
sig: &'a Signature,
brace_token: &'a Brace,
block: &'a B,
}