tracing_attributes/
attr.rs

1use std::collections::HashSet;
2use syn::{punctuated::Punctuated, Expr, Ident, LitInt, LitStr, Path, Token};
3
4use proc_macro2::TokenStream;
5use quote::{quote, quote_spanned, ToTokens};
6use syn::ext::IdentExt as _;
7use syn::parse::{Parse, ParseStream};
8
9/// Arguments to `#[instrument(err(...))]` and `#[instrument(ret(...))]` which describe how the
10/// return value event should be emitted.
11#[derive(Clone, Default, Debug)]
12pub(crate) struct EventArgs {
13    level: Option<Level>,
14    pub(crate) mode: FormatMode,
15}
16
17#[derive(Clone, Default, Debug)]
18pub(crate) struct InstrumentArgs {
19    level: Option<Level>,
20    pub(crate) name: Option<LitStrOrIdent>,
21    target: Option<LitStrOrIdent>,
22    pub(crate) parent: Option<Expr>,
23    pub(crate) follows_from: Option<Expr>,
24    pub(crate) skips: HashSet<Ident>,
25    pub(crate) skip_all: bool,
26    pub(crate) fields: Option<Fields>,
27    pub(crate) err_args: Option<EventArgs>,
28    pub(crate) ret_args: Option<EventArgs>,
29    /// Errors describing any unrecognized parse inputs that we skipped.
30    parse_warnings: Vec<syn::Error>,
31}
32
33impl InstrumentArgs {
34    pub(crate) fn level(&self) -> Level {
35        self.level.clone().unwrap_or(Level::Info)
36    }
37
38    pub(crate) fn target(&self) -> impl ToTokens {
39        if let Some(ref target) = self.target {
40            quote!(#target)
41        } else {
42            quote!(module_path!())
43        }
44    }
45
46    /// Generate "deprecation" warnings for any unrecognized attribute inputs
47    /// that we skipped.
48    ///
49    /// For backwards compatibility, we need to emit compiler warnings rather
50    /// than errors for unrecognized inputs. Generating a fake deprecation is
51    /// the only way to do this on stable Rust right now.
52    pub(crate) fn warnings(&self) -> impl ToTokens {
53        let warnings = self.parse_warnings.iter().map(|err| {
54            let msg = format!("found unrecognized input, {}", err);
55            let msg = LitStr::new(&msg, err.span());
56            // TODO(eliza): This is a bit of a hack, but it's just about the
57            // only way to emit warnings from a proc macro on stable Rust.
58            // Eventually, when the `proc_macro::Diagnostic` API stabilizes, we
59            // should definitely use that instead.
60            quote_spanned! {err.span()=>
61                #[warn(deprecated)]
62                {
63                    #[deprecated(since = "not actually deprecated", note = #msg)]
64                    const TRACING_INSTRUMENT_WARNING: () = ();
65                    let _ = TRACING_INSTRUMENT_WARNING;
66                }
67            }
68        });
69        quote! {
70            { #(#warnings)* }
71        }
72    }
73}
74
75impl Parse for InstrumentArgs {
76    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
77        let mut args = Self::default();
78        while !input.is_empty() {
79            let lookahead = input.lookahead1();
80            if lookahead.peek(kw::name) {
81                if args.name.is_some() {
82                    return Err(input.error("expected only a single `name` argument"));
83                }
84                let name = input.parse::<StrArg<kw::name>>()?.value;
85                args.name = Some(name);
86            } else if lookahead.peek(LitStr) {
87                // XXX: apparently we support names as either named args with an
88                // sign, _or_ as unnamed string literals. That's weird, but
89                // changing it is apparently breaking.
90                // This also means that when using idents for name, it must be via
91                // a named arg, i.e. `#[instrument(name = SOME_IDENT)]`.
92                if args.name.is_some() {
93                    return Err(input.error("expected only a single `name` argument"));
94                }
95                args.name = Some(input.parse()?);
96            } else if lookahead.peek(kw::target) {
97                if args.target.is_some() {
98                    return Err(input.error("expected only a single `target` argument"));
99                }
100                let target = input.parse::<StrArg<kw::target>>()?.value;
101                args.target = Some(target);
102            } else if lookahead.peek(kw::parent) {
103                if args.target.is_some() {
104                    return Err(input.error("expected only a single `parent` argument"));
105                }
106                let parent = input.parse::<ExprArg<kw::parent>>()?;
107                args.parent = Some(parent.value);
108            } else if lookahead.peek(kw::follows_from) {
109                if args.target.is_some() {
110                    return Err(input.error("expected only a single `follows_from` argument"));
111                }
112                let follows_from = input.parse::<ExprArg<kw::follows_from>>()?;
113                args.follows_from = Some(follows_from.value);
114            } else if lookahead.peek(kw::level) {
115                if args.level.is_some() {
116                    return Err(input.error("expected only a single `level` argument"));
117                }
118                args.level = Some(input.parse()?);
119            } else if lookahead.peek(kw::skip) {
120                if !args.skips.is_empty() {
121                    return Err(input.error("expected only a single `skip` argument"));
122                }
123                if args.skip_all {
124                    return Err(input.error("expected either `skip` or `skip_all` argument"));
125                }
126                let Skips(skips) = input.parse()?;
127                args.skips = skips;
128            } else if lookahead.peek(kw::skip_all) {
129                if args.skip_all {
130                    return Err(input.error("expected only a single `skip_all` argument"));
131                }
132                if !args.skips.is_empty() {
133                    return Err(input.error("expected either `skip` or `skip_all` argument"));
134                }
135                let _ = input.parse::<kw::skip_all>()?;
136                args.skip_all = true;
137            } else if lookahead.peek(kw::fields) {
138                if args.fields.is_some() {
139                    return Err(input.error("expected only a single `fields` argument"));
140                }
141                args.fields = Some(input.parse()?);
142            } else if lookahead.peek(kw::err) {
143                let _ = input.parse::<kw::err>();
144                let err_args = EventArgs::parse(input)?;
145                args.err_args = Some(err_args);
146            } else if lookahead.peek(kw::ret) {
147                let _ = input.parse::<kw::ret>()?;
148                let ret_args = EventArgs::parse(input)?;
149                args.ret_args = Some(ret_args);
150            } else if lookahead.peek(Token![,]) {
151                let _ = input.parse::<Token![,]>()?;
152            } else {
153                // We found a token that we didn't expect!
154                // We want to emit warnings for these, rather than errors, so
155                // we'll add it to the list of unrecognized inputs we've seen so
156                // far and keep going.
157                args.parse_warnings.push(lookahead.error());
158                // Parse the unrecognized token tree to advance the parse
159                // stream, and throw it away so we can keep parsing.
160                let _ = input.parse::<proc_macro2::TokenTree>();
161            }
162        }
163        Ok(args)
164    }
165}
166
167impl EventArgs {
168    pub(crate) fn level(&self, default: Level) -> Level {
169        self.level.clone().unwrap_or(default)
170    }
171}
172
173impl Parse for EventArgs {
174    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
175        if !input.peek(syn::token::Paren) {
176            return Ok(Self::default());
177        }
178        let content;
179        let _ = syn::parenthesized!(content in input);
180        let mut result = Self::default();
181        let mut parse_one_arg =
182            || {
183                let lookahead = content.lookahead1();
184                if lookahead.peek(kw::level) {
185                    if result.level.is_some() {
186                        return Err(content.error("expected only a single `level` argument"));
187                    }
188                    result.level = Some(content.parse()?);
189                } else if result.mode != FormatMode::default() {
190                    return Err(content.error("expected only a single format argument"));
191                } else if let Some(ident) = content.parse::<Option<Ident>>()? {
192                    match ident.to_string().as_str() {
193                        "Debug" => result.mode = FormatMode::Debug,
194                        "Display" => result.mode = FormatMode::Display,
195                        _ => return Err(syn::Error::new(
196                            ident.span(),
197                            "unknown event formatting mode, expected either `Debug` or `Display`",
198                        )),
199                    }
200                }
201                Ok(())
202            };
203        parse_one_arg()?;
204        if !content.is_empty() {
205            if content.lookahead1().peek(Token![,]) {
206                let _ = content.parse::<Token![,]>()?;
207                parse_one_arg()?;
208            } else {
209                return Err(content.error("expected `,` or `)`"));
210            }
211        }
212        Ok(result)
213    }
214}
215
216#[derive(Debug, Clone)]
217pub(super) enum LitStrOrIdent {
218    LitStr(LitStr),
219    Ident(Ident),
220}
221
222impl ToTokens for LitStrOrIdent {
223    fn to_tokens(&self, tokens: &mut TokenStream) {
224        match self {
225            LitStrOrIdent::LitStr(target) => target.to_tokens(tokens),
226            LitStrOrIdent::Ident(ident) => ident.to_tokens(tokens),
227        }
228    }
229}
230
231impl Parse for LitStrOrIdent {
232    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
233        input
234            .parse::<LitStr>()
235            .map(LitStrOrIdent::LitStr)
236            .or_else(|_| input.parse::<Ident>().map(LitStrOrIdent::Ident))
237    }
238}
239
240struct StrArg<T> {
241    value: LitStrOrIdent,
242    _p: std::marker::PhantomData<T>,
243}
244
245impl<T: Parse> Parse for StrArg<T> {
246    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
247        let _ = input.parse::<T>()?;
248        let _ = input.parse::<Token![=]>()?;
249        let value = input.parse()?;
250        Ok(Self {
251            value,
252            _p: std::marker::PhantomData,
253        })
254    }
255}
256
257struct ExprArg<T> {
258    value: Expr,
259    _p: std::marker::PhantomData<T>,
260}
261
262impl<T: Parse> Parse for ExprArg<T> {
263    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
264        let _ = input.parse::<T>()?;
265        let _ = input.parse::<Token![=]>()?;
266        let value = input.parse()?;
267        Ok(Self {
268            value,
269            _p: std::marker::PhantomData,
270        })
271    }
272}
273
274struct Skips(HashSet<Ident>);
275
276impl Parse for Skips {
277    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
278        let _ = input.parse::<kw::skip>();
279        let content;
280        let _ = syn::parenthesized!(content in input);
281        let names = content.parse_terminated(Ident::parse_any, Token![,])?;
282        let mut skips = HashSet::new();
283        for name in names {
284            if skips.contains(&name) {
285                return Err(syn::Error::new(
286                    name.span(),
287                    "tried to skip the same field twice",
288                ));
289            } else {
290                skips.insert(name);
291            }
292        }
293        Ok(Self(skips))
294    }
295}
296
297#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
298pub(crate) enum FormatMode {
299    #[default]
300    Default,
301    Display,
302    Debug,
303}
304
305#[derive(Clone, Debug)]
306pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>);
307
308#[derive(Clone, Debug)]
309pub(crate) struct Field {
310    pub(crate) name: Punctuated<Ident, Token![.]>,
311    pub(crate) value: Option<Expr>,
312    pub(crate) kind: FieldKind,
313}
314
315#[derive(Clone, Debug, Eq, PartialEq)]
316pub(crate) enum FieldKind {
317    Debug,
318    Display,
319    Value,
320}
321
322impl Parse for Fields {
323    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
324        let _ = input.parse::<kw::fields>();
325        let content;
326        let _ = syn::parenthesized!(content in input);
327        let fields = content.parse_terminated(Field::parse, Token![,])?;
328        Ok(Self(fields))
329    }
330}
331
332impl ToTokens for Fields {
333    fn to_tokens(&self, tokens: &mut TokenStream) {
334        self.0.to_tokens(tokens)
335    }
336}
337
338impl Parse for Field {
339    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
340        let mut kind = FieldKind::Value;
341        if input.peek(Token![%]) {
342            input.parse::<Token![%]>()?;
343            kind = FieldKind::Display;
344        } else if input.peek(Token![?]) {
345            input.parse::<Token![?]>()?;
346            kind = FieldKind::Debug;
347        };
348        let name = Punctuated::parse_separated_nonempty_with(input, Ident::parse_any)?;
349        let value = if input.peek(Token![=]) {
350            input.parse::<Token![=]>()?;
351            if input.peek(Token![%]) {
352                input.parse::<Token![%]>()?;
353                kind = FieldKind::Display;
354            } else if input.peek(Token![?]) {
355                input.parse::<Token![?]>()?;
356                kind = FieldKind::Debug;
357            };
358            Some(input.parse()?)
359        } else {
360            None
361        };
362        Ok(Self { name, value, kind })
363    }
364}
365
366impl ToTokens for Field {
367    fn to_tokens(&self, tokens: &mut TokenStream) {
368        if let Some(ref value) = self.value {
369            let name = &self.name;
370            let kind = &self.kind;
371            tokens.extend(quote! {
372                #name = #kind #value
373            })
374        } else if self.kind == FieldKind::Value {
375            // XXX(eliza): I don't like that fields without values produce
376            // empty fields rather than local variable shorthand...but,
377            // we've released a version where field names without values in
378            // `instrument` produce empty field values, so changing it now
379            // is a breaking change. agh.
380            let name = &self.name;
381            tokens.extend(quote!(#name = tracing::field::Empty))
382        } else {
383            self.kind.to_tokens(tokens);
384            self.name.to_tokens(tokens);
385        }
386    }
387}
388
389impl ToTokens for FieldKind {
390    fn to_tokens(&self, tokens: &mut TokenStream) {
391        match self {
392            FieldKind::Debug => tokens.extend(quote! { ? }),
393            FieldKind::Display => tokens.extend(quote! { % }),
394            _ => {}
395        }
396    }
397}
398
399#[derive(Clone, Debug)]
400pub(crate) enum Level {
401    Trace,
402    Debug,
403    Info,
404    Warn,
405    Error,
406    Path(Path),
407}
408
409impl Parse for Level {
410    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
411        let _ = input.parse::<kw::level>()?;
412        let _ = input.parse::<Token![=]>()?;
413        let lookahead = input.lookahead1();
414        if lookahead.peek(LitStr) {
415            let str: LitStr = input.parse()?;
416            match str.value() {
417                s if s.eq_ignore_ascii_case("trace") => Ok(Level::Trace),
418                s if s.eq_ignore_ascii_case("debug") => Ok(Level::Debug),
419                s if s.eq_ignore_ascii_case("info") => Ok(Level::Info),
420                s if s.eq_ignore_ascii_case("warn") => Ok(Level::Warn),
421                s if s.eq_ignore_ascii_case("error") => Ok(Level::Error),
422                _ => Err(input.error(
423                    "unknown verbosity level, expected one of \"trace\", \
424                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
425                )),
426            }
427        } else if lookahead.peek(LitInt) {
428            fn is_level(lit: &LitInt, expected: u64) -> bool {
429                match lit.base10_parse::<u64>() {
430                    Ok(value) => value == expected,
431                    Err(_) => false,
432                }
433            }
434            let int: LitInt = input.parse()?;
435            match &int {
436                i if is_level(i, 1) => Ok(Level::Trace),
437                i if is_level(i, 2) => Ok(Level::Debug),
438                i if is_level(i, 3) => Ok(Level::Info),
439                i if is_level(i, 4) => Ok(Level::Warn),
440                i if is_level(i, 5) => Ok(Level::Error),
441                _ => Err(input.error(
442                    "unknown verbosity level, expected one of \"trace\", \
443                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
444                )),
445            }
446        } else if lookahead.peek(Ident) {
447            Ok(Self::Path(input.parse()?))
448        } else {
449            Err(lookahead.error())
450        }
451    }
452}
453
454impl ToTokens for Level {
455    fn to_tokens(&self, tokens: &mut TokenStream) {
456        match self {
457            Level::Trace => tokens.extend(quote!(tracing::Level::TRACE)),
458            Level::Debug => tokens.extend(quote!(tracing::Level::DEBUG)),
459            Level::Info => tokens.extend(quote!(tracing::Level::INFO)),
460            Level::Warn => tokens.extend(quote!(tracing::Level::WARN)),
461            Level::Error => tokens.extend(quote!(tracing::Level::ERROR)),
462            Level::Path(ref pat) => tokens.extend(quote!(#pat)),
463        }
464    }
465}
466
467mod kw {
468    syn::custom_keyword!(fields);
469    syn::custom_keyword!(skip);
470    syn::custom_keyword!(skip_all);
471    syn::custom_keyword!(level);
472    syn::custom_keyword!(target);
473    syn::custom_keyword!(parent);
474    syn::custom_keyword!(follows_from);
475    syn::custom_keyword!(name);
476    syn::custom_keyword!(err);
477    syn::custom_keyword!(ret);
478}