Skip to content

Commit

Permalink
Add special handling of Extension and Clone for debug_handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Logan Nielsen committed Nov 1, 2023
1 parent d7258bf commit 9b68146
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
48 changes: 48 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,52 @@ 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;
|

0 comments on commit 9b68146

Please sign in to comment.