Skip to content

Commit b14e8da

Browse files
committed
Fix codegen issue for parameters with lifetimes
This commit implements a workaround for an [issue within rustc]. The problem showed itself when using e.g. a `Vec<&str>` argument in an async route handler (but not `&str`), which resulted in a "implementation of `FromForm` is not general enough" error. The workaround itself works by gathering all invocations of `FromForm`'s methods inside a block without any `.await` points [ref]. [issue within rustc]: rust-lang/rust#69663 [ref]: rust-lang/rust#57478 (comment)
1 parent 42a0fb8 commit b14e8da

File tree

2 files changed

+38
-30
lines changed

2 files changed

+38
-30
lines changed

core/codegen/src/attribute/route/mod.rs

+32-30
Original file line numberDiff line numberDiff line change
@@ -75,37 +75,39 @@ fn query_decls(route: &Route) -> Option<TokenStream> {
7575

7676
#[allow(non_snake_case)]
7777
Some(quote! {
78-
let mut __e = #_form::Errors::new();
79-
#(let mut #ident = #init_expr;)*
80-
81-
for _f in #__req.query_fields() {
82-
let _raw = (_f.name.source().as_str(), _f.value);
83-
let _key = _f.name.key_lossy().as_str();
84-
match (_raw, _key) {
85-
// Skip static parameters so <param..> doesn't see them.
86-
#(((#raw_name, #raw_value), _) => { /* skip */ },)*
87-
#((_, #matcher) => #push_expr,)*
88-
_ => { /* in case we have no trailing, ignore all else */ },
78+
let (#(#ident),*) = {
79+
let mut __e = #_form::Errors::new();
80+
#(let mut #ident = #init_expr;)*
81+
82+
for _f in #__req.query_fields() {
83+
let _raw = (_f.name.source().as_str(), _f.value);
84+
let _key = _f.name.key_lossy().as_str();
85+
match (_raw, _key) {
86+
// Skip static parameters so <param..> doesn't see them.
87+
#(((#raw_name, #raw_value), _) => { /* skip */ },)*
88+
#((_, #matcher) => #push_expr,)*
89+
_ => { /* in case we have no trailing, ignore all else */ },
90+
}
8991
}
90-
}
91-
92-
#(
93-
let #ident = match #finalize_expr {
94-
#_Ok(_v) => #_Some(_v),
95-
#_Err(_err) => {
96-
__e.extend(_err.with_name(#_form::NameView::new(#name)));
97-
#_None
98-
},
99-
};
100-
)*
101-
102-
if !__e.is_empty() {
103-
#_log::warn_!("Query string failed to match route declaration.");
104-
for _err in __e { #_log::warn_!("{}", _err); }
105-
return #Outcome::Forward(#__data);
106-
}
107-
108-
#(let #ident = #ident.unwrap();)*
92+
93+
#(
94+
let #ident = match #finalize_expr {
95+
#_Ok(_v) => #_Some(_v),
96+
#_Err(_err) => {
97+
__e.extend(_err.with_name(#_form::NameView::new(#name)));
98+
#_None
99+
},
100+
};
101+
)*
102+
103+
if !__e.is_empty() {
104+
#_log::warn_!("Query string failed to match route declaration.");
105+
for _err in __e { #_log::warn_!("{}", _err); }
106+
return #Outcome::Forward(#__data);
107+
}
108+
109+
(#(#ident.unwrap()),*)
110+
};
109111
})
110112
}
111113

core/codegen/tests/async-routes.rs

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ async fn hello(_origin: &Origin<'_>) -> &'static str {
1212
"Hello, world!"
1313
}
1414

15+
#[get("/repeated_query?<sort>")]
16+
async fn repeated_query(sort: Vec<&str>) -> &str {
17+
noop().await;
18+
sort[0]
19+
}
20+
1521
#[catch(404)]
1622
async fn not_found(req: &Request<'_>) -> String {
1723
noop().await;

0 commit comments

Comments
 (0)