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<LitStr>,
21    target: Option<LitStr>,
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                if args.name.is_some() {
91                    return Err(input.error("expected only a single `name` argument"));
92                }
93                args.name = Some(input.parse()?);
94            } else if lookahead.peek(kw::target) {
95                if args.target.is_some() {
96                    return Err(input.error("expected only a single `target` argument"));
97                }
98                let target = input.parse::<StrArg<kw::target>>()?.value;
99                args.target = Some(target);
100            } else if lookahead.peek(kw::parent) {
101                if args.target.is_some() {
102                    return Err(input.error("expected only a single `parent` argument"));
103                }
104                let parent = input.parse::<ExprArg<kw::parent>>()?;
105                args.parent = Some(parent.value);
106            } else if lookahead.peek(kw::follows_from) {
107                if args.target.is_some() {
108                    return Err(input.error("expected only a single `follows_from` argument"));
109                }
110                let follows_from = input.parse::<ExprArg<kw::follows_from>>()?;
111                args.follows_from = Some(follows_from.value);
112            } else if lookahead.peek(kw::level) {
113                if args.level.is_some() {
114                    return Err(input.error("expected only a single `level` argument"));
115                }
116                args.level = Some(input.parse()?);
117            } else if lookahead.peek(kw::skip) {
118                if !args.skips.is_empty() {
119                    return Err(input.error("expected only a single `skip` argument"));
120                }
121                if args.skip_all {
122                    return Err(input.error("expected either `skip` or `skip_all` argument"));
123                }
124                let Skips(skips) = input.parse()?;
125                args.skips = skips;
126            } else if lookahead.peek(kw::skip_all) {
127                if args.skip_all {
128                    return Err(input.error("expected only a single `skip_all` argument"));
129                }
130                if !args.skips.is_empty() {
131                    return Err(input.error("expected either `skip` or `skip_all` argument"));
132                }
133                let _ = input.parse::<kw::skip_all>()?;
134                args.skip_all = true;
135            } else if lookahead.peek(kw::fields) {
136                if args.fields.is_some() {
137                    return Err(input.error("expected only a single `fields` argument"));
138                }
139                args.fields = Some(input.parse()?);
140            } else if lookahead.peek(kw::err) {
141                let _ = input.parse::<kw::err>();
142                let err_args = EventArgs::parse(input)?;
143                args.err_args = Some(err_args);
144            } else if lookahead.peek(kw::ret) {
145                let _ = input.parse::<kw::ret>()?;
146                let ret_args = EventArgs::parse(input)?;
147                args.ret_args = Some(ret_args);
148            } else if lookahead.peek(Token![,]) {
149                let _ = input.parse::<Token![,]>()?;
150            } else {
151                // We found a token that we didn't expect!
152                // We want to emit warnings for these, rather than errors, so
153                // we'll add it to the list of unrecognized inputs we've seen so
154                // far and keep going.
155                args.parse_warnings.push(lookahead.error());
156                // Parse the unrecognized token tree to advance the parse
157                // stream, and throw it away so we can keep parsing.
158                let _ = input.parse::<proc_macro2::TokenTree>();
159            }
160        }
161        Ok(args)
162    }
163}
164
165impl EventArgs {
166    pub(crate) fn level(&self, default: Level) -> Level {
167        self.level.clone().unwrap_or(default)
168    }
169}
170
171impl Parse for EventArgs {
172    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
173        if !input.peek(syn::token::Paren) {
174            return Ok(Self::default());
175        }
176        let content;
177        let _ = syn::parenthesized!(content in input);
178        let mut result = Self::default();
179        let mut parse_one_arg =
180            || {
181                let lookahead = content.lookahead1();
182                if lookahead.peek(kw::level) {
183                    if result.level.is_some() {
184                        return Err(content.error("expected only a single `level` argument"));
185                    }
186                    result.level = Some(content.parse()?);
187                } else if result.mode != FormatMode::default() {
188                    return Err(content.error("expected only a single format argument"));
189                } else if let Some(ident) = content.parse::<Option<Ident>>()? {
190                    match ident.to_string().as_str() {
191                        "Debug" => result.mode = FormatMode::Debug,
192                        "Display" => result.mode = FormatMode::Display,
193                        _ => return Err(syn::Error::new(
194                            ident.span(),
195                            "unknown event formatting mode, expected either `Debug` or `Display`",
196                        )),
197                    }
198                }
199                Ok(())
200            };
201        parse_one_arg()?;
202        if !content.is_empty() {
203            if content.lookahead1().peek(Token![,]) {
204                let _ = content.parse::<Token![,]>()?;
205                parse_one_arg()?;
206            } else {
207                return Err(content.error("expected `,` or `)`"));
208            }
209        }
210        Ok(result)
211    }
212}
213
214struct StrArg<T> {
215    value: LitStr,
216    _p: std::marker::PhantomData<T>,
217}
218
219impl<T: Parse> Parse for StrArg<T> {
220    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
221        let _ = input.parse::<T>()?;
222        let _ = input.parse::<Token![=]>()?;
223        let value = input.parse()?;
224        Ok(Self {
225            value,
226            _p: std::marker::PhantomData,
227        })
228    }
229}
230
231struct ExprArg<T> {
232    value: Expr,
233    _p: std::marker::PhantomData<T>,
234}
235
236impl<T: Parse> Parse for ExprArg<T> {
237    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
238        let _ = input.parse::<T>()?;
239        let _ = input.parse::<Token![=]>()?;
240        let value = input.parse()?;
241        Ok(Self {
242            value,
243            _p: std::marker::PhantomData,
244        })
245    }
246}
247
248struct Skips(HashSet<Ident>);
249
250impl Parse for Skips {
251    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
252        let _ = input.parse::<kw::skip>();
253        let content;
254        let _ = syn::parenthesized!(content in input);
255        let names = content.parse_terminated(Ident::parse_any, Token![,])?;
256        let mut skips = HashSet::new();
257        for name in names {
258            if skips.contains(&name) {
259                return Err(syn::Error::new(
260                    name.span(),
261                    "tried to skip the same field twice",
262                ));
263            } else {
264                skips.insert(name);
265            }
266        }
267        Ok(Self(skips))
268    }
269}
270
271#[derive(Clone, Debug, Hash, PartialEq, Eq)]
272pub(crate) enum FormatMode {
273    Default,
274    Display,
275    Debug,
276}
277
278impl Default for FormatMode {
279    fn default() -> Self {
280        FormatMode::Default
281    }
282}
283
284#[derive(Clone, Debug)]
285pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>);
286
287#[derive(Clone, Debug)]
288pub(crate) struct Field {
289    pub(crate) name: Punctuated<Ident, Token![.]>,
290    pub(crate) value: Option<Expr>,
291    pub(crate) kind: FieldKind,
292}
293
294#[derive(Clone, Debug, Eq, PartialEq)]
295pub(crate) enum FieldKind {
296    Debug,
297    Display,
298    Value,
299}
300
301impl Parse for Fields {
302    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
303        let _ = input.parse::<kw::fields>();
304        let content;
305        let _ = syn::parenthesized!(content in input);
306        let fields = content.parse_terminated(Field::parse, Token![,])?;
307        Ok(Self(fields))
308    }
309}
310
311impl ToTokens for Fields {
312    fn to_tokens(&self, tokens: &mut TokenStream) {
313        self.0.to_tokens(tokens)
314    }
315}
316
317impl Parse for Field {
318    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
319        let mut kind = FieldKind::Value;
320        if input.peek(Token![%]) {
321            input.parse::<Token![%]>()?;
322            kind = FieldKind::Display;
323        } else if input.peek(Token![?]) {
324            input.parse::<Token![?]>()?;
325            kind = FieldKind::Debug;
326        };
327        let name = Punctuated::parse_separated_nonempty_with(input, Ident::parse_any)?;
328        let value = if input.peek(Token![=]) {
329            input.parse::<Token![=]>()?;
330            if input.peek(Token![%]) {
331                input.parse::<Token![%]>()?;
332                kind = FieldKind::Display;
333            } else if input.peek(Token![?]) {
334                input.parse::<Token![?]>()?;
335                kind = FieldKind::Debug;
336            };
337            Some(input.parse()?)
338        } else {
339            None
340        };
341        Ok(Self { name, value, kind })
342    }
343}
344
345impl ToTokens for Field {
346    fn to_tokens(&self, tokens: &mut TokenStream) {
347        if let Some(ref value) = self.value {
348            let name = &self.name;
349            let kind = &self.kind;
350            tokens.extend(quote! {
351                #name = #kind #value
352            })
353        } else if self.kind == FieldKind::Value {
354            // XXX(eliza): I don't like that fields without values produce
355            // empty fields rather than local variable shorthand...but,
356            // we've released a version where field names without values in
357            // `instrument` produce empty field values, so changing it now
358            // is a breaking change. agh.
359            let name = &self.name;
360            tokens.extend(quote!(#name = tracing::field::Empty))
361        } else {
362            self.kind.to_tokens(tokens);
363            self.name.to_tokens(tokens);
364        }
365    }
366}
367
368impl ToTokens for FieldKind {
369    fn to_tokens(&self, tokens: &mut TokenStream) {
370        match self {
371            FieldKind::Debug => tokens.extend(quote! { ? }),
372            FieldKind::Display => tokens.extend(quote! { % }),
373            _ => {}
374        }
375    }
376}
377
378#[derive(Clone, Debug)]
379pub(crate) enum Level {
380    Trace,
381    Debug,
382    Info,
383    Warn,
384    Error,
385    Path(Path),
386}
387
388impl Parse for Level {
389    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
390        let _ = input.parse::<kw::level>()?;
391        let _ = input.parse::<Token![=]>()?;
392        let lookahead = input.lookahead1();
393        if lookahead.peek(LitStr) {
394            let str: LitStr = input.parse()?;
395            match str.value() {
396                s if s.eq_ignore_ascii_case("trace") => Ok(Level::Trace),
397                s if s.eq_ignore_ascii_case("debug") => Ok(Level::Debug),
398                s if s.eq_ignore_ascii_case("info") => Ok(Level::Info),
399                s if s.eq_ignore_ascii_case("warn") => Ok(Level::Warn),
400                s if s.eq_ignore_ascii_case("error") => Ok(Level::Error),
401                _ => Err(input.error(
402                    "unknown verbosity level, expected one of \"trace\", \
403                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
404                )),
405            }
406        } else if lookahead.peek(LitInt) {
407            fn is_level(lit: &LitInt, expected: u64) -> bool {
408                match lit.base10_parse::<u64>() {
409                    Ok(value) => value == expected,
410                    Err(_) => false,
411                }
412            }
413            let int: LitInt = input.parse()?;
414            match &int {
415                i if is_level(i, 1) => Ok(Level::Trace),
416                i if is_level(i, 2) => Ok(Level::Debug),
417                i if is_level(i, 3) => Ok(Level::Info),
418                i if is_level(i, 4) => Ok(Level::Warn),
419                i if is_level(i, 5) => Ok(Level::Error),
420                _ => Err(input.error(
421                    "unknown verbosity level, expected one of \"trace\", \
422                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
423                )),
424            }
425        } else if lookahead.peek(Ident) {
426            Ok(Self::Path(input.parse()?))
427        } else {
428            Err(lookahead.error())
429        }
430    }
431}
432
433impl ToTokens for Level {
434    fn to_tokens(&self, tokens: &mut TokenStream) {
435        match self {
436            Level::Trace => tokens.extend(quote!(tracing::Level::TRACE)),
437            Level::Debug => tokens.extend(quote!(tracing::Level::DEBUG)),
438            Level::Info => tokens.extend(quote!(tracing::Level::INFO)),
439            Level::Warn => tokens.extend(quote!(tracing::Level::WARN)),
440            Level::Error => tokens.extend(quote!(tracing::Level::ERROR)),
441            Level::Path(ref pat) => tokens.extend(quote!(#pat)),
442        }
443    }
444}
445
446mod kw {
447    syn::custom_keyword!(fields);
448    syn::custom_keyword!(skip);
449    syn::custom_keyword!(skip_all);
450    syn::custom_keyword!(level);
451    syn::custom_keyword!(target);
452    syn::custom_keyword!(parent);
453    syn::custom_keyword!(follows_from);
454    syn::custom_keyword!(name);
455    syn::custom_keyword!(err);
456    syn::custom_keyword!(ret);
457}