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<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 
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                 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 
165 impl EventArgs {
level(&self, default: Level) -> Level166     pub(crate) fn level(&self, default: Level) -> Level {
167         self.level.clone().unwrap_or(default)
168     }
169 }
170 
171 impl Parse for EventArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
214 struct StrArg<T> {
215     value: LitStr,
216     _p: std::marker::PhantomData<T>,
217 }
218 
219 impl<T: Parse> Parse for StrArg<T> {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
231 struct ExprArg<T> {
232     value: Expr,
233     _p: std::marker::PhantomData<T>,
234 }
235 
236 impl<T: Parse> Parse for ExprArg<T> {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
248 struct Skips(HashSet<Ident>);
249 
250 impl Parse for Skips {
parse(input: ParseStream<'_>) -> syn::Result<Self>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)]
272 pub(crate) enum FormatMode {
273     Default,
274     Display,
275     Debug,
276 }
277 
278 impl Default for FormatMode {
default() -> Self279     fn default() -> Self {
280         FormatMode::Default
281     }
282 }
283 
284 #[derive(Clone, Debug)]
285 pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>);
286 
287 #[derive(Clone, Debug)]
288 pub(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)]
295 pub(crate) enum FieldKind {
296     Debug,
297     Display,
298     Value,
299 }
300 
301 impl Parse for Fields {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
311 impl ToTokens for Fields {
to_tokens(&self, tokens: &mut TokenStream)312     fn to_tokens(&self, tokens: &mut TokenStream) {
313         self.0.to_tokens(tokens)
314     }
315 }
316 
317 impl Parse for Field {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
345 impl ToTokens for Field {
to_tokens(&self, tokens: &mut TokenStream)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 
368 impl ToTokens for FieldKind {
to_tokens(&self, tokens: &mut TokenStream)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)]
379 pub(crate) enum Level {
380     Trace,
381     Debug,
382     Info,
383     Warn,
384     Error,
385     Path(Path),
386 }
387 
388 impl Parse for Level {
parse(input: ParseStream<'_>) -> syn::Result<Self>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 
433 impl ToTokens for Level {
to_tokens(&self, tokens: &mut TokenStream)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 
446 mod 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 }
458