Skip to content

Commit fef5384

Browse files
committed
attributes: speculative expand when parsing fails
1 parent a1868ea commit fef5384

File tree

2 files changed

+122
-23
lines changed

2 files changed

+122
-23
lines changed

tracing-appender/src/non_blocking.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ pub const DEFAULT_BUFFERED_LINES_LIMIT: usize = 128_000;
103103
#[must_use]
104104
#[derive(Debug)]
105105
pub struct WorkerGuard {
106-
guard: Option<JoinHandle<()>>,
106+
_guard: Option<JoinHandle<()>>,
107107
sender: Sender<Msg>,
108108
shutdown: Sender<()>,
109109
}
@@ -250,7 +250,7 @@ impl<'a> MakeWriter<'a> for NonBlocking {
250250
impl WorkerGuard {
251251
fn new(handle: JoinHandle<()>, sender: Sender<Msg>, shutdown: Sender<()>) -> Self {
252252
WorkerGuard {
253-
guard: Some(handle),
253+
_guard: Some(handle),
254254
sender,
255255
shutdown,
256256
}

tracing-attributes/src/lib.rs

Lines changed: 120 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ use quote::{quote, quote_spanned, ToTokens};
8888
use syn::ext::IdentExt as _;
8989
use syn::parse::{Parse, ParseStream};
9090
use syn::{
91-
punctuated::Punctuated, spanned::Spanned, Block, Expr, ExprAsync, ExprCall, FieldPat, FnArg,
92-
Ident, Item, ItemFn, LitInt, LitStr, Pat, PatIdent, PatReference, PatStruct, PatTuple,
93-
PatTupleStruct, PatType, Path, Signature, Stmt, Token, TypePath,
91+
punctuated::Punctuated, spanned::Spanned, Attribute, Block, Expr, ExprAsync, ExprCall,
92+
FieldPat, FnArg, Ident, Item, ItemFn, LitInt, LitStr, Pat, PatIdent, PatReference, PatStruct,
93+
PatTuple, PatTupleStruct, PatType, Path, Signature, Stmt, Token, TypePath, Visibility,
9494
};
9595
/// Instruments a function to create and enter a `tracing` [span] every time
9696
/// the function is called.
@@ -274,14 +274,45 @@ pub fn instrument(
274274
args: proc_macro::TokenStream,
275275
item: proc_macro::TokenStream,
276276
) -> proc_macro::TokenStream {
277-
let input = syn::parse_macro_input!(item as ItemFn);
278277
let args = syn::parse_macro_input!(args as InstrumentArgs);
279278

279+
instrument_precise(args.clone(), item.clone()).unwrap_or_else(|_err| {
280+
// FIXME: Ideally, we'd like to emit a warning but we can't,
281+
// so we ignore the parsing error for now.
282+
instrument_speculative(args, item)
283+
})
284+
}
285+
286+
/// Instrument the function, without parsing the function body (instead using the raw tokens).
287+
fn instrument_speculative(
288+
args: InstrumentArgs,
289+
item: proc_macro::TokenStream,
290+
) -> proc_macro::TokenStream {
291+
let input = syn::parse_macro_input!(item as MaybeItemFn<TokenStream>);
292+
let instrumented_function_name = input.sig.ident.to_string();
293+
gen_function(
294+
&input.as_ref(),
295+
args,
296+
instrumented_function_name.as_str(),
297+
None,
298+
)
299+
.into()
300+
}
301+
302+
/// Instrument the function, by fully parsing the function body,
303+
/// which allows us to rewrite some statements related to async_trait-like patterns.
304+
fn instrument_precise(
305+
args: InstrumentArgs,
306+
item: proc_macro::TokenStream,
307+
) -> Result<proc_macro::TokenStream, syn::Error> {
308+
let input = syn::parse::<ItemFn>(item)?;
280309
let instrumented_function_name = input.sig.ident.to_string();
281310

282311
// check for async_trait-like patterns in the block, and instrument
283312
// the future instead of the wrapper
284-
if let Some(internal_fun) = get_async_trait_info(&input.block, input.sig.asyncness.is_some()) {
313+
let res = if let Some(internal_fun) =
314+
get_async_trait_info(&input.block, input.sig.asyncness.is_some())
315+
{
285316
// let's rewrite some statements!
286317
let mut out_stmts: Vec<TokenStream> = input
287318
.block
@@ -301,7 +332,7 @@ pub fn instrument(
301332
out_stmts[iter] = match internal_fun.kind {
302333
// async-trait <= 0.1.43
303334
AsyncTraitKind::Function(fun) => gen_function(
304-
fun,
335+
&MaybeItemFnRef::from(fun),
305336
args,
306337
instrumented_function_name.as_str(),
307338
internal_fun.self_type.as_ref(),
@@ -335,26 +366,33 @@ pub fn instrument(
335366
)
336367
.into()
337368
} else {
338-
gen_function(&input, args, instrumented_function_name.as_str(), None).into()
339-
}
369+
gen_function(
370+
&(&input).into(),
371+
args,
372+
instrumented_function_name.as_str(),
373+
None,
374+
)
375+
.into()
376+
};
377+
378+
Ok(res)
340379
}
341380

342381
/// Given an existing function, generate an instrumented version of that function
343-
fn gen_function(
344-
input: &ItemFn,
382+
fn gen_function<'a, B: ToTokens + 'a>(
383+
input: &MaybeItemFnRef<'a, B>,
345384
args: InstrumentArgs,
346385
instrumented_function_name: &str,
347386
self_type: Option<&syn::TypePath>,
348387
) -> proc_macro2::TokenStream {
349388
// these are needed ahead of time, as ItemFn contains the function body _and_
350389
// isn't representable inside a quote!/quote_spanned! macro
351390
// (Syn's ToTokens isn't implemented for ItemFn)
352-
let ItemFn {
391+
let MaybeItemFnRef {
353392
attrs,
354393
vis,
355-
block,
356394
sig,
357-
..
395+
block,
358396
} = input;
359397

360398
let Signature {
@@ -397,8 +435,8 @@ fn gen_function(
397435
}
398436

399437
/// Instrument a block
400-
fn gen_block(
401-
block: &Block,
438+
fn gen_block<B: ToTokens>(
439+
block: &B,
402440
params: &Punctuated<FnArg, Token![,]>,
403441
async_context: bool,
404442
mut args: InstrumentArgs,
@@ -611,7 +649,68 @@ fn gen_block(
611649
)
612650
}
613651

614-
#[derive(Default, Debug)]
652+
/// This is a more flexible/imprecise `ItemFn` type,
653+
/// which's block may be anything that implements `ToTokens`.
654+
#[derive(Debug, Clone)]
655+
struct MaybeItemFn<B: ToTokens> {
656+
attrs: Vec<Attribute>,
657+
vis: Visibility,
658+
sig: Signature,
659+
block: B,
660+
}
661+
662+
impl<B: ToTokens> MaybeItemFn<B> {
663+
fn new(attrs: Vec<Attribute>, vis: Visibility, sig: Signature, block: B) -> Self {
664+
Self {
665+
attrs,
666+
vis,
667+
sig,
668+
block,
669+
}
670+
}
671+
672+
fn as_ref(&self) -> MaybeItemFnRef<'_, B> {
673+
MaybeItemFnRef {
674+
attrs: &self.attrs,
675+
vis: &self.vis,
676+
sig: &self.sig,
677+
block: &self.block,
678+
}
679+
}
680+
}
681+
682+
/// This parses a TokenStream as ItemFn/MaybeItemFn but skips the body.
683+
impl Parse for MaybeItemFn<TokenStream> {
684+
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
685+
let attrs = input.call(syn::Attribute::parse_outer)?;
686+
let vis: Visibility = input.parse()?;
687+
let sig: Signature = input.parse()?;
688+
let block: TokenStream = input.parse()?;
689+
Ok(Self::new(attrs, vis, sig, block))
690+
}
691+
}
692+
693+
/// A generic reference type for `MaybeItemFn`.
694+
#[derive(Debug, Clone)]
695+
struct MaybeItemFnRef<'a, B: ToTokens> {
696+
attrs: &'a Vec<Attribute>,
697+
vis: &'a Visibility,
698+
sig: &'a Signature,
699+
block: &'a B,
700+
}
701+
702+
impl<'a> From<&'a ItemFn> for MaybeItemFnRef<'a, Box<Block>> {
703+
fn from(val: &'a ItemFn) -> Self {
704+
MaybeItemFnRef {
705+
attrs: &val.attrs,
706+
vis: &val.vis,
707+
sig: &val.sig,
708+
block: &val.block,
709+
}
710+
}
711+
}
712+
713+
#[derive(Clone, Default, Debug)]
615714
struct InstrumentArgs {
616715
level: Option<Level>,
617716
name: Option<LitStr>,
@@ -803,7 +902,7 @@ impl Parse for Skips {
803902
}
804903
}
805904

806-
#[derive(Debug, Hash, PartialEq, Eq)]
905+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
807906
enum ErrorMode {
808907
Display,
809908
Debug,
@@ -836,17 +935,17 @@ impl Parse for ErrorMode {
836935
}
837936
}
838937

839-
#[derive(Debug)]
938+
#[derive(Clone, Debug)]
840939
struct Fields(Punctuated<Field, Token![,]>);
841940

842-
#[derive(Debug)]
941+
#[derive(Clone, Debug)]
843942
struct Field {
844943
name: Punctuated<Ident, Token![.]>,
845944
value: Option<Expr>,
846945
kind: FieldKind,
847946
}
848947

849-
#[derive(Debug, Eq, PartialEq)]
948+
#[derive(Clone, Debug, Eq, PartialEq)]
850949
enum FieldKind {
851950
Debug,
852951
Display,
@@ -930,7 +1029,7 @@ impl ToTokens for FieldKind {
9301029
}
9311030
}
9321031

933-
#[derive(Debug)]
1032+
#[derive(Clone, Debug)]
9341033
enum Level {
9351034
Str(LitStr),
9361035
Int(LitInt),

0 commit comments

Comments
 (0)