diff --git a/Cargo.toml b/Cargo.toml index 61d7e3d234..d5c6f25abf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,12 @@ lossy_float_literal = "warn" macro_use_imports = "warn" match_wildcard_for_single_variants = "warn" mem_forget = "warn" +must_use_candidate = "warn" needless_borrow = "warn" needless_continue = "warn" option_option = "warn" rest_pat_in_fully_bound_structs = "warn" +return_self_not_must_use = "warn" str_to_string = "warn" suboptimal_flops = "warn" todo = "warn" diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 6c49970b96..cbc5606b1b 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -34,6 +34,7 @@ where } /// The body type used in axum requests and responses. +#[must_use] #[derive(Debug)] pub struct Body(BoxBody); @@ -135,6 +136,7 @@ impl http_body::Body for Body { /// A stream of data frames. /// /// Created with [`Body::into_data_stream`]. +#[must_use] #[derive(Debug)] pub struct BodyDataStream { inner: Body, diff --git a/axum-core/src/error.rs b/axum-core/src/error.rs index 8c522c72b2..e77340e3cf 100644 --- a/axum-core/src/error.rs +++ b/axum-core/src/error.rs @@ -16,6 +16,7 @@ impl Error { } /// Convert an `Error` back into the underlying boxed trait object. + #[must_use] pub fn into_inner(self) -> BoxError { self.inner } diff --git a/axum-core/src/macros.rs b/axum-core/src/macros.rs index 8f2762486e..6bc24c3027 100644 --- a/axum-core/src/macros.rs +++ b/axum-core/src/macros.rs @@ -106,11 +106,13 @@ macro_rules! __define_rejection { } /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { self.to_string() } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { http::StatusCode::$status } @@ -179,6 +181,7 @@ macro_rules! __composite_rejection { impl $name { /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { match self { $( @@ -188,6 +191,7 @@ macro_rules! __composite_rejection { } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { match self { $( diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index a8734335e8..aada056aee 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -84,6 +84,7 @@ pub use cookie::Key; /// .route("/me", get(me)); /// # let app: Router = app; /// ``` +#[must_use = "`CookieJar` should be returned as part of a `Response`, otherwise it does nothing."] #[derive(Debug, Default, Clone)] pub struct CookieJar { jar: cookie::CookieJar, @@ -153,6 +154,7 @@ impl CookieJar { /// .map(|cookie| cookie.value().to_owned()); /// } /// ``` + #[must_use] pub fn get(&self, name: &str) -> Option<&Cookie<'static>> { self.jar.get(name) } @@ -169,7 +171,6 @@ impl CookieJar { /// jar.remove(Cookie::from("foo")) /// } /// ``` - #[must_use] pub fn remove>>(mut self, cookie: C) -> Self { self.jar.remove(cookie); self @@ -189,7 +190,6 @@ impl CookieJar { /// jar.add(Cookie::new("foo", "bar")) /// } /// ``` - #[must_use] #[allow(clippy::should_implement_trait)] pub fn add>>(mut self, cookie: C) -> Self { self.jar.add(cookie); diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index 841acb7484..cf72297c58 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -104,6 +104,7 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// } /// } /// ``` +#[must_use = "`PrivateCookieJar` should be returned as part of a `Response`, otherwise it does nothing."] pub struct PrivateCookieJar { jar: cookie::CookieJar, key: Key, @@ -201,6 +202,7 @@ impl PrivateCookieJar { /// .map(|cookie| cookie.value().to_owned()); /// } /// ``` + #[must_use] pub fn get(&self, name: &str) -> Option> { self.private_jar().get(name) } @@ -217,7 +219,6 @@ impl PrivateCookieJar { /// jar.remove(Cookie::from("foo")) /// } /// ``` - #[must_use] pub fn remove>>(mut self, cookie: C) -> Self { self.private_jar_mut().remove(cookie); self @@ -237,7 +238,6 @@ impl PrivateCookieJar { /// jar.add(Cookie::new("foo", "bar")) /// } /// ``` - #[must_use] #[allow(clippy::should_implement_trait)] pub fn add>>(mut self, cookie: C) -> Self { self.private_jar_mut().add(cookie); @@ -246,6 +246,7 @@ impl PrivateCookieJar { /// Authenticates and decrypts `cookie`, returning the plaintext version if decryption succeeds /// or `None` otherwise. + #[must_use] pub fn decrypt(&self, cookie: Cookie<'static>) -> Option> { self.private_jar().decrypt(cookie) } @@ -284,6 +285,7 @@ impl IntoResponse for PrivateCookieJar { } } +#[must_use = "iterators are lazy and do nothing unless consumed"] struct PrivateCookieJarIter<'a, K> { jar: &'a PrivateCookieJar, iter: cookie::Iter<'a>, diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index 3120674417..eb359ca380 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -121,6 +121,7 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// } /// } /// ``` +#[must_use = "`SignedCookieJar` should be returned as part of a `Response`, otherwise it does nothing."] pub struct SignedCookieJar { jar: cookie::CookieJar, key: Key, @@ -219,6 +220,7 @@ impl SignedCookieJar { /// .map(|cookie| cookie.value().to_owned()); /// } /// ``` + #[must_use] pub fn get(&self, name: &str) -> Option> { self.signed_jar().get(name) } @@ -235,7 +237,6 @@ impl SignedCookieJar { /// jar.remove(Cookie::from("foo")) /// } /// ``` - #[must_use] pub fn remove>>(mut self, cookie: C) -> Self { self.signed_jar_mut().remove(cookie); self @@ -255,7 +256,6 @@ impl SignedCookieJar { /// jar.add(Cookie::new("foo", "bar")) /// } /// ``` - #[must_use] #[allow(clippy::should_implement_trait)] pub fn add>>(mut self, cookie: C) -> Self { self.signed_jar_mut().add(cookie); @@ -264,6 +264,7 @@ impl SignedCookieJar { /// Verifies the authenticity and integrity of `cookie`, returning the plaintext version if /// verification succeeds or `None` otherwise. + #[must_use] pub fn verify(&self, cookie: Cookie<'static>) -> Option> { self.signed_jar().verify(cookie) } @@ -302,6 +303,7 @@ impl IntoResponse for SignedCookieJar { } } +#[must_use = "iterators are lazy and do nothing unless consumed"] struct SignedCookieJarIter<'a, K> { jar: &'a SignedCookieJar, iter: cookie::Iter<'a>, diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index e92dc1788e..39d67a0c92 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -150,6 +150,7 @@ impl Field { /// The field name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn name(&self) -> Option<&str> { self.inner.name() } @@ -157,16 +158,19 @@ impl Field { /// The file name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn file_name(&self) -> Option<&str> { self.inner.file_name() } /// Get the [content type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of the field. + #[must_use] pub fn content_type(&self) -> Option<&str> { self.inner.content_type().map(|m| m.as_ref()) } /// Get a map of headers as [`HeaderMap`]. + #[must_use] pub fn headers(&self) -> &HeaderMap { self.inner.headers() } @@ -253,6 +257,7 @@ impl MultipartError { } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { status_code_from_multer_error(&self.source) } diff --git a/axum-extra/src/response/file_stream.rs b/axum-extra/src/response/file_stream.rs index ed1afdff65..2f77168fd9 100644 --- a/axum-extra/src/response/file_stream.rs +++ b/axum-extra/src/response/file_stream.rs @@ -44,6 +44,7 @@ use tokio_util::io::ReaderStream; /// let app = Router::new().route("/file-stream", get(file_stream)); /// # let _: Router = app; /// ``` +#[must_use] #[derive(Debug)] pub struct FileStream { /// stream. diff --git a/axum-extra/src/response/multiple.rs b/axum-extra/src/response/multiple.rs index ddc6293beb..310210a129 100644 --- a/axum-extra/src/response/multiple.rs +++ b/axum-extra/src/response/multiple.rs @@ -8,6 +8,7 @@ use mime::Mime; /// Create multipart forms to be used in API responses. /// /// This struct implements [`IntoResponse`], and so it can be returned from a handler. +#[must_use] #[derive(Debug)] pub struct MultipartForm { parts: Vec, @@ -103,6 +104,7 @@ impl Part { /// let parts: Vec = vec![Part::text("foo".to_string(), "abc")]; /// let form = MultipartForm::from_iter(parts); /// ``` + #[must_use] pub fn text(name: String, contents: &str) -> Self { Self { name, @@ -127,6 +129,7 @@ impl Part { /// let parts: Vec = vec![Part::file("foo", "foo.txt", vec![0x68, 0x68, 0x20, 0x6d, 0x6f, 0x6d])]; /// let form = MultipartForm::from_iter(parts); /// ``` + #[must_use] pub fn file(field_name: &str, file_name: &str, contents: Vec) -> Self { Self { name: field_name.to_owned(), diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index cf85dc532a..45cb180e76 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -28,6 +28,7 @@ pub use self::typed::{SecondElementIs, TypedPath}; // Validates a path at compile time, used with the vpath macro. #[rustversion::since(1.80)] #[doc(hidden)] +#[must_use] pub const fn __private_validate_static_path(path: &'static str) -> &'static str { if path.is_empty() { panic!("Paths must start with a `/`. Use \"/\" for root routes") @@ -76,6 +77,7 @@ macro_rules! vpath { } /// Extension trait that adds additional methods to [`Router`]. +#[allow(clippy::return_self_not_must_use)] pub trait RouterExt: sealed::Sealed { /// Add a typed `GET` route to the router. /// diff --git a/axum-extra/src/typed_header.rs b/axum-extra/src/typed_header.rs index 7c08be9e38..41cf91819e 100644 --- a/axum-extra/src/typed_header.rs +++ b/axum-extra/src/typed_header.rs @@ -137,11 +137,13 @@ pub struct TypedHeaderRejection { impl TypedHeaderRejection { /// Name of the header that caused the rejection + #[must_use] pub fn name(&self) -> &http::header::HeaderName { self.name } /// Reason why the header extraction has failed + #[must_use] pub fn reason(&self) -> &TypedHeaderRejectionReason { &self.reason } diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index 0f5efba326..1dda92354c 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -58,6 +58,7 @@ pub struct MatchedPath(pub(crate) Arc); impl MatchedPath { /// Returns a `str` representation of the path. + #[must_use] pub fn as_str(&self) -> &str { &self.0 } diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 22ab7fa47c..e9832e8e84 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -146,6 +146,7 @@ impl Field<'_> { /// The field name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn name(&self) -> Option<&str> { self.inner.name() } @@ -153,16 +154,19 @@ impl Field<'_> { /// The file name found in the /// [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) /// header. + #[must_use] pub fn file_name(&self) -> Option<&str> { self.inner.file_name() } /// Get the [content type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of the field. + #[must_use] pub fn content_type(&self) -> Option<&str> { self.inner.content_type().map(|m| m.as_ref()) } /// Get a map of headers as [`HeaderMap`]. + #[must_use] pub fn headers(&self) -> &HeaderMap { self.inner.headers() } @@ -238,11 +242,13 @@ impl MultipartError { } /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { self.source.to_string() } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> http::StatusCode { status_code_from_multer_error(&self.source) } diff --git a/axum/src/extract/nested_path.rs b/axum/src/extract/nested_path.rs index 2e58d0e826..1d2c255e04 100644 --- a/axum/src/extract/nested_path.rs +++ b/axum/src/extract/nested_path.rs @@ -41,6 +41,7 @@ pub struct NestedPath(Arc); impl NestedPath { /// Returns a `str` representation of the path. + #[must_use] pub fn as_str(&self) -> &str { &self.0 } diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 9fd0837eb6..c2c01a039b 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -279,6 +279,7 @@ impl std::error::Error for PathDeserializationError {} /// This type is obtained through [`FailedToDeserializePathParams::kind`] or /// [`FailedToDeserializePathParams::into_kind`] and is useful for building /// more precise error messages. +#[must_use] #[derive(Debug, PartialEq, Eq)] #[non_exhaustive] pub enum ErrorKind { @@ -417,6 +418,7 @@ impl FailedToDeserializePathParams { } /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { match self.0.kind { ErrorKind::Message(_) @@ -432,6 +434,7 @@ impl FailedToDeserializePathParams { } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> StatusCode { match self.0.kind { ErrorKind::Message(_) @@ -523,6 +526,7 @@ where impl RawPathParams { /// Get an iterator over the path parameters. + #[must_use] pub fn iter(&self) -> RawPathParamsIter<'_> { self.into_iter() } @@ -561,11 +565,13 @@ pub struct InvalidUtf8InPathParam { impl InvalidUtf8InPathParam { /// Get the response body text used for this rejection. + #[must_use] pub fn body_text(&self) -> String { self.to_string() } /// Get the status code used for this rejection. + #[must_use] pub fn status(&self) -> StatusCode { StatusCode::BAD_REQUEST } diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index a7834bd03f..67d9247c8f 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -128,6 +128,7 @@ use tokio_tungstenite::{ /// /// [`MethodFilter`]: crate::routing::MethodFilter #[cfg_attr(docsrs, doc(cfg(feature = "ws")))] +#[must_use] pub struct WebSocketUpgrade { config: WebSocketConfig, /// The chosen protocol sent in the `Sec-WebSocket-Protocol` header of the response. @@ -580,6 +581,7 @@ pub struct Utf8Bytes(ts::Utf8Bytes); impl Utf8Bytes { /// Creates from a static str. #[inline] + #[must_use] pub const fn from_static(str: &'static str) -> Self { Self(ts::Utf8Bytes::from_static(str)) } diff --git a/axum/src/response/redirect.rs b/axum/src/response/redirect.rs index 4113c124e0..e33928cd26 100644 --- a/axum/src/response/redirect.rs +++ b/axum/src/response/redirect.rs @@ -56,11 +56,13 @@ impl Redirect { } /// Returns the HTTP status code of the `Redirect`. + #[must_use] pub fn status_code(&self) -> StatusCode { self.status_code } /// Returns the `Redirect`'s URI. + #[must_use] pub fn location(&self) -> &str { &self.location } diff --git a/axum/src/routing/method_filter.rs b/axum/src/routing/method_filter.rs index ddd7b21e00..4b3d142356 100644 --- a/axum/src/routing/method_filter.rs +++ b/axum/src/routing/method_filter.rs @@ -58,6 +58,7 @@ impl MethodFilter { } /// Performs the OR operation between the [`MethodFilter`] in `self` with `other`. + #[must_use] pub const fn or(self, other: Self) -> Self { Self(self.0 | other.0) } diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index b67f5798f3..4a39542bd1 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -703,6 +703,7 @@ impl MethodRouter<(), Infallible> { /// ``` /// /// [`MakeService`]: tower::make::MakeService + #[must_use] pub fn into_make_service(self) -> IntoMakeService { IntoMakeService::new(self.with_state(())) } @@ -736,6 +737,7 @@ impl MethodRouter<(), Infallible> { /// [`MakeService`]: tower::make::MakeService /// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info #[cfg(feature = "tokio")] + #[must_use] pub fn into_make_service_with_connect_info(self) -> IntoMakeServiceWithConnectInfo { IntoMakeServiceWithConnectInfo::new(self.with_state(())) } diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 5130c62e16..ce2425f1bc 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -317,6 +317,7 @@ where } /// True if the router currently has at least one route added. + #[must_use] pub fn has_routes(&self) -> bool { self.inner.path_router.has_routes() } @@ -513,6 +514,7 @@ where /// /// This is the same as [`Router::as_service`] instead it returns an owned [`Service`]. See /// that method for more details. + #[must_use] pub fn into_service(self) -> RouterIntoService { RouterIntoService { router: self, @@ -540,6 +542,7 @@ impl Router { /// ``` /// /// [`MakeService`]: tower::make::MakeService + #[must_use] pub fn into_make_service(self) -> IntoMakeService { // call `Router::with_state` such that everything is turned into `Route` eagerly // rather than doing that per request @@ -548,6 +551,7 @@ impl Router { #[doc = include_str!("../docs/routing/into_make_service_with_connect_info.md")] #[cfg(feature = "tokio")] + #[must_use] pub fn into_make_service_with_connect_info(self) -> IntoMakeServiceWithConnectInfo { // call `Router::with_state` such that everything is turned into `Route` eagerly // rather than doing that per request diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index 3891b60f0b..8f239e57c5 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -83,11 +83,13 @@ impl TestClient { } #[allow(dead_code)] + #[must_use] pub fn server_port(&self) -> u16 { self.addr.port() } } +#[must_use] pub struct RequestBuilder { builder: reqwest::RequestBuilder, }