Fix member-level @available attributes hoisted to mock class declaration#352
Merged
sidepelican merged 8 commits intoJun 13, 2026
Merged
Conversation
Behavioral @available attributes (noasync, deprecated) on protocol members were incorrectly placed on the generated mock class instead of on the individual generated members. Platform @available correctly stays on the class since stored properties cannot carry platform availability. Split the attribute pipeline: behavioral @available flows to member models (MethodModel, VariableModel) and is rendered on each generated declaration; platform @available continues through the existing side channel to the class. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Attributes like noasync and deprecated belong on the mock's public API (the func/var that conforms to the protocol), not on mock infrastructure (callCount, handler, state). Keeping infrastructure clean lets tests configure and inspect deprecated methods without warnings or restrictions. Also adds a duplicatedAttributes test covering the original reported issue where multiple deprecated methods caused attributes to concatenate onto the class declaration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rwarding Classify platform-scoped deprecations (@available(iOS, deprecated: 12.0)) as behavioral so they stay on the generated member instead of deprecating the whole mock class; existence-gating forms (introduced:/obsoleted:/ unavailable, version shorthand) keep hoisting. Forward behavioral attributes to generated required inits, consistent with all other member kinds; document the deliberate drop on typealiases. Add fixtures for mixed platform+behavioral, platform-scoped deprecation, gating characterization, Combine/Rx vars, subscripts, and inits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
farkasseb
force-pushed
the
fix/member-available-attributes
branch
from
June 11, 2026 00:35
32f36f0 to
e30a19c
Compare
farkasseb
added a commit
to farkasseb/mockolo
that referenced
this pull request
Jun 11, 2026
Resolves the computed-var template conflict by combining uber#353's getter call-count declaration with uber#352's attribute prefix. Also converts the getter-history Combine fixture to the dummy Combine types that uber#352 introduced in Tests/Types.swift (extended with Published/DummyPublisher), since the local dummies shadow the real Combine import — this also fixes the fixture's Linux incompatibility. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sidepelican
requested changes
Jun 11, 2026
sidepelican
left a comment
Collaborator
There was a problem hiding this comment.
It looks good, but there are several improvements.
- Create a
Attributestruct and store the parsed information fromAttributeListwithin[Attribute].Modelshould have this asvar attributes: [Attribute]instead of[String].- Add an
applyAttributeTemplateextension to the[Attribute]type that functions similarly to the currentasAttributePrefiximplementation. - Remove
isBehavioralAvailable,behavioralAvailableDescriptions,platformAvailableString, andplatformAvailableDescriptions, and add computed properties toAttributeas needed - When generating objects like
VariableModel, pass the attributes using syntax likeattributes.filter(\.isAvailable).
- Remove the redundant
attributesparameter fromapplyFooTemplateand instead reference it directly fromself. - Avoid adding unnecessary tests. Focus only on the minimum required tests to verify functionality.
- Ensure that the generated output remains consistent across executions.
sidepelican
force-pushed
the
fix/member-available-attributes
branch
from
June 11, 2026 01:56
01e50aa to
0ab7643
Compare
…stic Addresses review feedback on uber#352: - Parse AttributeList into plain Attribute values ([Attribute]) and plumb them end to end (parser -> models -> templates) instead of [String]; Model now declares `var attributes: [Attribute]` - Move @available classification into Attribute.Kind and drop the AttributeListSyntax helpers (isBehavioralAvailable, behavioralAvailableDescriptions, platformAvailableString, platformAvailableDescriptions); construction sites filter with attributes.filter(\.isBehavioralAvailable) / (\.isPlatformAvailable) - Replace [String].asAttributePrefix with applyAttributeTemplate() on [Attribute]; apply*Template functions read self.attributes instead of taking a redundant parameter - Replace the unordered Set dedupe of class-level hoisted attributes with order-preserving uniqued(), so output is consistent across executions and follows member source order; the memberPlatformAvailable fixture encoded one random Set ordering and is updated to source order - Dedupe now operates per individual attribute rather than per-member joined strings (intentional) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Thank you so much for the review @sidepelican ! I tried to implement what you were suggesting, please let me know what you think. Regarding the excessive testing, the trim is fine by me, but to calibrate future PRs: how do you weigh scenario coverage against fixture maintenance? My instinct was that more covered scenarios is better. |
Collaborator
|
Too many tests can increase maintenance costs. |
sidepelican
approved these changes
Jun 13, 2026
sidepelican
left a comment
Collaborator
There was a problem hiding this comment.
LGTM. Since the parse logic felt a bit complex, I refactored it into a simpler version here. I believe this should be sufficient.
Thanks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Member-level behavioral
@availableattributes (noasync,deprecated) were concatenated and hoisted to the mock class declaration instead of staying on the individual generated members.Example
Before — attributes from all deprecated members are concatenated onto the class declaration, causing a build error:
After — only the protocol-conforming methods carry their own attributes:
Attributes are placed only on the protocol-conforming declaration (funcs, vars, subscripts, and inits), not on mock infrastructure (callCount, handler, state). This keeps test setup and inspection clean — configuring a handler for a deprecated method won't trigger deprecation warnings, and
noasyncwon't restrict mock configuration from async tests.One deliberate consequence: a deprecated stored property still produces a deprecation warning inside the generated convenience init (
self.data = data) — seeding a deprecated property is deprecated usage, and the warning correctly points at the protocol still carrying the deprecated member.Note on platform availability
Existence-gating availability —
@available(iOS 16, *),introduced:,obsoleted:,unavailable— continues to hoist to the class declaration, same as before this fix, since the mock's infrastructure references the member's types unconditionally. Extended-form platform-scoped deprecations (@available(iOS, deprecated: 12.0)) are warning-only, so they stay on the member like their*-form counterparts; when a single attribute combines both (@available(iOS, introduced: 10.0, deprecated: 12.0)), gating wins and it hoists.Two smaller behavior notes: non-
@availableattributes no longer leak onto the class alongside a hoisted member attribute (only@availableis ever hoisted), and behavioral attributes on associatedtypes are deliberately dropped, since the generated typealias is referenced throughout the mock's own infrastructure.Hoisted class-level attributes are now deduplicated per individual attribute and rendered in member source order (order-preserving
uniqued()); previously an unorderedSetjoin made the order vary between executions.Test plan
@Fixturemocks, so expected output is conformance-checked by the compiler): deprecated members on the original reported issue, protocol-level + member-level@availablecoexistence, multiple attributes on one method, and platform attributes hoisting to the class🤖 Generated with Claude Code