diff options
Diffstat (limited to 'patches/syn-2.diff')
-rw-r--r-- | patches/syn-2.diff | 906 |
1 files changed, 906 insertions, 0 deletions
diff --git a/patches/syn-2.diff b/patches/syn-2.diff new file mode 100644 index 0000000..247e761 --- /dev/null +++ b/patches/syn-2.diff @@ -0,0 +1,906 @@ +diff --git a/src/attr.rs b/src/attr.rs +index ff875e1..9b778c8 100644 +--- a/src/attr.rs ++++ b/src/attr.rs +@@ -6,6 +6,14 @@ 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>, +@@ -14,53 +22,16 @@ pub(crate) struct InstrumentArgs { + 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_mode: Option<FormatMode>, +- pub(crate) ret_mode: Option<FormatMode>, ++ 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) -> impl ToTokens { +- fn is_level(lit: &LitInt, expected: u64) -> bool { +- match lit.base10_parse::<u64>() { +- Ok(value) => value == expected, +- Err(_) => false, +- } +- } +- +- match &self.level { +- Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("trace") => { +- quote!(tracing::Level::TRACE) +- } +- Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("debug") => { +- quote!(tracing::Level::DEBUG) +- } +- Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("info") => { +- quote!(tracing::Level::INFO) +- } +- Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("warn") => { +- quote!(tracing::Level::WARN) +- } +- Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("error") => { +- quote!(tracing::Level::ERROR) +- } +- Some(Level::Int(ref lit)) if is_level(lit, 1) => quote!(tracing::Level::TRACE), +- Some(Level::Int(ref lit)) if is_level(lit, 2) => quote!(tracing::Level::DEBUG), +- Some(Level::Int(ref lit)) if is_level(lit, 3) => quote!(tracing::Level::INFO), +- Some(Level::Int(ref lit)) if is_level(lit, 4) => quote!(tracing::Level::WARN), +- Some(Level::Int(ref lit)) if is_level(lit, 5) => quote!(tracing::Level::ERROR), +- Some(Level::Path(ref pat)) => quote!(#pat), +- Some(_) => quote! { +- compile_error!( +- "unknown verbosity level, expected one of \"trace\", \ +- \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5" +- ) +- }, +- None => quote!(tracing::Level::INFO), +- } ++ pub(crate) fn level(&self) -> Level { ++ self.level.clone().unwrap_or(Level::Info) + } + + pub(crate) fn target(&self) -> impl ToTokens { +@@ -146,20 +117,8 @@ impl Parse for InstrumentArgs { + 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")); +@@ -167,12 +126,12 @@ impl Parse for InstrumentArgs { + args.fields = Some(input.parse()?); + } else if lookahead.peek(kw::err) { + let _ = input.parse::<kw::err>(); +- let mode = FormatMode::parse(input)?; +- args.err_mode = Some(mode); ++ let err_args = EventArgs::parse(input)?; ++ args.err_args = Some(err_args); + } else if lookahead.peek(kw::ret) { + let _ = input.parse::<kw::ret>()?; +- let mode = FormatMode::parse(input)?; +- args.ret_mode = Some(mode); ++ let ret_args = EventArgs::parse(input)?; ++ args.ret_args = Some(ret_args); + } else if lookahead.peek(Token![,]) { + let _ = input.parse::<Token![,]>()?; + } else { +@@ -190,6 +149,55 @@ impl Parse for InstrumentArgs { + } + } + ++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) ++ } ++} ++ + struct StrArg<T> { + value: LitStr, + _p: std::marker::PhantomData<T>, +@@ -224,6 +232,19 @@ impl<T: Parse> Parse for ExprArg<T> { + } + } + ++struct IdentOrSelf(Ident); ++impl Parse for IdentOrSelf { ++ fn parse(input: ParseStream<'_>) -> syn::Result<Self> { ++ Ok(Self( ++ if let Ok(self_token) = input.parse::<Token![self]>() { ++ Ident::new("self", self_token.span) ++ } else { ++ input.parse()? ++ }, ++ )) ++ } ++} ++ + struct Skips(HashSet<Ident>); + + impl Parse for Skips { +@@ -231,16 +252,16 @@ impl Parse for Skips { + let _ = input.parse::<kw::skip>(); + let content; + let _ = syn::parenthesized!(content in input); +- let names: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse_any)?; ++ let names: Punctuated<IdentOrSelf, Token![,]> = Punctuated::parse_terminated(&content)?; + let mut skips = HashSet::new(); + for name in names { +- if skips.contains(&name) { ++ if skips.contains(&name.0) { + return Err(syn::Error::new( +- name.span(), ++ name.0.span(), + "tried to skip the same field twice", + )); + } else { +- skips.insert(name); ++ skips.insert(name.0); + } + } + Ok(Self(skips)) +@@ -260,27 +281,6 @@ impl Default for FormatMode { + } + } + +-impl Parse for FormatMode { +- fn parse(input: ParseStream<'_>) -> syn::Result<Self> { +- if !input.peek(syn::token::Paren) { +- return Ok(FormatMode::default()); +- } +- let content; +- let _ = syn::parenthesized!(content in input); +- let maybe_mode: Option<Ident> = content.parse()?; +- maybe_mode.map_or(Ok(FormatMode::default()), |ident| { +- match ident.to_string().as_str() { +- "Debug" => Ok(FormatMode::Debug), +- "Display" => Ok(FormatMode::Display), +- _ => Err(syn::Error::new( +- ident.span(), +- "unknown error mode, must be Debug or Display", +- )), +- } +- }) +- } +-} +- + #[derive(Clone, Debug)] + pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>); + +@@ -303,7 +303,7 @@ impl Parse for Fields { + let _ = input.parse::<kw::fields>(); + let content; + let _ = syn::parenthesized!(content in input); +- let fields: Punctuated<_, Token![,]> = content.parse_terminated(Field::parse)?; ++ let fields: Punctuated<_, Token![,]> = Punctuated::parse_terminated(&content)?; + Ok(Self(fields)) + } + } +@@ -376,9 +376,12 @@ impl ToTokens for FieldKind { + } + + #[derive(Clone, Debug)] +-enum Level { +- Str(LitStr), +- Int(LitInt), ++pub(crate) enum Level { ++ Trace, ++ Debug, ++ Info, ++ Warn, ++ Error, + Path(Path), + } + +@@ -388,9 +391,37 @@ impl Parse for Level { + let _ = input.parse::<Token![=]>()?; + let lookahead = input.lookahead1(); + if lookahead.peek(LitStr) { +- Ok(Self::Str(input.parse()?)) ++ 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) { +- Ok(Self::Int(input.parse()?)) ++ 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 { +@@ -399,10 +430,22 @@ impl Parse for Level { + } + } + ++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); +diff --git a/src/expand.rs b/src/expand.rs +index 7005b44..a4a463a 100644 +--- a/src/expand.rs ++++ b/src/expand.rs +@@ -10,7 +10,7 @@ use syn::{ + }; + + use crate::{ +- attr::{Field, Fields, FormatMode, InstrumentArgs}, ++ attr::{Field, Fields, FormatMode, InstrumentArgs, Level}, + MaybeItemFn, MaybeItemFnRef, + }; + +@@ -64,7 +64,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( + // 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(unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value)] ++ #[allow(unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value, clippy::unreachable)] + if false { + let __tracing_attr_fake_return: #return_type = + unreachable!("this is just for type inference, and is unreachable code"); +@@ -116,7 +116,8 @@ fn gen_block<B: ToTokens>( + .map(|name| quote!(#name)) + .unwrap_or_else(|| quote!(#instrumented_function_name)); + +- let level = args.level(); ++ let args_level = args.level(); ++ let level = args_level.clone(); + + let follows_from = args.follows_from.iter(); + let follows_from = quote! { +@@ -134,7 +135,7 @@ fn gen_block<B: ToTokens>( + .into_iter() + .flat_map(|param| match param { + FnArg::Typed(PatType { pat, ty, .. }) => { +- param_names(*pat, RecordType::parse_from_ty(&*ty)) ++ param_names(*pat, RecordType::parse_from_ty(&ty)) + } + FnArg::Receiver(_) => Box::new(iter::once(( + Ident::new("self", param.span()), +@@ -178,7 +179,7 @@ fn gen_block<B: ToTokens>( + let quoted_fields: Vec<_> = param_names + .iter() + .filter(|(param, _)| { +- if args.skip_all || args.skips.contains(param) { ++ if args.skips.contains(param) { + return false; + } + +@@ -232,21 +233,33 @@ fn gen_block<B: ToTokens>( + + let target = args.target(); + +- let err_event = match args.err_mode { +- Some(FormatMode::Default) | Some(FormatMode::Display) => { +- Some(quote!(tracing::error!(target: #target, error = %e))) ++ 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) ++ )), ++ } + } +- Some(FormatMode::Debug) => Some(quote!(tracing::error!(target: #target, error = ?e))), + _ => None, + }; + +- let ret_event = match args.ret_mode { +- Some(FormatMode::Display) => Some(quote!( +- tracing::event!(target: #target, #level, return = %x) +- )), +- Some(FormatMode::Default) | Some(FormatMode::Debug) => Some(quote!( +- tracing::event!(target: #target, #level, return = ?x) +- )), ++ 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, + }; + +@@ -464,10 +477,7 @@ fn param_names(pat: Pat, record_type: RecordType) -> Box<dyn Iterator<Item = (Id + .into_iter() + .flat_map(|p| param_names(p, RecordType::Debug)), + ), +- Pat::TupleStruct(PatTupleStruct { +- pat: PatTuple { elems, .. }, +- .. +- }) => Box::new( ++ Pat::TupleStruct(PatTupleStruct { elems, .. }) => Box::new( + elems + .into_iter() + .flat_map(|p| param_names(p, RecordType::Debug)), +@@ -551,7 +561,7 @@ impl<'block> AsyncInfo<'block> { + // 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) = stmt { ++ if let Stmt::Expr(expr, _) = stmt { + Some((stmt, expr)) + } else { + None +diff --git a/src/lib.rs b/src/lib.rs +index f5974e4..4b92520 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -12,11 +12,11 @@ + //! + //! ## Usage + //! +-//! First, add this to your `Cargo.toml`: ++//! In the `Cargo.toml`: + //! + //! ```toml + //! [dependencies] +-//! tracing-attributes = "0.1.23" ++//! tracing = "0.1" + //! ``` + //! + //! The [`#[instrument]`][instrument] attribute can now be added to a function +@@ -24,7 +24,7 @@ + //! called. For example: + //! + //! ``` +-//! use tracing_attributes::instrument; ++//! use tracing::instrument; + //! + //! #[instrument] + //! pub fn my_function(my_arg: usize) { +@@ -35,8 +35,8 @@ + //! ``` + //! + //! [`tracing`]: https://crates.io/crates/tracing ++//! [instrument]: macro@instrument + //! [span]: https://docs.rs/tracing/latest/tracing/span/index.html +-//! [instrument]: macro@self::instrument + //! + //! ## Supported Rust Versions + //! +@@ -52,19 +52,17 @@ + //! supported compiler version is not considered a semver breaking change as + //! long as doing so complies with this policy. + //! +-#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.23")] + #![doc( + html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", ++ html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico", + 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, +- const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, +@@ -79,12 +77,9 @@ + 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::ToTokens; ++use quote::{quote, ToTokens}; + use syn::parse::{Parse, ParseStream}; + use syn::{Attribute, ItemFn, Signature, Visibility}; + +@@ -93,7 +88,7 @@ mod expand; + /// Instruments a function to create and enter a `tracing` [span] every time + /// the function is called. + /// +-/// Unless overriden, a span with the [`INFO`] [level] will be generated. ++/// Unless overriden, a span with `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 +@@ -101,216 +96,27 @@ mod expand; + /// not implement `Value` will be recorded using [`std::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: ++/// [`Value` trait]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html + /// ++/// To skip recording a function's or method's argument, pass the argument's name ++/// to the `skip` argument on the `#[instrument]` macro. For example, ++/// `skip` 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) may be added to the +-/// generated span using the `fields` argument on the `#[instrument]` macro. 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. +-/// +-/// This supports the same [field syntax] as the `span!` and `event!` macros. ++/// 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. + /// + /// 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] +@@ -324,11 +130,15 @@ mod expand; + /// Setting the level for the generated span: + /// ``` + /// # use tracing_attributes::instrument; +-/// #[instrument(level = "debug")] ++/// # 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; +@@ -399,7 +209,7 @@ mod expand; + /// } + /// ``` + /// +-/// To add an additional context to the span, pass key-value pairs to `fields`: ++/// To add additional context to the span, pass key-value pairs to `fields`: + /// + /// ``` + /// # use tracing_attributes::instrument; +@@ -419,10 +229,21 @@ mod expand; + /// 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 ++/// The level of the return value event defaults to the same level as the span generated by `#[instrument]`. ++/// By default, this will be `TRACE`, but if the span 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`]. + /// +@@ -438,8 +259,8 @@ mod expand; + /// } + /// ``` + /// +-/// If the function returns a `Result<T, E>` and `E` implements `std::fmt::Display`, you can add +-/// `err` or `err(Display)` to emit error events when the function returns `Err`: ++/// 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; +@@ -449,9 +270,22 @@ mod expand; + /// } + /// ``` + /// ++/// 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)`: ++/// instead by writing `err(Debug)`: + /// + /// ``` + /// # use tracing_attributes::instrument; +@@ -510,44 +344,21 @@ mod expand; + /// } + /// ``` + /// +-/// Note than on `async-trait` <= 0.1.43, references to the `Self` +-/// type inside the `fields` argument were only allowed when the instrumented +-/// function is a method (i.e., the function receives `self` as an argument). +-/// For example, this *used to not work* because the instrument function +-/// didn't receive `self`: +-/// ``` +-/// # use tracing::instrument; +-/// use async_trait::async_trait; +-/// +-/// #[async_trait] +-/// pub trait Bar { +-/// async fn bar(); +-/// } +-/// +-/// #[derive(Debug)] +-/// struct BarImpl(usize); ++/// `const fn` cannot be instrumented, and will result in a compilation failure: + /// +-/// #[async_trait] +-/// impl Bar for BarImpl { +-/// #[instrument(fields(tmp = std::any::type_name::<Self>()))] +-/// async fn bar() {} +-/// } ++/// ```compile_fail ++/// # use tracing_attributes::instrument; ++/// #[instrument] ++/// const fn my_const_function() {} + /// ``` +-/// Instead, you should manually rewrite any `Self` types as the type for +-/// which you implement the trait: `#[instrument(fields(tmp = std::any::type_name::<Bar>()))]` +-/// (or maybe you can just bump `async-trait`). + /// + /// [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, +@@ -584,6 +395,13 @@ fn instrument_precise( + 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) { |