implement fill match arm assist for tuple of enums - #3689
Conversation
| return None; | ||
| } | ||
|
|
||
| enum_defs |
There was a problem hiding this comment.
IMHO, since using multi_cartesian_product is quite convoluted, I think it will be nice to add a comment describing what is going on here.
There was a problem hiding this comment.
I added a comment here. Let me know if you prefer I say something different.
SomeoneToIgnore
left a comment
There was a problem hiding this comment.
Awesome, I've noticed that I happen to match over tuples rather frequently, so this assist should be very handy, thank you.
I've left a few commments, those are mostly some minor things except for the panics (that would be really nice to address in the same PR).
On a more global note, it feels like we can generalize the whole approach (with some recursive match_ast! maybe) to avoid various else if resolve_... branches in the code doing more or less the same thing in the beginning.
But that's a separate thing, not required in this PR.
| format-buf = "1.0.0" | ||
| join_to_string = "0.1.3" | ||
| rustc-hash = "1.1.0" | ||
| itertools = "0.8.2" |
There was a problem hiding this comment.
There's a 0.9.0 version out in the wild, maybe we can use that, just to be up to date here at least.
There was a problem hiding this comment.
It seems all the other crates in this repo use 0.8.2, so changing only this crate would mean compiling both versions. IMO it seems nice to use the same version of a dependency across all the crates in a single project like this, just to reduce mental overhead in cases where the API has actually changed.
I'm not sure how dependency upgrades are typically done in this project, so let me know if there is some existing standard practice, but would you mind if I pushed this to a follow-up PR?
| let patterns = variants | ||
| .into_iter() | ||
| .filter_map(|variant| build_pat(ctx.db, module, variant)) | ||
| .collect::<Vec<_>>(); |
There was a problem hiding this comment.
.collect::<Vec<_>>()
Nit: You don't really need to collect anything here and can pass the iterator directly into the make::tuple_pat
There was a problem hiding this comment.
Good catch, fixed.
| .iter() | ||
| .map(|ty| match ty.as_adt() { | ||
| Some(Adt::Enum(e)) => e, | ||
| _ => panic!("handle the case of tuple containing non-enum"), |
There was a problem hiding this comment.
I don't think panicking during assist construction just because we did not implement something is a good idea: first of all, it pollutes the logs with big traces that do not say anything meaningful besides that the developers did not cover all cases (and this might be frequent, since this construction also happens on hover).
Also, with this panic, calling the Quick fix shortcut will show endless progress bar in VS Code, which is a bit distracting :)
So I propose to simply match the only case here and maybe leave a comment about the rest of the cases.
There was a problem hiding this comment.
Oops, I mean to go back and fix that. Fixed.
There was a problem hiding this comment.
Note that you can use todo!() or a //TODO comment for such cases: CI would prevent mering code with todos into master.
| } | ||
|
|
||
| pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { | ||
| let pats_str = pats.into_iter().map(|p| p.syntax().to_string()).join(", "); |
There was a problem hiding this comment.
Nit: p.syntax().to_string() can be just p.to_string() due to the recent improvements in the ast API.
| ); | ||
| } | ||
|
|
||
| #[test] |
There was a problem hiding this comment.
Currently, the assist panics on a single element tuple:
match (Some(22), ) {
}Might be a good idea to fix it :)
There was a problem hiding this comment.
This one was trickier than I thought. The panic comes from make::tuple_pat. I didn't dig much into it, but I think it is because a single element tuple doesn't get parsed as a TuplePat.
For now I handle this case by marking the assist as non-applicable if tuple_len < 2. I hope that is acceptable for now.
| }) | ||
| } | ||
|
|
||
| fn resolve_tuple_of_enum_def( |
There was a problem hiding this comment.
resolve_enum_def contains autoderef invocation.
This helps to detect more enum-related cases and might be a cool thing to support in resolve_tuple_of_enum_def too, now or later in some other PR.
Currently this code
let ref_opt = &Some(22);
match (ref_opt, ref_opt) {
}will invoke the panic!("handle the case of tuple containing non-enum"), line and fail since we don't recognise the enum behind the reference.
This all kind of hints that we might want to approach the resolution somehow more unified, but that is maybe an another task outside of this PR's scope.
There was a problem hiding this comment.
Thanks for pointing this out. I've added the autoderef to resolve_tuple_of_enum_def, but I agree it does feel like there should be some way to reduce some of the duplication here.
matklad
left a comment
There was a problem hiding this comment.
Overall, looks good to me!
I think after this PR it makes sense to look into graduating this assist into a diagnostic, similar to how we've dealt with missing struct fields:
That is, we should implement proper exhaustiveness checking at the hir layer.
| use std::iter; | ||
|
|
||
| use itertools::Itertools; | ||
|
|
There was a problem hiding this comment.
We generally treat all non-std external crates the same, so there's no need for a blank line here.
| ast::Pat::from(make::tuple_pat(patterns)) | ||
| }) | ||
| .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | ||
| .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) |
There was a problem hiding this comment.
This is not strictly related to this PR, but imho it would be better to put empty curly braces {} instead of a unit literal since the user almost all the time writes some block expression as a match arm (or a non-block expression which means removing the (), or {} anyway).
There was a problem hiding this comment.
I did see this suggestion in #3687, but I'd prefer to handle that in a separate PR if you don't mind?
SomeoneToIgnore
left a comment
There was a problem hiding this comment.
Great, way fewer panics and even more functionality than before.
Thank you for the PR and welcome with the follow-ups.
|
I believe I've cleaned up / fixed all review feedback. I'm not sure who typically presses the 'Resolve conversation` button. Let me know if you'd like to see anything else before this merges. |
GitHub does that when you change the code in a newer commit. |
|
bors r+ Thanks! To reiterate, if you feel like this, moving this to hir (somewhere around here) as a proper exhaustive match checking, that would be awesome. This will probably require reading rustc source code to learn how to do this properly though. |
Build succeeded |
Thanks for the merge. I'll take a look at moving this to hir after tackling some of the low hanging fruit follow ups mentioned in this PR. |
3697: update itertools version to 0.9.0 r=matklad a=JoshMcguigan Updating `itertools` version per [feedback on #3689](#3689 (comment)) from @SomeoneToIgnore. Worth noting that `chalk` still uses `itertools` v0.8.2, so perhaps it is worth a PR to update that repo as well so we don't have to build both versions? Co-authored-by: Josh Mcguigan <joshmcg88@gmail.com>
3700: fill match arms with empty block rather than unit tuple r=matklad a=JoshMcguigan As requested by @Veetaha in #3689 and #3687, this modifies the fill match arms assist to create match arms as an empty block `{}` rather than a unit tuple `()`. In one test I left one of the pre-existing match arms as a unit tuple, and added a body to another match arm, to demonstrate that the contents of existing match arms persist. Co-authored-by: Josh Mcguigan <joshmcg88@gmail.com>
3706: missing match arms diagnostic r=flodiebold a=JoshMcguigan Following up on #3689 (comment), this PR creates a missing match arms diagnostic. At the moment this is a very early draft, but I wanted to open it just to get some initial feedback. Initial questions: * Have I roughly created the correct boilerplate? * Inside the new `validate_match` function: * Am I correct in thinking I want to do validation by comparing the match arms against `match_expr`? And when analyzing `match_expr` I should be looking at it as a `hir_def::expr::Expr`? * I mostly copied the chained if-let statements from the struct validation. Shouldn't there be a non-failable way to get an AstPtr from the hir data structures? Thanks for all the guidance. Co-authored-by: Josh Mcguigan <joshmcg88@gmail.com>

This updates the fill match arm assist to work in cases where the user is matching on a tuple of enums.
Note, for now this does not apply when some match arms exist (other than the trivial
_), but I think this could be added in the future.I think this also lays the groundwork for filling match arms when matching on tuples of non-enum values, for example a tuple of an enum and a boolean.