Skip to content
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

in which parentheses are suggested for should-have-been-tuple-patterns #48527

Merged

Conversation

zackmdavis
Copy link
Member

@zackmdavis zackmdavis commented Feb 25, 2018

destructure_suggest_parens

Programmers used to working in some other languages (such as Python or
Go) might expect to be able to destructure values with comma-separated
identifiers but no parentheses on the left side of an assignment.

Previously, the first name in such code would get parsed as a
single-indentifier pattern—recognizing, for example, the
let a in let a, b = (1, 2);—whereupon we would have a fatal syntax
error on seeing an unexpected comma rather than the expected semicolon
(all the way nearer to the end of parse_full_stmt).

Instead, let's look for that comma when parsing the pattern, and if we
see it, make-believe that we're parsing the remaining elements in a
tuple pattern, so that we can suggest wrapping it all in parentheses. We
need to do this in a separate wrapper method called on a "top-level"
pattern, rather than within
parse_pat itself, because parse_pat gets called recursively to parse
the sub-patterns within a tuple pattern.

We could also do this for match arms, if let, and while let, but
we elect not to in this patch, as it seems less likely for users to make
the mistake in those contexts.

Resolves #48492.

r? @petrochenkov

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 25, 2018
@zackmdavis zackmdavis force-pushed the and_the_social_construction_of_tuples branch from 604a8a1 to 2a5fec6 Compare February 25, 2018 07:58
// suggestion-enhanced error here rather than choking on the comma
// later.
if let PatKind::Ident(_, _, ref sub) = pat.node {
if sub.is_none() { // just `ident`, not `ident @ pattern`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PatKind::Ident and sub.is_none here are artificial restrictions.
What we want is to parse PAT, PAT, ..., PAT instead of just PAT and return PatKind::Tuple containing those patterns.
It's not very important if those pattern are identifiers or not - if we can easily recover more, we should do it.
(You may want to modify parse_pat_tuple_elements to return partial result even in case of failure.)

@petrochenkov
Copy link
Contributor

We could also do this for match arms, if let, and while let, but
we elect not to in this patch

If supporting if let and other location means simply using parse_pat_fancy instead of parse_pat, I'd rather use parse_pat_fancy everywhere by default unless it conflicts with something.
Supporting "top level" patterns like if let and while let seems to be a part of the "baseline" to me.

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 25, 2018
@zackmdavis zackmdavis force-pushed the and_the_social_construction_of_tuples branch from 2a5fec6 to d615130 Compare February 28, 2018 05:09
@zackmdavis
Copy link
Member Author

@petrochenkov updated (force-push)

artificial restrictions [...] If supporting if let and other location means simply using parse_pat_fancy instead of parse_pat, I'd rather use parse_pat_fancy everywhere by default

You're right; fixed.

(You may want to modify parse_pat_tuple_elements to return partial result even in case of failure.)

The second commit here (d615130) revives the partial-result type from the abandoned #46721. This lets us check subsequent code that makes use of the destructured variables, which I think is exciting!—

zmd@ReflectiveCoherence:~/Code/rust$ rustc +stage1 recover.rs 
error: unexpected `,` in pattern
 --> recover.rs:2:10
  |
2 |     let a, b = (1, 2);
  |         -^-- help: try adding parentheses: `(a, b)`

error[E0277]: cannot divide `{integer}` by `{float}`
 --> recover.rs:3:15
  |
3 |     let c = a / 1.5;
  |               ^ no implementation for `{integer} / {float}`
  |
  = help: the trait `std::ops::Div<{float}>` is not implemented for `{integer}`

error: aborting due to 2 previous errors

(On master, we never get to the type error because parsing was interrupted by the bad pattern. With the first commit (ed6679c) but without d615130, the compiler will think that a has type ({integer}, {integer}) and that b doesn't exist.)

But there are other situations where autotupling can be misleading:

zmd@ReflectiveCoherence:~/Code/rust$ rustc +stage1 slice.rs 
error: unexpected `,` in pattern
 --> slice.rs:4:10
  |
4 |     let a, b = &[1, 2];
  |         -^-- help: try adding parentheses: `(a, b)`

error[E0658]: non-reference pattern used to match a reference (see issue #42640)
 --> slice.rs:4:9
  |
4 |     let a, b = &[1, 2];
  |         ^^^^ help: consider using a reference: `&a, b`
  |
  = help: add #![feature(match_default_bindings)] to the crate attributes to enable

error[E0308]: mismatched types
 --> slice.rs:4:9
  |
4 |     let a, b = &[1, 2];
  |         ^^^^ expected array of 2 elements, found tuple
  |
  = note: expected type `[{integer}; 2]`
             found type `(_, _)`

error: aborting due to 3 previous errors

(The E0658 suggestion is wrong, and the E0308 diagnostic in isolation makes it look like we support Python-like parenless tuples, which we don't.)

What do you think?

@zackmdavis
Copy link
Member Author

(Travis failure is only because I rebased on master without being aware of #48449.)

@zackmdavis
Copy link
Member Author

On further thought, I'm leaning toward the position that the incongruous type errors "downstream" are bad enough that we actually shouldn't try to recover more than the original error did. That is: parse_top_level_pat should return an Err (preventing us from parsing further) rather than just emitting a diagnostic and returning Ok ... still eager for reviewers' thoughts, though.

@petrochenkov petrochenkov added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 28, 2018
@estebank
Copy link
Contributor

@zackmdavis I agree with your assessment. Another (more involved) option would be to set the type of the tuple elements to TyErr and continue, but doing this would be non-trivial. If we try to go down that route, I'd rather do so in a follow up PR as part of a full revamp (piece meal improvement towards this in #46732).

In the meantime, I think it is very reasonable to bail out on the enclosing scope and continue (what you're doing). That way the rest of the code will be type checked.

@zackmdavis zackmdavis force-pushed the and_the_social_construction_of_tuples branch from d615130 to a36e77c Compare February 28, 2018 23:46
@zackmdavis
Copy link
Member Author

(force-pushed a more conservative version that returns Err in accordance with previous two comments)

r? @estebank

@petrochenkov petrochenkov self-assigned this Mar 1, 2018
@petrochenkov
Copy link
Contributor

petrochenkov commented Mar 1, 2018

Recovery is not required to be 100% correct, it's guessing by definition, it should only be correct in most cases.
If the let a, b = [1, 2] mistake is relatively rare in practice compared to let a, b = (1, 2) mistake, then we can issue extra diagnostics for it.

That said, even without recovery this is a nice improvement.
@bors r+

@bors
Copy link
Contributor

bors commented Mar 1, 2018

📌 Commit a36e77c has been approved by petrochenkov

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 1, 2018
@bors
Copy link
Contributor

bors commented Mar 1, 2018

💡 This pull request was already approved, no need to approve it again.

@bors
Copy link
Contributor

bors commented Mar 1, 2018

📌 Commit a36e77c has been approved by petrochenkov

@kennytm
Copy link
Member

kennytm commented Mar 2, 2018

@bors r-

The parse_pat_tuple_elements function was recently removed in #48500, replaced by parse_parenthesized_pat_list. Please rebase and update the code.

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Mar 2, 2018
@zackmdavis zackmdavis force-pushed the and_the_social_construction_of_tuples branch from a36e77c to 117a9fe Compare March 3, 2018 04:33
@zackmdavis
Copy link
Member Author

(rebased)

@estebank
Copy link
Contributor

estebank commented Mar 3, 2018

The rebase seems to have messed the parsing up:

[01:12:28] 2	  --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:48:17
[01:12:28] 3	   |
[01:12:28] 4	LL |     while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
[01:12:28] -	   |               --^------- help: try adding parentheses: `(b1, b2, b3)`
[01:12:28] +	   |               --^
[01:12:28] +	   |               |
[01:12:28] +	   |               help: try adding parentheses: `(b1,)`

// later.
let comma_span = self.span;
self.bump();
if let Err(mut err) = self.parse_parenthesized_pat_list() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep fn parse_parenthesized_pat_list but factor out fn parse_pat_list out of it and use it here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

Programmers used to working in some other languages (such as Python or
Go) might expect to be able to destructure values with comma-separated
identifiers but no parentheses on the left side of an assignment.

Previously, the first name in such code would get parsed as a
single-indentifier pattern—recognizing, for example, the
`let a` in `let a, b = (1, 2);`—whereupon we would have a fatal syntax
error on seeing an unexpected comma rather than the expected semicolon
(all the way nearer to the end of `parse_full_stmt`).

Instead, let's look for that comma when parsing the pattern, and if we
see it, momentarily make-believe that we're parsing the remaining
elements in a tuple pattern, so that we can suggest wrapping it all in
parentheses. We need to do this in a separate wrapper method called on
the top-level pattern (or `|`-patterns) in a `let` statement, `for`
loop, `if`- or `while let` expression, or match arm rather than within
`parse_pat` itself, because `parse_pat` gets called recursively to parse
the sub-patterns within a tuple pattern.

Resolves rust-lang#48492.
@zackmdavis zackmdavis force-pushed the and_the_social_construction_of_tuples branch from 117a9fe to 1f04597 Compare March 8, 2018 19:33
@zackmdavis
Copy link
Member Author

(updated)

@estebank
Copy link
Contributor

estebank commented Mar 8, 2018

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Mar 8, 2018

📌 Commit 1f04597 has been approved by estebank

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Mar 8, 2018
Manishearth added a commit to Manishearth/rust that referenced this pull request Mar 9, 2018
…ion_of_tuples, r=estebank

in which parentheses are suggested for should-have-been-tuple-patterns

![destructure_suggest_parens](https://user-images.githubusercontent.com/1076988/36638335-48b082d4-19a7-11e8-9726-0d043544df2f.png)

Programmers used to working in some other languages (such as Python or
Go) might expect to be able to destructure values with comma-separated
identifiers but no parentheses on the left side of an assignment.

Previously, the first name in such code would get parsed as a
single-indentifier pattern—recognizing, for example, the
`let a` in `let a, b = (1, 2);`—whereupon we would have a fatal syntax
error on seeing an unexpected comma rather than the expected semicolon
(all the way nearer to the end of `parse_full_stmt`).

Instead, let's look for that comma when parsing the pattern, and if we
see it, make-believe that we're parsing the remaining elements in a
tuple pattern, so that we can suggest wrapping it all in parentheses. We
need to do this in a separate wrapper method called on a "top-level"
pattern, rather than within
`parse_pat` itself, because `parse_pat` gets called recursively to parse
the sub-patterns within a tuple pattern.

~~We could also do this for `match` arms, `if let`, and `while let`, but
we elect not to in this patch, as it seems less likely for users to make
the mistake in those contexts.~~

Resolves rust-lang#48492.

r? @petrochenkov
bors added a commit that referenced this pull request Mar 9, 2018
Rollup of 5 pull requests

- Successful merges: #48527, #48588, #48801, #48856, #48857
- Failed merges:
@bors bors merged commit 1f04597 into rust-lang:master Mar 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants