Skip to content

Commit 4dfe046

Browse files
Add fake return to improve span on type error in tracing::instrument
1 parent 1b2a054 commit 4dfe046

File tree

1 file changed

+68
-20
lines changed

1 file changed

+68
-20
lines changed

tracing-attributes/src/expand.rs

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ use std::iter;
22

33
use proc_macro2::TokenStream;
44
use quote::{quote, quote_spanned, ToTokens};
5+
use syn::visit_mut::VisitMut;
56
use syn::{
67
punctuated::Punctuated, spanned::Spanned, Block, Expr, ExprAsync, ExprCall, FieldPat, FnArg,
78
Ident, Item, ItemFn, Pat, PatIdent, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType,
8-
Path, Signature, Stmt, Token, TypePath,
9+
Path, ReturnType, Signature, Stmt, Token, Type, TypePath,
910
};
1011

1112
use crate::{
@@ -18,7 +19,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
1819
input: MaybeItemFnRef<'a, B>,
1920
args: InstrumentArgs,
2021
instrumented_function_name: &str,
21-
self_type: Option<&syn::TypePath>,
22+
self_type: Option<&TypePath>,
2223
) -> proc_macro2::TokenStream {
2324
// these are needed ahead of time, as ItemFn contains the function body _and_
2425
// isn't representable inside a quote!/quote_spanned! macro
@@ -31,7 +32,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
3132
} = input;
3233

