Improve 'FromForm' derive error spans.

This commit is contained in:
Sergio Benitez 2021-03-03 23:13:09 -08:00
parent 398a044eb0
commit 7628546ca2
8 changed files with 132 additions and 14 deletions

View File

@ -19,7 +19,7 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1.0" quote = "1.0"
devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "bd221a4" } devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "3ebe83" }
[dev-dependencies] [dev-dependencies]
rocket = { version = "0.5.0-dev", path = "../../core/lib" } rocket = { version = "0.5.0-dev", path = "../../core/lib" }

View File

@ -18,7 +18,7 @@ proc-macro = true
indexmap = "1.0" indexmap = "1.0"
quote = "1.0" quote = "1.0"
rocket_http = { version = "0.5.0-dev", path = "../http/" } rocket_http = { version = "0.5.0-dev", path = "../http/" }
devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "bd221a4" } devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "3ebe83" }
unicode-xid = "0.2" unicode-xid = "0.2"
glob = "0.3" glob = "0.3"

View File

@ -328,7 +328,7 @@ fn incomplete_route(
let attribute = Attribute { let attribute = Attribute {
method: SpanWrapped { method: SpanWrapped {
full_span: method_span, span: method_span, value: Method(method) full_span: method_span, key_span: None, span: method_span, value: Method(method)
}, },
uri: method_attribute.uri, uri: method_attribute.uri,
data: method_attribute.data, data: method_attribute.data,

View File

@ -3,8 +3,8 @@ use devise::{*, ext::{TypeExt, SpanDiagnosticExt}};
use syn::visit_mut::VisitMut; use syn::visit_mut::VisitMut;
use syn::visit::Visit; use syn::visit::Visit;
use crate::exports::*;
use crate::proc_macro2::{Span, TokenStream, TokenTree}; use crate::proc_macro2::{Span, TokenStream, TokenTree};
use crate::syn_ext::IdentExt;
use crate::name::Name; use crate::name::Name;
pub struct FormField { pub struct FormField {
@ -15,7 +15,7 @@ pub struct FormField {
#[derive(FromMeta)] #[derive(FromMeta)]
pub struct FieldAttr { pub struct FieldAttr {
pub name: Option<FormField>, pub name: Option<FormField>,
pub validate: Option<syn::Expr>, pub validate: Option<SpanWrapped<syn::Expr>>,
} }
impl FieldAttr { impl FieldAttr {
@ -89,6 +89,7 @@ impl FieldExt for Field<'_> {
fn name_view(&self) -> Result<syn::Expr> { fn name_view(&self) -> Result<syn::Expr> {
let field_name = self.field_name()?; let field_name = self.field_name()?;
define_spanned_export!(self.span() => _form);
let name_view = quote_spanned! { self.span() => let name_view = quote_spanned! { self.span() =>
#_form::NameBuf::from((__c.__parent, #field_name)) #_form::NameBuf::from((__c.__parent, #field_name))
}; };
@ -201,7 +202,7 @@ pub fn validators<'v>(
parent: &'v syn::Ident, // field ident (if local) or form ident (if !local) parent: &'v syn::Ident, // field ident (if local) or form ident (if !local)
local: bool, local: bool,
) -> Result<impl Iterator<Item = syn::Expr> + 'v> { ) -> Result<impl Iterator<Item = syn::Expr> + 'v> {
Ok(FieldAttr::from_attrs(FieldAttr::NAME, &field.attrs)? let exprs = FieldAttr::from_attrs(FieldAttr::NAME, &field.attrs)?
.into_iter() .into_iter()
.filter_map(|a| a.validate) .filter_map(|a| a.validate)
.map(move |expr| { .map(move |expr| {
@ -219,9 +220,21 @@ pub fn validators<'v>(
}) })
.filter(move |(_, is_local)| *is_local == local) .filter(move |(_, is_local)| *is_local == local)
.map(move |(mut expr, _)| { .map(move |(mut expr, _)| {
let field = field.ident(); let field_span = field.ident().span()
let mut v = ValidationMutator { parent, local, field, visited: false }; .join(field.ty.span())
.unwrap_or(field.ty.span());
let field_ident = field.ident().clone().with_span(field_span);
let mut v = ValidationMutator { parent, local, field: &field_ident, visited: false };
v.visit_expr_mut(&mut expr); v.visit_expr_mut(&mut expr);
expr
})) let span = expr.key_span.unwrap_or(field_span);
define_spanned_export!(span => _form);
syn::parse2(quote_spanned!(span => {
let __result: #_form::Result<'_, ()> = #expr;
__result
})).unwrap()
});
Ok(exprs)
} }

View File

@ -336,3 +336,44 @@ note: error occurred while deriving `FromForm`
136 | #[derive(FromForm)] 136 | #[derive(FromForm)]
| ^^^^^^^^ | ^^^^^^^^
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0425]: cannot find function `unknown` in this scope
--> $DIR/from_form.rs:150:24
|
150 | #[field(validate = unknown())]
| ^^^^^^^ not found in this scope
error[E0308]: mismatched types
--> $DIR/from_form.rs:144:24
|
144 | #[field(validate = 123)]
| -------- ^^^ expected enum `Result`, found integer
| |
| expected due to this
|
= note: expected enum `Result<(), Errors<'_>>`
found type `{integer}`
error[E0308]: mismatched types
--> $DIR/from_form.rs:157:5
|
157 | first: String,
| ^^^^^^^^^^^^^ expected enum `TempFile`, found struct `std::string::String`
|
= note: expected reference `&TempFile<'_>`
found reference `&std::string::String`
error[E0308]: mismatched types
--> $DIR/from_form.rs:163:5
|
163 | first: String,
| ^^^^^^^^^^^^^ expected enum `TempFile`, found struct `std::string::String`
|
= note: expected reference `&TempFile<'_>`
found reference `&std::string::String`
error[E0308]: mismatched types
--> $DIR/from_form.rs:162:28
|
162 | #[field(validate = ext("hello"))]
| ^^^^^^^ expected struct `ContentType`, found `&str`

View File

@ -359,3 +359,44 @@ error: [note] error occurred while deriving `FromForm`
| ^^^^^^^^ | ^^^^^^^^
| |
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0425]: cannot find function `unknown` in this scope
--> $DIR/from_form.rs:150:24
|
150 | #[field(validate = unknown())]
| ^^^^^^^ not found in this scope
error[E0308]: mismatched types
--> $DIR/from_form.rs:144:24
|
144 | #[field(validate = 123)]
| -------- ^^^ expected enum `std::result::Result`, found integer
| |
| expected due to this
|
= note: expected enum `std::result::Result<(), Errors<'_>>`
found type `{integer}`
error[E0308]: mismatched types
--> $DIR/from_form.rs:157:12
|
157 | first: String,
| ^^^^^^ expected enum `TempFile`, found struct `std::string::String`
|
= note: expected reference `&TempFile<'_>`
found reference `&std::string::String`
error[E0308]: mismatched types
--> $DIR/from_form.rs:163:12
|
163 | first: String,
| ^^^^^^ expected enum `TempFile`, found struct `std::string::String`
|
= note: expected reference `&TempFile<'_>`
found reference `&std::string::String`
error[E0308]: mismatched types
--> $DIR/from_form.rs:162:28
|
162 | #[field(validate = ext("hello"))]
| ^^^^^^^ expected struct `ContentType`, found `&str`

View File

@ -139,4 +139,28 @@ struct BadName3 {
field: String, field: String,
} }
#[derive(FromForm)]
struct Validate0 {
#[field(validate = 123)]
first: String,
}
#[derive(FromForm)]
struct Validate1 {
#[field(validate = unknown())]
first: String,
}
#[derive(FromForm)]
struct Validate2 {
#[field(validate = ext(rocket::http::ContentType::HTML))]
first: String,
}
#[derive(FromForm)]
struct Validate3 {
#[field(validate = ext("hello"))]
first: String,
}
fn main() { } fn main() { }

View File

@ -1,6 +1,6 @@
#[macro_use]extern crate rocket; #[macro_use]extern crate rocket;
use rocket::http::Status; use rocket::http::{Status, ContentType};
use rocket::form::{Form, Contextual, FromForm, FromFormField, Context}; use rocket::form::{Form, Contextual, FromForm, FromFormField, Context};
use rocket::data::TempFile; use rocket::data::TempFile;
@ -39,7 +39,7 @@ struct Submission<'v> {
date: time::Date, date: time::Date,
#[field(validate = len(1..=250))] #[field(validate = len(1..=250))]
r#abstract: &'v str, r#abstract: &'v str,
#[field(validate = ext("pdf"))] #[field(validate = ext(ContentType::PDF))]
file: TempFile<'v>, file: TempFile<'v>,
#[field(validate = len(1..))] #[field(validate = len(1..))]
category: Vec<Category>, category: Vec<Category>,
@ -52,8 +52,7 @@ struct Account<'v> {
#[field(validate = len(1..))] #[field(validate = len(1..))]
name: &'v str, name: &'v str,
password: Password<'v>, password: Password<'v>,
#[field(validate = contains('@'))] #[field(validate = contains('@').or_else(msg!("invalid email address")))]
#[field(validate = omits(self.password.first))]
email: &'v str, email: &'v str,
} }