aboutsummaryrefslogtreecommitdiff
path: root/patches/syn-2.diff
diff options
context:
space:
mode:
Diffstat (limited to 'patches/syn-2.diff')
-rw-r--r--patches/syn-2.diff906
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) {