3334
let Signature {
34-
output: return_type,
35+
output,
3536
inputs: params,
3637
unsafety,
3738
asyncness,
@@ -49,8 +50,34 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
4950

5051
let warnings = args.warnings();
5152

53+
let block = if let ReturnType::Type(_, return_type) = &output {
54+
let return_type = erase_impl_trait(return_type);
55+
// Install a fake return statement as the first thing in the function
56+
// body, so that we eagerly infer that the return type is what we
57+
// declared in the async fn signature.
58+
let fake_return_edge = quote_spanned! {return_type.span()=>
59+
#[allow(unreachable_code)]
60+
if false {
61+
let __tracing_attr_fake_return: #return_type = panic!();
62+
return __tracing_attr_fake_return;
63+
}
64+
};
65+
quote! {
66+
{
67+
#fake_return_edge
68+
#block
69+
}
70+
}
71+
} else {
72+
quote! {
73+
{
74+
let _: () = #block;
75+
}
76+
}
77+
};
78+
5279
let body = gen_block(
53-
block,
80+
&block,
5481
params,
5582
asyncness.is_some(),
5683
args,
@@ -60,7 +87,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
6087

6188
quote!(
6289
#(#attrs) *
63-
#vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #return_type
90+
#vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #output
6491
#where_clause
6592
{
6693
#warnings
@@ -76,7 +103,7 @@ fn gen_block<B: ToTokens>(
76103
async_context: bool,
77104
mut args: InstrumentArgs,
78105
instrumented_function_name: &str,
79-
self_type: Option<&syn::TypePath>,
106+
self_type: Option<&TypePath>,
80107
) -> proc_macro2::TokenStream {
81108
// generate the span's name
82109
let span_name = args
@@ -393,11 +420,11 @@ impl RecordType {
393420
"Wrapping",
394421
];
395422

396-
/// Parse `RecordType` from [syn::Type] by looking up
423+
/// Parse `RecordType` from [Type] by looking up
397424
/// the [RecordType::TYPES_FOR_VALUE] array.
398-
fn parse_from_ty(ty: &syn::Type) -> Self {
425+
fn parse_from_ty(ty: &Type) -> Self {
399426
match ty {
400-
syn::Type::Path(syn::TypePath { path, .. })
427+
Type::Path(TypePath { path, .. })
401428
if path
402429
.segments
403430
.iter()
@@ -410,9 +437,7 @@ impl RecordType {
410437
{
411438
RecordType::Value
412439
}
413-
syn::Type::Reference(syn::TypeReference { elem, .. }) => {
414-
RecordType::parse_from_ty(&*elem)
415-
}
440+
Type::Reference(syn::TypeReference { elem, .. }) => RecordType::parse_from_ty(&*elem),
416441
_ => RecordType::Debug,
417442
}
418443
}
@@ -471,7 +496,7 @@ pub(crate) struct AsyncInfo<'block> {
471496
// statement that must be patched
472497
source_stmt: &'block Stmt,
473498
kind: AsyncKind<'block>,
474-
self_type: Option<syn::TypePath>,
499+
self_type: Option<TypePath>,
475500
input: &'block ItemFn,
476501
}
477502

@@ -606,11 +631,11 @@ impl<'block> AsyncInfo<'block> {
606631
if ident == "_self" {
607632
let mut ty = *ty.ty.clone();
608633
// extract the inner type if the argument is "&self" or "&mut self"
609-
if let syn::Type::Reference(syn::TypeReference { elem, .. }) = ty {
634+
if let Type::Reference(syn::TypeReference { elem, .. }) = ty {
610635
ty = *elem;
611636
}
612637

613-
if let syn::Type::Path(tp) = ty {
638+
if let Type::Path(tp) = ty {
614639
self_type = Some(tp);
615640
break;
616641
}
@@ -722,7 +747,7 @@ struct IdentAndTypesRenamer<'a> {
722747
idents: Vec<(Ident, Ident)>,
723748
}
724749

725-
impl<'a> syn::visit_mut::VisitMut for IdentAndTypesRenamer<'a> {
750+
impl<'a> VisitMut for IdentAndTypesRenamer<'a> {
726751
// we deliberately compare strings because we want to ignore the spans
727752
// If we apply clippy's lint, the behavior changes
728753
#[allow(clippy::cmp_owned)]
@@ -734,11 +759,11 @@ impl<'a> syn::visit_mut::VisitMut for IdentAndTypesRenamer<'a> {
734759
}
735760
}
736761

737-
fn visit_type_mut(&mut self, ty: &mut syn::Type) {
762+
fn visit_type_mut(&mut self, ty: &mut Type) {
738763
for (type_name, new_type) in &self.types {
739-
if let syn::Type::Path(TypePath { path, .. }) = ty {
764+
if let Type::Path(TypePath { path, .. }) = ty {
740765
if path_to_string(path) == *type_name {
741-
*ty = syn::Type::Path(new_type.clone());
766+
*ty = Type::Path(new_type.clone());
742767
}
743768
}
744769
}
@@ -751,10 +776,33 @@ struct AsyncTraitBlockReplacer<'a> {
751776
patched_block: Block,
752777
}
753778

754-
impl<'a> syn::visit_mut::VisitMut for AsyncTraitBlockReplacer<'a> {
779+
impl<'a> VisitMut for AsyncTraitBlockReplacer<'a> {
755780
fn visit_block_mut(&mut self, i: &mut Block) {
756781
if i == self.block {
757782
*i = self.patched_block.clone();
758783
}
759784
}
760785
}
786+
787+
// Replaces any `impl Trait` with `_` so it can be used as the type in
788+
// a `let` statement's LHS.
789+
struct ImplTraitEraser;
790+
791+
impl VisitMut for ImplTraitEraser {
792+
fn visit_type_mut(&mut self, t: &mut Type) {
793+
if let Type::ImplTrait(..) = t {
794+
*t = syn::TypeInfer {
795+
underscore_token: Token![_](t.span()),
796+
}
797+
.into();
798+
} else {
799+
syn::visit_mut::visit_type_mut(self, t);
800+
}
801+
}
802+
}
803+
804+
fn erase_impl_trait(ty: &Type) -> Type {
805+
let mut ty = ty.clone();
806+
ImplTraitEraser.visit_type_mut(&mut ty);
807+
ty
808+
}

0 commit comments

Comments
 (0)