diff --git a/autometrics-macros/src/lib.rs b/autometrics-macros/src/lib.rs index ac1486d..c90f911 100644 --- a/autometrics-macros/src/lib.rs +++ b/autometrics-macros/src/lib.rs @@ -1,6 +1,6 @@ use crate::parse::{AutometricsArgs, Item}; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use std::env; use syn::{ @@ -22,6 +22,13 @@ pub fn autometrics( item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let args = parse_macro_input!(args as parse::AutometricsArgs); + + // if the input has yet to be modified by the `async-trait` crate, we will fail parsing in + // in the next line, so lets error early now + if let Err(err) = check_async_trait(&item) { + return err.into_compile_error().into(); + } + let item = parse_macro_input!(item as Item); let result = match item { @@ -37,6 +44,19 @@ pub fn autometrics( output.into() } +fn check_async_trait(input: &proc_macro::TokenStream) -> Result<()> { + let str = input.to_string(); + + if str.contains("#[async_trait]") || str.contains("#[async_trait::async_trait]") { + Err(syn::Error::new( + Span::call_site(), + "#[async_trait] must be defined BEFORE #[autometrics]", + )) + } else { + Ok(()) + } +} + #[proc_macro_derive(ResultLabels, attributes(label))] pub fn result_labels(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as syn::DeriveInput); diff --git a/autometrics/Cargo.toml b/autometrics/Cargo.toml index db32c9a..ecdf880 100644 --- a/autometrics/Cargo.toml +++ b/autometrics/Cargo.toml @@ -122,6 +122,7 @@ tracing-opentelemetry-0-20 = { package = "tracing-opentelemetry", version = "0.2 tracing-opentelemetry-0-21 = { package = "tracing-opentelemetry", version = "0.21.0", default-features = false, optional = true } [dev-dependencies] +async-trait = "0.1.74" axum = { version = "0.6", features = ["tokio"] } criterion = "0.5" http = "0.2" diff --git a/autometrics/tests/result_labels/fail/async_trait_support.rs b/autometrics/tests/result_labels/fail/async_trait_support.rs new file mode 100644 index 0000000..95c539a --- /dev/null +++ b/autometrics/tests/result_labels/fail/async_trait_support.rs @@ -0,0 +1,34 @@ +use async_trait::async_trait; +use autometrics::autometrics; + +// https://github.com/autometrics-dev/autometrics-rs/issues/141 + +#[async_trait] +trait TestTrait { + async fn method() -> bool; + async fn self_method(&self) -> bool; +} + +#[derive(Default)] +struct TestStruct; + +#[autometrics] +#[async_trait] +impl TestTrait for TestStruct { + async fn method() -> bool { + true + } + + async fn self_method(&self) -> bool { + true + } +} + +fn main() { + let ts = TestStruct::default(); + + async move { + ::method().await; + ts.self_method().await; + }; +} diff --git a/autometrics/tests/result_labels/fail/async_trait_support.stderr b/autometrics/tests/result_labels/fail/async_trait_support.stderr new file mode 100644 index 0000000..a24e2f7 --- /dev/null +++ b/autometrics/tests/result_labels/fail/async_trait_support.stderr @@ -0,0 +1,29 @@ +error: #[async_trait] must be defined BEFORE #[autometrics] + --> tests/result_labels/fail/async_trait_support.rs:15:1 + | +15 | #[autometrics] + | ^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `autometrics` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `TestStruct: TestTrait` is not satisfied + --> tests/result_labels/fail/async_trait_support.rs:31:9 + | +31 | ::method().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TestTrait` is not implemented for `TestStruct` + +error[E0599]: no method named `self_method` found for struct `TestStruct` in the current scope + --> tests/result_labels/fail/async_trait_support.rs:32:12 + | +13 | struct TestStruct; + | ----------------- method `self_method` not found for this struct +... +32 | ts.self_method().await; + | ^^^^^^^^^^^ method not found in `TestStruct` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `TestTrait` defines an item `self_method`, perhaps you need to implement it + --> tests/result_labels/fail/async_trait_support.rs:7:1 + | +7 | trait TestTrait { + | ^^^^^^^^^^^^^^^ diff --git a/autometrics/tests/compile_test.rs b/autometrics/tests/result_labels/pass/generics.rs similarity index 93% rename from autometrics/tests/compile_test.rs rename to autometrics/tests/result_labels/pass/generics.rs index ec54f77..f36e6b3 100644 --- a/autometrics/tests/compile_test.rs +++ b/autometrics/tests/result_labels/pass/generics.rs @@ -35,9 +35,8 @@ fn issue_121_d() -> Result { } } -#[test] -fn invoke_issue_121() { - // we need to handle all three code generation cases +fn main() { + // we need to handle all four code generation cases issue_121_a().unwrap(); issue_121_b().unwrap(); issue_121_c().unwrap();