Skip to content

Commit fd2094c

Browse files
committed
Use call site hygiene in FromForm derive.
This commit changes the `FromForm` derive codegen so that it consistently emits tokens with call site hygiene. This allows `FromForm` derives to be emitted my macros more reliably.
1 parent 7b17de6 commit fd2094c

File tree

8 files changed

+239
-101
lines changed

8 files changed

+239
-101
lines changed

core/codegen/src/derive/form_field.rs

+8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ use quote::{ToTokens, TokenStreamExt};
99
use crate::syn_ext::IdentExt;
1010
use crate::name::Name;
1111

12+
macro_rules! quote_spanned {
13+
($span:expr => $($token:tt)*) => (
14+
quote::quote_spanned!(
15+
proc_macro2::Span::call_site().located_at($span) => $($token)*
16+
)
17+
)
18+
}
19+
1220
#[derive(Debug)]
1321
pub enum FieldName {
1422
Cased(Name),

core/codegen/src/derive/from_form.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,20 @@ use syn::parse::Parser;
44
use devise::*;
55

66
use crate::exports::*;
7-
use crate::derive::form_field::{*, FieldName::*};
7+
use crate::derive::form_field::FieldName::*;
8+
use crate::derive::form_field::{FieldExt, default, first_duplicate, validators};
89
use crate::syn_ext::{GenericsExt as _, TypeExt as _};
910

1011
type WherePredicates = syn::punctuated::Punctuated<syn::WherePredicate, syn::Token![,]>;
1112

13+
macro_rules! quote_spanned {
14+
($span:expr => $($token:tt)*) => (
15+
quote::quote_spanned!(
16+
proc_macro2::Span::call_site().located_at($span) => $($token)*
17+
)
18+
)
19+
}
20+
1221
// F: fn(field_ty: Ty, field_context: Expr)
1322
fn fields_map<F>(fields: Fields<'_>, map_f: F) -> Result<TokenStream>
1423
where F: Fn(&syn::Type, &syn::Expr) -> TokenStream
@@ -32,8 +41,8 @@ fn fields_map<F>(fields: Fields<'_>, map_f: F) -> Result<TokenStream>
3241
}
3342

3443
matchers.extend(field.field_names()?.into_iter().map(|f| match f {
35-
Cased(name) => quote!(#name => { #push }),
36-
Uncased(name) => quote!(__n if __n.as_uncased() == #name => { #push }),
44+
Cased(f) => quote_spanned!(ty.span() => #f => { #push }),
45+
Uncased(f) => quote_spanned!(ty.span() => __n if __n.as_uncased() == #f => { #push }),
3746
}));
3847
}
3948

@@ -192,7 +201,7 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
192201
let (ctxt_ty, gen) = context_type(input)?;
193202
let (_, ty_gen, _) = gen.split_for_impl();
194203
let output = mapper::input_default(mapper, input)?;
195-
Ok(quote! {
204+
Ok(quote_spanned! { ctxt_ty.span() =>
196205
async fn push_data(
197206
__c: &mut #ctxt_ty #ty_gen,
198207
__f: #_form::DataField<'r, '_>
@@ -234,7 +243,7 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
234243
let ident = fields.iter().map(|f| f.context_ident());
235244
let builder = fields.builder(|f| {
236245
let ident = f.context_ident();
237-
quote!(#ident.unwrap())
246+
quote_spanned!(ident.span() => #ident.unwrap())
238247
});
239248

240249
Ok(quote_spanned!(fields.span() =>

core/codegen/tests/from_form.rs

+47
Original file line numberDiff line numberDiff line change
@@ -991,3 +991,50 @@ struct Q<T>(T);
991991
// This is here to ensure we don't warn, which we can't test with trybuild.
992992
#[derive(FromForm)]
993993
pub struct JsonTokenBad<T>(Q<T>);
994+
995+
#[test]
996+
fn range() {
997+
use std::ops::{Range, RangeFrom, RangeTo, RangeToInclusive};
998+
999+
let range: Range<usize> = strict("start=1&end=2").unwrap();
1000+
assert_eq!(range, Range { start: 1, end: 2 });
1001+
1002+
let range: Range<isize> = strict_encoded("start=-1&end=2").unwrap();
1003+
assert_eq!(range, Range { start: -1, end: 2 });
1004+
1005+
let range: Range<isize> = strict("start=-1&end=-95").unwrap();
1006+
assert_eq!(range, Range { start: -1, end: -95 });
1007+
1008+
let range: Range<isize> = strict_encoded("start=-1&end=-95").unwrap();
1009+
assert_eq!(range, Range { start: -1, end: -95 });
1010+
1011+
let range: RangeFrom<char> = strict("start=a").unwrap();
1012+
assert_eq!(range, RangeFrom { start: 'a' });
1013+
1014+
let range: RangeTo<char> = strict("end=z").unwrap();
1015+
assert_eq!(range, RangeTo { end: 'z' });
1016+
1017+
let range: RangeToInclusive<u8> = strict("end=255").unwrap();
1018+
assert_eq!(range, RangeToInclusive { end: 255 });
1019+
1020+
// now with compound, non-Step values
1021+
let form_string = &["start.start=0", "start.end=1", "end.start=1", "end.end=2"].join("&");
1022+
let range: Range<Range<usize>> = strict(&form_string).unwrap();
1023+
assert_eq!(range, Range {
1024+
start: Range { start: 0, end : 1},
1025+
end: Range { start: 1, end : 2 }
1026+
});
1027+
1028+
let form_string = &[
1029+
"start.description=some%20task",
1030+
"start.completed=false",
1031+
"end.description=yet%20more%20work",
1032+
"end.completed=true",
1033+
].join("&");
1034+
1035+
let range: Range<TodoTask> = strict_encoded(form_string).unwrap();
1036+
assert_eq!(range, Range {
1037+
start: TodoTask { description: "some task".into(), completed: false, },
1038+
end: TodoTask { description: "yet more work".into(), completed: true, },
1039+
});
1040+
}

core/codegen/tests/ui-fail-nightly/from_form.stderr

+21-1
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ note: error occurred while deriving `FromForm`
379379
error: duplicate default field expression
380380
--> tests/ui-fail-nightly/from_form.rs:184:23
381381
|
382+
181 | #[derive(FromForm)]
383+
| -------- in this derive macro expansion
384+
...
382385
184 | #[field(default = 2)]
383386
| ^
384387
|
@@ -393,6 +396,9 @@ note: error occurred while deriving `FromForm`
393396
error: duplicate default expressions
394397
--> tests/ui-fail-nightly/from_form.rs:190:23
395398
|
399+
188 | #[derive(FromForm)]
400+
| -------- in this derive macro expansion
401+
189 | struct Default3 {
396402
190 | #[field(default = 1, default_with = None)]
397403
| ^
398404
|
@@ -412,6 +418,9 @@ note: error occurred while deriving `FromForm`
412418
error: duplicate default expressions
413419
--> tests/ui-fail-nightly/from_form.rs:197:23
414420
|
421+
194 | #[derive(FromForm)]
422+
| -------- in this derive macro expansion
423+
...
415424
197 | #[field(default = 1)]
416425
| ^
417426
|
@@ -487,13 +496,17 @@ error[E0308]: mismatched types
487496
help: the type constructed contains `{integer}` due to the type of the argument passed
488497
--> tests/ui-fail-nightly/from_form.rs:171:23
489498
|
499+
169 | #[derive(FromForm)]
500+
| -------- in this derive macro expansion
501+
170 | struct Default0 {
490502
171 | #[field(default = 123)]
491503
| ^^^ this argument influences the type of `Some`
492504
note: tuple variant defined here
493505
--> $RUST/core/src/option.rs
494506
|
495507
| Some(#[stable(feature = "rust1", since = "1.0.0")] T),
496508
| ^^^^
509+
= note: this error originates in the derive macro `FromForm` (in Nightly builds, run with -Z macro-backtrace for more info)
497510

498511
error[E0308]: mismatched types
499512
--> tests/ui-fail-nightly/from_form.rs:203:33
@@ -520,8 +533,14 @@ note: tuple variant defined here
520533
error[E0277]: the trait bound `bool: From<&str>` is not satisfied
521534
--> tests/ui-fail-nightly/from_form.rs:209:23
522535
|
536+
207 | #[derive(FromForm)]
537+
| -------- in this derive macro expansion
538+
208 | struct Default6 {
523539
209 | #[field(default = "no conversion")]
524-
| ^^^^^^^^^^^^^^^ the trait `From<&str>` is not implemented for `bool`, which is required by `&str: Into<_>`
540+
| ^^^^^^^^^^^^^^^
541+
| |
542+
| the trait `From<&str>` is not implemented for `bool`, which is required by `&str: Into<_>`
543+
| this tail expression is of type `&str`
525544
|
526545
= help: the following other types implement trait `From<T>`:
527546
<bool as From<format_description::parse::format_item::HourBase>>
@@ -533,3 +552,4 @@ error[E0277]: the trait bound `bool: From<&str>` is not satisfied
533552
<bool as From<format_description::parse::format_item::WeekdayOneIndexed>>
534553
<bool as From<format_description::parse::format_item::YearBase>>
535554
= note: required for `&str` to implement `Into<bool>`
555+
= note: this error originates in the derive macro `FromForm` (in Nightly builds, run with -Z macro-backtrace for more info)

core/codegen/tests/ui-fail-nightly/from_form_type_errors.stderr

+56-42
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ error[E0277]: the trait bound `Unknown: FromFormField<'_>` is not satisfied
1919
error[E0277]: the trait bound `Unknown: FromFormField<'_>` is not satisfied
2020
--> tests/ui-fail-nightly/from_form_type_errors.rs:5:10
2121
|
22-
5 | #[derive(FromForm)]
23-
| ^^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Unknown`, which is required by `_::FromFormGeneratedContext<'r>: std::marker::Send`
22+
5 | #[derive(FromForm)]
23+
| ^-------
24+
| |
25+
| __________in this derive macro expansion
26+
| |
27+
6 | | struct BadType3 {
28+
| |_______________^ the trait `FromFormField<'_>` is not implemented for `Unknown`, which is required by `_::FromFormGeneratedContext<'r>: std::marker::Send`
2429
|
2530
= help: the following other types implement trait `FromFormField<'v>`:
2631
&'v [u8]
@@ -36,6 +41,8 @@ error[E0277]: the trait bound `Unknown: FromFormField<'_>` is not satisfied
3641
note: required because it appears within the type `_::FromFormGeneratedContext<'r>`
3742
--> tests/ui-fail-nightly/from_form_type_errors.rs:6:8
3843
|
44+
5 | #[derive(FromForm)]
45+
| -------- in this derive macro expansion
3946
6 | struct BadType3 {
4047
| ^^^^^^^^
4148
note: required by a bound in `rocket::form::FromForm::Context`
@@ -66,8 +73,13 @@ error[E0277]: the trait bound `Foo<usize>: FromFormField<'_>` is not satisfied
6673
error[E0277]: the trait bound `Foo<usize>: FromFormField<'_>` is not satisfied
6774
--> tests/ui-fail-nightly/from_form_type_errors.rs:12:10
6875
|
69-
12 | #[derive(FromForm)]
70-
| ^^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Foo<usize>`, which is required by `_::FromFormGeneratedContext<'r>: std::marker::Send`
76+
12 | #[derive(FromForm)]
77+
| ^-------
78+
| |
79+
| __________in this derive macro expansion
80+
| |
81+
13 | | struct Other {
82+
| |____________^ the trait `FromFormField<'_>` is not implemented for `Foo<usize>`, which is required by `_::FromFormGeneratedContext<'r>: std::marker::Send`
7183
|
7284
= help: the following other types implement trait `FromFormField<'v>`:
7385
&'v [u8]
@@ -83,6 +95,8 @@ error[E0277]: the trait bound `Foo<usize>: FromFormField<'_>` is not satisfied
8395
note: required because it appears within the type `_::FromFormGeneratedContext<'r>`
8496
--> tests/ui-fail-nightly/from_form_type_errors.rs:13:8
8597
|
98+
12 | #[derive(FromForm)]
99+
| -------- in this derive macro expansion
86100
13 | struct Other {
87101
| ^^^^^
88102
note: required by a bound in `rocket::form::FromForm::Context`
@@ -111,29 +125,32 @@ error[E0277]: the trait bound `Unknown: FromFormField<'_>` is not satisfied
111125
= note: required for `Unknown` to implement `FromForm<'r>`
112126
= note: this error originates in the derive macro `FromForm` (in Nightly builds, run with -Z macro-backtrace for more info)
113127

114-
error[E0277]: the trait bound `Unknown: FromFormField<'_>` is not satisfied
115-
--> tests/ui-fail-nightly/from_form_type_errors.rs:7:5
128+
error[E0277]: the trait bound `Unknown: FromForm<'r>` is not satisfied
129+
--> tests/ui-fail-nightly/from_form_type_errors.rs:7:12
116130
|
117131
7 | field: Unknown,
118-
| ^^^^^^^^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Unknown`, which is required by `Unknown: FromForm<'r>`
132+
| ^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Unknown`, which is required by `Unknown: FromForm<'r>`
119133
|
120-
= help: the following other types implement trait `FromFormField<'v>`:
121-
&'v [u8]
122-
&'v str
123-
Capped<&'v [u8]>
124-
Capped<&'v str>
125-
Capped<Cow<'v, str>>
126-
Capped<TempFile<'v>>
127-
Capped<std::string::String>
128-
Cow<'v, str>
134+
= help: the following other types implement trait `FromForm<'r>`:
135+
<(A, B) as FromForm<'v>>
136+
<Arc<T> as FromForm<'v>>
137+
<BTreeMap<K, V> as FromForm<'v>>
138+
<BadType3 as FromForm<'r>>
139+
<Contextual<'v, T> as FromForm<'v>>
140+
<HashMap<K, V> as FromForm<'v>>
141+
<Lenient<T> as FromForm<'v>>
142+
<Other as FromForm<'r>>
129143
and $N others
130144
= note: required for `Unknown` to implement `FromForm<'r>`
131145

132-
error[E0277]: the trait bound `Unknown: FromFormField<'r>` is not satisfied
146+
error[E0277]: the trait bound `Unknown: FromFormField<'_>` is not satisfied
133147
--> tests/ui-fail-nightly/from_form_type_errors.rs:7:12
134148
|
149+
5 | #[derive(FromForm)]
150+
| -------- in this derive macro expansion
151+
6 | struct BadType3 {
135152
7 | field: Unknown,
136-
| ^^^^^^^ the trait `FromFormField<'r>` is not implemented for `Unknown`
153+
| ^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Unknown`, which is required by `Unknown: FromForm<'r>`
137154
|
138155
= help: the following other types implement trait `FromFormField<'v>`:
139156
&'v [u8]
@@ -145,11 +162,8 @@ error[E0277]: the trait bound `Unknown: FromFormField<'r>` is not satisfied
145162
Capped<std::string::String>
146163
Cow<'v, str>
147164
and $N others
148-
note: required by a bound in `FromFieldContext`
149-
--> $WORKSPACE/core/lib/src/form/from_form_field.rs
150-
|
151-
| pub struct FromFieldContext<'v, T: FromFormField<'v>> {
152-
| ^^^^^^^^^^^^^^^^^ required by this bound in `FromFieldContext`
165+
= note: required for `Unknown` to implement `FromForm<'r>`
166+
= note: this error originates in the derive macro `FromForm` (in Nightly builds, run with -Z macro-backtrace for more info)
153167

154168
error[E0277]: the trait bound `Foo<usize>: FromFormField<'_>` is not satisfied
155169
--> tests/ui-fail-nightly/from_form_type_errors.rs:12:10
@@ -170,29 +184,32 @@ error[E0277]: the trait bound `Foo<usize>: FromFormField<'_>` is not satisfied
170184
= note: required for `Foo<usize>` to implement `FromForm<'r>`
171185
= note: this error originates in the derive macro `FromForm` (in Nightly builds, run with -Z macro-backtrace for more info)
172186

173-
error[E0277]: the trait bound `Foo<usize>: FromFormField<'_>` is not satisfied
174-
--> tests/ui-fail-nightly/from_form_type_errors.rs:14:5
187+
error[E0277]: the trait bound `Foo<usize>: FromForm<'r>` is not satisfied
188+
--> tests/ui-fail-nightly/from_form_type_errors.rs:14:12
175189
|
176190
14 | field: Foo<usize>,
177-
| ^^^^^^^^^^^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Foo<usize>`, which is required by `Foo<usize>: FromForm<'r>`
191+
| ^^^^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Foo<usize>`, which is required by `Foo<usize>: FromForm<'r>`
178192
|
179-
= help: the following other types implement trait `FromFormField<'v>`:
180-
&'v [u8]
181-
&'v str
182-
Capped<&'v [u8]>
183-
Capped<&'v str>
184-
Capped<Cow<'v, str>>
185-
Capped<TempFile<'v>>
186-
Capped<std::string::String>
187-
Cow<'v, str>
193+
= help: the following other types implement trait `FromForm<'r>`:
194+
<(A, B) as FromForm<'v>>
195+
<Arc<T> as FromForm<'v>>
196+
<BTreeMap<K, V> as FromForm<'v>>
197+
<BadType3 as FromForm<'r>>
198+
<Contextual<'v, T> as FromForm<'v>>
199+
<HashMap<K, V> as FromForm<'v>>
200+
<Lenient<T> as FromForm<'v>>
201+
<Other as FromForm<'r>>
188202
and $N others
189203
= note: required for `Foo<usize>` to implement `FromForm<'r>`
190204

191-
error[E0277]: the trait bound `Foo<usize>: FromFormField<'r>` is not satisfied
205+
error[E0277]: the trait bound `Foo<usize>: FromFormField<'_>` is not satisfied
192206
--> tests/ui-fail-nightly/from_form_type_errors.rs:14:12
193207
|
208+
12 | #[derive(FromForm)]
209+
| -------- in this derive macro expansion
210+
13 | struct Other {
194211
14 | field: Foo<usize>,
195-
| ^^^^^^^^^^ the trait `FromFormField<'r>` is not implemented for `Foo<usize>`
212+
| ^^^^^^^^^^ the trait `FromFormField<'_>` is not implemented for `Foo<usize>`, which is required by `Foo<usize>: FromForm<'r>`
196213
|
197214
= help: the following other types implement trait `FromFormField<'v>`:
198215
&'v [u8]
@@ -204,8 +221,5 @@ error[E0277]: the trait bound `Foo<usize>: FromFormField<'r>` is not satisfied
204221
Capped<std::string::String>
205222
Cow<'v, str>
206223
and $N others
207-
note: required by a bound in `FromFieldContext`
208-
--> $WORKSPACE/core/lib/src/form/from_form_field.rs
209-
|
210-
| pub struct FromFieldContext<'v, T: FromFormField<'v>> {
211-
| ^^^^^^^^^^^^^^^^^ required by this bound in `FromFieldContext`
224+
= note: required for `Foo<usize>` to implement `FromForm<'r>`
225+
= note: this error originates in the derive macro `FromForm` (in Nightly builds, run with -Z macro-backtrace for more info)

0 commit comments

Comments
 (0)