-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
C#-style named arguments emit type ascription errors #116411
Comments
This case will be easier than the one in #115192, because |
@rustbot claim |
@rustbot claim |
Anyone up to mentor? Not my first GitHub contribution, but it is my first contribution to the Rust project. |
Please do not use the experts map, it is outdated and one of the people you pinged is on vacation. One of the people on the map has not been a compiler contributor for four years. |
@nouritsu I am available for help. For the Looking at it we probably only need to modify the error branch here to also look for "is the thing parsed so far a bare ident, do we have a rust/compiler/rustc_parse/src/parser/expr.rs Lines 127 to 142 in 4e41880
|
I understand, I'll start working on this after I get back from school in ≈16 hours |
So far I think the match arm should look like - Some(i) if self.may_recover() && self.look_ahead(1, |t| t == &token::Colon) && self.look_ahead(2, |t| t == /*?Is a Type?*/) I am unsure how to check if the 2nd look ahead is suitable to start a type. Edit: could |
Looking at
A PR with the "ignore" approach would be acceptable, I'd prefer the first approach for functionality, eventually. |
I'd love to work on the first approach, sounds more elegant than just ignoring the error. I would require some time to understand |
Also question: why not go with the second approach of |
Because right now
let snapshot = self.snapshot();
let recovered_foo = match self.parse_foo() {
Ok(foo) => {
err.note("we know that it was followed by a foo");
err.emit();
foo
}
Err(mut foo_err) => {
// The parse failed, so we clean up to before the parse.
foo_err.cancel();
self.restore_snapshot(snapshot);
return Err(err);
}
}; The tricky thing is when you have a lot of state in flight (like parsing multiple things in a row) you have to be very careful to actually handle every possible code path and clean up appropriately. |
right, I understand snapshots and the control flow based on above conversation - fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
match self.parse_expr_res(restrictions, None) {
Ok(expr) => Ok(expr),
Err(mut err) => match self.token.ident() {
Some(i) if self.may_recover() && self.look_ahead(1, |t| t == &token::Colon) => {
let snapshot = self.create_snapshot_for_diagnostic();
match self.parse_ty() {
Ok(_) => { /* user is trying to use type scription syntax */ }
Err(_) => { /* user is trying to c# style use named arguments */ }
}
self.restore_snapshot(snapshot);
}
Some((Ident { name: kw::Underscore, .. }, false))
if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
{
// Special-case handling of `foo(_, _, _)`
err.emit();
self.bump();
Ok(self.mk_expr(self.prev_token.span, ExprKind::Err))
}
_ => Err(err),
},
}
} I'll move the restore to just before an error is returned. How do I return a type ascription error or a named argument error? Usually in other rust projects there is an enum that contains error variants, like - enum E {
LengthError,
TypeError,
FormatError,
} Does the parser have such an enum? If not, what do I use? I tried to dig through PResult then the DiagnosticBuilder but I don't think that's the correct direction. |
|
I see, for now I'd like to switch my approach to the "ignore the problem and emit error" option. This would help me understand the structure slightly better. I don't understand how doing adding a branch that checks for Note that after I get the ignore approach working, I'll be switching to the snapshot approach and make a PR |
If you can parse an expression after the |
Try to unify the handling of arguments: method and function calls should have the same errors for this (except the struct literal suggestion). |
I have identified why the code never enters the branch. fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
match self.parse_expr_res(restrictions, None) {
Ok(expr) => Ok(expr),
Err(mut err) => match self.token.ident() {
Some((Ident { name: kw::Underscore, .. }, false))
if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
{
// Special-case handling of `foo(_, _, _)`
err.emit();
self.bump();
Ok(self.mk_expr(self.prev_token.span, ExprKind::Err))
}
Some(_) if self.may_recover() && self.look_ahead(0, |t| t == &token::Colon) => {
/* my branch */
panic!("Entered Branch")
}
s => {
println!(
"{}\t{}\t{:?}\t{:?}\t{:?}",
self.may_recover(),
self.look_ahead(0, |t| t == &token::Colon),
s,
self.token,
self.prev_token,
);
Err(err)
}
},
}
} Testing with the following code -
The logged variables have values as follows -
The parser progresses to the colon without ever being bumped. Should I match against Or should I match against I think the second approach is cleaner, I'll implement it for now but would love your input. |
I'm waiting for a response. Meanwhile, I'm exploring neighboring and parent functions to understand rust semantics better. |
When doing a look_ahead check and finding what you want, you'll want to call
You don't need to do For what you want to do, in most cases it is fine to leave stray whitespace, but the way to deal with it is with |
I'm done with my exams, I'll start working on this again. |
I think I have come up with a solution - fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
match self.parse_expr_res(restrictions, None) {
Ok(expr) => Ok(expr),
Err(mut err) => match self.token.ident() {
Some((Ident { name: kw::Underscore, .. }, false))
if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
{
// Special-case handling of `foo(_, _, _)`
err.emit();
self.bump();
Ok(self.mk_expr(self.prev_token.span, ExprKind::Err))
}
None if self.may_recover()
&& self.prev_token.is_ident()
&& self.token.kind == token::Colon =>
{
err.span_suggestion_verbose(
self.prev_token.span.until(self.look_ahead(1, |t| t.span)),
"if this is a parameter, remove the name for the parameter",
"",
Applicability::MaybeIncorrect,
);
self.bump();
err.emit();
Ok(self.mk_expr(self.token.span, ExprKind::Err))
}
_ => Err(err),
},
}
} Test code - fn main() {
let a = 50;
foo(x: a);
}
fn foo(x: i32) {} fn main() {
let a = 50;
Foo::bar(x: a);
}
struct Foo {
pub fn bar(x : i32){}
} Note - |
@nouritsu you should create a PR with your code, it'll be easier to discuss specific changes. You might notice that you have a second parse error because the |
Created a PR. |
Can we remove the |
"easy" is unfortunately always "relative to other issues" but yes, this has proven much more annoying than I expected it to be for anyone. Quite sorry, @nouritsu. |
No need for the apology, this has been an excellent learning experience. I look forward to contributing to rust in the future. |
Code
Current output
Desired output
Rationale and extra context
Many close variations emit really bad diagnostics as well, often referencing type ascription, and all of these variations being really bad causes incredible amounts of thrashing while trying to learn Rust.
Other cases
No response
Anything else?
Note that this suggestion also is wrong, it should be
"size"
, because&'static str
(and probably a few other relevant types) implements IntoPy for PyString.The text was updated successfully, but these errors were encountered: