pointfreeco / swift-case-paths

🧰 Case paths extends the key path hierarchy to enum cases.
https://www.pointfree.co/collections/enums-and-structs/case-paths
MIT License
904 stars 105 forks source link

Fix fatal bug in `is(_:)` #142

Closed Ryu0118 closed 8 months ago

Ryu0118 commented 8 months ago

Hi there, I found a bug in is(_:).

In version 1.2.0, the handling of optional associated values has been improved. Specifically, the subscript(case:) now correctly returns a value without unnecessarily wrapping it in an additional Optional layer when the associated value is already an Optional. However, this improvement has introduced a significant bug.

Consider the following enum as an example:

enum Foo {
  case c1
  case c2(String?)
}

When using the is(_:) method with c2's associated value set to nil, like so:

Foo.c2(nil).is(\.c2) // false

The expected result would be true, but it incorrectly returns false. This is a direct consequence of the changes in version 1.2.0. Prior to this change, the value of self[case: keyPath] used in the implementation of is(_:) would be Optional(nil), bug which is not nil, thus functioning correctly. However, post-change, the value of self[case: keyPath] becomes nil, leading to incorrect behavior even when the case matches.

On the other hand, with a non-nil associated value such as:

Foo.c2("foo").is(\.c2) // true

The method works correctly because self[case: keyPath] returns "foo".

This pull request introduces a code change to prevent the execution of the line at 52 in Optional+CasePathable that was added in 1.2.0:

public subscript<Member>(
  dynamicMember keyPath: KeyPath<Value.AllCasePaths, AnyCasePath<Value, Member?>>
) -> Case<Member>
where Value: CasePathable {
  self[dynamicMember: keyPath].some
}