Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add special handling of Extension and Clone for debug_handler #2301

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 54 additions & 0 deletions axum-macros/src/debug_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream {
let check_extractor_count = check_extractor_count(&item_fn);
let check_path_extractor = check_path_extractor(&item_fn);
let check_output_impls_into_response = check_output_impls_into_response(&item_fn);
let check_extension_clone_response = check_extension_clone(&item_fn);

// If the function is generic, we can't reliably check its inputs or whether the future it
// returns is `Send`. Skip those checks to avoid unhelpful additional compiler errors.
Expand Down Expand Up @@ -73,6 +74,7 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream {
#check_extractor_count
#check_path_extractor
#check_output_impls_into_response
#check_extension_clone_response
#check_inputs_and_future_send
}
}
Expand Down Expand Up @@ -355,6 +357,58 @@ fn check_input_order(item_fn: &ItemFn) -> Option<TokenStream> {
}
}

fn check_extension_clone(item_fn: &ItemFn) -> TokenStream {
let mut tokens = TokenStream::new();

for (idx, arg) in item_fn.sig.inputs.iter().enumerate() {
if let FnArg::Typed(pat_type) = arg {
let ty = &*pat_type.ty;
if is_extension_type(ty) {
let span = ty.span();
let check_fn =
format_ident!("__check_{}_{}_clone", item_fn.sig.ident, idx, span = span);
let call_check_fn = format_ident!(
"__call_check_{}_{}_clone",
item_fn.sig.ident,
idx,
span = span
);

tokens.extend(quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #check_fn()
where
#ty: Clone,
{}

// we have to call the function to actually trigger a compile error
// since the function is generic, just defining it is not enough
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #call_check_fn()
{
#check_fn();
}
});
}
}
}

tokens
}

fn is_extension_type(ty: &Type) -> bool {
if let Type::Path(type_path) = ty {
if let Some(segment) = type_path.path.segments.last() {
return segment.ident == "Extension";
}
}
false
}

fn request_consuming_type_name(ty: &Type) -> Option<&'static str> {
let path = match ty {
Type::Path(type_path) => &type_path.path,
Expand Down
9 changes: 9 additions & 0 deletions axum-macros/tests/debug_handler/fail/not_clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
use axum::extract::Extension;

struct NonCloneType;

#[debug_handler]
async fn test_extension_non_clone(_: Extension<NonCloneType>) {}

fn main() {}
14 changes: 14 additions & 0 deletions axum-macros/tests/debug_handler/fail/not_clone.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0277]: the trait bound `NonCloneType: Clone` is not satisfied
--> tests/debug_handler/fail/not_clone.rs:7:38
|
7 | async fn test_extension_non_clone(_: Extension<NonCloneType>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonCloneType`
|
= note: required for `Extension<NonCloneType>` to implement `Clone`
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
help: consider annotating `NonCloneType` with `#[derive(Clone)]`
|
4 + #[derive(Clone)]
5 | struct NonCloneType;
|