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

Macro bonanza #2553

Merged
merged 113 commits into from
Nov 13, 2023
Merged

Macro bonanza #2553

merged 113 commits into from
Nov 13, 2023

Conversation

stephencelis
Copy link
Member

@stephencelis stephencelis commented Nov 8, 2023

This branch introduces a few new tools and changes made possible by Swift 5.9 macros.

Discussion forthcoming here: #2555

Copy link
Contributor

@Matejkob Matejkob left a comment

Choose a reason for hiding this comment

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

Hi there! 👋

I've just taken a look at the macro implementation and have left a few inline comments. While I hope these observations will be helpful, please feel free to disregard any that you don't find useful. I understand that different perspectives can lead to varying approaches!

Great use case of Swift Macro! I love it!

Sending much love,
Mateusz 🫶

Sources/ComposableArchitecture/Macros.swift Outdated Show resolved Hide resolved
Comment on lines +73 to +81
let method = $0.decl.as(FunctionDeclSyntax.self),
method.name.text == "reduce",
method.signature.parameterClause.parameters.count == 2,
let state = method.signature.parameterClause.parameters.first,
state.firstName.text == "into",
state.type.as(AttributedTypeSyntax.self)?.specifier?.text == "inout",
method.signature.parameterClause.parameters.last?.firstName.text == "action",
method.signature.effectSpecifiers == nil,
method.signature.returnClause?.type.as(IdentifierTypeSyntax.self) != nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of using a large conditional, why not simply compare the FunctionDeclSyntax.description string with the expected description of the reduce method? I suppose one could add additional trivia to the function, like this:

func    reduce(into state: inout State, action: Action) -> Effect<Action>

However, I believe we would still be able to handle that. We are comparing only the argument names, not their types, but am I right in thinking it's safe to assume they will be State and Action, respectively?

Copy link
Contributor

Choose a reason for hiding this comment

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

The question remains how to handle access modifiers. The best solution would be to ensure that it is the same as the type declaration, but I'm not sure if this will be possible. Maybe it can be implemented based on the body property access modifier? If the two are the same then emit a warning.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I'm thinking about an edge case where someone might have code like this:

var body: some Reducer<State, Action> {
  // somewhere here private reduce is called
}

private func reduce(into state: inout State, action: Action) -> Action {
  // ...
}

In this scenario, wouldn't the private reduce method, be detected instead of the one intended from the protocol?

Copy link
Member Author

@stephencelis stephencelis Nov 11, 2023

Choose a reason for hiding this comment

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

While the current might be more complex, I'm comfortable with the checks, and I think it's checking just what needs to be checked and nothing more. Folks can technically define a reduce that matches the protocol conformance but uses a custom type for State or Action (since those are just associated types), and one could also define custom secondary names. An extreme example:

func reduce(
  into myState: MyState,
  action myAction: MyAction<Something>
) -> EffectOf<MyFeature>

Copy link
Member Author

Choose a reason for hiding this comment

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

In this scenario, wouldn't the private reduce method, be detected instead of the one intended from the protocol?

That's not valid Swift, so the compiler would complain at the conformance level:

🛑 Method 'reduce(into:action:)' must be as accessible as its enclosing type because it matches a requirement in protocol 'Reducer'

Copy link
Contributor

Choose a reason for hiding this comment

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

In this scenario, wouldn't the private reduce method, be detected instead of the one intended from the protocol?

That's not valid Swift, so the compiler would complain at the conformance level:

🛑 Method 'reduce(into:action:)' must be as accessible as its enclosing type because it matches a requirement in protocol 'Reducer'

OK, I think I understand what happened here. In theory, the following code should indeed result in an error, but... it seems that, thanks to the implementation of the Reduce protocol being handled by a macro, the code works just fine 🤯

@Reducer
struct MyTest {
  struct State {}

  enum Action {
    case none
  }

  var body: some Reducer<State, Action> { Reduce { _, _ in .none } }

  private func reduce(into state: inout State, action: Action) -> Effect<Action> {
    .none
  }
Screenshot 2023-11-11 at 9 12 05 PM

Copy link
Contributor

Choose a reason for hiding this comment

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

While the current might be more complex, I'm comfortable with the checks, and I think it's checking just what needs to be checked and nothing more. Folks can technically define a reduce that matches the protocol conformance but uses a custom type for State or Action (since those are just associated types), and one could also define custom secondary names. An extreme example:

func reduce(
  into myState: MyState,
  action myAction: MyAction<Something>
) -> EffectOf<MyFeature>

Sorry, I'm a bit confused now. So, is your intention that every method like this should produce an error, right? Even ones with different types, like this example:

@Reducer
struct MyTest {
  struct State {}

  enum Action {
    case none
  }

  enum LocalAction {
    case some
  }

  var body: some Reducer<State, Action> { Reduce { _, _ in .none } }

  private func reduce(into state: inout State, action: LocalAction) -> Effect<Action> {
    .none
  }
}

Is that correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

@Matejkob I think there's only so much we can do from the macro to detect that a method matches the conformance. Someone could type alias the Effect return type so we can't do any checking there, and while we could traverse the reducer to see if there are State or Action types/aliases and use that to silence the warning for your above example, I'm not sure it's worth the extra work when at the end of the day, defining a method named reduce is confusing enough that we think a rename is warranted for folks to silence the warning. In fact, I did just add a syntax visitor that tries to fix-it a rename.

Copy link
Contributor

Choose a reason for hiding this comment

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

@stephencelis Thanks for your explanation. I understand the challenges with the macro approach and agree that a rename is a more straightforward solution. Appreciate your work on this and the helpful response!

Sources/ComposableArchitectureMacros/ReducerMacro.swift Outdated Show resolved Hide resolved
@stephencelis stephencelis merged commit 57e804f into main Nov 13, 2023
5 checks passed
@stephencelis stephencelis deleted the case-key-paths branch November 13, 2023 20:57
cgrindel-self-hosted-renovate bot referenced this pull request in cgrindel/rules_swift_package_manager Nov 15, 2023
…ure to from: "1.4.2" (#737)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[pointfreeco/swift-composable-architecture](https://togithub.com/pointfreeco/swift-composable-architecture)
| minor | `from: "1.3.0"` -> `from: "1.4.2"` |

---

### Release Notes

<details>
<summary>pointfreeco/swift-composable-architecture
(pointfreeco/swift-composable-architecture)</summary>

###
[`v1.4.2`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/1.4.2)

[Compare
Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/1.4.1...1.4.2)

#### What's Changed

- Fixed: `swift-case-paths` is now correctly pinned from 1.1.0, which
should avoid some SPM resolution issues (thanks
[@&#8203;bdolewski-intellias](https://togithub.com/bdolewski-intellias),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2577](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2577)).

#### New Contributors

-
[@&#8203;bdolewski-intellias](https://togithub.com/bdolewski-intellias)
made their first contribution in
[https://github.com/pointfreeco/swift-composable-architecture/pull/2577](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2577)

**Full Changelog**:
pointfreeco/swift-composable-architecture@1.4.1...1.4.2

###
[`v1.4.1`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/1.4.1)

[Compare
Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/1.4.0...1.4.1)

#### What's Changed

- Fixed: A regression in `Reducer.forEach` was introduced in 1.4 that
broke compilation in some cases, notably in the TCACoordinators library.
This regression has been corrected (thanks
[@&#8203;rhysm94](https://togithub.com/rhysm94),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2570](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2570)).
- Infrastructure: Updated Japanese and Korean README translations
(thanks [@&#8203;Achoo-kr](https://togithub.com/Achoo-kr),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2563](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2563),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2562](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2562)).
- Infrastructure: Fixed documentation links (thanks
[@&#8203;johankool](https://togithub.com/johankool),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2566](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2566),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2576](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2576));
fixed migration guide typos (thanks
[@&#8203;kalupas226](https://togithub.com/kalupas226),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2569](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2569);
[@&#8203;woxtu](https://togithub.com/woxtu),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2575](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2575);
[@&#8203;Ryu0118](https://togithub.com/Ryu0118),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2574](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2574)).
- Infrastructure: Clean up case study (thanks
[@&#8203;mike123789-dev](https://togithub.com/mike123789-dev),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2567](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2567)).

#### New Contributors

- [@&#8203;Achoo-kr](https://togithub.com/Achoo-kr) made their first
contribution in
[https://github.com/pointfreeco/swift-composable-architecture/pull/2563](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2563)
- [@&#8203;mike123789-dev](https://togithub.com/mike123789-dev) made
their first contribution in
[https://github.com/pointfreeco/swift-composable-architecture/pull/2567](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2567)
- [@&#8203;woxtu](https://togithub.com/woxtu) made their first
contribution in
[https://github.com/pointfreeco/swift-composable-architecture/pull/2575](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2575)
- [@&#8203;rhysm94](https://togithub.com/rhysm94) made their first
contribution in
[https://github.com/pointfreeco/swift-composable-architecture/pull/2570](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2570)

**Full Changelog**:
pointfreeco/swift-composable-architecture@1.4.0...1.4.1

###
[`v1.4.0`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/1.4.0)

[Compare
Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/1.3.0...1.4.0)

#### What's Changed

- Added: The `@Reducer` macro
([https://github.com/pointfreeco/swift-composable-architecture/pull/2553](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2553)).
See the [migration
guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.4/)
for more details.
- Added: Reducer builder support for `any Reducer<State, Action>`
([https://github.com/pointfreeco/swift-composable-architecture/pull/2533](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2533)).
- Fixed: Silenced a SwiftUI sendability warning
([https://github.com/pointfreeco/swift-composable-architecture/pull/2540](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2540)).
- Fixed: Alert and confirmation dialog helpers now use `Text(verbatim:
"")` to avoid localization warnings
([https://github.com/pointfreeco/swift-composable-architecture/pull/2541](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2541)).
- Fixed: `Reducer.onChange` no longer requires an `Equatable`
conformance (thanks
[@&#8203;lukaskubanek](https://togithub.com/lukaskubanek),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2545](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2545)).
- Infrastructure: Updated Swift compiler version to 5.7.1 to follow
Apple's policy (thanks
[@&#8203;jaesung-0o0](https://togithub.com/jaesung-0o0),
[https://github.com/pointfreeco/swift-composable-architecture/pull/2549](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2549)).
- Fixed: Short circuit equatability of ordered sets when counts don't
match
([https://github.com/pointfreeco/swift-composable-architecture/pull/2556](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2556)).
- Infrastructure: Added previews to integration test cases by
([https://github.com/pointfreeco/swift-composable-architecture/pull/2551](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2551))

#### New Contributors

- [@&#8203;lukaskubanek](https://togithub.com/lukaskubanek) made their
first contribution in
[https://github.com/pointfreeco/swift-composable-architecture/pull/2545](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2545)

**Full Changelog**:
pointfreeco/swift-composable-architecture@1.3.0...1.4.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config help](https://togithub.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://togithub.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi4xMDAuMCIsInVwZGF0ZWRJblZlciI6IjM2LjEwMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiJ9-->

Co-authored-by: Self-hosted Renovate Bot <361546+cgrindel-self-hosted-renovate[bot]@users.noreply.github.enterprise.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants