Allow custom generic bounds in 'Responder' derive.

This commit is contained in:
Sergio Benitez 2021-05-26 02:26:11 -07:00
parent 02d6c4c6f1
commit 41d7138540
14 changed files with 626 additions and 98 deletions

View File

@ -14,7 +14,7 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1.0" 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] [dev-dependencies]
version_check = "0.9" version_check = "0.9"

View File

@ -19,12 +19,12 @@ indexmap = "1.0"
quote = "1.0" quote = "1.0"
syn = { version = "1.0.72", features = ["full", "visit", "visit-mut", "extra-traits"] } syn = { version = "1.0.72", features = ["full", "visit", "visit-mut", "extra-traits"] }
proc-macro2 = "1.0.27" 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/" } rocket_http = { version = "0.5.0-dev", path = "../http/" }
unicode-xid = "0.2" unicode-xid = "0.2"
glob = "0.3" glob = "0.3"
[dev-dependencies] [dev-dependencies]
rocket = { version = "0.5.0-dev", path = "../lib" } rocket = { version = "0.5.0-dev", path = "../lib", features = ["json"] }
version_check = "0.9" version_check = "0.9"
trybuild = "1.0" trybuild = "1.0"

View File

@ -47,8 +47,8 @@ fn context_type(input: Input<'_>) -> (TokenStream, Option<syn::WhereClause>) {
} }
let span = input.ident().span(); let span = input.ident().span();
gen.add_type_bound(&syn::parse_quote!(#_form::FromForm<#lifetime>)); gen.add_type_bound(syn::parse_quote!(#_form::FromForm<#lifetime>));
gen.add_type_bound(&syn::TypeParamBound::from(lifetime)); gen.add_type_bound(syn::TypeParamBound::from(lifetime));
let (_, ty_gen, where_clause) = gen.split_for_impl(); let (_, ty_gen, where_clause) = gen.split_for_impl();
(quote_spanned!(span => FromFormGeneratedContext #ty_gen), where_clause.cloned()) (quote_spanned!(span => FromFormGeneratedContext #ty_gen), where_clause.cloned())
} }

View File

@ -1,12 +1,17 @@
use quote::ToTokens; use quote::ToTokens;
use devise::{*, ext::{TypeExt, SpanDiagnosticExt}}; use devise::{*, ext::{TypeExt, SpanDiagnosticExt, GenericsExt}};
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use syn::punctuated::Punctuated;
use syn::parse::Parser;
use crate::exports::*; use crate::exports::*;
use crate::http_codegen::{ContentType, Status}; use crate::http_codegen::{ContentType, Status};
type WherePredicates = Punctuated<syn::WherePredicate, syn::Token![,]>;
#[derive(Debug, Default, FromMeta)] #[derive(Debug, Default, FromMeta)]
struct ItemAttr { struct ItemAttr {
bound: Option<SpanWrapped<String>>,
content_type: Option<SpanWrapped<ContentType>>, content_type: Option<SpanWrapped<ContentType>>,
status: Option<SpanWrapped<Status>>, status: Option<SpanWrapped<Status>>,
} }
@ -17,20 +22,32 @@ struct FieldAttr {
} }
pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream { 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) DeriveGenerator::build_for(input, impl_tokens)
.support(Support::Struct | Support::Enum | Support::Lifetime | Support::Type) .support(Support::Struct | Support::Enum | Support::Lifetime | Support::Type)
.replace_generic(1, 0) .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() .validator(ValidatorBuild::new()
.input_validate(|_, i| match i.generics().lifetimes().count() > 1 { .input_validate(|_, i| match i.generics().lifetimes().count() > 1 {
true => Err(i.generics().span().error("only one lifetime is supported")), true => Err(i.generics().span().error("only one lifetime is supported")),
false => Ok(()) 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() { .fields_validate(|_, fields| match fields.is_empty() {
true => return Err(fields.span().error("need at least one field")), true => return Err(fields.span().error("need at least one field")),
false => Ok(()) false => Ok(())
@ -38,7 +55,7 @@ pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
) )
.inner_mapper(MapperBuild::new() .inner_mapper(MapperBuild::new()
.with_output(|_, output| quote! { .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 #output
} }
}) })

View File

@ -758,29 +758,52 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream {
/// ///
/// # Generics /// # 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 /// generic. If a type generic is present, the generated implementation will
/// require a bound of `Responder` for the generic. As such, the generic should /// require a bound of `Responder<'r, 'o>` for each generic unless a
/// be used as a `Responder`: /// `#[response(bound = ...)]` attribute as used:
/// ///
/// ```rust /// ```rust
/// # #[macro_use] extern crate rocket; /// # #[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)] /// #[derive(Responder)]
/// #[response(status = 404, content_type = "html")] /// #[response(status = 404, content_type = "html")]
/// struct NotFoundHtml<T>(T); /// struct NotFoundHtml<T>(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<T>(Json<T>);
///
/// // 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<T, E> {
/// Ok(Json<T>),
/// #[response(status = 404)]
/// Err(E, ContentType)
/// }
/// ``` /// ```
/// ///
/// If a lifetime generic is present, it will be used as the second lifetime /// If a lifetime generic is present, it will be replace with `'o` in the
/// paramter `'o` parameter in `impl Responder<'r, 'o>`: /// generated implementation `impl Responder<'r, 'o>`:
/// ///
/// ```rust /// ```rust
/// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket;
/// // Generates `impl<'r, 'o> Responder<'r, 'o> for NotFoundHtmlString<'o>`.
/// #[derive(Responder)] /// #[derive(Responder)]
/// #[response(status = 404, content_type = "html")] /// #[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 /// ```rust
/// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket;

View File

@ -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<Build> {
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("/<code>")] 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<Build> {
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());
}
}

View File

@ -1,6 +1,6 @@
use rocket::local::asynchronous::Client; use rocket::local::asynchronous::Client;
use rocket::response::Responder;
use rocket::http::{Status, ContentType, Cookie}; use rocket::http::{Status, ContentType, Cookie};
use rocket::response::Responder;
#[derive(Responder)] #[derive(Responder)]
pub enum Foo<'r> { 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.content_type(), Some(ContentType::new("application", "x-custom")));
assert_eq!(r.body_mut().to_string().await.unwrap(), "just a 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<T>),
#[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");
}

View File

@ -0,0 +1 @@
../ui-fail/responder.rs

View File

@ -1,158 +1,217 @@
error: need at least one field 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` 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 error: need at least one field
--> $DIR/responder.rs:10:14 --> $DIR/responder.rs:7:14
| |
10 | struct Thing2(); 7 | struct Thing2();
| ^^ | ^^
| |
note: error occurred while deriving `Responder` note: error occurred while deriving `Responder`
--> $DIR/responder.rs:9:10 --> $DIR/responder.rs:6:10
| |
9 | #[derive(Responder)] 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 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` 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 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` 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 error: invalid or unknown content type
--> $DIR/responder.rs:24:15 --> $DIR/responder.rs:25:27
| |
24 | struct Thing5<T>(T); 25 | #[response(content_type = "")]
| ^ | ^^
| |
note: error occurred while deriving `Responder` 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 error: invalid or unknown content type
--> $DIR/responder.rs:28:23 --> $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` 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 error: invalid value: expected string literal
--> $DIR/responder.rs:33:31 --> $DIR/responder.rs:33:27
| |
33 | #[response(content_type = "")] 33 | #[response(content_type = 100)]
| ^^ | ^^^
| |
note: error occurred while deriving `Responder` 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 error: status must be in range [100, 599]
--> $DIR/responder.rs:40:31 --> $DIR/responder.rs:37:21
| |
40 | #[response(content_type = "idk")] 37 | #[response(status = 8)]
| ^^^^^ | ^
| |
note: error occurred while deriving `Responder` 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 error: invalid value: expected unsigned integer literal
--> $DIR/responder.rs:47:31 --> $DIR/responder.rs:41:21
| |
47 | #[response(content_type = 100)] 41 | #[response(status = "404")]
| ^^^ | ^^^^^
| |
note: error occurred while deriving `Responder` 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) error: invalid value: expected unsigned integer literal
--> $DIR/responder.rs:54:25 --> $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` note: error occurred while deriving `Responder`
--> $DIR/responder.rs:52:10 --> $DIR/responder.rs:52:10
| |
52 | #[derive(Responder)] 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 error: invalid bound syntax: expected `:`
--> $DIR/responder.rs:61:25 --> $DIR/responder.rs:65:20
| |
61 | #[response(status = "404")] 65 | #[response(bound = "ponies are cool")]
| ^^^^^ | ^^^^^^^^^^^^^^^^^
| |
note: error occurred while deriving `Responder` 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 error: invalid bound syntax: expected `,`
--> $DIR/responder.rs:68:25 --> $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` 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 error[E0277]: the trait bound `Header<'_>: From<E>` is not satisfied
--> $DIR/responder.rs:75:45 --> $DIR/responder.rs:22:24
| |
75 | #[response(status = 404, content_type = 120)] 22 | struct Thing6<T, E>(T, E); // NO ERROR
| ^^^ | ^ the trait `From<E>` is not implemented for `Header<'_>`
| |
note: error occurred while deriving `Responder` = note: required because of the requirements on the impl of `Into<Header<'_>>` for `E`
--> $DIR/responder.rs:73:10 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<E>)]
| ^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
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>(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>(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<'_, '_>)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1 @@
../ui-fail/responder.rs

View File

@ -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<E>` is not satisfied
--> $DIR/responder.rs:22:24
|
22 | struct Thing6<T, E>(T, E); // NO ERROR
| ^ the trait `From<E>` is not implemented for `Header<'_>`
|
= note: required because of the requirements on the impl of `Into<Header<'_>>` 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<E>)]
| ^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied
--> $DIR/responder.rs:58:19
|
58 | struct Thing15<T>(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>(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<'_, '_>)]
| ^^^^^^^^^^^^^^^^^^^

View File

@ -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>(T); // NO ERROR
#[derive(Responder)]
struct Thing6<T, E>(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>(T);
#[derive(Responder)]
#[response(bound = "T: std::fmt::Display")]
struct Thing16<T>(T);
#[derive(Responder)]
#[response(bound = "ponies are cool")]
struct Thing17<T>(T);
#[derive(Responder)]
#[response(bound = "T: ROCKETS + ARE COOLER")]
struct Thing18<T>(T);
fn main() {}

View File

@ -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

View File

@ -160,6 +160,7 @@ impl<'r, T: Deserialize<'r>> MsgPack<T> {
Self::from_bytes(local_cache!(req, bytes)) Self::from_bytes(local_cache!(req, bytes))
} }
} }
#[crate::async_trait] #[crate::async_trait]
impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack<T> { impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack<T> {
type Error = Error; type Error = Error;