From 52c5634c52a67c17b6b31ca9d29843c3769a7a2d Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 2 Sep 2023 04:04:19 +0800 Subject: [PATCH 01/23] Improve debug_handler macro on tuple output types. Refs: #2173 --- axum-macros/src/debug_handler.rs | 151 +++++++++++++++++- .../debug_handler/fail/wrong_return_tuple.rs | 11 ++ .../fail/wrong_return_tuple.stderr | 18 +++ 3 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs create mode 100644 axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 0fbfc0e9af..bc2a31a482 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -4,9 +4,9 @@ use crate::{ attr_parsing::{parse_assignment_attribute, second}, with_position::{Position, WithPosition}, }; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; -use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, Token, Type}; +use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, ReturnType, Token, Type}; pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream { let Attrs { state_ty } = attr; @@ -15,6 +15,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_tuples = check_output_tuples(&item_fn); let check_output_impls_into_response = check_output_impls_into_response(&item_fn); // If the function is generic, we can't reliably check its inputs or whether the future it @@ -72,6 +73,7 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream { #item_fn #check_extractor_count #check_path_extractor + #check_output_tuples #check_output_impls_into_response #check_inputs_and_future_send } @@ -284,6 +286,142 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr .collect::() } +///If the output is a tuple with 2 or more elements, +/// it checks with the following pattern, +/// first element => StatusCode || Parts || IntoResponseParts +///last element => IntoResponse +///other elements => IntoResponseParts +///the max numbers of IntoResponseParts(16) +fn check_output_tuples(item_fn: &ItemFn) -> Option { + //Extract tuple types + let elements = match &item_fn.sig.output { + ReturnType::Type(_, ty) => match &**ty { + Type::Tuple(tuple) => &tuple.elems, + _ => return None, + }, + _ => return None, + }; + + if elements.len() < 2 { + return None; + } + //Amount of IntoRequestParts + let mut parts_amount = 0; + + let token_stream = WithPosition::new(elements.iter()) + .enumerate() + .map(|(_idx, arg)| match &arg { + Position::First(ty) => { + let typename = extract_clean_typename(ty); + if typename.is_none() { + quote! {} + } else { + let typename = typename.unwrap(); + match &*typename.to_string() { + "Parts" => quote! {}, + "Response" => quote! {}, + "StatusCode" => { + quote! {} + } + _ => { + parts_amount += 1; + check_into_response_parts(ty) + } + } + } + } + Position::Last(ty) => check_into_response(ty), + Position::Middle(ty) => { + parts_amount += 1; + if parts_amount >= 16 { + let error_message = format!("Output Tuple cannot have more than 16 arguments."); + let error = syn::Error::new_spanned(&item_fn.sig.output, error_message) + .to_compile_error(); + error + } else { + //todo check Named IntoResponse like Json, and hint that it should be placed last. + check_into_response_parts(ty) + } + } + _ => quote! {}, + }) + .collect::(); + Some(token_stream) +} + +fn check_into_response(ty: &Type) -> TokenStream { + let (span, ty) = (ty.span(), ty.clone()); + + let check_fn = format_ident!("__axum_macros_check_into_response_check", span = span,); + + let call_check_fn = format_ident!("__axum_macros_check_into_response_call_check", span = span,); + + let call_check_fn_body = quote_spanned! {span=> + #check_fn(); + }; + + let from_request_bound = quote_spanned! {span=> + #ty: ::axum::response::IntoResponse + }; + quote::quote_spanned! {span=> + #[allow(warnings)] + #[allow(unreachable_code)] + #[doc(hidden)] + fn #check_fn() + where + #from_request_bound, + {} + + // 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() + { + #call_check_fn_body + } + } +} + +fn check_into_response_parts(ty: &Type) -> TokenStream { + let (span, ty) = (ty.span(), ty.clone()); + + let check_fn = format_ident!("__axum_macros_check_into_response_parts_check", span = span,); + + let call_check_fn = format_ident!( + "__axum_macros_check_into_response_parts_call_check", + span = span, + ); + + let call_check_fn_body = quote_spanned! {span=> + #check_fn(); + }; + + let from_request_bound = quote_spanned! {span=> + #ty: ::axum::response::IntoResponseParts + }; + quote::quote_spanned! {span=> + #[allow(warnings)] + #[allow(unreachable_code)] + #[doc(hidden)] + fn #check_fn() + where + #from_request_bound, + {} + + // 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() + { + #call_check_fn_body + } + } +} + fn check_input_order(item_fn: &ItemFn) -> Option { let types_that_consume_the_request = item_fn .sig @@ -355,14 +493,17 @@ fn check_input_order(item_fn: &ItemFn) -> Option { } } -fn request_consuming_type_name(ty: &Type) -> Option<&'static str> { +fn extract_clean_typename(ty: &Type) -> Option<&Ident> { let path = match ty { Type::Path(type_path) => &type_path.path, _ => return None, }; + path.segments.last().map(|p| &p.ident) +} - let ident = match path.segments.last() { - Some(path_segment) => &path_segment.ident, +fn request_consuming_type_name(ty: &Type) -> Option<&'static str> { + let ident = match extract_clean_typename(ty) { + Some(ident) => ident, None => return None, }; diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs new file mode 100644 index 0000000000..fa5197a81d --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs @@ -0,0 +1,11 @@ + +#[axum::debug_handler] +async fn handler() -> ( + axum::http::StatusCode, + axum::Json<&'static str>, + axum::response::AppendHeaders<[( axum::http::HeaderName,&'static str); 1]>, +) { + panic!() +} + +fn main(){} \ No newline at end of file diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr new file mode 100644 index 0000000000..8c75c43d53 --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `Json<&'static str>: IntoResponseParts` is not satisfied + --> tests/debug_handler/fail/wrong_return_tuple.rs:5:5 + | +5 | axum::Json<&'static str>, + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoResponseParts` is not implemented for `Json<&'static str>` + | + = help: the following other types implement trait `IntoResponseParts`: + (T1, T2) + (T1, T2, T3) + (T1, T2, T3, T4) + (T1, T2, T3, T4, T5) + (T1, T2, T3, T4, T5, T6) + (T1, T2, T3, T4, T5, T6, T7) + (T1, T2, T3, T4, T5, T6, T7, T8) + (T1, T2, T3, T4, T5, T6, T7, T8, T9) + and $N others + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable From c8acbe77c4063d8a2b4f045e944b75947799a40e Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 2 Sep 2023 21:46:16 +0800 Subject: [PATCH 02/23] fix duplicate checking function names --- axum-macros/src/debug_handler.rs | 45 ++++++++++++----- .../fail/output_tuple_too_many.rs | 29 +++++++++++ .../fail/output_tuple_too_many.stderr | 48 +++++++++++++++++++ 3 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs create mode 100644 axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index bc2a31a482..421d856e37 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -4,7 +4,7 @@ use crate::{ attr_parsing::{parse_assignment_attribute, second}, with_position::{Position, WithPosition}, }; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, ReturnType, Token, Type}; @@ -311,6 +311,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { let token_stream = WithPosition::new(elements.iter()) .enumerate() .map(|(_idx, arg)| match &arg { + //First element type in the tuple Position::First(ty) => { let typename = extract_clean_typename(ty); if typename.is_none() { @@ -325,7 +326,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { } _ => { parts_amount += 1; - check_into_response_parts(ty) + check_into_response_parts(ty, parts_amount) } } } @@ -333,14 +334,13 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { Position::Last(ty) => check_into_response(ty), Position::Middle(ty) => { parts_amount += 1; - if parts_amount >= 16 { + if parts_amount > 16 { let error_message = format!("Output Tuple cannot have more than 16 arguments."); let error = syn::Error::new_spanned(&item_fn.sig.output, error_message) .to_compile_error(); error } else { - //todo check Named IntoResponse like Json, and hint that it should be placed last. - check_into_response_parts(ty) + check_into_response_parts(ty, parts_amount) } } _ => quote! {}, @@ -349,6 +349,22 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { Some(token_stream) } +fn _check_into_response_not_parts(ty: &Type) -> Option<&'static str> { + let name = extract_clean_typename(ty); + match name { + Some(name) => { + let s = match &*name { + "Json" => "Json<_>", + "String" => "String", + //todo add more types that implement IntoResponse but not IntoResponseParts + _ => return None, + }; + Some(s) + } + None => None, + } +} + fn check_into_response(ty: &Type) -> TokenStream { let (span, ty) = (ty.span(), ty.clone()); @@ -384,13 +400,16 @@ fn check_into_response(ty: &Type) -> TokenStream { } } -fn check_into_response_parts(ty: &Type) -> TokenStream { +fn check_into_response_parts(ty: &Type, index: i8) -> TokenStream { let (span, ty) = (ty.span(), ty.clone()); - let check_fn = format_ident!("__axum_macros_check_into_response_parts_check", span = span,); + let check_fn = format_ident!( + "__axum_macros_check_into_response_parts_{index}_check", + span = span, + ); let call_check_fn = format_ident!( - "__axum_macros_check_into_response_parts_call_check", + "__axum_macros_check_into_response_parts_{index}_call_check", span = span, ); @@ -493,21 +512,21 @@ fn check_input_order(item_fn: &ItemFn) -> Option { } } -fn extract_clean_typename(ty: &Type) -> Option<&Ident> { +fn extract_clean_typename(ty: &Type) -> Option { let path = match ty { Type::Path(type_path) => &type_path.path, _ => return None, }; - path.segments.last().map(|p| &p.ident) + path.segments.last().map(|p| p.ident.to_string()) } fn request_consuming_type_name(ty: &Type) -> Option<&'static str> { - let ident = match extract_clean_typename(ty) { - Some(ident) => ident, + let typename = match extract_clean_typename(ty) { + Some(tn) => tn, None => return None, }; - let type_name = match &*ident.to_string() { + let type_name = match &*typename { "Json" => "Json<_>", "RawBody" => "RawBody<_>", "RawForm" => "RawForm", diff --git a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs new file mode 100644 index 0000000000..c0d2a3e12b --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs @@ -0,0 +1,29 @@ +use axum::response::AppendHeaders; + +#[axum::debug_handler] +async fn handler( +) -> ( + axum::http::StatusCode, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, + axum::http::StatusCode, +) { + panic!() +} + +fn main(){} \ No newline at end of file diff --git a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr new file mode 100644 index 0000000000..9cd05bcbba --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr @@ -0,0 +1,48 @@ +error: Output Tuple cannot have more than 16 arguments. + --> tests/debug_handler/fail/output_tuple_too_many.rs:5:3 + | +5 | ) -> ( + | ___^ +6 | | axum::http::StatusCode, +7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, +8 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, +... | +24 | | axum::http::StatusCode, +25 | | ) { + | |_^ + +error[E0277]: the trait bound `(StatusCode, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, StatusCode): IntoResponse` is not satisfied + --> tests/debug_handler/fail/output_tuple_too_many.rs:5:6 + | +5 | ) -> ( + | ______^ +6 | | axum::http::StatusCode, +7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, +8 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, +... | +24 | | axum::http::StatusCode, +25 | | ) { + | |_^ the trait `IntoResponse` is not implemented for `(StatusCode, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, StatusCode)` + | + = help: the following other types implement trait `IntoResponse`: + () + (R,) + (Response<()>, R) + (Response<()>, T1, R) + (Response<()>, T1, T2, R) + (Response<()>, T1, T2, T3, R) + (Response<()>, T1, T2, T3, T4, R) + (Response<()>, T1, T2, T3, T4, T5, R) + and $N others +note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` + --> tests/debug_handler/fail/output_tuple_too_many.rs:5:6 + | +5 | ) -> ( + | ______^ +6 | | axum::http::StatusCode, +7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, +8 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, +... | +24 | | axum::http::StatusCode, +25 | | ) { + | |_^ required by this bound in `check` From b31520000147ad9e135a1638698a856c1917e43e Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 2 Sep 2023 22:19:05 +0800 Subject: [PATCH 03/23] added hints when named IntoResponse is placed in the middle of tuple. It gives the hint "This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element." But currently it only detects Json and String, more types can be added. --- axum-macros/src/debug_handler.rs | 12 ++++- .../fail/wrong_return_tuple.stderr | 45 +++++++++++++------ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 421d856e37..540a0fc416 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -306,7 +306,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { return None; } //Amount of IntoRequestParts - let mut parts_amount = 0; + let mut parts_amount: i8 = 0; let token_stream = WithPosition::new(elements.iter()) .enumerate() @@ -340,7 +340,15 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { .to_compile_error(); error } else { - check_into_response_parts(ty, parts_amount) + let name = _check_into_response_not_parts(ty); + match name { + Some(_) => { + quote_spanned!{ty.span() => + compile_error!("This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element"); + } + }, + None => check_into_response_parts(ty, parts_amount) + } } } _ => quote! {}, diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr index 8c75c43d53..204fe934b9 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr @@ -1,18 +1,37 @@ -error[E0277]: the trait bound `Json<&'static str>: IntoResponseParts` is not satisfied +error: This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element --> tests/debug_handler/fail/wrong_return_tuple.rs:5:5 | 5 | axum::Json<&'static str>, - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoResponseParts` is not implemented for `Json<&'static str>` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `(StatusCode, Json<&str>, AppendHeaders<[(HeaderName, &str); 1]>): IntoResponse` is not satisfied + --> tests/debug_handler/fail/wrong_return_tuple.rs:3:23 | - = help: the following other types implement trait `IntoResponseParts`: - (T1, T2) - (T1, T2, T3) - (T1, T2, T3, T4) - (T1, T2, T3, T4, T5) - (T1, T2, T3, T4, T5, T6) - (T1, T2, T3, T4, T5, T6, T7) - (T1, T2, T3, T4, T5, T6, T7, T8) - (T1, T2, T3, T4, T5, T6, T7, T8, T9) +3 | async fn handler() -> ( + | _______________________^ +4 | | axum::http::StatusCode, +5 | | axum::Json<&'static str>, +6 | | axum::response::AppendHeaders<[( axum::http::HeaderName,&'static str); 1]>, +7 | | ) { + | |_^ the trait `IntoResponse` is not implemented for `(StatusCode, Json<&str>, AppendHeaders<[(HeaderName, &str); 1]>)` + | + = help: the following other types implement trait `IntoResponse`: + () + (R,) + (Response<()>, R) + (Response<()>, T1, R) + (Response<()>, T1, T2, R) + (Response<()>, T1, T2, T3, R) + (Response<()>, T1, T2, T3, T4, R) + (Response<()>, T1, T2, T3, T4, T5, R) and $N others - = help: see issue #48214 - = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable +note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` + --> tests/debug_handler/fail/wrong_return_tuple.rs:3:23 + | +3 | async fn handler() -> ( + | _______________________^ +4 | | axum::http::StatusCode, +5 | | axum::Json<&'static str>, +6 | | axum::response::AppendHeaders<[( axum::http::HeaderName,&'static str); 1]>, +7 | | ) { + | |_^ required by this bound in `check` From 4cd8498aeb723ba7eda85a4eaf7680173c085b6f Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 2 Sep 2023 22:26:16 +0800 Subject: [PATCH 04/23] formatted the file --- axum-macros/src/debug_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 540a0fc416..ff7309296b 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -343,7 +343,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { let name = _check_into_response_not_parts(ty); match name { Some(_) => { - quote_spanned!{ty.span() => + quote_spanned!{ty.span()=> compile_error!("This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element"); } }, From 1007d378db1bfe31ecdb8d640d4293beb13f69e2 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 11 Sep 2023 21:14:43 +0800 Subject: [PATCH 05/23] Added more tests, handled single tuple, and used filter and map for the Position::First --- axum-macros/src/debug_handler.rs | 66 ++++++++++++------- .../fail/single_wrong_return_tuple.rs | 12 ++++ .../fail/single_wrong_return_tuple.stderr | 21 ++++++ .../debug_handler/fail/wrong_return_tuple.rs | 21 +++++- .../fail/wrong_return_tuple.stderr | 53 ++++++--------- 5 files changed, 114 insertions(+), 59 deletions(-) create mode 100644 axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs create mode 100644 axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index ff7309296b..1927794044 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -4,7 +4,7 @@ use crate::{ attr_parsing::{parse_assignment_attribute, second}, with_position::{Position, WithPosition}, }; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, ReturnType, Token, Type}; @@ -286,7 +286,7 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr .collect::() } -///If the output is a tuple with 2 or more elements, +///If the output is a tuple with 1 or more elements, /// it checks with the following pattern, /// first element => StatusCode || Parts || IntoResponseParts ///last element => IntoResponse @@ -301,10 +301,18 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { }, _ => return None, }; + let handler_ident = &item_fn.sig.ident; - if elements.len() < 2 { + if elements.len() == 0 { return None; } + if elements.len() == 1 { + return Some(check_into_response( + handler_ident, + elements.first().unwrap(), + )); + } + //Amount of IntoRequestParts let mut parts_amount: i8 = 0; @@ -313,25 +321,23 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { .map(|(_idx, arg)| match &arg { //First element type in the tuple Position::First(ty) => { - let typename = extract_clean_typename(ty); - if typename.is_none() { - quote! {} - } else { - let typename = typename.unwrap(); - match &*typename.to_string() { - "Parts" => quote! {}, - "Response" => quote! {}, + extract_clean_typename(ty) + .filter(|typename| { + match &**typename { + "Parts" => false, + "Response" => false, "StatusCode" => { - quote! {} - } - _ => { - parts_amount += 1; - check_into_response_parts(ty, parts_amount) + false } + _ => true } - } + }) + .map(|_| { + parts_amount += 1; + check_into_response_parts(ty,handler_ident, parts_amount) + }).unwrap_or(quote!{}) } - Position::Last(ty) => check_into_response(ty), + Position::Last(ty) => check_into_response(handler_ident,ty), Position::Middle(ty) => { parts_amount += 1; if parts_amount > 16 { @@ -347,7 +353,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { compile_error!("This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element"); } }, - None => check_into_response_parts(ty, parts_amount) + None => check_into_response_parts(ty,handler_ident, parts_amount) } } } @@ -373,12 +379,20 @@ fn _check_into_response_not_parts(ty: &Type) -> Option<&'static str> { } } -fn check_into_response(ty: &Type) -> TokenStream { +fn check_into_response(handler: &Ident, ty: &Type) -> TokenStream { let (span, ty) = (ty.span(), ty.clone()); - let check_fn = format_ident!("__axum_macros_check_into_response_check", span = span,); + let check_fn = format_ident!( + "__axum_macros_check_{}_into_response_check", + handler, + span = span, + ); - let call_check_fn = format_ident!("__axum_macros_check_into_response_call_check", span = span,); + let call_check_fn = format_ident!( + "__axum_macros_check_{}_into_response_call_check", + handler, + span = span, + ); let call_check_fn_body = quote_spanned! {span=> #check_fn(); @@ -408,16 +422,18 @@ fn check_into_response(ty: &Type) -> TokenStream { } } -fn check_into_response_parts(ty: &Type, index: i8) -> TokenStream { +fn check_into_response_parts(ty: &Type, ident: &Ident, index: i8) -> TokenStream { let (span, ty) = (ty.span(), ty.clone()); let check_fn = format_ident!( - "__axum_macros_check_into_response_parts_{index}_check", + "__axum_macros_check_{}_into_response_parts_{index}_check", + ident, span = span, ); let call_check_fn = format_ident!( - "__axum_macros_check_into_response_parts_{index}_call_check", + "__axum_macros_check_{}_into_response_parts_{index}_call_check", + ident, span = span, ); diff --git a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs new file mode 100644 index 0000000000..e85f1f4561 --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs @@ -0,0 +1,12 @@ +#![allow(unused_parens)] + +struct NotIntoResponse; + +#[axum::debug_handler] +async fn handler() -> (NotIntoResponse) { + panic!() +} + +fn main(){ + +} \ No newline at end of file diff --git a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr new file mode 100644 index 0000000000..8909373553 --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `NotIntoResponse: IntoResponse` is not satisfied + --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 + | +6 | async fn handler() -> (NotIntoResponse) { + | ^^^^^^^^^^^^^^^^^ the trait `IntoResponse` is not implemented for `NotIntoResponse` + | + = help: the following other types implement trait `IntoResponse`: + &'static [u8; N] + &'static [u8] + &'static str + () + (R,) + (Response<()>, R) + (Response<()>, T1, R) + (Response<()>, T1, T2, R) + and $N others +note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` + --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 + | +6 | async fn handler() -> (NotIntoResponse) { + | ^^^^^^^^^^^^^^^^^ required by this bound in `check` diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs index fa5197a81d..8cf5a3587f 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs @@ -1,6 +1,7 @@ +#![allow(unused_parens)] #[axum::debug_handler] -async fn handler() -> ( +async fn named_type() -> ( axum::http::StatusCode, axum::Json<&'static str>, axum::response::AppendHeaders<[( axum::http::HeaderName,&'static str); 1]>, @@ -8,4 +9,22 @@ async fn handler() -> ( panic!() } + +struct CustomIntoResponse{ + +} +impl axum::response::IntoResponse for CustomIntoResponse{ + fn into_response(self) -> axum::response::Response { + todo!() + } +} +#[axum::debug_handler] +async fn custom_type() -> ( + axum::http::StatusCode, + CustomIntoResponse, + axum::response::AppendHeaders<[( axum::http::HeaderName,&'static str); 1]>, +) { + panic!() +} + fn main(){} \ No newline at end of file diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr index 204fe934b9..2909f06492 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr @@ -1,37 +1,24 @@ error: This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element - --> tests/debug_handler/fail/wrong_return_tuple.rs:5:5 + --> tests/debug_handler/fail/wrong_return_tuple.rs:6:5 | -5 | axum::Json<&'static str>, +6 | axum::Json<&'static str>, | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `(StatusCode, Json<&str>, AppendHeaders<[(HeaderName, &str); 1]>): IntoResponse` is not satisfied - --> tests/debug_handler/fail/wrong_return_tuple.rs:3:23 - | -3 | async fn handler() -> ( - | _______________________^ -4 | | axum::http::StatusCode, -5 | | axum::Json<&'static str>, -6 | | axum::response::AppendHeaders<[( axum::http::HeaderName,&'static str); 1]>, -7 | | ) { - | |_^ the trait `IntoResponse` is not implemented for `(StatusCode, Json<&str>, AppendHeaders<[(HeaderName, &str); 1]>)` - | - = help: the following other types implement trait `IntoResponse`: - () - (R,) - (Response<()>, R) - (Response<()>, T1, R) - (Response<()>, T1, T2, R) - (Response<()>, T1, T2, T3, R) - (Response<()>, T1, T2, T3, T4, R) - (Response<()>, T1, T2, T3, T4, T5, R) - and $N others -note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` - --> tests/debug_handler/fail/wrong_return_tuple.rs:3:23 - | -3 | async fn handler() -> ( - | _______________________^ -4 | | axum::http::StatusCode, -5 | | axum::Json<&'static str>, -6 | | axum::response::AppendHeaders<[( axum::http::HeaderName,&'static str); 1]>, -7 | | ) { - | |_^ required by this bound in `check` +error[E0277]: the trait bound `CustomIntoResponse: IntoResponseParts` is not satisfied + --> tests/debug_handler/fail/wrong_return_tuple.rs:24:5 + | +24 | CustomIntoResponse, + | ^^^^^^^^^^^^^^^^^^ the trait `IntoResponseParts` is not implemented for `CustomIntoResponse` + | + = help: the following other types implement trait `IntoResponseParts`: + (T1, T2) + (T1, T2, T3) + (T1, T2, T3, T4) + (T1, T2, T3, T4, T5) + (T1, T2, T3, T4, T5, T6) + (T1, T2, T3, T4, T5, T6, T7) + (T1, T2, T3, T4, T5, T6, T7, T8) + (T1, T2, T3, T4, T5, T6, T7, T8, T9) + and $N others + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable From a5d9d0ecb2e5ac55f92d6d47c32753041d06a010 Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 13 Sep 2023 16:59:12 +0800 Subject: [PATCH 06/23] Use Position::Only to handle single tuple --- axum-macros/src/debug_handler.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 1927794044..0d2c923f72 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, vec}; use crate::{ attr_parsing::{parse_assignment_attribute, second}, @@ -303,16 +303,6 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { }; let handler_ident = &item_fn.sig.ident; - if elements.len() == 0 { - return None; - } - if elements.len() == 1 { - return Some(check_into_response( - handler_ident, - elements.first().unwrap(), - )); - } - //Amount of IntoRequestParts let mut parts_amount: i8 = 0; @@ -356,8 +346,11 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { None => check_into_response_parts(ty,handler_ident, parts_amount) } } - } - _ => quote! {}, + }, + Position::Only(ty) => check_into_response( + handler_ident, + ty, + ), }) .collect::(); Some(token_stream) From 1b259baedda5d9c90acc92c6efe24e388bd8ea15 Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 13 Sep 2023 17:01:19 +0800 Subject: [PATCH 07/23] cargo fix --- axum-macros/src/debug_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 0d2c923f72..95da22849e 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, vec}; +use std::{collections::HashSet}; use crate::{ attr_parsing::{parse_assignment_attribute, second}, From eab871618e06f2b2dcb2c38a4c699f399dc87b56 Mon Sep 17 00:00:00 2001 From: Brian Date: Fri, 6 Oct 2023 02:19:56 +0800 Subject: [PATCH 08/23] fix clippy warnings --- .idea/.gitignore | 8 ++++++++ .idea/axum.iml | 9 +++++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ axum-macros/src/debug_handler.rs | 22 +++++++--------------- axum/src/routing/tests/fallback.rs | 12 ++++++++++++ 6 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/axum.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000..13566b81b0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/axum.iml b/.idea/axum.iml new file mode 100644 index 0000000000..d6ebd48059 --- /dev/null +++ b/.idea/axum.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000..f217b524be --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..35eb1ddfbb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 95da22849e..a556a13bff 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet}; +use std::collections::HashSet; use crate::{ attr_parsing::{parse_assignment_attribute, second}, @@ -299,7 +299,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { Type::Tuple(tuple) => &tuple.elems, _ => return None, }, - _ => return None, + ReturnType::Default => return None, }; let handler_ident = &item_fn.sig.ident; @@ -313,28 +313,20 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { Position::First(ty) => { extract_clean_typename(ty) .filter(|typename| { - match &**typename { - "Parts" => false, - "Response" => false, - "StatusCode" => { - false - } - _ => true - } + matches!(&**typename, "Parts" | "Response" | "StatusCode") }) .map(|_| { parts_amount += 1; check_into_response_parts(ty,handler_ident, parts_amount) - }).unwrap_or(quote!{}) + }).unwrap_or_default() } Position::Last(ty) => check_into_response(handler_ident,ty), Position::Middle(ty) => { parts_amount += 1; if parts_amount > 16 { - let error_message = format!("Output Tuple cannot have more than 16 arguments."); - let error = syn::Error::new_spanned(&item_fn.sig.output, error_message) - .to_compile_error(); - error + let error_message = "Output Tuple cannot have more than 16 arguments."; + syn::Error::new_spanned(&item_fn.sig.output, error_message) + .to_compile_error() } else { let name = _check_into_response_not_parts(ty); match name { diff --git a/axum/src/routing/tests/fallback.rs b/axum/src/routing/tests/fallback.rs index d0800467b8..3c98a4040c 100644 --- a/axum/src/routing/tests/fallback.rs +++ b/axum/src/routing/tests/fallback.rs @@ -31,6 +31,18 @@ async fn nest() { assert_eq!(res.text().await, "fallback"); } +#[crate::test] +async fn two() { + let app = Router::new() + .route("/first", get(|| async {})) + .route("/second", get(|| async {})) + .fallback(get(|| async { "fallback" })); + let client = TestClient::new(app); + let res = client.get("/does-not-exist").send().await; + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.text().await, "fallback"); +} + #[crate::test] async fn or() { let one = Router::new().route("/one", get(|| async {})); From e6a639d2cc184281a3dedc1ff356f7820dc7109a Mon Sep 17 00:00:00 2001 From: SpeedReach <37238439+SpeedReach@users.noreply.github.com> Date: Fri, 10 Nov 2023 00:17:24 +0800 Subject: [PATCH 09/23] Fixed tests, and simplify error messages. I did an early return when the tuple check fails, cause the `check_output_impls_into_response` would clutter the error message in this case. --- axum-macros/src/debug_handler.rs | 18 +++++----- .../fail/output_tuple_too_many.stderr | 36 ------------------- .../fail/single_wrong_return_tuple.stderr | 16 ++++----- .../fail/wrong_return_tuple.stderr | 14 ++++---- 4 files changed, 25 insertions(+), 59 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index a556a13bff..9efb1f2964 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -16,6 +16,9 @@ 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_tuples = check_output_tuples(&item_fn); + if !check_output_tuples.is_empty() { + return check_output_tuples; + } let check_output_impls_into_response = check_output_impls_into_response(&item_fn); // If the function is generic, we can't reliably check its inputs or whether the future it @@ -292,28 +295,28 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr ///last element => IntoResponse ///other elements => IntoResponseParts ///the max numbers of IntoResponseParts(16) -fn check_output_tuples(item_fn: &ItemFn) -> Option { +fn check_output_tuples(item_fn: &ItemFn) -> TokenStream { //Extract tuple types let elements = match &item_fn.sig.output { ReturnType::Type(_, ty) => match &**ty { Type::Tuple(tuple) => &tuple.elems, - _ => return None, + _ => return quote! {}, }, - ReturnType::Default => return None, + ReturnType::Default => return quote! {}, }; let handler_ident = &item_fn.sig.ident; //Amount of IntoRequestParts let mut parts_amount: i8 = 0; - - let token_stream = WithPosition::new(elements.iter()) + WithPosition::new(elements.iter()) .enumerate() .map(|(_idx, arg)| match &arg { //First element type in the tuple Position::First(ty) => { + extract_clean_typename(ty) .filter(|typename| { - matches!(&**typename, "Parts" | "Response" | "StatusCode") + !matches!(&**typename, "Parts" | "Response" | "StatusCode") }) .map(|_| { parts_amount += 1; @@ -344,8 +347,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> Option { ty, ), }) - .collect::(); - Some(token_stream) + .collect::() } fn _check_into_response_not_parts(ty: &Type) -> Option<&'static str> { diff --git a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr index 9cd05bcbba..750ef3729c 100644 --- a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr +++ b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr @@ -10,39 +10,3 @@ error: Output Tuple cannot have more than 16 arguments. 24 | | axum::http::StatusCode, 25 | | ) { | |_^ - -error[E0277]: the trait bound `(StatusCode, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, StatusCode): IntoResponse` is not satisfied - --> tests/debug_handler/fail/output_tuple_too_many.rs:5:6 - | -5 | ) -> ( - | ______^ -6 | | axum::http::StatusCode, -7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, -8 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, -... | -24 | | axum::http::StatusCode, -25 | | ) { - | |_^ the trait `IntoResponse` is not implemented for `(StatusCode, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, AppendHeaders<[(HeaderName, &str); 1]>, StatusCode)` - | - = help: the following other types implement trait `IntoResponse`: - () - (R,) - (Response<()>, R) - (Response<()>, T1, R) - (Response<()>, T1, T2, R) - (Response<()>, T1, T2, T3, R) - (Response<()>, T1, T2, T3, T4, R) - (Response<()>, T1, T2, T3, T4, T5, R) - and $N others -note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` - --> tests/debug_handler/fail/output_tuple_too_many.rs:5:6 - | -5 | ) -> ( - | ______^ -6 | | axum::http::StatusCode, -7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, -8 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, -... | -24 | | axum::http::StatusCode, -25 | | ) { - | |_^ required by this bound in `check` diff --git a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr index 8909373553..a6ffb0ed1c 100644 --- a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr +++ b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr @@ -5,14 +5,14 @@ error[E0277]: the trait bound `NotIntoResponse: IntoResponse` is not satisfied | ^^^^^^^^^^^^^^^^^ the trait `IntoResponse` is not implemented for `NotIntoResponse` | = help: the following other types implement trait `IntoResponse`: - &'static [u8; N] - &'static [u8] - &'static str - () - (R,) - (Response<()>, R) - (Response<()>, T1, R) - (Response<()>, T1, T2, R) + Box + Box<[u8]> + axum::body::Bytes + Body + axum::extract::rejection::FailedToBufferBody + axum::extract::rejection::LengthLimitError + axum::extract::rejection::UnknownBodyError + axum::extract::rejection::InvalidUtf8 and $N others note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr index 2909f06492..f16d780125 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr @@ -11,14 +11,14 @@ error[E0277]: the trait bound `CustomIntoResponse: IntoResponseParts` is not sat | ^^^^^^^^^^^^^^^^^^ the trait `IntoResponseParts` is not implemented for `CustomIntoResponse` | = help: the following other types implement trait `IntoResponseParts`: + AppendHeaders + HeaderMap + Extension + Extensions + Option + [(K, V); N] + (T1,) (T1, T2) - (T1, T2, T3) - (T1, T2, T3, T4) - (T1, T2, T3, T4, T5) - (T1, T2, T3, T4, T5, T6) - (T1, T2, T3, T4, T5, T6, T7) - (T1, T2, T3, T4, T5, T6, T7, T8) - (T1, T2, T3, T4, T5, T6, T7, T8, T9) and $N others = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable From 863d21ff4692aa211e2d2baeedba670bbedbe4e9 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 20:25:07 +0100 Subject: [PATCH 10/23] remove `.idea` dir --- .idea/.gitignore | 8 -------- .idea/axum.iml | 9 --------- .idea/modules.xml | 8 -------- .idea/vcs.xml | 6 ------ 4 files changed, 31 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/axum.iml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81b0..0000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/axum.iml b/.idea/axum.iml deleted file mode 100644 index d6ebd48059..0000000000 --- a/.idea/axum.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index f217b524be..0000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfbb..0000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From e87f8f1e526bcd24b46ef27d22809a53a112df1a Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 20:26:43 +0100 Subject: [PATCH 11/23] fix tests --- .../tests/debug_handler/fail/single_wrong_return_tuple.stderr | 2 +- axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr index a6ffb0ed1c..d1ac6d9d8e 100644 --- a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr +++ b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr @@ -12,7 +12,7 @@ error[E0277]: the trait bound `NotIntoResponse: IntoResponse` is not satisfied axum::extract::rejection::FailedToBufferBody axum::extract::rejection::LengthLimitError axum::extract::rejection::UnknownBodyError - axum::extract::rejection::InvalidUtf8 + bytes::bytes_mut::BytesMut and $N others note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr index f16d780125..85f7abb8fe 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr @@ -17,8 +17,8 @@ error[E0277]: the trait bound `CustomIntoResponse: IntoResponseParts` is not sat Extensions Option [(K, V); N] + () (T1,) - (T1, T2) and $N others = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable From c9f40cef7245ffdcda7a81bf472b1c969cea64d5 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 21:44:52 +0100 Subject: [PATCH 12/23] clean up code a bit --- axum-macros/src/debug_handler.rs | 157 ++++++++++-------- axum-macros/src/with_position.rs | 4 +- .../fail/output_tuple_too_many.stderr | 2 +- .../fail/wrong_return_tuple.stderr | 2 +- 4 files changed, 91 insertions(+), 74 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 9efb1f2964..9c8a0baa25 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -16,10 +16,11 @@ 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_tuples = check_output_tuples(&item_fn); - if !check_output_tuples.is_empty() { - return check_output_tuples; - } - let check_output_impls_into_response = check_output_impls_into_response(&item_fn); + let check_output_impls_into_response = if check_output_tuples.is_empty() { + check_output_impls_into_response(&item_fn) + } else { + check_output_tuples + }; // 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. @@ -76,7 +77,6 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream { #item_fn #check_extractor_count #check_path_extractor - #check_output_tuples #check_output_impls_into_response #check_inputs_and_future_send } @@ -185,7 +185,7 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr FnArg::Typed(typed) => is_self_pat_type(typed), }); - WithPosition::new(item_fn.sig.inputs.iter()) + WithPosition::new(&item_fn.sig.inputs) .enumerate() .map(|(idx, arg)| { let must_impl_from_request_parts = match &arg { @@ -289,80 +289,59 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr .collect::() } -///If the output is a tuple with 1 or more elements, -/// it checks with the following pattern, -/// first element => StatusCode || Parts || IntoResponseParts -///last element => IntoResponse -///other elements => IntoResponseParts -///the max numbers of IntoResponseParts(16) fn check_output_tuples(item_fn: &ItemFn) -> TokenStream { - //Extract tuple types - let elements = match &item_fn.sig.output { + let elems = match &item_fn.sig.output { ReturnType::Type(_, ty) => match &**ty { Type::Tuple(tuple) => &tuple.elems, _ => return quote! {}, }, ReturnType::Default => return quote! {}, }; - let handler_ident = &item_fn.sig.ident; - //Amount of IntoRequestParts - let mut parts_amount: i8 = 0; - WithPosition::new(elements.iter()) - .enumerate() - .map(|(_idx, arg)| match &arg { - //First element type in the tuple - Position::First(ty) => { + let handler_ident = &item_fn.sig.ident; - extract_clean_typename(ty) - .filter(|typename| { - !matches!(&**typename, "Parts" | "Response" | "StatusCode") - }) - .map(|_| { - parts_amount += 1; - check_into_response_parts(ty,handler_ident, parts_amount) - }).unwrap_or_default() - } - Position::Last(ty) => check_into_response(handler_ident,ty), - Position::Middle(ty) => { - parts_amount += 1; - if parts_amount > 16 { - let error_message = "Output Tuple cannot have more than 16 arguments."; - syn::Error::new_spanned(&item_fn.sig.output, error_message) + match elems.len() { + 0 => quote! {}, + n if n > 17 => syn::Error::new_spanned( + &item_fn.sig.output, + "Cannot return tuples with more 17 elements", + ) + .to_compile_error(), + _ => WithPosition::new(elems) + .enumerate() + .map(|(idx, arg)| match arg { + Position::First(ty) => match extract_clean_typename(ty).as_deref() { + Some("StatusCode" | "Response") => quote! {}, + Some("Parts") => check_is_response_parts(ty, handler_ident, idx), + Some(_) | None => { + if let Some(tn) = well_known_last_response_type(ty) { + syn::Error::new_spanned( + ty, + format!( + "`{tn}` must be the last element \ + in a response tuple" + ), + ) + .to_compile_error() + } else { + check_into_response_parts(ty, handler_ident, idx) + } + } + }, + Position::Middle(ty) => { + if let Some(tn) = well_known_last_response_type(ty) { + syn::Error::new_spanned( + ty, + format!("`{tn}` must be the last element in a response tuple"), + ) .to_compile_error() - } else { - let name = _check_into_response_not_parts(ty); - match name { - Some(_) => { - quote_spanned!{ty.span()=> - compile_error!("This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element"); - } - }, - None => check_into_response_parts(ty,handler_ident, parts_amount) + } else { + check_into_response_parts(ty, handler_ident, idx) } } - }, - Position::Only(ty) => check_into_response( - handler_ident, - ty, - ), - }) - .collect::() -} - -fn _check_into_response_not_parts(ty: &Type) -> Option<&'static str> { - let name = extract_clean_typename(ty); - match name { - Some(name) => { - let s = match &*name { - "Json" => "Json<_>", - "String" => "String", - //todo add more types that implement IntoResponse but not IntoResponseParts - _ => return None, - }; - Some(s) - } - None => None, + Position::Last(ty) | Position::Only(ty) => check_into_response(handler_ident, ty), + }) + .collect::(), } } @@ -409,7 +388,26 @@ fn check_into_response(handler: &Ident, ty: &Type) -> TokenStream { } } -fn check_into_response_parts(ty: &Type, ident: &Ident, index: i8) -> TokenStream { +fn check_is_response_parts(ty: &Type, ident: &Ident, index: usize) -> TokenStream { + let (span, ty) = (ty.span(), ty.clone()); + + let check_fn = format_ident!( + "__axum_macros_check_{}_is_response_parts_{index}_check", + ident, + span = span, + ); + + quote_spanned! {span=> + #[allow(warnings)] + #[allow(unreachable_code)] + #[doc(hidden)] + fn #check_fn(parts: #ty) -> ::axum::http::response::Parts { + parts + } + } +} + +fn check_into_response_parts(ty: &Type, ident: &Ident, index: usize) -> TokenStream { let (span, ty) = (ty.span(), ty.clone()); let check_fn = format_ident!( @@ -502,7 +500,7 @@ fn check_input_order(item_fn: &ItemFn) -> Option { compile_error!(#error); }) } else { - let types = WithPosition::new(types_that_consume_the_request.into_iter()) + let types = WithPosition::new(types_that_consume_the_request) .map(|pos| match pos { Position::First((_, type_name, _)) | Position::Middle((_, type_name, _)) => { format!("`{type_name}`, ") @@ -555,6 +553,25 @@ fn request_consuming_type_name(ty: &Type) -> Option<&'static str> { Some(type_name) } +fn well_known_last_response_type(ty: &Type) -> Option<&'static str> { + let typename = match extract_clean_typename(ty) { + Some(tn) => tn, + None => return None, + }; + + let type_name = match &*typename { + "Json" => "Json<_>", + "Protobuf" => "Protobuf", + "JsonLines" => "JsonLines<_>", + "Form" => "Form<_>", + "Bytes" => "Bytes", + "String" => "String", + _ => return None, + }; + + Some(type_name) +} + fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream { let ty = match &item_fn.sig.output { syn::ReturnType::Default => return quote! {}, diff --git a/axum-macros/src/with_position.rs b/axum-macros/src/with_position.rs index 2e0caa5022..e064a3f01e 100644 --- a/axum-macros/src/with_position.rs +++ b/axum-macros/src/with_position.rs @@ -40,10 +40,10 @@ impl WithPosition where I: Iterator, { - pub(crate) fn new(iter: I) -> WithPosition { + pub(crate) fn new(iter: impl IntoIterator) -> WithPosition { WithPosition { handled_first: false, - peekable: iter.fuse().peekable(), + peekable: iter.into_iter().fuse().peekable(), } } } diff --git a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr index 750ef3729c..91f13db953 100644 --- a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr +++ b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr @@ -1,4 +1,4 @@ -error: Output Tuple cannot have more than 16 arguments. +error: Cannot return tuples with more 17 elements --> tests/debug_handler/fail/output_tuple_too_many.rs:5:3 | 5 | ) -> ( diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr index 85f7abb8fe..5e1eb0bae0 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.stderr @@ -1,4 +1,4 @@ -error: This type only implements IntoResponse but not IntoResponseParts, try moving it to the last element +error: `Json<_>` must be the last element in a response tuple --> tests/debug_handler/fail/wrong_return_tuple.rs:6:5 | 6 | axum::Json<&'static str>, From 98bb82802d3acc005b9ba4be954b4df154afaabf Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 21:52:54 +0100 Subject: [PATCH 13/23] add test for returning request parts --- .../debug_handler/fail/returning_request_parts.rs | 10 ++++++++++ .../debug_handler/fail/returning_request_parts.stderr | 8 ++++++++ 2 files changed, 18 insertions(+) create mode 100644 axum-macros/tests/debug_handler/fail/returning_request_parts.rs create mode 100644 axum-macros/tests/debug_handler/fail/returning_request_parts.stderr diff --git a/axum-macros/tests/debug_handler/fail/returning_request_parts.rs b/axum-macros/tests/debug_handler/fail/returning_request_parts.rs new file mode 100644 index 0000000000..e29cba3f23 --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/returning_request_parts.rs @@ -0,0 +1,10 @@ +#[axum::debug_handler] +async fn handler( +) -> ( + axum::http::request::Parts, // this should be response parts, not request parts + axum::http::StatusCode, +) { + panic!() +} + +fn main(){} diff --git a/axum-macros/tests/debug_handler/fail/returning_request_parts.stderr b/axum-macros/tests/debug_handler/fail/returning_request_parts.stderr new file mode 100644 index 0000000000..c3935ad52d --- /dev/null +++ b/axum-macros/tests/debug_handler/fail/returning_request_parts.stderr @@ -0,0 +1,8 @@ +error[E0308]: mismatched types + --> tests/debug_handler/fail/returning_request_parts.rs:4:5 + | +4 | axum::http::request::Parts, // this should be response parts, not request parts + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected `axum::http::response::Parts`, found `axum::http::request::Parts` + | expected `axum::http::response::Parts` because of return type From b3c3363bdbd7c50f682348ed9cab4cdb6f1f48e5 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 21:54:36 +0100 Subject: [PATCH 14/23] changelog --- axum-macros/CHANGELOG.md | 4 +++- axum/CHANGELOG.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/axum-macros/CHANGELOG.md b/axum-macros/CHANGELOG.md index 90dfd9bc59..1e8897eb0c 100644 --- a/axum-macros/CHANGELOG.md +++ b/axum-macros/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- None. +- **fixed:** Improve `debug_handler` on tuple response types ([#2201]) + +[#2201]: https://github.com/tokio-rs/axum/pull/2201 # 0.4.0 (27. November, 2023) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 6d52cd6c4e..3dd43f1a33 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- None. +- **fixed:** Improve `debug_handler` on tuple response types ([#2201]) + +[#2201]: https://github.com/tokio-rs/axum/pull/2201 # 0.7.3 (29. December, 2023) From f04d3a4b6ac896877f51108e009c7c792309a89a Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:31:54 +0100 Subject: [PATCH 15/23] fix indent --- axum-macros/src/debug_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 9c8a0baa25..0d9c83c452 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -319,7 +319,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> TokenStream { ty, format!( "`{tn}` must be the last element \ - in a response tuple" + in a response tuple" ), ) .to_compile_error() From 1439d035b196fc33f4dbfe91015ce90e034d5a0e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:32:19 +0100 Subject: [PATCH 16/23] inline format args --- axum-macros/src/debug_handler.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 0d9c83c452..1fa82332cc 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -349,14 +349,12 @@ fn check_into_response(handler: &Ident, ty: &Type) -> TokenStream { let (span, ty) = (ty.span(), ty.clone()); let check_fn = format_ident!( - "__axum_macros_check_{}_into_response_check", - handler, + "__axum_macros_check_{handler}_into_response_check", span = span, ); let call_check_fn = format_ident!( - "__axum_macros_check_{}_into_response_call_check", - handler, + "__axum_macros_check_{handler}_into_response_call_check", span = span, ); From 84638dfc9f93d31dd9472cd199f20a52cab52cec Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:32:38 +0100 Subject: [PATCH 17/23] formatting --- axum-macros/src/debug_handler.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 1fa82332cc..95d9f69dc4 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -379,8 +379,7 @@ fn check_into_response(handler: &Ident, ty: &Type) -> TokenStream { #[allow(warnings)] #[allow(unreachable_code)] #[doc(hidden)] - fn #call_check_fn() - { + fn #call_check_fn() { #call_check_fn_body } } From 40e06f22b25a17b3aa7f90421212511a40fd5672 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:32:52 +0100 Subject: [PATCH 18/23] use import --- axum-macros/src/debug_handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 95d9f69dc4..baece114d9 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -365,7 +365,7 @@ fn check_into_response(handler: &Ident, ty: &Type) -> TokenStream { let from_request_bound = quote_spanned! {span=> #ty: ::axum::response::IntoResponse }; - quote::quote_spanned! {span=> + quote_spanned! {span=> #[allow(warnings)] #[allow(unreachable_code)] #[doc(hidden)] @@ -426,7 +426,7 @@ fn check_into_response_parts(ty: &Type, ident: &Ident, index: usize) -> TokenStr let from_request_bound = quote_spanned! {span=> #ty: ::axum::response::IntoResponseParts }; - quote::quote_spanned! {span=> + quote_spanned! {span=> #[allow(warnings)] #[allow(unreachable_code)] #[doc(hidden)] From 80e51a1212889481a4425d126e24092bdd33c57d Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:33:15 +0100 Subject: [PATCH 19/23] more formatting --- axum-macros/src/debug_handler.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index baece114d9..b0cee3274e 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -280,8 +280,7 @@ fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStr #[allow(warnings)] #[allow(unreachable_code)] #[doc(hidden)] - fn #call_check_fn() - { + fn #call_check_fn() { #call_check_fn_body } } @@ -440,8 +439,7 @@ fn check_into_response_parts(ty: &Type, ident: &Ident, index: usize) -> TokenStr #[allow(warnings)] #[allow(unreachable_code)] #[doc(hidden)] - fn #call_check_fn() - { + fn #call_check_fn() { #call_check_fn_body } } From 0750e26aa24bbc9c7cde5bf50f6f51c6a4a527db Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:33:38 +0100 Subject: [PATCH 20/23] ? --- axum-macros/src/debug_handler.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index b0cee3274e..26d969fdb6 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -525,10 +525,7 @@ fn extract_clean_typename(ty: &Type) -> Option { } fn request_consuming_type_name(ty: &Type) -> Option<&'static str> { - let typename = match extract_clean_typename(ty) { - Some(tn) => tn, - None => return None, - }; + let typename = extract_clean_typename(ty)?; let type_name = match &*typename { "Json" => "Json<_>", From 44402d547a8d13dc0b67b99070b41a7eba0ca375 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:34:20 +0100 Subject: [PATCH 21/23] more formatting --- axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs | 2 +- .../tests/debug_handler/fail/single_wrong_return_tuple.rs | 4 +--- axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs index c0d2a3e12b..a6f1ba0866 100644 --- a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs +++ b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs @@ -26,4 +26,4 @@ async fn handler( panic!() } -fn main(){} \ No newline at end of file +fn main() {} diff --git a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs index e85f1f4561..452fa21d2e 100644 --- a/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs +++ b/axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs @@ -7,6 +7,4 @@ async fn handler() -> (NotIntoResponse) { panic!() } -fn main(){ - -} \ No newline at end of file +fn main() {} diff --git a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs index 8cf5a3587f..c2b4249520 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs +++ b/axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs @@ -27,4 +27,4 @@ async fn custom_type() -> ( panic!() } -fn main(){} \ No newline at end of file +fn main() {} From e9adb827cc681254840d9df4d1b98a8e8f106143 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:36:58 +0100 Subject: [PATCH 22/23] Update axum-macros/src/debug_handler.rs Co-authored-by: Jonas Platte --- axum-macros/src/debug_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 26d969fdb6..90e2ae9262 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -303,7 +303,7 @@ fn check_output_tuples(item_fn: &ItemFn) -> TokenStream { 0 => quote! {}, n if n > 17 => syn::Error::new_spanned( &item_fn.sig.output, - "Cannot return tuples with more 17 elements", + "Cannot return tuples with more than 17 elements", ) .to_compile_error(), _ => WithPosition::new(elems) From 0ae6972791cfd61f6e08b5a8ea9131f3f35f639f Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 30 Dec 2023 22:44:52 +0100 Subject: [PATCH 23/23] fix UI test output --- .../tests/debug_handler/fail/output_tuple_too_many.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr index 91f13db953..b6eb85079d 100644 --- a/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr +++ b/axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr @@ -1,4 +1,4 @@ -error: Cannot return tuples with more 17 elements +error: Cannot return tuples with more than 17 elements --> tests/debug_handler/fail/output_tuple_too_many.rs:5:3 | 5 | ) -> (