Skip to content

Commit 4848d7d

Browse files
e-nomemhawkw
andauthored
attributes: fix handling of inner attributes (#2307)
## Motivation When the `instrument` attribute is used on a function with inner attributes, the proc macro generates code above the attributes within the function block that causes compilation errors. These should be parsed out separately and handled. Fixes #2294 ## Solution I updated `MaybeItemFn` and `MaybeItemFnRef` to so they hold both the outer and inner attributes for the instrumented function and updated the codegen to inlcude them in the appropriate locations. I couldn't preserve the existing implementation of `From<&'_ ItemFn> for MaybeItemFnRef<'_, Box<Block>>`, because it is now necessary to separate the inner and outer attributes of the `ItemFn` into two separate `Vec`s. That implementation was replaced with a `From<ItemFn> for MaybeItemFn`, which uses `Iterator::partition` to separate out the inner and outer attributes. Co-authored-by: Eliza Weisman <[email protected]>
1 parent bc95f2e commit 4848d7d

File tree

4 files changed

+72
-31
lines changed

4 files changed

+72
-31
lines changed

tracing-attributes/src/expand.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use syn::{
1111

1212
use crate::{
1313
attr::{Field, Fields, FormatMode, InstrumentArgs},
14-
MaybeItemFnRef,
14+
MaybeItemFn, MaybeItemFnRef,
1515
};
1616

1717
/// Given an existing function, generate an instrumented version of that function
@@ -25,7 +25,8 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
2525
// isn't representable inside a quote!/quote_spanned! macro
2626
// (Syn's ToTokens isn't implemented for ItemFn)
2727
let MaybeItemFnRef {
28-
attrs,
28+
outer_attrs,
29+
inner_attrs,
2930
vis,
3031
sig,
3132
block,
@@ -87,10 +88,11 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
8788
);
8889

8990
quote!(
90-
#(#attrs) *
91+
#(#outer_attrs) *
9192
#vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #output
9293
#where_clause
9394
{
95+
#(#inner_attrs) *
9496
#warnings
9597
#body
9698
}
@@ -657,7 +659,7 @@ impl<'block> AsyncInfo<'block> {
657659
self,
658660
args: InstrumentArgs,
659661
instrumented_function_name: &str,
660-
) -> proc_macro::TokenStream {
662+
) -> Result<proc_macro::TokenStream, syn::Error> {
661663
// let's rewrite some statements!
662664
let mut out_stmts: Vec<TokenStream> = self
663665
.input
@@ -678,12 +680,15 @@ impl<'block> AsyncInfo<'block> {
678680
// instrument the future by rewriting the corresponding statement
679681
out_stmts[iter] = match self.kind {
680682
// `Box::pin(immediately_invoked_async_fn())`
681-
AsyncKind::Function(fun) => gen_function(
682-
fun.into(),
683-
args,
684-
instrumented_function_name,
685-
self.self_type.as_ref(),
686-
),
683+
AsyncKind::Function(fun) => {
684+
let fun = MaybeItemFn::from(fun.clone());
685+
gen_function(
686+
fun.as_ref(),
687+
args,
688+
instrumented_function_name,
689+
self.self_type.as_ref(),
690+
)
691+
}
687692
// `async move { ... }`, optionally pinned
688693
AsyncKind::Async {
689694
async_expr,
@@ -714,13 +719,13 @@ impl<'block> AsyncInfo<'block> {
714719
let vis = &self.input.vis;
715720
let sig = &self.input.sig;
716721
let attrs = &self.input.attrs;
717-
quote!(
722+
Ok(quote!(
718723
#(#attrs) *
719724
#vis #sig {
720725
#(#out_stmts) *
721726
}
722727
)
723-
.into()
728+
.into())
724729
}
725730
}
726731

tracing-attributes/src/lib.rs

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
use proc_macro2::TokenStream;
8484
use quote::ToTokens;
8585
use syn::parse::{Parse, ParseStream};
86-
use syn::{Attribute, Block, ItemFn, Signature, Visibility};
86+
use syn::{Attribute, ItemFn, Signature, Visibility};
8787

8888
mod attr;
8989
mod expand;
@@ -388,11 +388,13 @@ fn instrument_precise(
388388
// check for async_trait-like patterns in the block, and instrument
389389
// the future instead of the wrapper
390390
if let Some(async_like) = expand::AsyncInfo::from_fn(&input) {
391-
return Ok(async_like.gen_async(args, instrumented_function_name.as_str()));
391+
return async_like.gen_async(args, instrumented_function_name.as_str());
392392
}
393393

394+
let input = MaybeItemFn::from(input);
395+
394396
Ok(expand::gen_function(
395-
(&input).into(),
397+
input.as_ref(),
396398
args,
397399
instrumented_function_name.as_str(),
398400
None,
@@ -404,7 +406,8 @@ fn instrument_precise(
404406
/// which's block is just a `TokenStream` (it may contain invalid code).
405407
#[derive(Debug, Clone)]
406408
struct MaybeItemFn {
407-
attrs: Vec<Attribute>,
409+
outer_attrs: Vec<Attribute>,
410+
inner_attrs: Vec<Attribute>,
408411
vis: Visibility,
409412
sig: Signature,
410413
block: TokenStream,
@@ -413,7 +416,8 @@ struct MaybeItemFn {
413416
impl MaybeItemFn {
414417
fn as_ref(&self) -> MaybeItemFnRef<'_, TokenStream> {
415418
MaybeItemFnRef {
416-
attrs: &self.attrs,
419+
outer_attrs: &self.outer_attrs,
420+
inner_attrs: &self.inner_attrs,
417421
vis: &self.vis,
418422
sig: &self.sig,
419423
block: &self.block,
@@ -425,36 +429,50 @@ impl MaybeItemFn {
425429
/// (just like `ItemFn`, but skips parsing the body).
426430
impl Parse for MaybeItemFn {
427431
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
428-
let attrs = input.call(syn::Attribute::parse_outer)?;
432+
let outer_attrs = input.call(Attribute::parse_outer)?;
429433
let vis: Visibility = input.parse()?;
430434
let sig: Signature = input.parse()?;
435+
let inner_attrs = input.call(Attribute::parse_inner)?;
431436
let block: TokenStream = input.parse()?;
432437
Ok(Self {
433-
attrs,
438+
outer_attrs,
439+
inner_attrs,
434440
vis,
435441
sig,
436442
block,
437443
})
438444
}
439445
}
440446

447+
impl From<ItemFn> for MaybeItemFn {
448+
fn from(
449+
ItemFn {
450+
attrs,
451+
vis,
452+
sig,
453+
block,
454+
}: ItemFn,
455+
) -> Self {
456+
let (outer_attrs, inner_attrs) = attrs
457+
.into_iter()
458+
.partition(|attr| attr.style == syn::AttrStyle::Outer);
459+
Self {
460+
outer_attrs,
461+
inner_attrs,
462+
vis,
463+
sig,
464+
block: block.to_token_stream(),
465+
}
466+
}
467+
}
468+
441469
/// A generic reference type for `MaybeItemFn`,
442470
/// that takes a generic block type `B` that implements `ToTokens` (eg. `TokenStream`, `Block`).
443471
#[derive(Debug, Clone)]
444472
struct MaybeItemFnRef<'a, B: ToTokens> {
445-
attrs: &'a Vec<Attribute>,
473+
outer_attrs: &'a Vec<Attribute>,
474+
inner_attrs: &'a Vec<Attribute>,
446475
vis: &'a Visibility,
447476
sig: &'a Signature,
448477
block: &'a B,
449478
}
450-
451-
impl<'a> From<&'a ItemFn> for MaybeItemFnRef<'a, Box<Block>> {
452-
fn from(val: &'a ItemFn) -> Self {
453-
MaybeItemFnRef {
454-
attrs: &val.attrs,
455-
vis: &val.vis,
456-
sig: &val.sig,
457-
block: &val.block,
458-
}
459-
}
460-
}

tracing-attributes/tests/async_fn.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ async fn test_ret_impl_trait_err(n: i32) -> Result<impl Iterator<Item = i32>, &'
3232
#[instrument]
3333
async fn test_async_fn_empty() {}
3434

35+
// Reproduces a compile error when an instrumented function body contains inner
36+
// attributes (https://github.com/tokio-rs/tracing/issues/2294).
37+
#[deny(unused_variables)]
38+
#[instrument]
39+
async fn repro_async_2294() {
40+
#![allow(unused_variables)]
41+
let i = 42;
42+
}
43+
3544
// Reproduces https://github.com/tokio-rs/tracing/issues/1613
3645
#[instrument]
3746
// LOAD-BEARING `#[rustfmt::skip]`! This is necessary to reproduce the bug;

tracing-attributes/tests/instrument.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ use tracing::Level;
33
use tracing_attributes::instrument;
44
use tracing_mock::*;
55

6+
// Reproduces a compile error when an instrumented function body contains inner
7+
// attributes (https://github.com/tokio-rs/tracing/issues/2294).
8+
#[deny(unused_variables)]
9+
#[instrument]
10+
fn repro_2294() {
11+
#![allow(unused_variables)]
12+
let i = 42;
13+
}
14+
615
#[test]
716
fn override_everything() {
817
#[instrument(target = "my_target", level = "debug")]

0 commit comments

Comments
 (0)