diff --git a/contrib/sync_db_pools/codegen/Cargo.toml b/contrib/sync_db_pools/codegen/Cargo.toml index b0a6d2b0..55ed587c 100644 --- a/contrib/sync_db_pools/codegen/Cargo.toml +++ b/contrib/sync_db_pools/codegen/Cargo.toml @@ -14,7 +14,7 @@ proc-macro = true [dependencies] quote = "1.0" -devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "df00b5" } +devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "f2431b" } [dev-dependencies] version_check = "0.9" diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml index 942bc6be..d06594bd 100644 --- a/core/codegen/Cargo.toml +++ b/core/codegen/Cargo.toml @@ -19,12 +19,12 @@ indexmap = "1.0" quote = "1.0" syn = { version = "1.0.72", features = ["full", "visit", "visit-mut", "extra-traits"] } proc-macro2 = "1.0.27" -devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "df00b5" } +devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "f2431b" } rocket_http = { version = "0.5.0-dev", path = "../http/" } unicode-xid = "0.2" glob = "0.3" [dev-dependencies] -rocket = { version = "0.5.0-dev", path = "../lib" } +rocket = { version = "0.5.0-dev", path = "../lib", features = ["json"] } version_check = "0.9" trybuild = "1.0" diff --git a/core/codegen/src/derive/from_form.rs b/core/codegen/src/derive/from_form.rs index c334fd5d..f6b37c57 100644 --- a/core/codegen/src/derive/from_form.rs +++ b/core/codegen/src/derive/from_form.rs @@ -47,8 +47,8 @@ fn context_type(input: Input<'_>) -> (TokenStream, Option) { } let span = input.ident().span(); - gen.add_type_bound(&syn::parse_quote!(#_form::FromForm<#lifetime>)); - gen.add_type_bound(&syn::TypeParamBound::from(lifetime)); + gen.add_type_bound(syn::parse_quote!(#_form::FromForm<#lifetime>)); + gen.add_type_bound(syn::TypeParamBound::from(lifetime)); let (_, ty_gen, where_clause) = gen.split_for_impl(); (quote_spanned!(span => FromFormGeneratedContext #ty_gen), where_clause.cloned()) } diff --git a/core/codegen/src/derive/responder.rs b/core/codegen/src/derive/responder.rs index d5b3a01d..203f4248 100644 --- a/core/codegen/src/derive/responder.rs +++ b/core/codegen/src/derive/responder.rs @@ -1,12 +1,17 @@ use quote::ToTokens; -use devise::{*, ext::{TypeExt, SpanDiagnosticExt}}; +use devise::{*, ext::{TypeExt, SpanDiagnosticExt, GenericsExt}}; use proc_macro2::TokenStream; +use syn::punctuated::Punctuated; +use syn::parse::Parser; use crate::exports::*; use crate::http_codegen::{ContentType, Status}; +type WherePredicates = Punctuated; + #[derive(Debug, Default, FromMeta)] struct ItemAttr { + bound: Option>, content_type: Option>, status: Option>, } @@ -17,20 +22,32 @@ struct FieldAttr { } pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream { - let impl_tokens = quote!(impl<'__r, '__o: '__r> ::rocket::response::Responder<'__r, '__o>); + let impl_tokens = quote!(impl<'r, 'o: 'r> ::rocket::response::Responder<'r, 'o>); DeriveGenerator::build_for(input, impl_tokens) .support(Support::Struct | Support::Enum | Support::Lifetime | Support::Type) .replace_generic(1, 0) - .type_bound(quote!(::rocket::response::Responder<'__r, '__o>)) + .type_bound_mapper(MapperBuild::new() + .try_input_map(|_, input| { + ItemAttr::one_from_attrs("response", input.attrs())? + .and_then(|attr| attr.bound) + .map(|bound| { + let span = bound.span; + let bounds = WherePredicates::parse_terminated.parse_str(&bound) + .map_err(|e| span.error(format!("invalid bound syntax: {}", e)))?; + Ok(quote_respanned!(span => #bounds)) + }) + .unwrap_or_else(|| { + let bound = quote!(::rocket::response::Responder<'r, 'o>); + let preds = input.generics().parsed_bounded_types(bound)?; + Ok(quote!(#preds)) + }) + }) + ) .validator(ValidatorBuild::new() .input_validate(|_, i| match i.generics().lifetimes().count() > 1 { true => Err(i.generics().span().error("only one lifetime is supported")), false => Ok(()) }) - .input_validate(|_, i| match i.generics().type_params().count() > 1 { - true => Err(i.generics().span().error("only one type generic is supported")), - false => Ok(()) - }) .fields_validate(|_, fields| match fields.is_empty() { true => return Err(fields.span().error("need at least one field")), false => Ok(()) @@ -38,7 +55,7 @@ pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream { ) .inner_mapper(MapperBuild::new() .with_output(|_, output| quote! { - fn respond_to(self, __req: &'__r #Request<'_>) -> #_response::Result<'__o> { + fn respond_to(self, __req: &'r #Request<'_>) -> #_response::Result<'o> { #output } }) diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index fe035993..e9d5608f 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -758,29 +758,52 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream { /// /// # Generics /// -/// The derive accepts at most one type generic and at most one lifetime +/// The derive accepts any number of type generics and at most one lifetime /// generic. If a type generic is present, the generated implementation will -/// require a bound of `Responder` for the generic. As such, the generic should -/// be used as a `Responder`: +/// require a bound of `Responder<'r, 'o>` for each generic unless a +/// `#[response(bound = ...)]` attribute as used: /// /// ```rust /// # #[macro_use] extern crate rocket; +/// use rocket::serde::Serialize; +/// use rocket::serde::json::Json; +/// use rocket::http::ContentType; +/// use rocket::response::Responder; +/// +/// // The bound `T: Responder` will be added to the generated implementation. /// #[derive(Responder)] /// #[response(status = 404, content_type = "html")] /// struct NotFoundHtml(T); +/// +/// // The bound `T: Serialize` will be added to the generated implementation. +/// // This would fail to compile otherwise. +/// #[derive(Responder)] +/// #[response(bound = "T: Serialize", status = 404)] +/// struct NotFoundJson(Json); +/// +/// // The bounds `T: Serialize, E: Responder` will be added to the generated +/// // implementation. This would fail to compile otherwise. +/// #[derive(Responder)] +/// #[response(bound = "T: Serialize, E: Responder<'r, 'o>")] +/// enum MyResult { +/// Ok(Json), +/// #[response(status = 404)] +/// Err(E, ContentType) +/// } /// ``` /// -/// If a lifetime generic is present, it will be used as the second lifetime -/// paramter `'o` parameter in `impl Responder<'r, 'o>`: +/// If a lifetime generic is present, it will be replace with `'o` in the +/// generated implementation `impl Responder<'r, 'o>`: /// /// ```rust /// # #[macro_use] extern crate rocket; +/// // Generates `impl<'r, 'o> Responder<'r, 'o> for NotFoundHtmlString<'o>`. /// #[derive(Responder)] /// #[response(status = 404, content_type = "html")] -/// struct NotFoundHtmlString<'o>(&'o str); +/// struct NotFoundHtmlString<'a>(&'a str); /// ``` /// -/// Both a type generic and lifetime generic may be used: +/// Both type generics and lifetime generic may be used: /// /// ```rust /// # #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/catcher.rs b/core/codegen/tests/catcher.rs new file mode 100644 index 00000000..e984560b --- /dev/null +++ b/core/codegen/tests/catcher.rs @@ -0,0 +1,60 @@ +// Rocket sometimes generates mangled identifiers that activate the +// non_snake_case lint. We deny the lint in this test to ensure that +// code generation uses #[allow(non_snake_case)] in the appropriate places. +#![deny(non_snake_case)] + +#[macro_use] extern crate rocket; + +use rocket::{Request, Rocket, Build}; +use rocket::local::blocking::Client; +use rocket::http::{Status, ContentType}; + +#[catch(404)] fn not_found_0() -> &'static str { "404-0" } +#[catch(404)] fn not_found_1(_: &Request) -> &'static str { "404-1" } +#[catch(404)] fn not_found_2(_: Status, _: &Request) -> &'static str { "404-2" } +#[catch(default)] fn all(_: Status, r: &Request) -> String { r.uri().to_string() } + +#[test] +fn test_simple_catchers() { + fn rocket() -> Rocket { + rocket::build() + .register("/0", catchers![not_found_0]) + .register("/1", catchers![not_found_1]) + .register("/2", catchers![not_found_2]) + .register("/", catchers![all]) + } + + let client = Client::debug(rocket()).unwrap(); + for i in 0..6 { + let response = client.get(format!("/{}", i)).dispatch(); + assert_eq!(response.status(), Status::NotFound); + + match i { + 0..=2 => assert_eq!(response.into_string().unwrap(), format!("404-{}", i)), + _ => assert_eq!(response.into_string().unwrap(), format!("/{}", i)), + } + } +} + +#[get("/")] fn forward(code: u16) -> Status { Status::new(code) } +#[catch(400)] fn forward_400(status: Status, _: &Request) -> String { status.code.to_string() } +#[catch(404)] fn forward_404(status: Status, _: &Request) -> String { status.code.to_string() } +#[catch(444)] fn forward_444(status: Status, _: &Request) -> String { status.code.to_string() } +#[catch(500)] fn forward_500(status: Status, _: &Request) -> String { status.code.to_string() } + +#[test] +fn test_status_param() { + fn rocket() -> Rocket { + rocket::build() + .mount("/", routes![forward]) + .register("/", catchers![forward_400, forward_404, forward_444, forward_500]) + } + + let client = Client::debug(rocket()).unwrap(); + for code in &[400, 404, 444, 400, 800, 3480] { + let response = client.get(uri!(forward(*code))).dispatch(); + let code = std::cmp::min(*code, 500); + assert_eq!(response.status(), Status::new(code)); + assert_eq!(response.into_string().unwrap(), code.to_string()); + } +} diff --git a/core/codegen/tests/responder.rs b/core/codegen/tests/responder.rs index 98b0e241..fbe84bb6 100644 --- a/core/codegen/tests/responder.rs +++ b/core/codegen/tests/responder.rs @@ -1,6 +1,6 @@ use rocket::local::asynchronous::Client; -use rocket::response::Responder; use rocket::http::{Status, ContentType, Cookie}; +use rocket::response::Responder; #[derive(Responder)] pub enum Foo<'r> { @@ -107,3 +107,42 @@ async fn responder_baz() { assert_eq!(r.content_type(), Some(ContentType::new("application", "x-custom"))); assert_eq!(r.body_mut().to_string().await.unwrap(), "just a custom"); } + +use rocket::serde::json::Json; + +// The bounds `T: Serialize, E: Responder` will be added to the generated +// implementation. This would fail to compile otherwise. +#[derive(Responder)] +#[response(bound = "T: rocket::serde::Serialize, E: Responder<'r, 'o>")] +enum MyResult<'a, T, E> { + Ok(Json), + #[response(status = 404)] + Err(E, ContentType), + #[response(status = 500)] + Other(&'a str), +} + +#[rocket::async_test] +async fn generic_responder() { + let client = Client::debug_with(vec![]).await.expect("valid rocket"); + let local_req = client.get("/"); + let req = local_req.inner(); + + let v: MyResult<_, ()> = MyResult::Ok(Json("hi")); + let mut r = v.respond_to(req).unwrap(); + assert_eq!(r.status(), Status::Ok); + assert_eq!(r.content_type().unwrap(), ContentType::JSON); + assert_eq!(r.body_mut().to_string().await.unwrap(), "\"hi\""); + + let v: MyResult<(), &[u8]> = MyResult::Err(&[7, 13, 23], ContentType::JPEG); + let mut r = v.respond_to(req).unwrap(); + assert_eq!(r.status(), Status::NotFound); + assert_eq!(r.content_type().unwrap(), ContentType::JPEG); + assert_eq!(r.body_mut().to_bytes().await.unwrap(), vec![7, 13, 23]); + + let v: MyResult<(), &[u8]> = MyResult::Other("beep beep"); + let mut r = v.respond_to(req).unwrap(); + assert_eq!(r.status(), Status::InternalServerError); + assert_eq!(r.content_type().unwrap(), ContentType::Text); + assert_eq!(r.body_mut().to_string().await.unwrap(), "beep beep"); +} diff --git a/core/codegen/tests/ui-fail-nightly/responder.rs b/core/codegen/tests/ui-fail-nightly/responder.rs new file mode 120000 index 00000000..3a70d154 --- /dev/null +++ b/core/codegen/tests/ui-fail-nightly/responder.rs @@ -0,0 +1 @@ +../ui-fail/responder.rs \ No newline at end of file diff --git a/core/codegen/tests/ui-fail-nightly/responder.stderr b/core/codegen/tests/ui-fail-nightly/responder.stderr index 95785fde..d2837381 100644 --- a/core/codegen/tests/ui-fail-nightly/responder.stderr +++ b/core/codegen/tests/ui-fail-nightly/responder.stderr @@ -1,158 +1,217 @@ error: need at least one field - --> $DIR/responder.rs:6:1 + --> $DIR/responder.rs:4:1 | -6 | struct Thing1; +4 | struct Thing1; | ^^^^^^^^^^^^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:5:10 + --> $DIR/responder.rs:3:10 | -5 | #[derive(Responder)] +3 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) error: need at least one field - --> $DIR/responder.rs:10:14 - | -10 | struct Thing2(); - | ^^ - | + --> $DIR/responder.rs:7:14 + | +7 | struct Thing2(); + | ^^ + | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:9:10 - | -9 | #[derive(Responder)] - | ^^^^^^^^^ + --> $DIR/responder.rs:6:10 + | +6 | #[derive(Responder)] + | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) error: need at least one field - --> $DIR/responder.rs:15:5 + --> $DIR/responder.rs:13:12 | -15 | Bark, - | ^^^^ +13 | enum Foo { Bark, } + | ^^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:13:10 + --> $DIR/responder.rs:12:10 | -13 | #[derive(Responder)] +12 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) error: only one lifetime is supported - --> $DIR/responder.rs:20:14 + --> $DIR/responder.rs:16:14 | -20 | struct Thing4<'a, 'b>(&'a str, &'b str); +16 | struct Thing4<'a, 'b>(&'a str, &'b str); | ^^^^^^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:19:10 + --> $DIR/responder.rs:15:10 | -19 | #[derive(Responder)] +15 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: type generics are not supported - --> $DIR/responder.rs:24:15 +error: invalid or unknown content type + --> $DIR/responder.rs:25:27 | -24 | struct Thing5(T); - | ^ +25 | #[response(content_type = "")] + | ^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:23:10 + --> $DIR/responder.rs:24:10 | -23 | #[derive(Responder)] +24 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: type generics are not supported - --> $DIR/responder.rs:28:23 +error: invalid or unknown content type + --> $DIR/responder.rs:29:27 | -28 | struct Thing6<'a, 'b, T>(&'a str, &'b str, T); - | ^ +29 | #[response(content_type = "idk")] + | ^^^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:27:10 + --> $DIR/responder.rs:28:10 | -27 | #[derive(Responder)] +28 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid or unknown content-type - --> $DIR/responder.rs:33:31 +error: invalid value: expected string literal + --> $DIR/responder.rs:33:27 | -33 | #[response(content_type = "")] - | ^^ +33 | #[response(content_type = 100)] + | ^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:31:10 + --> $DIR/responder.rs:32:10 | -31 | #[derive(Responder)] +32 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid or unknown content-type - --> $DIR/responder.rs:40:31 +error: status must be in range [100, 599] + --> $DIR/responder.rs:37:21 | -40 | #[response(content_type = "idk")] - | ^^^^^ +37 | #[response(status = 8)] + | ^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:38:10 + --> $DIR/responder.rs:36:10 | -38 | #[derive(Responder)] +36 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid value: expected string - --> $DIR/responder.rs:47:31 +error: invalid value: expected unsigned integer literal + --> $DIR/responder.rs:41:21 | -47 | #[response(content_type = 100)] - | ^^^ +41 | #[response(status = "404")] + | ^^^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:45:10 + --> $DIR/responder.rs:40:10 | -45 | #[derive(Responder)] +40 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: status must be in range [100, 600) - --> $DIR/responder.rs:54:25 +error: invalid value: expected unsigned integer literal + --> $DIR/responder.rs:45:21 | -54 | #[response(status = 8)] - | ^ +45 | #[response(status = "404", content_type = "html")] + | ^^^^^ + | +note: error occurred while deriving `Responder` + --> $DIR/responder.rs:44:10 + | +44 | #[derive(Responder)] + | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid value: expected string literal + --> $DIR/responder.rs:49:41 + | +49 | #[response(status = 404, content_type = 120)] + | ^^^ + | +note: error occurred while deriving `Responder` + --> $DIR/responder.rs:48:10 + | +48 | #[derive(Responder)] + | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid value: expected string literal + --> $DIR/responder.rs:53:20 + | +53 | #[response(bound = 10)] + | ^^ | note: error occurred while deriving `Responder` --> $DIR/responder.rs:52:10 | 52 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid value: expected unsigned integer - --> $DIR/responder.rs:61:25 +error: invalid bound syntax: expected `:` + --> $DIR/responder.rs:65:20 | -61 | #[response(status = "404")] - | ^^^^^ +65 | #[response(bound = "ponies are cool")] + | ^^^^^^^^^^^^^^^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:59:10 + --> $DIR/responder.rs:64:10 | -59 | #[derive(Responder)] +64 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid value: expected unsigned integer - --> $DIR/responder.rs:68:25 +error: invalid bound syntax: expected `,` + --> $DIR/responder.rs:69:20 | -68 | #[response(status = "404", content_type = "html")] - | ^^^^^ +69 | #[response(bound = "T: ROCKETS + ARE COOLER")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: error occurred while deriving `Responder` - --> $DIR/responder.rs:66:10 + --> $DIR/responder.rs:68:10 | -66 | #[derive(Responder)] +68 | #[derive(Responder)] | ^^^^^^^^^ + = note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid value: expected string - --> $DIR/responder.rs:75:45 +error[E0277]: the trait bound `Header<'_>: From` is not satisfied + --> $DIR/responder.rs:22:24 | -75 | #[response(status = 404, content_type = 120)] - | ^^^ +22 | struct Thing6(T, E); // NO ERROR + | ^ the trait `From` is not implemented for `Header<'_>` | -note: error occurred while deriving `Responder` - --> $DIR/responder.rs:73:10 + = note: required because of the requirements on the impl of `Into>` for `E` +help: consider extending the `where` bound, but there might be an alternative better way to express this requirement | -73 | #[derive(Responder)] - | ^^^^^^^^^ +21 | #[derive(Responder, Header<'_>: From)] + | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied + --> $DIR/responder.rs:58:19 + | +58 | struct Thing15(T); + | ^ the trait `Responder<'_, '_>` is not implemented for `T` + | + = note: required by `respond_to` +help: consider further restricting this bound + | +57 | #[response(bound = "T: std::fmt::Display" + rocket::response::Responder<'_, '_>)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied + --> $DIR/responder.rs:62:19 + | +62 | struct Thing16(T); + | ^ the trait `Responder<'_, '_>` is not implemented for `T` + | + = note: required by `respond_to` +help: consider further restricting this bound + | +61 | #[response(bound = "T: std::fmt::Display" + rocket::response::Responder<'_, '_>)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/core/codegen/tests/ui-fail-stable/responder.rs b/core/codegen/tests/ui-fail-stable/responder.rs new file mode 120000 index 00000000..3a70d154 --- /dev/null +++ b/core/codegen/tests/ui-fail-stable/responder.rs @@ -0,0 +1 @@ +../ui-fail/responder.rs \ No newline at end of file diff --git a/core/codegen/tests/ui-fail-stable/responder.stderr b/core/codegen/tests/ui-fail-stable/responder.stderr new file mode 100644 index 00000000..98c7a4c2 --- /dev/null +++ b/core/codegen/tests/ui-fail-stable/responder.stderr @@ -0,0 +1,231 @@ +error: need at least one field + --> $DIR/responder.rs:4:1 + | +4 | struct Thing1; + | ^^^^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:3:10 + | +3 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: need at least one field + --> $DIR/responder.rs:7:14 + | +7 | struct Thing2(); + | ^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:6:10 + | +6 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: need at least one field + --> $DIR/responder.rs:13:12 + | +13 | enum Foo { Bark, } + | ^^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:12:10 + | +12 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: only one lifetime is supported + --> $DIR/responder.rs:16:14 + | +16 | struct Thing4<'a, 'b>(&'a str, &'b str); + | ^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:15:10 + | +15 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid or unknown content type + --> $DIR/responder.rs:25:27 + | +25 | #[response(content_type = "")] + | ^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:24:10 + | +24 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid or unknown content type + --> $DIR/responder.rs:29:27 + | +29 | #[response(content_type = "idk")] + | ^^^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:28:10 + | +28 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid value: expected string literal + --> $DIR/responder.rs:33:27 + | +33 | #[response(content_type = 100)] + | ^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:32:10 + | +32 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: status must be in range [100, 599] + --> $DIR/responder.rs:37:21 + | +37 | #[response(status = 8)] + | ^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:36:10 + | +36 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid value: expected unsigned integer literal + --> $DIR/responder.rs:41:21 + | +41 | #[response(status = "404")] + | ^^^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:40:10 + | +40 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid value: expected unsigned integer literal + --> $DIR/responder.rs:45:21 + | +45 | #[response(status = "404", content_type = "html")] + | ^^^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:44:10 + | +44 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid value: expected string literal + --> $DIR/responder.rs:49:41 + | +49 | #[response(status = 404, content_type = 120)] + | ^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:48:10 + | +48 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid value: expected string literal + --> $DIR/responder.rs:53:20 + | +53 | #[response(bound = 10)] + | ^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:52:10 + | +52 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid bound syntax: expected `:` + --> $DIR/responder.rs:65:20 + | +65 | #[response(bound = "ponies are cool")] + | ^^^^^^^^^^^^^^^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:64:10 + | +64 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid bound syntax: expected `,` + --> $DIR/responder.rs:69:20 + | +69 | #[response(bound = "T: ROCKETS + ARE COOLER")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: [note] error occurred while deriving `Responder` + --> $DIR/responder.rs:68:10 + | +68 | #[derive(Responder)] + | ^^^^^^^^^ + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Header<'_>: From` is not satisfied + --> $DIR/responder.rs:22:24 + | +22 | struct Thing6(T, E); // NO ERROR + | ^ the trait `From` is not implemented for `Header<'_>` + | + = note: required because of the requirements on the impl of `Into>` for `E` +help: consider extending the `where` bound, but there might be an alternative better way to express this requirement + | +21 | #[derive(Responder, Header<'_>: From)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied + --> $DIR/responder.rs:58:19 + | +58 | struct Thing15(T); + | ^ the trait `Responder<'_, '_>` is not implemented for `T` + | + = note: required by `respond_to` +help: consider further restricting this bound + | +57 | #[response(bound = "T: std::fmt::Display" + Responder<'_, '_>)] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied + --> $DIR/responder.rs:62:19 + | +62 | struct Thing16(T); + | ^ the trait `Responder<'_, '_>` is not implemented for `T` + | + = note: required by `respond_to` +help: consider further restricting this bound + | +61 | #[response(bound = "T: std::fmt::Display" + Responder<'_, '_>)] + | ^^^^^^^^^^^^^^^^^^^ diff --git a/core/codegen/tests/ui-fail/responder.rs b/core/codegen/tests/ui-fail/responder.rs new file mode 100644 index 00000000..ec1271e7 --- /dev/null +++ b/core/codegen/tests/ui-fail/responder.rs @@ -0,0 +1,72 @@ +#[macro_use] extern crate rocket; + +#[derive(Responder)] +struct Thing1; + +#[derive(Responder)] +struct Thing2(); + +#[derive(Responder)] +enum Bar { } // NO ERROR + +#[derive(Responder)] +enum Foo { Bark, } + +#[derive(Responder)] +struct Thing4<'a, 'b>(&'a str, &'b str); + +#[derive(Responder)] +struct Thing5(T); // NO ERROR + +#[derive(Responder)] +struct Thing6(T, E); // NO ERROR + +#[derive(Responder)] +#[response(content_type = "")] +struct Thing7(()); + +#[derive(Responder)] +#[response(content_type = "idk")] +struct Thing8(()); + +#[derive(Responder)] +#[response(content_type = 100)] +struct Thing9(()); + +#[derive(Responder)] +#[response(status = 8)] +struct Thing10(()); + +#[derive(Responder)] +#[response(status = "404")] +struct Thing11(()); + +#[derive(Responder)] +#[response(status = "404", content_type = "html")] +struct Thing12(()); + +#[derive(Responder)] +#[response(status = 404, content_type = 120)] +struct Thing13(()); + +#[derive(Responder)] +#[response(bound = 10)] +struct Thing14(()); + +#[derive(Responder)] +#[response(bound = "T: std::fmt::Display")] +struct Thing15(T); + +#[derive(Responder)] +#[response(bound = "T: std::fmt::Display")] +struct Thing16(T); + +#[derive(Responder)] +#[response(bound = "ponies are cool")] +struct Thing17(T); + +#[derive(Responder)] +#[response(bound = "T: ROCKETS + ARE COOLER")] +struct Thing18(T); + +fn main() {} diff --git a/core/codegen/tests/ui-fail/synchronize.sh b/core/codegen/tests/ui-fail/synchronize.sh new file mode 100755 index 00000000..dc5c69bd --- /dev/null +++ b/core/codegen/tests/ui-fail/synchronize.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -e + +# Symlinks all of the tests in this directory with those in sibling +# `ui-fail-stable` and `ui-fail-nightly` directories. + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +stable="${SCRIPT_DIR}/../ui-fail-stable" +nightly="${SCRIPT_DIR}/../ui-fail-nightly" +anchor="$(basename ${SCRIPT_DIR})" + +echo ":: Synchronizing..." +echo " stable: ${stable}" +echo " nightly: ${nightly}" +echo " anchor: ${anchor}" + +for dir in "${stable}" "${nightly}"; do + find "${dir}" -type l -delete + + for file in "${SCRIPT_DIR}"/*.rs; do + ln -s "../${anchor}/$(basename $file)" "${dir}/" + done +done diff --git a/core/lib/src/serde/msgpack.rs b/core/lib/src/serde/msgpack.rs index e471c573..c83ec050 100644 --- a/core/lib/src/serde/msgpack.rs +++ b/core/lib/src/serde/msgpack.rs @@ -160,6 +160,7 @@ impl<'r, T: Deserialize<'r>> MsgPack { Self::from_bytes(local_cache!(req, bytes)) } } + #[crate::async_trait] impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack { type Error = Error;