From 4332e94090fe056c30dfa28724601838b38935e2 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 12:19:56 +0200 Subject: [PATCH 1/7] style: use standard `let..else` construct `let...else` provides a standard construct for this same pattern that people can easily recognize (and is IMO much less confusing). It's also more compact. This fixes the `manual_let_else` clippy lint. --- Cargo.toml | 1 + axum-extra/src/extract/json_deserializer.rs | 12 +++--------- axum-macros/src/from_request/mod.rs | 8 ++------ axum/src/extract/matched_path.rs | 4 +--- axum/src/extract/mod.rs | 8 ++------ axum/src/extract/ws.rs | 4 +--- 6 files changed, 10 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d5c6f25abf..3b484bbc56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ inefficient_to_string = "warn" linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" +manual_let_else = "warn" match_wildcard_for_single_variants = "warn" mem_forget = "warn" must_use_candidate = "warn" diff --git a/axum-extra/src/extract/json_deserializer.rs b/axum-extra/src/extract/json_deserializer.rs index 051ab0f1bd..8d53101999 100644 --- a/axum-extra/src/extract/json_deserializer.rs +++ b/axum-extra/src/extract/json_deserializer.rs @@ -183,21 +183,15 @@ composite_rejection! { } fn json_content_type(headers: &HeaderMap) -> bool { - let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) { - content_type - } else { + let Some(content_type) = headers.get(header::CONTENT_TYPE) else { return false; }; - let content_type = if let Ok(content_type) = content_type.to_str() { - content_type - } else { + let Ok(content_type) = content_type.to_str() else { return false; }; - let mime = if let Ok(mime) = content_type.parse::() { - mime - } else { + let Ok(mime) = content_type.parse::() else { return false; }; diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index 451ba9bfb1..cc8bca7bb2 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -642,9 +642,7 @@ fn extract_fields( } fn peel_option(ty: &syn::Type) -> Option<&syn::Type> { - let type_path = if let syn::Type::Path(type_path) = ty { - type_path - } else { + let syn::Type::Path(type_path) = ty else { return None; }; @@ -673,9 +671,7 @@ fn peel_option(ty: &syn::Type) -> Option<&syn::Type> { } fn peel_result_ok(ty: &syn::Type) -> Option<&syn::Type> { - let type_path = if let syn::Type::Path(type_path) = ty { - type_path - } else { + let syn::Type::Path(type_path) = ty else { return None; }; diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index 1dda92354c..eb6ef49929 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -103,9 +103,7 @@ pub(crate) fn set_matched_path_for_request( route_id_to_path: &HashMap>, extensions: &mut http::Extensions, ) { - let matched_path = if let Some(matched_path) = route_id_to_path.get(&id) { - matched_path - } else { + let Some(matched_path) = route_id_to_path.get(&id) else { #[cfg(debug_assertions)] panic!("should always have a matched path for a route id"); #[cfg(not(debug_assertions))] diff --git a/axum/src/extract/mod.rs b/axum/src/extract/mod.rs index 8a0af3da49..d2b19b684d 100644 --- a/axum/src/extract/mod.rs +++ b/axum/src/extract/mod.rs @@ -81,15 +81,11 @@ pub use self::ws::WebSocketUpgrade; // this is duplicated in `axum-extra/src/extract/form.rs` pub(super) fn has_content_type(headers: &HeaderMap, expected_content_type: &mime::Mime) -> bool { - let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) { - content_type - } else { + let Some(content_type) = headers.get(header::CONTENT_TYPE) else { return false; }; - let content_type = if let Ok(content_type) = content_type.to_str() { - content_type - } else { + let Ok(content_type) = content_type.to_str() else { return false; }; diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 67d9247c8f..83d336cb92 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -488,9 +488,7 @@ fn header_eq(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool } fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { - let header = if let Some(header) = headers.get(&key) { - header - } else { + let Some(header) = headers.get(&key) else { return false; }; From fcfca8c2f7f9644300e1d707184b3a1a36b97062 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Mon, 7 Jul 2025 23:05:54 +0200 Subject: [PATCH 2/7] refactor: simplify and combine `match` arms `match` arms with identical arm bodies are confusing to the reader because they hide the intent to the reader. This fixes the `match_same_arms` clippy lint. --- Cargo.toml | 1 + axum/src/extract/path/de.rs | 3 +-- axum/src/routing/method_routing.rs | 3 +-- axum/src/routing/mod.rs | 10 ++++------ 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3b484bbc56..27079158f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" manual_let_else = "warn" +match_same_arms = "warn" match_wildcard_for_single_variants = "warn" mem_forget = "warn" must_use_candidate = "warn" diff --git a/axum/src/extract/path/de.rs b/axum/src/extract/path/de.rs index ca78bb9e23..aae2ac7b75 100644 --- a/axum/src/extract/path/de.rs +++ b/axum/src/extract/path/de.rs @@ -639,8 +639,7 @@ enum KeyOrIdx<'de> { impl<'de> KeyOrIdx<'de> { fn key(&self) -> &'de str { match &self { - Self::Key(key) => key, - Self::Idx { key, .. } => key, + Self::Idx { key, .. } | Self::Key(key) => key, } } } diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 4a39542bd1..62f43595e9 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -573,8 +573,7 @@ impl AllowHeader { match (self, other) { (Self::Skip, _) | (_, Self::Skip) => Self::Skip, (Self::None, Self::None) => Self::None, - (Self::None, Self::Bytes(pick)) => Self::Bytes(pick), - (Self::Bytes(pick), Self::None) => Self::Bytes(pick), + (Self::None, Self::Bytes(pick)) | (Self::Bytes(pick), Self::None) => Self::Bytes(pick), (Self::Bytes(mut a), Self::Bytes(b)) => { a.extend_from_slice(b","); a.extend_from_slice(&b); diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index ce2425f1bc..eea39c4261 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -256,16 +256,13 @@ where map_inner!(self, mut this => { match (this.default_fallback, default_fallback) { - // both have the default fallback + // other has a default fallback // use the one from other - (true, true) => {} + (_, true) => {} // this has default fallback, other has a custom fallback (true, false) => { this.default_fallback = false; } - // this has a custom fallback, other has a default - (false, true) => { - } // both have a custom fallback, not allowed (false, false) => { panic!("Cannot merge two `Router`s that both have a fallback") @@ -707,8 +704,9 @@ where { fn merge(self, other: Self) -> Option { match (self, other) { - (Self::Default(_), pick @ Self::Default(_)) => Some(pick), + // If either are `Default`, return the opposite one. (Self::Default(_), pick) | (pick, Self::Default(_)) => Some(pick), + // Otherwise, return None _ => None, } } From 2660fb04e3321cbb524e1d6bc9731e0866cda522 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Mon, 7 Jul 2025 23:09:15 +0200 Subject: [PATCH 3/7] style: use `if let..else` instead of single match `if let..else` statements nest less than `match` statements with two arms and are therefore more readable. This fixes the `single_match_else` clippy lint. --- Cargo.toml | 1 + axum-extra/src/extract/cached.rs | 11 ++--- axum-macros/src/from_request/mod.rs | 63 +++++++++++++---------------- 3 files changed, 32 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27079158f1..00385cbafc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ needless_continue = "warn" option_option = "warn" rest_pat_in_fully_bound_structs = "warn" return_self_not_must_use = "warn" +single_match_else = "warn" str_to_string = "warn" suboptimal_flops = "warn" todo = "warn" diff --git a/axum-extra/src/extract/cached.rs b/axum-extra/src/extract/cached.rs index 64b4c3056f..eefe2e3de1 100644 --- a/axum-extra/src/extract/cached.rs +++ b/axum-extra/src/extract/cached.rs @@ -88,13 +88,10 @@ where type Rejection = T::Rejection; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - match Extension::>::from_request_parts(parts, state).await { - Ok(Extension(CachedEntry(value))) => Ok(Self(value)), - Err(_) => { - let value = T::from_request_parts(parts, state).await?; - parts.extensions.insert(CachedEntry(value.clone())); - Ok(Self(value)) - } + if let Ok(Extension(CachedEntry(value))) = Extension::>::from_request_parts(parts, state).await { Ok(Self(value)) } else { + let value = T::from_request_parts(parts, state).await?; + parts.extensions.insert(CachedEntry(value.clone())); + Ok(Self(value)) } } } diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index cc8bca7bb2..4474a68489 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -111,23 +111,20 @@ pub(crate) fn expand(item: syn::Item, tr: Trait) -> syn::Result { state, } = parse_attrs("from_request", &attrs)?; - let state = match state { - Some((_, state)) => State::Custom(state), - None => { - let mut inferred_state_types: HashSet<_> = - infer_state_type_from_field_types(&fields) - .chain(infer_state_type_from_field_attributes(&fields)) - .collect(); - - if let Some((_, via)) = &via { - inferred_state_types.extend(state_from_via(&ident, via)); - } + let state = if let Some((_, state)) = state { State::Custom(state) } else { + let mut inferred_state_types: HashSet<_> = + infer_state_type_from_field_types(&fields) + .chain(infer_state_type_from_field_attributes(&fields)) + .collect(); + + if let Some((_, via)) = &via { + inferred_state_types.extend(state_from_via(&ident, via)); + } - match inferred_state_types.len() { - 0 => State::Default(syn::parse_quote!(S)), - 1 => State::Custom(inferred_state_types.iter().next().unwrap().to_owned()), - _ => State::CannotInfer, - } + match inferred_state_types.len() { + 0 => State::Default(syn::parse_quote!(S)), + 1 => State::Custom(inferred_state_types.iter().next().unwrap().to_owned()), + _ => State::CannotInfer, } }; @@ -335,17 +332,14 @@ fn impl_struct_by_extracting_each_field( state: &State, tr: Trait, ) -> syn::Result { - let trait_fn_body = match state { - State::CannotInfer => quote! { - ::std::unimplemented!() - }, - _ => { - let extract_fields = extract_fields(&fields, &rejection, tr)?; - quote! { - ::std::result::Result::Ok(Self { - #(#extract_fields)* - }) - } + let trait_fn_body = if let State::CannotInfer = state { quote! { + ::std::unimplemented!() + } } else { + let extract_fields = extract_fields(&fields, &rejection, tr)?; + quote! { + ::std::result::Result::Ok(Self { + #(#extract_fields)* + }) } }; @@ -417,15 +411,12 @@ fn extract_fields( tr: Trait, ) -> syn::Result> { fn member(field: &syn::Field, index: usize) -> TokenStream { - match &field.ident { - Some(ident) => quote! { #ident }, - _ => { - let member = syn::Member::Unnamed(syn::Index { - index: index as u32, - span: field.span(), - }); - quote! { #member } - } + if let Some(ident) = &field.ident { quote! { #ident } } else { + let member = syn::Member::Unnamed(syn::Index { + index: index as u32, + span: field.span(), + }); + quote! { #member } } } From cd4af835b8292b3a4b5ca8475edc716e6f47b533 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Mon, 7 Jul 2025 23:17:48 +0200 Subject: [PATCH 4/7] style: use `matches!` instead of `if let` Pattern matching that are really just checking for equality are more idiomatically described using the `matches!` macro instead of `if let` statements. `if let` statements use Yoda notation [1] which increase cognitive load while reading the code. This fixes the `equatable_if_let` clippy lint. [1]: https://en.wikipedia.org/wiki/Yoda_conditions --- Cargo.toml | 1 + axum-macros/src/from_request/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 00385cbafc..6fd486957a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ await_holding_lock = "warn" dbg_macro = "warn" empty_enum = "warn" enum_glob_use = "warn" +equatable_if_let = "warn" exit = "warn" filter_map_next = "warn" fn_params_excessive_bools = "warn" diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index 4474a68489..319fbeb996 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -144,7 +144,7 @@ pub(crate) fn expand(item: syn::Item, tr: Trait) -> syn::Result { } }; - if let State::CannotInfer = state { + if matches!(state, State::CannotInfer) { let attr_name = match tr { Trait::FromRequest => "from_request", Trait::FromRequestParts => "from_request_parts", @@ -332,7 +332,7 @@ fn impl_struct_by_extracting_each_field( state: &State, tr: Trait, ) -> syn::Result { - let trait_fn_body = if let State::CannotInfer = state { quote! { + let trait_fn_body = if matches!(state, State::CannotInfer) { quote! { ::std::unimplemented!() } } else { let extract_fields = extract_fields(&fields, &rejection, tr)?; From a8baa2aecfeb3c7b71d8500ddf9e1685544d2bd4 Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Wed, 2 Jul 2025 16:15:54 +0200 Subject: [PATCH 5/7] style: use `let..else` for router downcast Seems to not have been found by clippy on my machine. This fixes the `manual_let_else` clippy lint. See-also: 4332e94090fe056c30dfa28724601838b38935e2 --- axum/src/routing/mod.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index eea39c4261..2d4c01418d 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -187,14 +187,11 @@ where T::Response: IntoResponse, T::Future: Send + 'static, { - let service = match try_downcast::(service) { - Ok(_) => { - panic!( - "Invalid route: `Router::route_service` cannot be used with `Router`s. \ - Use `Router::nest` instead" - ); - } - Err(service) => service, + let Err(service) = try_downcast::(service) else { + panic!( + "Invalid route: `Router::route_service` cannot be used with `Router`s. \ + Use `Router::nest` instead" + ); }; tap_inner!(self, mut this => { From 59505ca5bd9ab52c89059774c4ee14bf32518d1a Mon Sep 17 00:00:00 2001 From: Theodore Bjernhed Date: Tue, 8 Jul 2025 15:51:55 +0200 Subject: [PATCH 6/7] style(fmt): run `cargo fmt` Forgot to run cargo-fmt during the previous commits. --- axum-extra/src/extract/cached.rs | 6 +++++- axum-macros/src/from_request/mod.rs | 16 +++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/axum-extra/src/extract/cached.rs b/axum-extra/src/extract/cached.rs index eefe2e3de1..8dfde30d30 100644 --- a/axum-extra/src/extract/cached.rs +++ b/axum-extra/src/extract/cached.rs @@ -88,7 +88,11 @@ where type Rejection = T::Rejection; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - if let Ok(Extension(CachedEntry(value))) = Extension::>::from_request_parts(parts, state).await { Ok(Self(value)) } else { + if let Ok(Extension(CachedEntry(value))) = + Extension::>::from_request_parts(parts, state).await + { + Ok(Self(value)) + } else { let value = T::from_request_parts(parts, state).await?; parts.extensions.insert(CachedEntry(value.clone())); Ok(Self(value)) diff --git a/axum-macros/src/from_request/mod.rs b/axum-macros/src/from_request/mod.rs index 319fbeb996..f967fd105f 100644 --- a/axum-macros/src/from_request/mod.rs +++ b/axum-macros/src/from_request/mod.rs @@ -111,7 +111,9 @@ pub(crate) fn expand(item: syn::Item, tr: Trait) -> syn::Result { state, } = parse_attrs("from_request", &attrs)?; - let state = if let Some((_, state)) = state { State::Custom(state) } else { + let state = if let Some((_, state)) = state { + State::Custom(state) + } else { let mut inferred_state_types: HashSet<_> = infer_state_type_from_field_types(&fields) .chain(infer_state_type_from_field_attributes(&fields)) @@ -332,9 +334,11 @@ fn impl_struct_by_extracting_each_field( state: &State, tr: Trait, ) -> syn::Result { - let trait_fn_body = if matches!(state, State::CannotInfer) { quote! { - ::std::unimplemented!() - } } else { + let trait_fn_body = if matches!(state, State::CannotInfer) { + quote! { + ::std::unimplemented!() + } + } else { let extract_fields = extract_fields(&fields, &rejection, tr)?; quote! { ::std::result::Result::Ok(Self { @@ -411,7 +415,9 @@ fn extract_fields( tr: Trait, ) -> syn::Result> { fn member(field: &syn::Field, index: usize) -> TokenStream { - if let Some(ident) = &field.ident { quote! { #ident } } else { + if let Some(ident) = &field.ident { + quote! { #ident } + } else { let member = syn::Member::Unnamed(syn::Index { index: index as u32, span: field.span(), From cb663a09645a52002ef380a0839d38b36228be4d Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 9 Jul 2025 20:45:27 +0200 Subject: [PATCH 7/7] Fix indentation --- axum/src/routing/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 2d4c01418d..1f88480ffb 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -190,7 +190,7 @@ where let Err(service) = try_downcast::(service) else { panic!( "Invalid route: `Router::route_service` cannot be used with `Router`s. \ - Use `Router::nest` instead" + Use `Router::nest` instead" ); };