Skip to content

Commit 92cb2f0

Browse files
e-nomemhawkw
andcommitted
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 8b01ea9 commit 92cb2f0

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
@@ -86,7 +86,7 @@ extern crate proc_macro;
8686
use proc_macro2::TokenStream;
8787
use quote::ToTokens;
8888
use syn::parse::{Parse, ParseStream};
89-
use syn::{Attribute, Block, ItemFn, Signature, Visibility};
89+
use syn::{Attribute, ItemFn, Signature, Visibility};
9090

9191
mod attr;
9292
mod expand;
@@ -587,11 +587,13 @@ fn instrument_precise(
587587
// check for async_trait-like patterns in the block, and instrument
588588
// the future instead of the wrapper
589589
if let Some(async_like) = expand::AsyncInfo::from_fn(&input) {
590-
return Ok(async_like.gen_async(args, instrumented_function_name.as_str()));
590+
return async_like.gen_async(args, instrumented_function_name.as_str());
591591
}
592592

593+
let input = MaybeItemFn::from(input);
594+
593595
Ok(expand::gen_function(
594-
(&input).into(),
596+
input.as_ref(),
595597
args,
596598
instrumented_function_name.as_str(),
597599
None,
@@ -603,7 +605,8 @@ fn instrument_precise(
603605
/// which's block is just a `TokenStream` (it may contain invalid code).
604606
#[derive(Debug, Clone)]
605607
struct MaybeItemFn {
606-
attrs: Vec<Attribute>,
608+
outer_attrs: Vec<Attribute>,
609+
inner_attrs: Vec<Attribute>,
607610
vis: Visibility,
608611
sig: Signature,
609612
block: TokenStream,
@@ -612,7 +615,8 @@ struct MaybeItemFn {
612615
impl MaybeItemFn {
613616
fn as_ref(&self) -> MaybeItemFnRef<'_, TokenStream> {
614617
MaybeItemFnRef {
615-
attrs: &self.attrs,
618+
outer_attrs: &self.outer_attrs,
619+
inner_attrs: &self.inner_attrs,
616620
vis: &self.vis,
617621
sig: &self.sig,
618622
block: &self.block,
@@ -624,36 +628,50 @@ impl MaybeItemFn {
624628
/// (just like `ItemFn`, but skips parsing the body).
625629
impl Parse for MaybeItemFn {
626630
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
627-
let attrs = input.call(syn::Attribute::parse_outer)?;
631+
let outer_attrs = input.call(Attribute::parse_outer)?;
628632
let vis: Visibility = input.parse()?;
629633
let sig: Signature = input.parse()?;
634+
let inner_attrs = input.call(Attribute::parse_inner)?;
630635
let block: TokenStream = input.parse()?;
631636
Ok(Self {
632-
attrs,
637+
outer_attrs,
638+
inner_attrs,
633639
vis,
634640
sig,
635641
block,
636642
})
637643
}
638644
}
639645

646+
impl From<ItemFn> for MaybeItemFn {
647+
fn from(
648+
ItemFn {
649+
attrs,
650+
vis,
651+
sig,
652+
block,
653+
}: ItemFn,
654+
) -> Self {
655+
let (outer_attrs, inner_attrs) = attrs
656+
.into_iter()
657+
.partition(|attr| attr.style == syn::AttrStyle::Outer);
658+
Self {
659+
outer_attrs,
660+
inner_attrs,
661+
vis,
662+
sig,
663+
block: block.to_token_stream(),
664+
}
665+
}
666+
}
667+
640668
/// A generic reference type for `MaybeItemFn`,
641669
/// that takes a generic block type `B` that implements `ToTokens` (eg. `TokenStream`, `Block`).
642670
#[derive(Debug, Clone)]
643671
struct MaybeItemFnRef<'a, B: ToTokens> {
644-
attrs: &'a Vec<Attribute>,
672+
outer_attrs: &'a Vec<Attribute>,
673+
inner_attrs: &'a Vec<Attribute>,
645674
vis: &'a Visibility,
646675
sig: &'a Signature,
647676
block: &'a B,
648677
}
649-
650-
impl<'a> From<&'a ItemFn> for MaybeItemFnRef<'a, Box<Block>> {
651-
fn from(val: &'a ItemFn) -> Self {
652-
MaybeItemFnRef {
653-
attrs: &val.attrs,
654-
vis: &val.vis,
655-
sig: &val.sig,
656-
block: &val.block,
657-
}
658-
}
659-
}

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)