Update to syn-2 am: ed8925a283

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/tracing-attributes/+/2523895

Change-Id: Ia90b305dfe71833d9d6c9e2745a9e81bc1a8c8b4
Signed-off-by: Automerger Merge Worker <[email protected]>
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) {
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 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) 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 @@
                 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 @@
                 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 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 @@
     }
 }
 
+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 @@
         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 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 @@
         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 @@
 }
 
 #[derive(Clone, Debug)]
-enum Level {
-    Str(LitStr),
-    Int(LitInt),
+pub(crate) enum Level {
+    Trace,
+    Debug,
+    Info,
+    Warn,
+    Error,
     Path(Path),
 }
 
@@ -388,9 +391,37 @@
         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 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 crate::{
-    attr::{Field, Fields, FormatMode, InstrumentArgs},
+    attr::{Field, Fields, FormatMode, InstrumentArgs, Level},
     MaybeItemFn, MaybeItemFnRef,
 };
 
@@ -64,7 +64,7 @@
     // 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 @@
         .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 @@
             .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 @@
         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 @@
 
     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 @@
                 .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 @@
         // 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 @@
 /// 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 @@
 /// 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.
+/// [`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:
-///
+/// 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 @@
 /// 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 @@
 /// }
 /// ```
 ///
-/// 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 @@
 ///     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 @@
 /// }
 /// ```
 ///
-/// 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 @@
 /// }
 /// ```
 ///
+/// 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 @@
 /// }
 /// ```
 ///
-/// 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`:
+/// `const fn` cannot be instrumented, and will result in a compilation failure:
+///
+/// ```compile_fail
+/// # use tracing_attributes::instrument;
+/// #[instrument]
+/// const fn my_const_function() {}
 /// ```
-/// # use tracing::instrument;
-/// use async_trait::async_trait;
-///
-/// #[async_trait]
-/// pub trait Bar {
-///     async fn bar();
-/// }
-///
-/// #[derive(Debug)]
-/// struct BarImpl(usize);
-///
-/// #[async_trait]
-/// impl Bar for BarImpl {
-///     #[instrument(fields(tmp = std::any::type_name::<Self>()))]
-///     async fn bar() {}
-/// }
-/// ```
-/// 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 @@
     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) {