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

Add Access Level Inheritance #130

Merged
merged 1 commit into from
Dec 3, 2024
Merged

Add Access Level Inheritance #130

merged 1 commit into from
Dec 3, 2024

Conversation

Matejkob
Copy link
Owner

@Matejkob Matejkob commented Nov 30, 2024

This PR introduces access level inheritance for generated spies.

Key Changes

Generated spies now inherit the protocol’s access control (public, package, internal, private, and fileprivate). For private protocols, spies are generated with fileprivate access to meet Swift’s requirements.

Spies include an initializer with the same access level as the protocol, enabling their creation outside the current module.

Implementation Details

The core concept of access level inheritance is implemented using a syntax rewriter:

final class AccessLevelModifierRewriter: SyntaxRewriter {
  let newAccessLevel: DeclModifierSyntax

  init(newAccessLevel: DeclModifierSyntax) {
    // Ensure private protocols result in fileprivate members for compatibility
    if newAccessLevel.name.text == TokenSyntax.keyword(.private).text {
      self.newAccessLevel = DeclModifierSyntax(name: .keyword(.fileprivate))
    } else {
      self.newAccessLevel = newAccessLevel
    }
  }

  override func visit(_ node: DeclModifierListSyntax) -> DeclModifierListSyntax {
    if node.parent?.is(FunctionParameterSyntax.self) == true {
      return node
    }
    return DeclModifierListSyntax { newAccessLevel }
  }
}

The AccessLevelModifierRewriter is applied after the spy class is created, ensuring that all members inherit the access level consistently:

if let accessLevel = extractor.extractAccessLevel(from: protocolDeclaration) {
  let accessLevelModifierRewriter = AccessLevelModifierRewriter(newAccessLevel: accessLevel)

  spyClassDeclaration =
    accessLevelModifierRewriter
    .rewrite(spyClassDeclaration)
    .cast(ClassDeclSyntax.self)
}

This guarantees that the entire spy class, including all members, always reflects the protocol’s access level.

@Matejkob Matejkob self-assigned this Nov 30, 2024
@Matejkob Matejkob added the enhancement New feature or request label Nov 30, 2024
@Matejkob Matejkob linked an issue Nov 30, 2024 that may be closed by this pull request
Copy link

codecov bot commented Nov 30, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.79%. Comparing base (7d07458) to head (c53222a).
Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #130      +/-   ##
==========================================
+ Coverage   98.73%   98.79%   +0.06%     
==========================================
  Files          20       21       +1     
  Lines         949      998      +49     
==========================================
+ Hits          937      986      +49     
  Misses         12       12              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@armintelker
Copy link

I like it, and from what I see, this is what the community wishes for. However, I can't give you a proper review because I am not deep enough into this library to understand the full impact of this PR.

@Matejkob Matejkob merged commit d9b6878 into main Dec 3, 2024
22 checks passed
@Matejkob Matejkob deleted the access-levels branch December 3, 2024 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use protocol's access control on the spy's declaration
2 participants