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

Fix replace-if-let-with-match generates non-exhausive match #18797

Merged
merged 2 commits into from
Dec 31, 2024

Conversation

profetia
Copy link
Contributor

@profetia profetia commented Dec 30, 2024

Why

Closes: #8690 and #10583

Continuation of #10491. r? @Veykril if you don't mind?

The reason why the original author were unable to reproduce the first case in #8690 might be because of misuses of minicore, which creates an error in the types and make it fallback to the wildcard. Maybe we should give more information about minicore in the docs?

I extended the patterns marked as FIXME in #10491, while also covering the cases involving literals.

What

  • Add test cases for replace_if_let_with_match
  • Cover more cases for using wildcards on make_else_arm

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Dec 30, 2024
Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 left a comment

Choose a reason for hiding this comment

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

I'd be more happy with some approach based on the analysis on the usefulness of the pattern (didn't check if something like that is possible), but this is good enough for now.

crates/ide-assists/src/utils.rs Outdated Show resolved Hide resolved
crates/ide-assists/src/utils.rs Outdated Show resolved Hide resolved
.pat()
.map_or(true, |pat| check_pat_variant_nested_or_literal_with_depth(&pat, depth)),
ast::Pat::TuplePat(pat) => {
pat.fields().any(|pat| check_pat_variant_nested_or_literal_with_depth(&pat, depth))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pat.fields().any(|pat| check_pat_variant_nested_or_literal_with_depth(&pat, depth))
pat.fields().any(|pat| check_pat_variant_nested_or_literal_with_depth(&pat, depth + 1))

Should be depth + 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think nesting tuple pattern is fine if it does not contain literal patterns or tuple struct patterns or else.

let bar: Result<(i32, i32, Option<i32>), ()> = Ok((1, 2, Some(3)));
if let Ok((first, second, third)) = bar {} // fine
if let Ok((1, second, third)) = bar {} // bad
if let Ok((first, second, Some(third))) = bar {} // bad

Copy link
Contributor

Choose a reason for hiding this comment

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

Then why the others (e.g. OrPat) have a depth limit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair point, I must have missed it. Just fixed it.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's also in TupleStructPat, and if you're going to not use depth, why not just remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I doubt about TupleStructPat, it should not be nested.

let bar: Result<Option<i32>, ()> = Ok(Some(1));
if let Ok(Some(a)) = bar {} // bad

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah so this is the reason - then RecordPat should also be depth + 1 (Enum::Variant {}) - and perhaps also rename depth to something more informative, e.g. depth_after_refutable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair. You remind me that I forgot to consider record in enum Enum::Variant {} at all. My current test cases only covered structs MyStruct { a, b }.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed. I also covered cases like MyStruct(i32, i32)

ast::Pat::RecordPat(pat) => pat.record_pat_field_list().map_or(true, |pat| {
pat.fields().any(|pat| {
pat.pat()
.map_or(true, |pat| check_pat_variant_nested_or_literal_with_depth(&pat, depth))
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too depth + 1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similar reason to TuplePat

crates/ide-assists/src/utils.rs Outdated Show resolved Hide resolved
@@ -728,6 +728,363 @@ fn foo(x: Result<i32, ()>) {
);
}

#[test]
fn test_if_let_with_match_variant_nested_or_literal() {
Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 Dec 30, 2024

Choose a reason for hiding this comment

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

I think this test can benefit from a split to several tests, but I'll leave that to you.

@ChayimFriedman2 ChayimFriedman2 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 Dec 30, 2024
@profetia
Copy link
Contributor Author

Updated. Thanks for your time reviewing this and happy new year!

@profetia
Copy link
Contributor Author

@rustbot review

@rustbot rustbot 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 Dec 31, 2024
@Veykril
Copy link
Member

Veykril commented Dec 31, 2024

Maybe we should give more information about minicore in the docs?

Do we talk about minicore in our dev guide? Probably not in which case we absolutely should

@Veykril
Copy link
Member

Veykril commented Dec 31, 2024

I'd be more happy with some approach based on the analysis on the usefulness of the pattern (didn't check if something like that is possible), but this is good enough for now.

I think thats something @Nadrieril specifically tried to enable with the librarification of the exhaustiveness checking code in rustc (that we now use). Please correct me if I am wrong :)

@Nadrieril
Copy link
Member

Indeed using the exhaustiveness checker should be possible here. For this case, I'd expect we do something like: let pat be the pattern in the if let. Pass vec![pat] to MatchCheckCtx.compute_match_usefulness. non_exhaustiveness_witnesses in the output contains a list of patterns that must be added to pat to make an exhaustive match. Simplest behavior is: if this list has a single element, use that for the other match arm; otherwise use _.

@profetia
Copy link
Contributor Author

Seems MatchCheckCtx is already in the library. Let me see if I can use that instead.

@profetia
Copy link
Contributor Author

Seems MatchCheckCtx is already in the library. Let me see if I can use that instead.

I'm not an expert in rust analyzer but after some attempts I feel there might be a lot of work to be done before the MatchCheckCtx can be used as a tool outside hir-ty. Indeed this is a good place for the exhaustiveness checker.

Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 left a comment

Choose a reason for hiding this comment

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

Thanks! I think this is enough for now.

@ChayimFriedman2 ChayimFriedman2 added this pull request to the merge queue Dec 31, 2024
Merged via the queue into rust-lang:master with commit 085ad10 Dec 31, 2024
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Replacing if-let with match can lead to match expression with non-exhaustive patterns
5 participants