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: LitStr = 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: Expr = 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: ParseBuffer<'_>;
254 let _ = syn::parenthesized!(content in input);
255 let names: Punctuated = content.parse_terminated(parser:Ident::parse_any, separator:Token![,])?;
256 let mut skips: HashSet = HashSet::new();
257 for name: Ident in names {
258 if skips.contains(&name) {
259 return Err(syn::Error::new(
260 name.span(),
261 message:"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: ParseBuffer<'_>;
305 let _ = syn::parenthesized!(content in input);
306 let fields: Punctuated = content.parse_terminated(parser:Field::parse, separator: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: &Expr) = self.value {
348 let name: &Punctuated = &self.name;
349 let kind: &FieldKind = &self.kind;
350 tokens.extend(iter: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: &Punctuated = &self.name;
360 tokens.extend(iter: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(iter:quote! { ? }),
372 FieldKind::Display => tokens.extend(iter: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(iter:quote!(tracing::Level::TRACE)),
437 Level::Debug => tokens.extend(iter:quote!(tracing::Level::DEBUG)),
438 Level::Info => tokens.extend(iter:quote!(tracing::Level::INFO)),
439 Level::Warn => tokens.extend(iter:quote!(tracing::Level::WARN)),
440 Level::Error => tokens.extend(iter:quote!(tracing::Level::ERROR)),
441 Level::Path(ref pat: &Path) => tokens.extend(iter: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}
458