• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::collections::HashSet;
2 use syn::{punctuated::Punctuated, Expr, Ident, LitInt, LitStr, Path, Token};
3 
4 use proc_macro2::TokenStream;
5 use quote::{quote, quote_spanned, ToTokens};
6 use syn::ext::IdentExt as _;
7 use 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)]
12 pub(crate) struct EventArgs {
13     level: Option<Level>,
14     pub(crate) mode: FormatMode,
15 }
16 
17 #[derive(Clone, Default, Debug)]
18 pub(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 
33 impl InstrumentArgs {
level(&self) -> Level34     pub(crate) fn level(&self) -> Level {
35         self.level.clone().unwrap_or(Level::Info)
36     }
37 
target(&self) -> impl ToTokens38     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.
warnings(&self) -> impl ToTokens52     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 
75 impl Parse for InstrumentArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
167 impl EventArgs {
level(&self, default: Level) -> Level168     pub(crate) fn level(&self, default: Level) -> Level {
169         self.level.clone().unwrap_or(default)
170     }
171 }
172 
173 impl Parse for EventArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>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)]
217 pub(super) enum LitStrOrIdent {
218     LitStr(LitStr),
219     Ident(Ident),
220 }
221 
222 impl ToTokens for LitStrOrIdent {
to_tokens(&self, tokens: &mut TokenStream)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 
231 impl Parse for LitStrOrIdent {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
240 struct StrArg<T> {
241     value: LitStrOrIdent,
242     _p: std::marker::PhantomData<T>,
243 }
244 
245 impl<T: Parse> Parse for StrArg<T> {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
257 struct ExprArg<T> {
258     value: Expr,
259     _p: std::marker::PhantomData<T>,
260 }
261 
262 impl<T: Parse> Parse for ExprArg<T> {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
274 struct Skips(HashSet<Ident>);
275 
276 impl Parse for Skips {
parse(input: ParseStream<'_>) -> syn::Result<Self>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)]
298 pub(crate) enum FormatMode {
299     #[default]
300     Default,
301     Display,
302     Debug,
303 }
304 
305 #[derive(Clone, Debug)]
306 pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>);
307 
308 #[derive(Clone, Debug)]
309 pub(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)]
316 pub(crate) enum FieldKind {
317     Debug,
318     Display,
319     Value,
320 }
321 
322 impl Parse for Fields {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
332 impl ToTokens for Fields {
to_tokens(&self, tokens: &mut TokenStream)333     fn to_tokens(&self, tokens: &mut TokenStream) {
334         self.0.to_tokens(tokens)
335     }
336 }
337 
338 impl Parse for Field {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
366 impl ToTokens for Field {
to_tokens(&self, tokens: &mut TokenStream)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 
389 impl ToTokens for FieldKind {
to_tokens(&self, tokens: &mut TokenStream)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)]
400 pub(crate) enum Level {
401     Trace,
402     Debug,
403     Info,
404     Warn,
405     Error,
406     Path(Path),
407 }
408 
409 impl Parse for Level {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
454 impl ToTokens for Level {
to_tokens(&self, tokens: &mut TokenStream)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 
467 mod 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 }
479