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