gohanlon / swift-memberwise-init-macro

Swift Macro for enhanced automatic inits.
MIT License
122 stars 8 forks source link

Provide init at the requested access level or fail #15

Closed gohanlon closed 1 year ago

gohanlon commented 1 year ago

@MemberwiseInit now creates initializers at the specified access level or fails if it cannot do so. This change sharpens the safety and clarity of the macro, providing developers with immediate and actionable diagnostics when the desired initialization level cannot be met due to access restrictions on properties.

Caveat: the diagnostics don't include fix-its (yet).

Developers can resolve errors by adjusting property access levels or by configuring individual properties with @Init attributes to match the desired initializer access level.

The PR reflects a change in the design philosophy behind @MemberwiseInit, moving it away from being a superset of Swift's default memberwise initializer towards a tool that enforces explicit developer intent with respect to access levels. This shift is grounded in the belief that predictable, explicit behavior aligns better with Swift's ethos of safe and intentional coding practices.

Resolves #10.

gohanlon commented 1 year ago

Originally, @MemberwiseInit behaved like Swift's memberwise initializer with additional features, adapting to the access levels of struct properties automatically. This flexible behavior aimed to reduce boilerplate while respecting Swift's default access control rules.

However, experience has demonstrated that this adaptability can lead to surprising outcomes. For instance, when a developer specifies @MemberwiseInit(.public), they have a clear expectation of a public initializer. If even a single private property exists within the struct, the generated initializer is not public, and the developer's intent is not fulfilled.

The new design tenet proposes that instead of silently adapting to the lowest common access level, @MemberwiseInit should validate that all properties match or are less restrictive than the requested initializer access level. If not, it deliberately fails the compilation with a clear diagnostic message, forcing immediate attention and resolution. This approach has several advantages:

  1. Clear Intent: @MemberwiseInit becomes a declaration of the developer's explicit intention, thereby avoiding any ambiguity about the desired access level for the initializer.
  2. Safety: By failing fast when expectations aren't met, it prevents unintended access level leaks that could compromise the encapsulation and safety of the code.
  3. Simplification: It reduces complexity within the macro's implementation and the mental model required to use it, as the behavior is more direct and predictable.
  4. Documentation: The need for extensive documentation is lessened as the intuitive behavior will be self-explanatory through the use and immediate feedback provided by diagnostic messages.