-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Detect =
-> :
typo in let bindings
#45452
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r=me modulo the fact that it displays ?:
12 | let x: Vec::with_capacity(10, 20); | ||
| -- ^^ | ||
| || | ||
| |help: did you mean assign here?: `=` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ?:
looks funny, don't we have some convention for dealing with that? cc @oli-obk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea:
rust/src/librustc_errors/diagnostic.rs
Lines 231 to 236 in 9ae6ed7
/// The message | |
/// * should not end in any punctuation (a `:` is added automatically) | |
/// * should not be a question | |
/// * should not contain any parts like "the following", "as shown" | |
/// * may look like "to do xyz, use" or "to do xyz, use abc" | |
/// * may contain a name of a function, variable or type, but not whole expressions |
src/libsyntax/parse/parser.rs
Outdated
err.emit(); | ||
// As this was parsed successfuly, continue as if the code has been fixed for the | ||
// rest of the file. It will still fail due to the emitted error, but we avoid | ||
// extra noise. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
let (err, ty) = if self.eat(&token::Colon) { | ||
// Save the state of the parser before parsing type normally, in case there is a `:` | ||
// instead of an `=` typo. | ||
let parser_snapshot_before_type = self.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this something we do elsewhere? Maybe we should find some way to make it more obvious that people might be cloning the parser like this? It sort of surprises me, but I think it makes sense, and I can't see any obvious reasons it will cause problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the first time something like this was introduced to the parser was in #42578 to verify wether a as usize < b
could be parsed and if it could, emit a suggestion for the correct code (a as usize) < b
. It might have been used elsewhere since. I thought about adding a method to the parser but balked at the idea to not give the impression that we should use of this error recovery style everywhere.
<nonblocking rant> I dislike this continuing tendency of issuing custom diagnostics for all possible accidental typos and mistakes without any account for probability of them occurring in practice. </nonblocking rant> |
Sadly, other than adding remote logging to the compiler, all we can rely on is our own impressions, experience and anecdotal evidence on how often some errors happen to make those decisions. That being said, I understand the dislike for making the compiler more complex for very small corner cases.
This is true, and it would be fool hardy endeavor to even try. That being said, I wouldn't be surprised if there's a funnel for filed tickets of "1 filed <- 10 affected <- 1000 annoyed". This PR in particular "fixes" the issue filed in a general enough way that doesn't necessarily help experienced developers (they know where to look), but helps heaps to newcomers by pointing them in the right direction by going "Hey! The error happens there, but look over here too!". Can this become annoyingly verbose? Sure, and there's a conversation to be had about reducing the verbosity of existing errors, but don't underplay how useful it can be for newcomers to go out of our way to explain anything remotely non-obvious. [aside] This makes me think, may be there would be value to have a mode where the spans still appear but the label text and suggestions are not shown, for experienced devs. An intermediary between current output and "short messages". In this case, something along the lines of:
#42578 is an example of a diagnostic that could not be accomplished as cleanly otherwise, and that remediates a problem has tripped multiple people before. It is definitely a corner case, but the usability gain is in my eyes clear.
I agree with this, but I disagree that for example this PR doesn't do that. Sure, I'm adding the rollback machinery to be able to infer that what was written as the binding's type could be parsed as the initializer, but in the general case it also points out to the binding saying "The parse error you've got over there happened while looking at this bindings type", which is much more generally useful, specially when dealing with code spanning multiple lines. I would also go as far as to claim that even when improving the general errors is always a great use of our time, even if this is sometimes just not possible, covering a wide range of small corner cases with very localized and relevant error messages can change the user experience from "this is hard, but understandable" to "this is magical". The universe of possible typos is unlimited, but the amount of likely typos and mistakes is much more constrained. The ones I've seen most often in person and in the issue tracker fall under one of two categories: "keys close together", for which we shouldn't be doing anything special over what we're already doing, and "rust/other language mismatch", like I would like to have a longer conversation on this in order to hear more of your thoughts. |
One thing I've been really wanting to do that's related here is to see some progress on "telemetry". That is, I think we need some way to log information about what people have tried to compile, what error messages they've gotten, and how they responded to these error messages so that we can collect data. Obviously this would need to be an "opt-in" and transparent process, but I imagine many people would happily agree. That would be tremendously useful in helping us gauge priorities. I'd love to see some concerted effort here -- probably the best thing would be to start super small. i.e., have some website where people can copy-and-paste an error message that they found confusing and hit a simple button. But it's going to take effort on the back-end to triage, categorize, and diagnose this sort of thing as well. In my ideal world, this would be the domain of the compiler error messages Working Group long term. =) |
Well, let's make a final decision here! I don't really know if this is a common source of confusion, but it certainly seems to me like the kind of thing that one could stare at for hours before having a 🤦♀️ moment, so I'm inclined to r+, but also to (independently) make a real effort towards trying to get some data on which to base future decisions. |
When encountering a let binding type error, attempt to parse as initializer instead. If successful, it is likely just a typo: ```rust fn main() { let x: Vec::with_capacity(10); } ``` ``` error: expected type, found `10` --> file.rs:3:31 | 3 | let x: Vec::with_capacity(10, 20); | -- ^^ | || | |help: did you mean assign here?: `=` | while parsing the type for `x` ```
@nikomatsakis created #45857 to track the telemetry talk. Are there any further changes I should make to this PR? |
@bors r+ |
📌 Commit 9dc7abe has been approved by |
Detect `=` -> `:` typo in let bindings When encountering a let binding type error, attempt to parse as initializer instead. If successful, it is likely just a typo: ```rust fn main() { let x: Vec::with_capacity(10); } ``` ``` error: expected type, found `10` --> file.rs:3:31 | 3 | let x: Vec::with_capacity(10, 20); | -- ^^ | || | |help: did you mean assign here?: `=` | while parsing the type for `x` ``` Fix #43703.
☀️ Test successful - status-appveyor, status-travis |
When encountering a let binding type error, attempt to parse as
initializer instead. If successful, it is likely just a typo:
Fix #43703.