Skip to content

Commit

Permalink
Fix codegen issue for parameters with lifetimes
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
jakubdabek committed Aug 21, 2021
1 parent 42a0fb8 commit b14e8da
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 30 deletions.
62 changes: 32 additions & 30 deletions core/codegen/src/attribute/route/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,37 +75,39 @@ fn query_decls(route: &Route) -> Option<TokenStream> {

#[allow(non_snake_case)]
Some(quote! {
let mut __e = #_form::Errors::new();
#(let mut #ident = #init_expr;)*

for _f in #__req.query_fields() {
let _raw = (_f.name.source().as_str(), _f.value);
let _key = _f.name.key_lossy().as_str();
match (_raw, _key) {
// Skip static parameters so <param..> doesn't see them.
#(((#raw_name, #raw_value), _) => { /* skip */ },)*
#((_, #matcher) => #push_expr,)*
_ => { /* in case we have no trailing, ignore all else */ },
let (#(#ident),*) = {
let mut __e = #_form::Errors::new();
#(let mut #ident = #init_expr;)*

for _f in #__req.query_fields() {
let _raw = (_f.name.source().as_str(), _f.value);
let _key = _f.name.key_lossy().as_str();
match (_raw, _key) {
// Skip static parameters so <param..> doesn't see them.
#(((#raw_name, #raw_value), _) => { /* skip */ },)*
#((_, #matcher) => #push_expr,)*
_ => { /* in case we have no trailing, ignore all else */ },
}
}
}

#(
let #ident = match #finalize_expr {
#_Ok(_v) => #_Some(_v),
#_Err(_err) => {
__e.extend(_err.with_name(#_form::NameView::new(#name)));
#_None
},
};
)*

if !__e.is_empty() {
#_log::warn_!("Query string failed to match route declaration.");
for _err in __e { #_log::warn_!("{}", _err); }
return #Outcome::Forward(#__data);
}

#(let #ident = #ident.unwrap();)*

#(
let #ident = match #finalize_expr {
#_Ok(_v) => #_Some(_v),
#_Err(_err) => {
__e.extend(_err.with_name(#_form::NameView::new(#name)));
#_None
},
};
)*

if !__e.is_empty() {
#_log::warn_!("Query string failed to match route declaration.");
for _err in __e { #_log::warn_!("{}", _err); }
return #Outcome::Forward(#__data);
}

(#(#ident.unwrap()),*)
};
})
}

Expand Down
6 changes: 6 additions & 0 deletions core/codegen/tests/async-routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ async fn hello(_origin: &Origin<'_>) -> &'static str {
"Hello, world!"
}

#[get("/repeated_query?<sort>")]
async fn repeated_query(sort: Vec<&str>) -> &str {
noop().await;
sort[0]
}

#[catch(404)]
async fn not_found(req: &Request<'_>) -> String {
noop().await;
Expand Down

0 comments on commit b14e8da

Please sign in to comment.