-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
error[E0425]: cannot find value rocket_param_id
in this scope
#635
Comments
Can you make a simple reproducible test case? Since at least one and probably two different kinds of macros are involved I wouldn't want to try to guess what's happening here. For issues with the codegen, building with |
Thanks, with --> C:\Users\me\.cargo\git\checkouts\rocket-8bf16d9ca7e90bdc\3894e4e\codegen\src\decorators\route.rs:286
Route params: RouteParams { annotated_fn: Function(Spanned { node: (delete#0, FnDecl { inputs: [Arg { ty: type(Customer), pat: pat(4294967295: me), id: NodeId(4294967295) }, Arg { ty: type(i64), pat: pat(4294967295: id), id: NodeId(4294967295) }], output: Ty(type(Res<()>)), variadic: false }), span: Span { lo: BytePos(37053), hi: BytePos(37311), ctxt: #0 } }), method: Spanned { node: Post, span: Span { lo: BytePos(37028), hi: BytePos(37032), ctxt: #0 } }, uri: Spanned { node: Uri { uri: "/delete/<id>", path: (0, 12), query: None, fragment: None, segment_count: 2 }, span: Span { lo: BytePos(37035), hi: BytePos(37049), ctxt: #0 } }, data_param: None, query_param: None, format: None, rank: None }
--> C:\Users\me\.cargo\git\checkouts\rocket-8bf16d9ca7e90bdc\3894e4e\codegen\src\utils\mod.rs:56
Emitting item:
#[allow(unreachable_code)]
fn rocket_route_fn_delete<'_b>(__req: &'_b ::rocket::Request,
__data: ::rocket::Data)
-> ::rocket::handler::Outcome<'_b> {
#[allow(non_snake_case, unreachable_patterns)]
let rocket_param_id: i64 =
match match __req.get_param_str(0usize) {
Some(s) =>
<i64 as ::rocket::request::FromParam>::from_param(s),
None => return ::rocket::Outcome::Forward(__data),
} {
Ok(v) => v,
Err(e) => {
println!(" => Failed to parse '{}': {:?}" , stringify ! (
id ) , e);
return ::rocket::Outcome::Forward(__data)
}
};
#[allow(non_snake_case, unreachable_patterns)]
let rocket_param_me: Customer =
match ::rocket::request::FromRequest::from_request(__req) {
::rocket::Outcome::Success(v) => v,
::rocket::Outcome::Forward(_) =>
return ::rocket::Outcome::Forward(__data),
::rocket::Outcome::Failure((code, _)) => {
return ::rocket::Outcome::Failure(code)
}
};
let responder = delete(rocket_param_me, rocket_param_id);
::rocket::handler::Outcome::from(__req, responder)
}
--> C:\Users\me\.cargo\git\checkouts\rocket-8bf16d9ca7e90bdc\3894e4e\codegen\src\utils\mod.rs:56
Emitting item:
/// Rocket code generated static route information structure.
#[allow(non_upper_case_globals)]
#[rocket_route_info]
pub static static_rocket_route_info_for_delete: ::rocket::StaticRouteInfo =
::rocket::StaticRouteInfo{name: "delete",
method: ::rocket::http::Method::Post,
path: "/delete/<id>",
handler: rocket_route_fn_delete,
format: None,
rank: None,};
--> C:\Users\me\.cargo\git\checkouts\rocket-8bf16d9ca7e90bdc\3894e4e\codegen\src\utils\mod.rs:56
Emitting item:
#[rocket_route(static_rocket_route_info_for_delete)]
fn delete(me: Customer, id: i64) -> Res<()> {
jassert!(junwrap ! ( jtry ! ( Driver :: find ( id ) ) ) . customer_id ==
me . user_id , "UNAUTHORIZED");
jtry!(User :: delete ( id ));
ok(())
}
--> C:\Users\me\.cargo\git\checkouts\rocket-8bf16d9ca7e90bdc\3894e4e\codegen\src\utils\mod.rs:56
Emitting item:
macro_rules! rocket_uri_for_delete(( $ ( $ token : tt ) * ) => {
rocket_internal_uri ! (
"/delete/<id>" , ( id : i64 ) , $ ( $ token
) * ) }); |
I really need to get this to work (for my job) asap. Is there anything else I can try to get this to at least compile for now (without going back to nightly 03-06)? |
Btw, for all the generated macro_rules! gen_handler {
($model:ident, $table:ident, $table_name:expr, [$($fkm:ident)*], {$($validate:meta $field:ident: $type:ty,)+}) => (
pub mod $table {
use rocket_contrib::Json;
use controllers::*;
use models::customer::Customer;
use super::super::$table::{$model as Model, New, Update};
use models::validation::validate;
#[derive(Clone, Default, Deserialize)]
#[serde(default)]
pub struct Edit {
pub id: Option<i64>,
$(pub $field: $type,)+
}
#[post("/edit", data = "<body>")]
pub fn edit(me: Customer, body: Json<Edit>) -> Res<()> {
let req = body.into_inner();
if let Some(id) = req.id {
let model = junwrap!(jtry!(Model::find(id)));
jassert!(model.customer_id == me.user_id, "UNAUTHORIZED"); // must be owner
let mut update = Update::now();
$(
if req.$field != model.$field {
update = update.$field(req.$field.clone());
}
)+
let update = jtry_user!(validate(me.user_id, update));
jtry!(model.update(&update));
} else {
let nu = New {
customer_id: me.user_id,
$($field: req.$field,)+
};
let nu = jtry_user!(validate(me.user_id, nu));
jtry!(Model::create_from(nu));
}
jtry!(me.scale_data_changed());
ok(())
}
#[post("/delete/<id>")]
fn delete(me: Customer, id: i64) -> Res<()> {
jassert!(junwrap!(jtry!(Model::find(id))).customer_id == me.user_id, "UNAUTHORIZED");
jtry!(Model::delete(id));
jtry!(me.scale_data_changed());
ok(())
}
use $crate::models::views::$table::View;
#[post("/list")]
fn list(me: Customer) -> Res<Vec<View>> {
ok(jtry!(Model::all_of_customer(me.user_id)).into_iter().map(|t| View::from(t)).collect())
}
}
)
} And it's called for a list of model structs through macro_rules! gen_all {
($($model:ident, $table:ident, $table_name:expr, [$($fkm:ident)*], {$($validate:meta $field:ident: $type:ty,)+})+) => (
pub mod handlers {
$(gen_handler!($model, $table, $table_name, [$($fkm)*], { validate(custom = "allow") active: bool, $($validate $field: $type,)+ });)+
}
$(gen_orm!($model, $table, $table_name, [$($fkm)*], { validate(custom = "allow") active: bool, $($validate $field: $type,)+ });)+
pub mod views {
$(gen_view!($model, $table, $table_name, [$($fkm)*], { active: bool, $($field: $type,)+ });)+
}
)
} And it all used to work perfectly before :) |
Btw, these top-level functions (outside any macro) don't give an error: #[post("/user-delete/<id>")]
fn delete(me: User, id: i64) -> Res<()> {
if me.role == UserRole::Admin || me.role == UserRole::Customer && junwrap!(jtry!(Driver::find(id))).customer_id == me.id {
jtry!(User::delete(id));
}
ok(())
}
// in a different module:
#[post("/delete/<id>")]
fn delete(me: Customer, id: i64) -> Res<()> {
jassert!(junwrap!(jtry!(Driver::find(id))).customer_id == me.user_id, "UNAUTHORIZED");
jtry!(User::delete(id));
ok(())
} Even though the generated code is basically the same as for the ones in the macro (with respect to |
Try cargo-expand to view all generated code. |
I tried it, but it shows nothing, probably because the compilation gives errors(?) |
Does |
Minimal test case, reproduced on #![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[get("/<id>")]
fn works(id: i32) -> String {
format!("id: {}", id)
}
macro_rules! make_handler {
() => {
#[get("/<id>")]
fn fails(id: i32) -> String {
format!("id: {}", id)
}
}
}
make_handler!();
fn main() {
rocket::ignite().mount("/", routes![works, fails]).launch();
} I thought macro hygiene could be involved in some way but have no idea how to even attempt to investigate that. |
The expanded code I got from @jebrosen 's code: cargo +nightly rustc -- -Z unstable-options --pretty=expanded #![feature(prelude_import)]
#![no_std]
#![feature(plugin)]
#![plugin(rocket_codegen)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
extern crate rocket;
#[allow(unreachable_code)]
fn rocket_route_fn_works<'_b>(__req: &'_b ::rocket::Request,
__data: ::rocket::Data)
-> ::rocket::handler::Outcome<'_b> {
#[allow(non_snake_case, unreachable_patterns)]
let rocket_param_id: i32 =
match match __req.get_param_str(0usize) {
Some(s) =>
<i32 as ::rocket::request::FromParam>::from_param(s),
None => return ::rocket::Outcome::Forward(__data),
} {
Ok(v) => v,
Err(e) => {
::io::_print(::std::fmt::Arguments::new_v1_formatted(&[" => Failed to parse \'",
"\': ",
"\n"],
&match (&"id",
&e)
{
(arg0,
arg1)
=>
[::std::fmt::ArgumentV1::new(arg0,
::std::fmt::Display::fmt),
::std::fmt::ArgumentV1::new(arg1,
::std::fmt::Debug::fmt)],
},
&[::std::fmt::rt::v1::Argument{position:
::std::fmt::rt::v1::Position::At(0usize),
format:
::std::fmt::rt::v1::FormatSpec{fill:
' ',
align:
::std::fmt::rt::v1::Alignment::Unknown,
flags:
0u32,
precision:
::std::fmt::rt::v1::Count::Implied,
width:
::std::fmt::rt::v1::Count::Implied,},},
::std::fmt::rt::v1::Argument{position:
::std::fmt::rt::v1::Position::At(1usize),
format:
::std::fmt::rt::v1::FormatSpec{fill:
' ',
align:
::std::fmt::rt::v1::Alignment::Unknown,
flags:
0u32,
precision:
::std::fmt::rt::v1::Count::Implied,
width:
::std::fmt::rt::v1::Count::Implied,},}]));
return ::rocket::Outcome::Forward(__data)
}
};
let responder = works(rocket_param_id);
::rocket::handler::Outcome::from(__req, responder)
}
/// Rocket code generated static route information structure.
#[allow(non_upper_case_globals)]
#[rocket_route_info]
pub static static_rocket_route_info_for_works: ::rocket::StaticRouteInfo =
::rocket::StaticRouteInfo{method: ::rocket::http::Method::Get,
path: "/<id>",
handler: rocket_route_fn_works,
format: None,
rank: None,};
#[rocket_route(static_rocket_route_info_for_works)]
fn works(id: i32) -> String {
::fmt::format(::std::fmt::Arguments::new_v1_formatted(&["id: "],
&match (&id,) {
(arg0,) =>
[::std::fmt::ArgumentV1::new(arg0,
::std::fmt::Display::fmt)],
},
&[::std::fmt::rt::v1::Argument{position:
::std::fmt::rt::v1::Position::At(0usize),
format:
::std::fmt::rt::v1::FormatSpec{fill:
' ',
align:
::std::fmt::rt::v1::Alignment::Unknown,
flags:
0u32,
precision:
::std::fmt::rt::v1::Count::Implied,
width:
::std::fmt::rt::v1::Count::Implied,},}]))
}
macro_rules! make_handler(( ) => {
# [ get ( "/<id>" ) ] fn fails ( id : i32 ) ->
String { format ! ( "id: {}" , id ) } });
#[allow(unreachable_code)]
fn rocket_route_fn_fails<'_b>(__req: &'_b ::rocket::Request,
__data: ::rocket::Data)
-> ::rocket::handler::Outcome<'_b> {
#[allow(non_snake_case, unreachable_patterns)]
let rocket_param_id: i32 =
match match __req.get_param_str(0usize) {
Some(s) =>
<i32 as ::rocket::request::FromParam>::from_param(s),
None => return ::rocket::Outcome::Forward(__data),
} {
Ok(v) => v,
Err(e) => {
::io::_print(::std::fmt::Arguments::new_v1_formatted(&[" => Failed to parse \'",
"\': ",
"\n"],
&match (&"id",
&e)
{
(arg0,
arg1)
=>
[::std::fmt::ArgumentV1::new(arg0,
::std::fmt::Display::fmt),
::std::fmt::ArgumentV1::new(arg1,
::std::fmt::Debug::fmt)],
},
&[::std::fmt::rt::v1::Argument{position:
::std::fmt::rt::v1::Position::At(0usize),
format:
::std::fmt::rt::v1::FormatSpec{fill:
' ',
align:
::std::fmt::rt::v1::Alignment::Unknown,
flags:
0u32,
precision:
::std::fmt::rt::v1::Count::Implied,
width:
::std::fmt::rt::v1::Count::Implied,},},
::std::fmt::rt::v1::Argument{position:
::std::fmt::rt::v1::Position::At(1usize),
format:
::std::fmt::rt::v1::FormatSpec{fill:
' ',
align:
::std::fmt::rt::v1::Alignment::Unknown,
flags:
0u32,
precision:
::std::fmt::rt::v1::Count::Implied,
width:
::std::fmt::rt::v1::Count::Implied,},}]));
return ::rocket::Outcome::Forward(__data)
}
};
let responder = fails(rocket_param_id);
::rocket::handler::Outcome::from(__req, responder)
}
/// Rocket code generated static route information structure.
#[allow(non_upper_case_globals)]
#[rocket_route_info]
pub static static_rocket_route_info_for_fails: ::rocket::StaticRouteInfo =
::rocket::StaticRouteInfo{method: ::rocket::http::Method::Get,
path: "/<id>",
handler: rocket_route_fn_fails,
format: None,
rank: None,};
#[rocket_route(static_rocket_route_info_for_fails)]
fn fails(id: i32) -> String {
::fmt::format(::std::fmt::Arguments::new_v1_formatted(&["id: "],
&match (&id,) {
(arg0,) =>
[::std::fmt::ArgumentV1::new(arg0,
::std::fmt::Display::fmt)],
},
&[::std::fmt::rt::v1::Argument{position:
::std::fmt::rt::v1::Position::At(0usize),
format:
::std::fmt::rt::v1::FormatSpec{fill:
' ',
align:
::std::fmt::rt::v1::Alignment::Unknown,
flags:
0u32,
precision:
::std::fmt::rt::v1::Count::Implied,
width:
::std::fmt::rt::v1::Count::Implied,},}]))
}
fn main() {
rocket::ignite().mount("/",
<[_]>::into_vec(box
[::rocket::Route::from(&static_rocket_route_info_for_works),
::rocket::Route::from(&static_rocket_route_info_for_fails)])).launch();
} |
Compiling the expanded output of that test case works after minor tweaking, so I think there is some kind of namespace issue, macro_rules! hygiene or otherwise. It's at https://ptpb.pw/IFyp , I added only the feature attributes and ::std prefixes necessary for it to compile. |
For this test case, |
@jebrosen Maybe we should report this issue to rust-lang/rust? |
I think reporting to the rust issue tracker would be appropriate, but I need to sleep now. I don't see anything obviously responsible in recent issues or PRs, but more eyes will help. |
Thanks, I'm using nightly 04-18 with rev 3aefe4f for now.. |
A PR recently landed that may have fixed this issue. Can anyone confirm? (@Boscop) |
New nightly builds haven't happened for a few days, but I had already built |
Can confirm that this is still broken on |
I updated from But I need to use And I can't stay on the old nightly because sentry doesn't work with it.. So what should I do? |
I found that "passing in" the identifier works: macro_rules! make_handler {
($id:ident) => {
#[get("/<id>")]
fn now_works($id: i32) -> String {
format!("id: {}", $id)
}
}
} Unfortunately it has to be "passed in" from every layer of macro invocations all the way up to the original non-macro context and it's an ugly hack. In your case it looks like you would need at least the I don't know how much effort we should put into solving on the Rocket side (or even rustc) given #693 -- a quick test I whipped up suggests that #[route] and #[catch] being proc_macros may even resolve this problem. |
@jebrosen Why shouldn't this be fixed in rustc? Generating rocket handlers is a valid use case (and very common in my projects).. This workaround makes the code even harder to read / follow, especially because it affects every macro in the call stack and the top-level invocation. Btw, I tried your suggestion and it works, but it's weird that it was only required for the macro_rules! gen_handler {
($id:ident, $model:ident, $table:ident, $table_name:expr, [$($fkm:ident)*], {$($validate:meta $field:ident: $type:ty,)+}) => (
pub mod $table {
use rocket_contrib::Json;
use controllers::*;
use models::customer::Customer;
use super::super::$table::{$model as Model, New, Update};
use models::validation::validate;
#[derive(Clone, Default, Deserialize)]
#[serde(default)]
pub struct Edit {
pub id: Option<i64>,
$(pub $field: $type,)+
}
#[post("/edit", data = "<body>")]
pub fn edit(me: Customer, body: Json<Edit>) -> Res<()> {
let req = body.into_inner();
if let Some(id) = req.id {
let model = junwrap!(jtry!(Model::find(id)));
jassert!(model.customer_id == me.user_id, "UNAUTHORIZED");
let mut update = Update::now();
$(
if req.$field != model.$field {
update = update.$field(req.$field.clone());
}
)+
let update = jtry_user!(validate(me.user_id, update));
jtry!(model.update(&update));
} else {
let nu = New {
customer_id: me.user_id,
$($field: req.$field,)+
};
let nu = jtry_user!(validate(me.user_id, nu));
jtry!(Model::create_from(nu));
}
jtry!(me.scale_data_changed());
ok(())
}
#[post("/delete/<id>")]
fn delete(me: Customer, $id: i64) -> Res<()> {
jassert!(junwrap!(jtry!(Model::find($id))).customer_id == me.user_id, "UNAUTHORIZED");
jtry!(Model::delete($id));
jtry!(me.scale_data_changed());
ok(())
}
use $crate::models::views::$table::View;
#[post("/list")]
fn list(me: Customer) -> Res<Vec<View>> {
ok(jtry!(Model::all_of_customer(me.user_id)).into_iter().map(|t| View::from(t)).collect())
}
}
)
}
macro_rules! gen_all {
($id:ident, $($model:ident, $table:ident, $table_name:expr, [$($fkm:ident)*], {$($validate:meta $field:ident: $type:ty,)+})+) => (
pub mod handlers {
$(gen_handler!($id, $model, $table, $table_name, [$($fkm)*], { validate(custom = "allow") active: bool, $($validate $field: $type,)+ });)+
}
$(gen_orm!($model, $table, $table_name, [$($fkm)*], { validate(custom = "allow") active: bool, $($validate $field: $type,)+ });)+
pub mod views {
$(gen_view!($model, $table, $table_name, [$($fkm)*], { active: bool, $($field: $type,)+ });)+
}
)
} |
rustc is discouraging the use of and trying to remove some of the compiler features this issue involves (syntax extensions through compiler plugins, that is). When Rocket moves from the deprecated plugin architecture to
I completely overlooked that detail. Is everything fine if you give it a different name like |
@jebrosen Unfortunately, no:
|
I finally got around to taking a serious look at the codegen and have a pretty solid guess: This also gave me an idea of how to fix it, which appears to work pending further tests. Currently the ident I'm sorry I didn't look at this more closely before -- I must admit I was afraid to actually look at the syntax plugin code back then. diff --git a/core/codegen/src/decorators/route.rs b/core/codegen/src/decorators/route.rs
index e1b4538..c86ba2b 100644
--- a/core/codegen/src/decorators/route.rs
+++ b/core/codegen/src/decorators/route.rs
@@ -8,7 +8,7 @@ use utils::*;
use syntax::source_map::{Span, Spanned, dummy_spanned};
use syntax::tokenstream::TokenTree;
-use syntax::ast::{Arg, Ident, Item, Stmt, Expr, MetaItem, Path};
+use syntax::ast::{Arg, Ident, Item, Stmt, Expr, MetaItem, Path, PatKind};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
@@ -134,7 +134,8 @@ impl RouteParams {
let mut declared_set = HashSet::new();
for (i, param) in params.iter().enumerate() {
declared_set.insert(param.ident().name);
- let ty = match self.annotated_fn.find_input(¶m.ident().name) {
+ let input = self.annotated_fn.find_input(¶m.ident().name);
+ let ty = match input {
Some(arg) => strip_ty_lifetimes(arg.ty.clone()),
None => {
self.missing_declared_err(ecx, param.inner());
@@ -142,8 +143,12 @@ impl RouteParams {
}
};
+ let ident = match input.unwrap().pat.node {
+ PatKind::Ident(_, i, _) => i.prepend(PARAM_PREFIX),
+ _ => param.ident().prepend(PARAM_PREFIX),
+ };
+
// Note: the `None` case shouldn't happen if a route is matched.
- let ident = param.ident().prepend(PARAM_PREFIX);
let expr = match *param {
Param::Single(_) => quote_expr!(ecx, match __req.get_param_str($i) {
Some(s) => <$ty as ::rocket::request::FromParam>::from_param(s), I haven't verified it against anything more complex than the reproduction test I posted above, but I think this approach may be viable for inclusion in a v0.3 patch release -- @SergioBenitez ? |
@jebrosen Nice work! Happy to include that in a patch release. |
After updating my nightly (from 03-06) I'm getting this error, any idea how to fix it?
error[E0425]: cannot find value
rocket_param_id
in this scopeThis error happens only for my
delete
handler which is in a macro that's used 11 times, so I get the same error 11 times:The other handlers outside of macros, and even the other handlers inside macros but with only guard args and no url args don't cause any errors. E.g. this one is also inside the same macro but causes no error:
The text was updated successfully, but these errors were encountered: