1 //! A procedural macro attribute for instrumenting functions with [`tracing`].
2 //!
3 //! [`tracing`] is a framework for instrumenting Rust programs to collect
4 //! structured, event-based diagnostic information. This crate provides the
5 //! [`#[instrument]`][instrument] procedural macro attribute.
6 //!
7 //! Note that this macro is also re-exported by the main `tracing` crate.
8 //!
9 //! *Compiler support: [requires `rustc` 1.42+][msrv]*
10 //!
11 //! [msrv]: #supported-rust-versions
12 //!
13 //! ## Usage
14 //!
15 //! First, add this to your `Cargo.toml`:
16 //!
17 //! ```toml
18 //! [dependencies]
19 //! tracing-attributes = "0.1.16"
20 //! ```
21 //!
22 //! The [`#[instrument]`][instrument] attribute can now be added to a function
23 //! to automatically create and enter `tracing` [span] when that function is
24 //! called. For example:
25 //!
26 //! ```
27 //! use tracing_attributes::instrument;
28 //!
29 //! #[instrument]
30 //! pub fn my_function(my_arg: usize) {
31 //! // ...
32 //! }
33 //!
34 //! # fn main() {}
35 //! ```
36 //!
37 //! [`tracing`]: https://crates.io/crates/tracing
38 //! [span]: https://docs.rs/tracing/latest/tracing/span/index.html
39 //! [instrument]: attr.instrument.html
40 //!
41 //! ## Supported Rust Versions
42 //!
43 //! Tracing is built against the latest stable release. The minimum supported
44 //! version is 1.42. The current Tracing version is not guaranteed to build on
45 //! Rust versions earlier than the minimum supported version.
46 //!
47 //! Tracing follows the same compiler support policies as the rest of the Tokio
48 //! project. The current stable Rust compiler and the three most recent minor
49 //! versions before it will always be supported. For example, if the current
50 //! stable compiler version is 1.45, the minimum supported version will not be
51 //! increased past 1.42, three minor versions prior. Increasing the minimum
52 //! supported compiler version is not considered a semver breaking change as
53 //! long as doing so complies with this policy.
54 //!
55 #![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.16")]
56 #![doc(
57 html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
58 issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
59 )]
60 #![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
61 #![warn(
62 missing_debug_implementations,
63 missing_docs,
64 rust_2018_idioms,
65 unreachable_pub,
66 bad_style,
67 const_err,
68 dead_code,
69 improper_ctypes,
70 non_shorthand_field_patterns,
71 no_mangle_generic_items,
72 overflowing_literals,
73 path_statements,
74 patterns_in_fns_without_body,
75 private_in_public,
76 unconditional_recursion,
77 unused_allocation,
78 unused_comparisons,
79 unused_parens,
80 while_true
81 )]
82 // TODO: once `tracing` bumps its MSRV to 1.42, remove this allow.
83 #![allow(unused)]
84 extern crate proc_macro;
85
86 use std::collections::{HashMap, HashSet};
87 use std::iter;
88
89 use proc_macro2::TokenStream;
90 use quote::{quote, quote_spanned, ToTokens, TokenStreamExt as _};
91 use syn::ext::IdentExt as _;
92 use syn::parse::{Parse, ParseStream};
93 use syn::{
94 punctuated::Punctuated, spanned::Spanned, Block, Expr, ExprAsync, ExprCall, FieldPat, FnArg,
95 Ident, Item, ItemFn, LitInt, LitStr, Pat, PatIdent, PatReference, PatStruct, PatTuple,
96 PatTupleStruct, PatType, Path, Signature, Stmt, Token, TypePath,
97 };
98 /// Instruments a function to create and enter a `tracing` [span] every time
99 /// the function is called.
100 ///
101 /// Unless overriden, a span with `info` level will be generated.
102 /// The generated span's name will be the name of the function.
103 /// By default, all arguments to the function are included as fields on the
104 /// span. Arguments that are `tracing` [primitive types] implementing the
105 /// [`Value` trait] will be recorded as fields of that type. Types which do
106 /// not implement `Value` will be recorded using [`std::fmt::Debug`].
107 ///
108 /// [primitive types]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html#foreign-impls
109 /// [`Value` trait]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html.
110 ///
111 /// # Overriding Span Attributes
112 ///
113 /// To change the [name] of the generated span, add a `name` argument to the
114 /// `#[instrument]` macro, followed by an equals sign and a string literal. For
115 /// example:
116 ///
117 /// ```
118 /// # use tracing_attributes::instrument;
119 ///
120 /// // The generated span's name will be "my_span" rather than "my_function".
121 /// #[instrument(name = "my_span")]
122 /// pub fn my_function() {
123 /// // ... do something incredibly interesting and important ...
124 /// }
125 /// ```
126 ///
127 /// To override the [target] of the generated span, add a `target` argument to
128 /// the `#[instrument]` macro, followed by an equals sign and a string literal
129 /// for the new target. The [module path] is still recorded separately. For
130 /// example:
131 ///
132 /// ```
133 /// pub mod my_module {
134 /// # use tracing_attributes::instrument;
135 /// // The generated span's target will be "my_crate::some_special_target",
136 /// // rather than "my_crate::my_module".
137 /// #[instrument(target = "my_crate::some_special_target")]
138 /// pub fn my_function() {
139 /// // ... all kinds of neat code in here ...
140 /// }
141 /// }
142 /// ```
143 ///
144 /// Finally, to override the [level] of the generated span, add a `level`
145 /// argument, followed by an equals sign and a string literal with the name of
146 /// the desired level. Level names are not case sensitive. For example:
147 ///
148 /// ```
149 /// # use tracing_attributes::instrument;
150 /// // The span's level will be TRACE rather than INFO.
151 /// #[instrument(level = "trace")]
152 /// pub fn my_function() {
153 /// // ... I have written a truly marvelous implementation of this function,
154 /// // which this example is too narrow to contain ...
155 /// }
156 /// ```
157 ///
158 /// # Skipping Fields
159 ///
160 /// To skip recording one or more arguments to a function or method, pass
161 /// the argument's name inside the `skip()` argument on the `#[instrument]`
162 /// macro. This can be used when an argument to an instrumented function does
163 /// not implement [`fmt::Debug`], or to exclude an argument with a verbose or
164 /// costly `Debug` implementation. Note that:
165 ///
166 /// - multiple argument names can be passed to `skip`.
167 /// - arguments passed to `skip` do _not_ need to implement `fmt::Debug`.
168 ///
169 /// You can also use `skip_all` to skip all arguments.
170 ///
171 /// ## Examples
172 ///
173 /// ```
174 /// # use tracing_attributes::instrument;
175 /// # use std::collections::HashMap;
176 /// // This type doesn't implement `fmt::Debug`!
177 /// struct NonDebug;
178 ///
179 /// // `arg` will be recorded, while `non_debug` will not.
180 /// #[instrument(skip(non_debug))]
181 /// fn my_function(arg: usize, non_debug: NonDebug) {
182 /// // ...
183 /// }
184 ///
185 /// // These arguments are huge
186 /// #[instrument(skip_all)]
187 /// fn my_big_data_function(large: Vec<u8>, also_large: HashMap<String, String>) {
188 /// // ...
189 /// }
190 /// ```
191 ///
192 /// Skipping the `self` parameter:
193 ///
194 /// ```
195 /// # use tracing_attributes::instrument;
196 /// #[derive(Debug)]
197 /// struct MyType {
198 /// data: Vec<u8>, // Suppose this buffer is often quite long...
199 /// }
200 ///
201 /// impl MyType {
202 /// // Suppose we don't want to print an entire kilobyte of `data`
203 /// // every time this is called...
204 /// #[instrument(skip(self))]
205 /// pub fn my_method(&mut self, an_interesting_argument: usize) {
206 /// // ... do something (hopefully, using all that `data`!)
207 /// }
208 /// }
209 /// ```
210 ///
211 /// # Adding Fields
212 ///
213 /// Additional fields (key-value pairs with arbitrary data) may be added to the
214 /// generated span using the `fields` argument on the `#[instrument]` macro. Any
215 /// Rust expression can be used as a field value in this manner. These
216 /// expressions will be evaluated at the beginning of the function's body, so
217 /// arguments to the function may be used in these expressions. Field names may
218 /// also be specified *without* values. Doing so will result in an [empty field]
219 /// whose value may be recorded later within the function body.
220 ///
221 /// This supports the same [field syntax] as the `span!` and `event!` macros.
222 ///
223 /// Note that overlap between the names of fields and (non-skipped) arguments
224 /// will result in a compile error.
225 ///
226 /// ## Examples
227 ///
228 /// Adding a new field based on the value of an argument:
229 ///
230 /// ```
231 /// # use tracing_attributes::instrument;
232 ///
233 /// // This will record a field named "i" with the value of `i` *and* a field
234 /// // named "next" with the value of `i` + 1.
235 /// #[instrument(fields(next = i + 1))]
236 /// pub fn my_function(i: usize) {
237 /// // ...
238 /// }
239 /// ```
240 ///
241 /// Recording specific properties of a struct as their own fields:
242 ///
243 /// ```
244 /// # mod http {
245 /// # pub struct Error;
246 /// # pub struct Response<B> { pub(super) _b: std::marker::PhantomData<B> }
247 /// # pub struct Request<B> { _b: B }
248 /// # impl<B> std::fmt::Debug for Request<B> {
249 /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 /// # f.pad("request")
251 /// # }
252 /// # }
253 /// # impl<B> Request<B> {
254 /// # pub fn uri(&self) -> &str { "fake" }
255 /// # pub fn method(&self) -> &str { "GET" }
256 /// # }
257 /// # }
258 /// # use tracing_attributes::instrument;
259 ///
260 /// // This will record the request's URI and HTTP method as their own separate
261 /// // fields.
262 /// #[instrument(fields(http.uri = req.uri(), http.method = req.method()))]
263 /// pub fn handle_request<B>(req: http::Request<B>) -> http::Response<B> {
264 /// // ... handle the request ...
265 /// # http::Response { _b: std::marker::PhantomData }
266 /// }
267 /// ```
268 ///
269 /// This can be used in conjunction with `skip` or `skip_all` to record only
270 /// some fields of a struct:
271 /// ```
272 /// # use tracing_attributes::instrument;
273 /// // Remember the struct with the very large `data` field from the earlier
274 /// // example? Now it also has a `name`, which we might want to include in
275 /// // our span.
276 /// #[derive(Debug)]
277 /// struct MyType {
278 /// name: &'static str,
279 /// data: Vec<u8>,
280 /// }
281 ///
282 /// impl MyType {
283 /// // This will skip the `data` field, but will include `self.name`,
284 /// // formatted using `fmt::Display`.
285 /// #[instrument(skip(self), fields(self.name = %self.name))]
286 /// pub fn my_method(&mut self, an_interesting_argument: usize) {
287 /// // ... do something (hopefully, using all that `data`!)
288 /// }
289 /// }
290 /// ```
291 ///
292 /// Adding an empty field to be recorded later:
293 ///
294 /// ```
295 /// # use tracing_attributes::instrument;
296 ///
297 /// // This function does a very interesting and important mathematical calculation.
298 /// // Suppose we want to record both the inputs to the calculation *and* its result...
299 /// #[instrument(fields(result))]
300 /// pub fn do_calculation(input_1: usize, input_2: usize) -> usize {
301 /// // Rerform the calculation.
302 /// let result = input_1 + input_2;
303 ///
304 /// // Record the result as part of the current span.
305 /// tracing::Span::current().record("result", &result);
306 ///
307 /// // Now, the result will also be included on this event!
308 /// tracing::info!("calculation complete!");
309 ///
310 /// // ... etc ...
311 /// # 0
312 /// }
313 /// ```
314 ///
315 /// # Examples
316 ///
317 /// Instrumenting a function:
318 ///
319 /// ```
320 /// # use tracing_attributes::instrument;
321 /// #[instrument]
322 /// pub fn my_function(my_arg: usize) {
323 /// // This event will be recorded inside a span named `my_function` with the
324 /// // field `my_arg`.
325 /// tracing::info!("inside my_function!");
326 /// // ...
327 /// }
328 /// ```
329 /// Setting the level for the generated span:
330 /// ```
331 /// # use tracing_attributes::instrument;
332 /// #[instrument(level = "debug")]
333 /// pub fn my_function() {
334 /// // ...
335 /// }
336 /// ```
337 /// Overriding the generated span's name:
338 /// ```
339 /// # use tracing_attributes::instrument;
340 /// #[instrument(name = "my_name")]
341 /// pub fn my_function() {
342 /// // ...
343 /// }
344 /// ```
345 /// Overriding the generated span's target:
346 /// ```
347 /// # use tracing_attributes::instrument;
348 /// #[instrument(target = "my_target")]
349 /// pub fn my_function() {
350 /// // ...
351 /// }
352 /// ```
353 ///
354 /// To skip recording an argument, pass the argument's name to the `skip`:
355 ///
356 /// ```
357 /// # use tracing_attributes::instrument;
358 /// struct NonDebug;
359 ///
360 /// #[instrument(skip(non_debug))]
361 /// fn my_function(arg: usize, non_debug: NonDebug) {
362 /// // ...
363 /// }
364 /// ```
365 ///
366 /// To add an additional context to the span, pass key-value pairs to `fields`:
367 ///
368 /// ```
369 /// # use tracing_attributes::instrument;
370 /// #[instrument(fields(foo="bar", id=1, show=true))]
371 /// fn my_function(arg: usize) {
372 /// // ...
373 /// }
374 /// ```
375 ///
376 /// If the function returns a `Result<T, E>` and `E` implements `std::fmt::Display`, you can add
377 /// `err` to emit error events when the function returns `Err`:
378 ///
379 /// ```
380 /// # use tracing_attributes::instrument;
381 /// #[instrument(err)]
382 /// fn my_function(arg: usize) -> Result<(), std::io::Error> {
383 /// Ok(())
384 /// }
385 /// ```
386 ///
387 /// `async fn`s may also be instrumented:
388 ///
389 /// ```
390 /// # use tracing_attributes::instrument;
391 /// #[instrument]
392 /// pub async fn my_function() -> Result<(), ()> {
393 /// // ...
394 /// # Ok(())
395 /// }
396 /// ```
397 ///
398 /// It also works with [async-trait](https://crates.io/crates/async-trait)
399 /// (a crate that allows defining async functions in traits,
400 /// something not currently possible in Rust),
401 /// and hopefully most libraries that exhibit similar behaviors:
402 ///
403 /// ```
404 /// # use tracing::instrument;
405 /// use async_trait::async_trait;
406 ///
407 /// #[async_trait]
408 /// pub trait Foo {
409 /// async fn foo(&self, arg: usize);
410 /// }
411 ///
412 /// #[derive(Debug)]
413 /// struct FooImpl(usize);
414 ///
415 /// #[async_trait]
416 /// impl Foo for FooImpl {
417 /// #[instrument(fields(value = self.0, tmp = std::any::type_name::<Self>()))]
418 /// async fn foo(&self, arg: usize) {}
419 /// }
420 /// ```
421 ///
422 /// Note than on `async-trait` <= 0.1.43, references to the `Self`
423 /// type inside the `fields` argument were only allowed when the instrumented
424 /// function is a method (i.e., the function receives `self` as an argument).
425 /// For example, this *used to not work* because the instrument function
426 /// didn't receive `self`:
427 /// ```
428 /// # use tracing::instrument;
429 /// use async_trait::async_trait;
430 ///
431 /// #[async_trait]
432 /// pub trait Bar {
433 /// async fn bar();
434 /// }
435 ///
436 /// #[derive(Debug)]
437 /// struct BarImpl(usize);
438 ///
439 /// #[async_trait]
440 /// impl Bar for BarImpl {
441 /// #[instrument(fields(tmp = std::any::type_name::<Self>()))]
442 /// async fn bar() {}
443 /// }
444 /// ```
445 /// Instead, you should manually rewrite any `Self` types as the type for
446 /// which you implement the trait: `#[instrument(fields(tmp = std::any::type_name::<Bar>()))]`
447 /// (or maybe you can just bump `async-trait`).
448 ///
449 /// [span]: https://docs.rs/tracing/latest/tracing/span/index.html
450 /// [name]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.name
451 /// [target]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.target
452 /// [level]: https://docs.rs/tracing/latest/tracing/struct.Level.html
453 /// [module path]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.module_path
454 /// [`INFO`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.INFO
455 /// [empty field]: https://docs.rs/tracing/latest/tracing/field/struct.Empty.html
456 /// [field syntax]: https://docs.rs/tracing/latest/tracing/#recording-fields
457 /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html
458 #[proc_macro_attribute]
instrument( args: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream459 pub fn instrument(
460 args: proc_macro::TokenStream,
461 item: proc_macro::TokenStream,
462 ) -> proc_macro::TokenStream {
463 let input = syn::parse_macro_input!(item as ItemFn);
464 let args = syn::parse_macro_input!(args as InstrumentArgs);
465
466 let instrumented_function_name = input.sig.ident.to_string();
467
468 // check for async_trait-like patterns in the block, and instrument
469 // the future instead of the wrapper
470 if let Some(internal_fun) = get_async_trait_info(&input.block, input.sig.asyncness.is_some()) {
471 // let's rewrite some statements!
472 let mut out_stmts: Vec<TokenStream> = input
473 .block
474 .stmts
475 .iter()
476 .map(|stmt| stmt.to_token_stream())
477 .collect();
478
479 if let Some((iter, _stmt)) = input
480 .block
481 .stmts
482 .iter()
483 .enumerate()
484 .find(|(_iter, stmt)| *stmt == internal_fun.source_stmt)
485 {
486 // instrument the future by rewriting the corresponding statement
487 out_stmts[iter] = match internal_fun.kind {
488 // async-trait <= 0.1.43
489 AsyncTraitKind::Function(fun) => gen_function(
490 fun,
491 args,
492 instrumented_function_name.as_str(),
493 internal_fun.self_type.as_ref(),
494 ),
495 // async-trait >= 0.1.44
496 AsyncTraitKind::Async(async_expr) => {
497 let instrumented_block = gen_block(
498 &async_expr.block,
499 &input.sig.inputs,
500 true,
501 args,
502 instrumented_function_name.as_str(),
503 None,
504 );
505 let async_attrs = &async_expr.attrs;
506 quote! {
507 Box::pin(#(#async_attrs) * async move { #instrumented_block })
508 }
509 }
510 };
511 }
512
513 let vis = &input.vis;
514 let sig = &input.sig;
515 let attrs = &input.attrs;
516 quote!(
517 #(#attrs) *
518 #vis #sig {
519 #(#out_stmts) *
520 }
521 )
522 .into()
523 } else {
524 gen_function(&input, args, instrumented_function_name.as_str(), None).into()
525 }
526 }
527
528 /// Given an existing function, generate an instrumented version of that function
gen_function( input: &ItemFn, args: InstrumentArgs, instrumented_function_name: &str, self_type: Option<&syn::TypePath>, ) -> proc_macro2::TokenStream529 fn gen_function(
530 input: &ItemFn,
531 args: InstrumentArgs,
532 instrumented_function_name: &str,
533 self_type: Option<&syn::TypePath>,
534 ) -> proc_macro2::TokenStream {
535 // these are needed ahead of time, as ItemFn contains the function body _and_
536 // isn't representable inside a quote!/quote_spanned! macro
537 // (Syn's ToTokens isn't implemented for ItemFn)
538 let ItemFn {
539 attrs,
540 vis,
541 block,
542 sig,
543 ..
544 } = input;
545
546 let Signature {
547 output: return_type,
548 inputs: params,
549 unsafety,
550 asyncness,
551 constness,
552 abi,
553 ident,
554 generics:
555 syn::Generics {
556 params: gen_params,
557 where_clause,
558 ..
559 },
560 ..
561 } = sig;
562
563 let warnings = args.warnings();
564
565 let body = gen_block(
566 block,
567 params,
568 asyncness.is_some(),
569 args,
570 instrumented_function_name,
571 self_type,
572 );
573
574 quote!(
575 #(#attrs) *
576 #vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #return_type
577 #where_clause
578 {
579 #warnings
580 #body
581 }
582 )
583 }
584
585 /// Instrument a block
gen_block( block: &Block, params: &Punctuated<FnArg, Token![,]>, async_context: bool, mut args: InstrumentArgs, instrumented_function_name: &str, self_type: Option<&syn::TypePath>, ) -> proc_macro2::TokenStream586 fn gen_block(
587 block: &Block,
588 params: &Punctuated<FnArg, Token![,]>,
589 async_context: bool,
590 mut args: InstrumentArgs,
591 instrumented_function_name: &str,
592 self_type: Option<&syn::TypePath>,
593 ) -> proc_macro2::TokenStream {
594 let err = args.err;
595
596 // generate the span's name
597 let span_name = args
598 // did the user override the span's name?
599 .name
600 .as_ref()
601 .map(|name| quote!(#name))
602 .unwrap_or_else(|| quote!(#instrumented_function_name));
603
604 // generate this inside a closure, so we can return early on errors.
605 let span = (|| {
606 // Pull out the arguments-to-be-skipped first, so we can filter results
607 // below.
608 let param_names: Vec<(Ident, (Ident, RecordType))> = params
609 .clone()
610 .into_iter()
611 .flat_map(|param| match param {
612 FnArg::Typed(PatType { pat, ty, .. }) => {
613 param_names(*pat, RecordType::parse_from_ty(&*ty))
614 }
615 FnArg::Receiver(_) => Box::new(iter::once((
616 Ident::new("self", param.span()),
617 RecordType::Debug,
618 ))),
619 })
620 // Little dance with new (user-exposed) names and old (internal)
621 // names of identifiers. That way, we could do the following
622 // even though async_trait (<=0.1.43) rewrites "self" as "_self":
623 // ```
624 // #[async_trait]
625 // impl Foo for FooImpl {
626 // #[instrument(skip(self))]
627 // async fn foo(&self, v: usize) {}
628 // }
629 // ```
630 .map(|(x, record_type)| {
631 // if we are inside a function generated by async-trait <=0.1.43, we need to
632 // take care to rewrite "_self" as "self" for 'user convenience'
633 if self_type.is_some() && x == "_self" {
634 (Ident::new("self", x.span()), (x, record_type))
635 } else {
636 (x.clone(), (x, record_type))
637 }
638 })
639 .collect();
640
641 for skip in &args.skips {
642 if !param_names.iter().map(|(user, _)| user).any(|y| y == skip) {
643 return quote_spanned! {skip.span()=>
644 compile_error!("attempting to skip non-existent parameter")
645 };
646 }
647 }
648
649 let level = args.level();
650 let target = args.target();
651
652 // filter out skipped fields
653 let quoted_fields: Vec<_> = param_names
654 .iter()
655 .filter(|(param, _)| {
656 if args.skip_all || args.skips.contains(param) {
657 return false;
658 }
659
660 // If any parameters have the same name as a custom field, skip
661 // and allow them to be formatted by the custom field.
662 if let Some(ref fields) = args.fields {
663 fields.0.iter().all(|Field { ref name, .. }| {
664 let first = name.first();
665 first != name.last() || !first.iter().any(|name| name == ¶m)
666 })
667 } else {
668 true
669 }
670 })
671 .map(|(user_name, (real_name, record_type))| match record_type {
672 RecordType::Value => quote!(#user_name = #real_name),
673 RecordType::Debug => quote!(#user_name = tracing::field::debug(&#real_name)),
674 })
675 .collect();
676
677 // replace every use of a variable with its original name
678 if let Some(Fields(ref mut fields)) = args.fields {
679 let mut replacer = IdentAndTypesRenamer {
680 idents: param_names.into_iter().map(|(a, (b, _))| (a, b)).collect(),
681 types: Vec::new(),
682 };
683
684 // when async-trait <=0.1.43 is in use, replace instances
685 // of the "Self" type inside the fields values
686 if let Some(self_type) = self_type {
687 replacer.types.push(("Self", self_type.clone()));
688 }
689
690 for e in fields.iter_mut().filter_map(|f| f.value.as_mut()) {
691 syn::visit_mut::visit_expr_mut(&mut replacer, e);
692 }
693 }
694
695 let custom_fields = &args.fields;
696
697 quote!(tracing::span!(
698 target: #target,
699 #level,
700 #span_name,
701 #(#quoted_fields,)*
702 #custom_fields
703
704 ))
705 })();
706
707 // Generate the instrumented function body.
708 // If the function is an `async fn`, this will wrap it in an async block,
709 // which is `instrument`ed using `tracing-futures`. Otherwise, this will
710 // enter the span and then perform the rest of the body.
711 // If `err` is in args, instrument any resulting `Err`s.
712 if async_context {
713 if err {
714 quote_spanned!(block.span()=>
715 let __tracing_attr_span = #span;
716 tracing::Instrument::instrument(async move {
717 match async move { #block }.await {
718 #[allow(clippy::unit_arg)]
719 Ok(x) => Ok(x),
720 Err(e) => {
721 tracing::error!(error = %e);
722 Err(e)
723 }
724 }
725 }, __tracing_attr_span).await
726 )
727 } else {
728 quote_spanned!(block.span()=>
729 let __tracing_attr_span = #span;
730 tracing::Instrument::instrument(
731 async move { #block },
732 __tracing_attr_span
733 )
734 .await
735 )
736 }
737 } else if err {
738 quote_spanned!(block.span()=>
739 let __tracing_attr_span = #span;
740 let __tracing_attr_guard = __tracing_attr_span.enter();
741 #[allow(clippy::redundant_closure_call)]
742 match (move || #block)() {
743 #[allow(clippy::unit_arg)]
744 Ok(x) => Ok(x),
745 Err(e) => {
746 tracing::error!(error = %e);
747 Err(e)
748 }
749 }
750 )
751 } else {
752 quote_spanned!(block.span()=>
753 let __tracing_attr_span = #span;
754 let __tracing_attr_guard = __tracing_attr_span.enter();
755 #block
756 )
757 }
758 }
759
760 #[derive(Default, Debug)]
761 struct InstrumentArgs {
762 level: Option<Level>,
763 name: Option<LitStr>,
764 target: Option<LitStr>,
765 skips: HashSet<Ident>,
766 skip_all: bool,
767 fields: Option<Fields>,
768 err: bool,
769 /// Errors describing any unrecognized parse inputs that we skipped.
770 parse_warnings: Vec<syn::Error>,
771 }
772
773 impl InstrumentArgs {
level(&self) -> impl ToTokens774 fn level(&self) -> impl ToTokens {
775 fn is_level(lit: &LitInt, expected: u64) -> bool {
776 match lit.base10_parse::<u64>() {
777 Ok(value) => value == expected,
778 Err(_) => false,
779 }
780 }
781
782 match &self.level {
783 Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("trace") => {
784 quote!(tracing::Level::TRACE)
785 }
786 Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("debug") => {
787 quote!(tracing::Level::DEBUG)
788 }
789 Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("info") => {
790 quote!(tracing::Level::INFO)
791 }
792 Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("warn") => {
793 quote!(tracing::Level::WARN)
794 }
795 Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("error") => {
796 quote!(tracing::Level::ERROR)
797 }
798 Some(Level::Int(ref lit)) if is_level(lit, 1) => quote!(tracing::Level::TRACE),
799 Some(Level::Int(ref lit)) if is_level(lit, 2) => quote!(tracing::Level::DEBUG),
800 Some(Level::Int(ref lit)) if is_level(lit, 3) => quote!(tracing::Level::INFO),
801 Some(Level::Int(ref lit)) if is_level(lit, 4) => quote!(tracing::Level::WARN),
802 Some(Level::Int(ref lit)) if is_level(lit, 5) => quote!(tracing::Level::ERROR),
803 Some(Level::Path(ref pat)) => quote!(#pat),
804 Some(lit) => quote! {
805 compile_error!(
806 "unknown verbosity level, expected one of \"trace\", \
807 \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5"
808 )
809 },
810 None => quote!(tracing::Level::INFO),
811 }
812 }
813
target(&self) -> impl ToTokens814 fn target(&self) -> impl ToTokens {
815 if let Some(ref target) = self.target {
816 quote!(#target)
817 } else {
818 quote!(module_path!())
819 }
820 }
821
822 /// Generate "deprecation" warnings for any unrecognized attribute inputs
823 /// that we skipped.
824 ///
825 /// For backwards compatibility, we need to emit compiler warnings rather
826 /// than errors for unrecognized inputs. Generating a fake deprecation is
827 /// the only way to do this on stable Rust right now.
warnings(&self) -> impl ToTokens828 fn warnings(&self) -> impl ToTokens {
829 let warnings = self.parse_warnings.iter().map(|err| {
830 let msg = format!("found unrecognized input, {}", err);
831 let msg = LitStr::new(&msg, err.span());
832 // TODO(eliza): This is a bit of a hack, but it's just about the
833 // only way to emit warnings from a proc macro on stable Rust.
834 // Eventually, when the `proc_macro::Diagnostic` API stabilizes, we
835 // should definitely use that instead.
836 quote_spanned! {err.span()=>
837 #[warn(deprecated)]
838 {
839 #[deprecated(since = "not actually deprecated", note = #msg)]
840 const TRACING_INSTRUMENT_WARNING: () = ();
841 let _ = TRACING_INSTRUMENT_WARNING;
842 }
843 }
844 });
845 quote! {
846 { #(#warnings)* }
847 }
848 }
849 }
850
851 impl Parse for InstrumentArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>852 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
853 let mut args = Self::default();
854 while !input.is_empty() {
855 let lookahead = input.lookahead1();
856 if lookahead.peek(kw::name) {
857 if args.name.is_some() {
858 return Err(input.error("expected only a single `name` argument"));
859 }
860 let name = input.parse::<StrArg<kw::name>>()?.value;
861 args.name = Some(name);
862 } else if lookahead.peek(LitStr) {
863 // XXX: apparently we support names as either named args with an
864 // sign, _or_ as unnamed string literals. That's weird, but
865 // changing it is apparently breaking.
866 if args.name.is_some() {
867 return Err(input.error("expected only a single `name` argument"));
868 }
869 args.name = Some(input.parse()?);
870 } else if lookahead.peek(kw::target) {
871 if args.target.is_some() {
872 return Err(input.error("expected only a single `target` argument"));
873 }
874 let target = input.parse::<StrArg<kw::target>>()?.value;
875 args.target = Some(target);
876 } else if lookahead.peek(kw::level) {
877 if args.level.is_some() {
878 return Err(input.error("expected only a single `level` argument"));
879 }
880 args.level = Some(input.parse()?);
881 } else if lookahead.peek(kw::skip) {
882 if !args.skips.is_empty() {
883 return Err(input.error("expected only a single `skip` argument"));
884 }
885 if args.skip_all {
886 return Err(input.error("expected either `skip` or `skip_all` argument"));
887 }
888 let Skips(skips) = input.parse()?;
889 args.skips = skips;
890 } else if lookahead.peek(kw::skip_all) {
891 if args.skip_all {
892 return Err(input.error("expected only a single `skip_all` argument"));
893 }
894 if !args.skips.is_empty() {
895 return Err(input.error("expected either `skip` or `skip_all` argument"));
896 }
897 let _ = input.parse::<kw::skip_all>()?;
898 args.skip_all = true;
899 } else if lookahead.peek(kw::fields) {
900 if args.fields.is_some() {
901 return Err(input.error("expected only a single `fields` argument"));
902 }
903 args.fields = Some(input.parse()?);
904 } else if lookahead.peek(kw::err) {
905 let _ = input.parse::<kw::err>()?;
906 args.err = true;
907 } else if lookahead.peek(Token![,]) {
908 let _ = input.parse::<Token![,]>()?;
909 } else {
910 // We found a token that we didn't expect!
911 // We want to emit warnings for these, rather than errors, so
912 // we'll add it to the list of unrecognized inputs we've seen so
913 // far and keep going.
914 args.parse_warnings.push(lookahead.error());
915 // Parse the unrecognized token tree to advance the parse
916 // stream, and throw it away so we can keep parsing.
917 let _ = input.parse::<proc_macro2::TokenTree>();
918 }
919 }
920 Ok(args)
921 }
922 }
923
924 struct StrArg<T> {
925 value: LitStr,
926 _p: std::marker::PhantomData<T>,
927 }
928
929 impl<T: Parse> Parse for StrArg<T> {
parse(input: ParseStream<'_>) -> syn::Result<Self>930 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
931 let _ = input.parse::<T>()?;
932 let _ = input.parse::<Token![=]>()?;
933 let value = input.parse()?;
934 Ok(Self {
935 value,
936 _p: std::marker::PhantomData,
937 })
938 }
939 }
940
941 struct Skips(HashSet<Ident>);
942
943 impl Parse for Skips {
parse(input: ParseStream<'_>) -> syn::Result<Self>944 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
945 let _ = input.parse::<kw::skip>();
946 let content;
947 let _ = syn::parenthesized!(content in input);
948 let names: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse_any)?;
949 let mut skips = HashSet::new();
950 for name in names {
951 if skips.contains(&name) {
952 return Err(syn::Error::new(
953 name.span(),
954 "tried to skip the same field twice",
955 ));
956 } else {
957 skips.insert(name);
958 }
959 }
960 Ok(Self(skips))
961 }
962 }
963
964 #[derive(Debug)]
965 struct Fields(Punctuated<Field, Token![,]>);
966
967 #[derive(Debug)]
968 struct Field {
969 name: Punctuated<Ident, Token![.]>,
970 value: Option<Expr>,
971 kind: FieldKind,
972 }
973
974 #[derive(Debug, Eq, PartialEq)]
975 enum FieldKind {
976 Debug,
977 Display,
978 Value,
979 }
980
981 impl Parse for Fields {
parse(input: ParseStream<'_>) -> syn::Result<Self>982 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
983 let _ = input.parse::<kw::fields>();
984 let content;
985 let _ = syn::parenthesized!(content in input);
986 let fields: Punctuated<_, Token![,]> = content.parse_terminated(Field::parse)?;
987 Ok(Self(fields))
988 }
989 }
990
991 impl ToTokens for Fields {
to_tokens(&self, tokens: &mut TokenStream)992 fn to_tokens(&self, tokens: &mut TokenStream) {
993 self.0.to_tokens(tokens)
994 }
995 }
996
997 impl Parse for Field {
parse(input: ParseStream<'_>) -> syn::Result<Self>998 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
999 let mut kind = FieldKind::Value;
1000 if input.peek(Token![%]) {
1001 input.parse::<Token![%]>()?;
1002 kind = FieldKind::Display;
1003 } else if input.peek(Token![?]) {
1004 input.parse::<Token![?]>()?;
1005 kind = FieldKind::Debug;
1006 };
1007 let name = Punctuated::parse_separated_nonempty_with(input, Ident::parse_any)?;
1008 let value = if input.peek(Token![=]) {
1009 input.parse::<Token![=]>()?;
1010 if input.peek(Token![%]) {
1011 input.parse::<Token![%]>()?;
1012 kind = FieldKind::Display;
1013 } else if input.peek(Token![?]) {
1014 input.parse::<Token![?]>()?;
1015 kind = FieldKind::Debug;
1016 };
1017 Some(input.parse()?)
1018 } else {
1019 None
1020 };
1021 Ok(Self { name, value, kind })
1022 }
1023 }
1024
1025 impl ToTokens for Field {
to_tokens(&self, tokens: &mut TokenStream)1026 fn to_tokens(&self, tokens: &mut TokenStream) {
1027 if let Some(ref value) = self.value {
1028 let name = &self.name;
1029 let kind = &self.kind;
1030 tokens.extend(quote! {
1031 #name = #kind#value
1032 })
1033 } else if self.kind == FieldKind::Value {
1034 // XXX(eliza): I don't like that fields without values produce
1035 // empty fields rather than local variable shorthand...but,
1036 // we've released a version where field names without values in
1037 // `instrument` produce empty field values, so changing it now
1038 // is a breaking change. agh.
1039 let name = &self.name;
1040 tokens.extend(quote!(#name = tracing::field::Empty))
1041 } else {
1042 self.kind.to_tokens(tokens);
1043 self.name.to_tokens(tokens);
1044 }
1045 }
1046 }
1047
1048 impl ToTokens for FieldKind {
to_tokens(&self, tokens: &mut TokenStream)1049 fn to_tokens(&self, tokens: &mut TokenStream) {
1050 match self {
1051 FieldKind::Debug => tokens.extend(quote! { ? }),
1052 FieldKind::Display => tokens.extend(quote! { % }),
1053 _ => {}
1054 }
1055 }
1056 }
1057
1058 #[derive(Debug)]
1059 enum Level {
1060 Str(LitStr),
1061 Int(LitInt),
1062 Path(Path),
1063 }
1064
1065 impl Parse for Level {
parse(input: ParseStream<'_>) -> syn::Result<Self>1066 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1067 let _ = input.parse::<kw::level>()?;
1068 let _ = input.parse::<Token![=]>()?;
1069 let lookahead = input.lookahead1();
1070 if lookahead.peek(LitStr) {
1071 Ok(Self::Str(input.parse()?))
1072 } else if lookahead.peek(LitInt) {
1073 Ok(Self::Int(input.parse()?))
1074 } else if lookahead.peek(Ident) {
1075 Ok(Self::Path(input.parse()?))
1076 } else {
1077 Err(lookahead.error())
1078 }
1079 }
1080 }
1081
1082 /// Indicates whether a field should be recorded as `Value` or `Debug`.
1083 enum RecordType {
1084 /// The field should be recorded using its `Value` implementation.
1085 Value,
1086 /// The field should be recorded using `tracing::field::debug()`.
1087 Debug,
1088 }
1089
1090 impl RecordType {
1091 /// Array of primitive types which should be recorded as [RecordType::Value].
1092 const TYPES_FOR_VALUE: &'static [&'static str] = &[
1093 "bool",
1094 "str",
1095 "u8",
1096 "i8",
1097 "u16",
1098 "i16",
1099 "u32",
1100 "i32",
1101 "u64",
1102 "i64",
1103 "f32",
1104 "f64",
1105 "usize",
1106 "isize",
1107 "NonZeroU8",
1108 "NonZeroI8",
1109 "NonZeroU16",
1110 "NonZeroI16",
1111 "NonZeroU32",
1112 "NonZeroI32",
1113 "NonZeroU64",
1114 "NonZeroI64",
1115 "NonZeroUsize",
1116 "NonZeroIsize",
1117 "Wrapping",
1118 ];
1119
1120 /// Parse `RecordType` from [syn::Type] by looking up
1121 /// the [RecordType::TYPES_FOR_VALUE] array.
parse_from_ty(ty: &syn::Type) -> Self1122 fn parse_from_ty(ty: &syn::Type) -> Self {
1123 match ty {
1124 syn::Type::Path(syn::TypePath { path, .. })
1125 if path
1126 .segments
1127 .iter()
1128 .last()
1129 .map(|path_segment| {
1130 let ident = path_segment.ident.to_string();
1131 Self::TYPES_FOR_VALUE.iter().any(|&t| t == ident)
1132 })
1133 .unwrap_or(false) =>
1134 {
1135 RecordType::Value
1136 }
1137 syn::Type::Reference(syn::TypeReference { elem, .. }) => {
1138 RecordType::parse_from_ty(&*elem)
1139 }
1140 _ => RecordType::Debug,
1141 }
1142 }
1143 }
1144
param_names(pat: Pat, record_type: RecordType) -> Box<dyn Iterator<Item = (Ident, RecordType)>>1145 fn param_names(pat: Pat, record_type: RecordType) -> Box<dyn Iterator<Item = (Ident, RecordType)>> {
1146 match pat {
1147 Pat::Ident(PatIdent { ident, .. }) => Box::new(iter::once((ident, record_type))),
1148 Pat::Reference(PatReference { pat, .. }) => param_names(*pat, record_type),
1149 // We can't get the concrete type of fields in the struct/tuple
1150 // patterns by using `syn`. e.g. `fn foo(Foo { x, y }: Foo) {}`.
1151 // Therefore, the struct/tuple patterns in the arguments will just
1152 // always be recorded as `RecordType::Debug`.
1153 Pat::Struct(PatStruct { fields, .. }) => Box::new(
1154 fields
1155 .into_iter()
1156 .flat_map(|FieldPat { pat, .. }| param_names(*pat, RecordType::Debug)),
1157 ),
1158 Pat::Tuple(PatTuple { elems, .. }) => Box::new(
1159 elems
1160 .into_iter()
1161 .flat_map(|p| param_names(p, RecordType::Debug)),
1162 ),
1163 Pat::TupleStruct(PatTupleStruct {
1164 pat: PatTuple { elems, .. },
1165 ..
1166 }) => Box::new(
1167 elems
1168 .into_iter()
1169 .flat_map(|p| param_names(p, RecordType::Debug)),
1170 ),
1171
1172 // The above *should* cover all cases of irrefutable patterns,
1173 // but we purposefully don't do any funny business here
1174 // (such as panicking) because that would obscure rustc's
1175 // much more informative error message.
1176 _ => Box::new(iter::empty()),
1177 }
1178 }
1179
1180 mod kw {
1181 syn::custom_keyword!(fields);
1182 syn::custom_keyword!(skip);
1183 syn::custom_keyword!(skip_all);
1184 syn::custom_keyword!(level);
1185 syn::custom_keyword!(target);
1186 syn::custom_keyword!(name);
1187 syn::custom_keyword!(err);
1188 }
1189
1190 enum AsyncTraitKind<'a> {
1191 // old construction. Contains the function
1192 Function(&'a ItemFn),
1193 // new construction. Contains a reference to the async block
1194 Async(&'a ExprAsync),
1195 }
1196
1197 struct AsyncTraitInfo<'a> {
1198 // statement that must be patched
1199 source_stmt: &'a Stmt,
1200 kind: AsyncTraitKind<'a>,
1201 self_type: Option<syn::TypePath>,
1202 }
1203
1204 // Get the AST of the inner function we need to hook, if it was generated
1205 // by async-trait.
1206 // When we are given a function annotated by async-trait, that function
1207 // is only a placeholder that returns a pinned future containing the
1208 // user logic, and it is that pinned future that needs to be instrumented.
1209 // Were we to instrument its parent, we would only collect information
1210 // regarding the allocation of that future, and not its own span of execution.
1211 // Depending on the version of async-trait, we inspect the block of the function
1212 // to find if it matches the pattern
1213 // `async fn foo<...>(...) {...}; Box::pin(foo<...>(...))` (<=0.1.43), or if
1214 // it matches `Box::pin(async move { ... }) (>=0.1.44). We the return the
1215 // statement that must be instrumented, along with some other informations.
1216 // 'gen_body' will then be able to use that information to instrument the
1217 // proper function/future.
1218 // (this follows the approach suggested in
1219 // https://github.com/dtolnay/async-trait/issues/45#issuecomment-571245673)
get_async_trait_info(block: &Block, block_is_async: bool) -> Option<AsyncTraitInfo<'_>>1220 fn get_async_trait_info(block: &Block, block_is_async: bool) -> Option<AsyncTraitInfo<'_>> {
1221 // are we in an async context? If yes, this isn't a async_trait-like pattern
1222 if block_is_async {
1223 return None;
1224 }
1225
1226 // list of async functions declared inside the block
1227 let inside_funs = block.stmts.iter().filter_map(|stmt| {
1228 if let Stmt::Item(Item::Fn(fun)) = &stmt {
1229 // If the function is async, this is a candidate
1230 if fun.sig.asyncness.is_some() {
1231 return Some((stmt, fun));
1232 }
1233 }
1234 None
1235 });
1236
1237 // last expression of the block (it determines the return value
1238 // of the block, so that if we are working on a function whose
1239 // `trait` or `impl` declaration is annotated by async_trait,
1240 // this is quite likely the point where the future is pinned)
1241 let (last_expr_stmt, last_expr) = block.stmts.iter().rev().find_map(|stmt| {
1242 if let Stmt::Expr(expr) = stmt {
1243 Some((stmt, expr))
1244 } else {
1245 None
1246 }
1247 })?;
1248
1249 // is the last expression a function call?
1250 let (outside_func, outside_args) = match last_expr {
1251 Expr::Call(ExprCall { func, args, .. }) => (func, args),
1252 _ => return None,
1253 };
1254
1255 // is it a call to `Box::pin()`?
1256 let path = match outside_func.as_ref() {
1257 Expr::Path(path) => &path.path,
1258 _ => return None,
1259 };
1260 if !path_to_string(path).ends_with("Box::pin") {
1261 return None;
1262 }
1263
1264 // Does the call take an argument? If it doesn't,
1265 // it's not gonna compile anyway, but that's no reason
1266 // to (try to) perform an out of bounds access
1267 if outside_args.is_empty() {
1268 return None;
1269 }
1270
1271 // Is the argument to Box::pin an async block that
1272 // captures its arguments?
1273 if let Expr::Async(async_expr) = &outside_args[0] {
1274 // check that the move 'keyword' is present
1275 async_expr.capture?;
1276
1277 return Some(AsyncTraitInfo {
1278 source_stmt: last_expr_stmt,
1279 kind: AsyncTraitKind::Async(async_expr),
1280 self_type: None,
1281 });
1282 }
1283
1284 // Is the argument to Box::pin a function call itself?
1285 let func = match &outside_args[0] {
1286 Expr::Call(ExprCall { func, .. }) => func,
1287 _ => return None,
1288 };
1289
1290 // "stringify" the path of the function called
1291 let func_name = match **func {
1292 Expr::Path(ref func_path) => path_to_string(&func_path.path),
1293 _ => return None,
1294 };
1295
1296 // Was that function defined inside of the current block?
1297 // If so, retrieve the statement where it was declared and the function itself
1298 let (stmt_func_declaration, func) = inside_funs
1299 .into_iter()
1300 .find(|(_, fun)| fun.sig.ident == func_name)?;
1301
1302 // If "_self" is present as an argument, we store its type to be able to rewrite "Self" (the
1303 // parameter type) with the type of "_self"
1304 let mut self_type = None;
1305 for arg in &func.sig.inputs {
1306 if let FnArg::Typed(ty) = arg {
1307 if let Pat::Ident(PatIdent { ref ident, .. }) = *ty.pat {
1308 if ident == "_self" {
1309 let mut ty = *ty.ty.clone();
1310 // extract the inner type if the argument is "&self" or "&mut self"
1311 if let syn::Type::Reference(syn::TypeReference { elem, .. }) = ty {
1312 ty = *elem;
1313 }
1314
1315 if let syn::Type::Path(tp) = ty {
1316 self_type = Some(tp);
1317 break;
1318 }
1319 }
1320 }
1321 }
1322 }
1323
1324 Some(AsyncTraitInfo {
1325 source_stmt: stmt_func_declaration,
1326 kind: AsyncTraitKind::Function(func),
1327 self_type,
1328 })
1329 }
1330
1331 // Return a path as a String
path_to_string(path: &Path) -> String1332 fn path_to_string(path: &Path) -> String {
1333 use std::fmt::Write;
1334 // some heuristic to prevent too many allocations
1335 let mut res = String::with_capacity(path.segments.len() * 5);
1336 for i in 0..path.segments.len() {
1337 write!(&mut res, "{}", path.segments[i].ident)
1338 .expect("writing to a String should never fail");
1339 if i < path.segments.len() - 1 {
1340 res.push_str("::");
1341 }
1342 }
1343 res
1344 }
1345
1346 /// A visitor struct to replace idents and types in some piece
1347 /// of code (e.g. the "self" and "Self" tokens in user-supplied
1348 /// fields expressions when the function is generated by an old
1349 /// version of async-trait).
1350 struct IdentAndTypesRenamer<'a> {
1351 types: Vec<(&'a str, TypePath)>,
1352 idents: Vec<(Ident, Ident)>,
1353 }
1354
1355 impl<'a> syn::visit_mut::VisitMut for IdentAndTypesRenamer<'a> {
1356 // we deliberately compare strings because we want to ignore the spans
1357 // If we apply clippy's lint, the behavior changes
1358 #[allow(clippy::cmp_owned)]
visit_ident_mut(&mut self, id: &mut Ident)1359 fn visit_ident_mut(&mut self, id: &mut Ident) {
1360 for (old_ident, new_ident) in &self.idents {
1361 if id.to_string() == old_ident.to_string() {
1362 *id = new_ident.clone();
1363 }
1364 }
1365 }
1366
visit_type_mut(&mut self, ty: &mut syn::Type)1367 fn visit_type_mut(&mut self, ty: &mut syn::Type) {
1368 for (type_name, new_type) in &self.types {
1369 if let syn::Type::Path(TypePath { path, .. }) = ty {
1370 if path_to_string(path) == *type_name {
1371 *ty = syn::Type::Path(new_type.clone());
1372 }
1373 }
1374 }
1375 }
1376 }
1377
1378 // A visitor struct that replace an async block by its patched version
1379 struct AsyncTraitBlockReplacer<'a> {
1380 block: &'a Block,
1381 patched_block: Block,
1382 }
1383
1384 impl<'a> syn::visit_mut::VisitMut for AsyncTraitBlockReplacer<'a> {
visit_block_mut(&mut self, i: &mut Block)1385 fn visit_block_mut(&mut self, i: &mut Block) {
1386 if i == self.block {
1387 *i = self.patched_block.clone();
1388 }
1389 }
1390 }
1391