<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Hi all,<div class=""><br class=""></div><div class="">I finally updated my proposal to use a documentation tag instead of an attribute (thanks Janosch): <a href="https://gist.github.com/zneak/e53494c38bb3739201ac" class="">https://gist.github.com/zneak/e53494c38bb3739201ac</a></div><div class=""><div class="">
<br class="Apple-interchange-newline"><span style="color: rgb(0, 0, 0); font-family: 'Lucida Grande'; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none;" class="">Félix</span></div><div class=""><br class="webkit-block-placeholder"></div><div class="">---</div><div class=""><br class="webkit-block-placeholder"></div></div><div class=""><div class=""><b class=""># `intendeduse` Documentation Attribute</b></div><div class=""><br class=""></div><div class="">* Proposal: SE-NNNN</div><div class="">* Author(s): [Félix Cloutier](<a href="https://github.com/zneak" class="">https://github.com/zneak</a>)</div><div class="">* Status: **Draft**</div><div class="">* Review manager: TBD</div><div class=""><br class=""></div><div class=""><b class="">## Introduction</b></div><div class=""><br class=""></div><div class="">This proposal suggests a new documentation field that can be used to hint that a symbol is relevant only in certain contexts. This hint can be picked up by tools (like SourceKit) and analyzers (like [SwiftLint][1]) to offer better diagnostics or less irrelevant autocomplete suggestions.</div><div class=""><br class=""></div><div class=""><b class="">## Motivation</b></div><div class=""><br class=""></div><div class="">Swift does not adopt typical object-oriented access modifiers. Instead, its `public`, `internal` and `private` modifiers map closer to the linker's notion of visibility. While this reduces surprises about what's accessible and what isn't in a native program, in some scenarios it will make symbols discoverable in locations where they aren't helpful, cluttering autocomplete suggestions at best and misleading developers at worst.</div><div class=""><br class=""></div><div class="">There are several relevant scenarios where one could prefer to show a member in specific contexts only. For instance:</div><div class=""><br class=""></div><div class=""> * Some Swift users like to separate a class's implementation across several files by defining the core in one and using extensions in the others. This pattern is also popular in Objective-C, where class implementations can be split into multiple categories. Other languages have similar notions: for instance, C# supports "partial classes" which do essentially the same thing. However, using this pattern in Swift, some class members members that could otherwise be marked `private` now need to be marked `internal` to be reachable from extension files. The members now show up as autocompleting suggestions in every file of the project, even though the developer only cares about them in a few select files. ([swift-evolution thread][2])</div><div class=""> * Even though Swift generally does not encourage classes to grow into a hierarchy, it is sometimes useful to have functions that are meant to be called from subclasses. These currently have to be marked `internal` or `public` because Swift does not have a `protected` modifier. Even though there is no intention to enforce more specific access control with this proposal, a member marked "for subclasses" is useful documentation to other developers.</div><div class=""> * Some classes, like NSView, expose significant API surface for customization. It can become overwhelming, to the point where developers are pushed to make a chart of which method are intended for external use and which methods are intended to be merely overridden (and not called directly). All these methods will clutter autocomplete and obscure which ones should be called directly. ([swift-evolution thread][5])</div><div class=""> * Some Swift users interoperate with Objective-C to a degree large enough that it makes sense to have API entry points better suited for one language or the other. On its side, Apple added an Objective-C attribute called `swift_private` that indicates that a symbol can be used from Objective-C but not from Swift. On the Swift side, some developers would like to be able to mark an entry point as meant specifically for Objective-C, simply because a better one exists for Swift. The Objective-C-specific entry point will however show side-by-side with the Swift one because there's no way to mask it. If it becomes possible to call Swift code from C or any other language, it may eventually become a problem there as well. ([swift-evolution thread][4])</div><div class=""> * Tools generating Swift code may find it useful to mark members as "tool-private": members that need to have a looser access modifier for technical reasons but that shouldn't be used directly by the client. This is the use case that .NET's similar [`EditorBrowsable`][3] attribute supports.</div><div class=""><br class=""></div><div class=""><b class="">## Proposed solution</b></div><div class=""><br class=""></div><div class="">This proposal suggests a new `- intendeduse` (name to be bikeshedded in typical Internet fashion) documentation tag that hints whether a member should be used in a certain context or not. It is not meant to implement access control: the compiler must not restrict access to the member based on it. It is meant to document symbols and help tools offer better suggestions.</div><div class=""><br class=""></div><div class="">It exists in the spirit of the new-ish `keyword`, `recommended` and `recommendedover` documentation tags that allow "[to cooperate with code completion engine to deliver more effective code completion results][6]".</div><div class=""><br class=""></div><div class="">The documentation tag would accept a context parameter and an optional message that explains why the symbol is not intended to be used in the full extent of its access modifier.</div><div class=""><br class=""></div><div class="">Tools like SourceKit and SwiftLint would be responsible for how they wish to interpret the hint. For instance, Xcode could hide or strike members that don't belong. SwiftLint could detect violations and report them, showing the message parameter (if any).</div><div class=""><br class=""></div><div class=""><b class="">## Detailed design</b></div><div class=""><br class=""></div><div class="">The basic syntax would be:</div><div class=""><br class=""></div><div class=""> /// - intendeduse: `context` (`optional message`)</div><div class=""><br class=""></div><div class="">Backticks are used as delimiters here and are not intended to be part of the syntax. For instance:</div><div class=""><br class=""></div><div class=""> /// - intendeduse: private (don't call me directly!)</div><div class=""><br class=""></div><div class="">Context values could be one of:</div><div class=""><br class=""></div><div class=""> * `extension`: this type member is intended to be used from extensions.</div><div class=""> * `interop`: this symbol is intended to be used from other languages that interface with Swift.</div><div class=""> * `override`: this symbol is intended to be overridden only.</div><div class=""> * `private`: this symbol is not intended to be used directly.</div><div class=""> * `subclass`: this type member is intended to be used from subclasses.</div><div class=""><br class=""></div><div class="">The `extension` context can only be used on type members. A member with this intended context is considered "in context" if it is used from a file that defines an extension to its host type.</div><div class=""><br class=""></div><div class="">The `interop` context can be used on any non-private symbol [somebody remind me if mixing Swift and Objective-C in the same target allows Objective-C to access `internal` members?]. These symbols are never considered "in context" when used in Swift code.</div><div class=""><br class=""></div><div class="">The `override` context can be used on any virtual symbol. These symbols are intended to be overridden by subclasses to implement customized behavior. These symbols are considered "in context" when editing the declaring class or an extension to it.</div><div class=""><br class=""></div><div class="">The `private` context can be used on any non-private symbol. These symbols are never considered "in context". [The paradox is that they will be used somewhere, otherwise they wouldn't be needed at all. While it's pretty clear that these shouldn't show up in autocompletion lists, it's unclear how a linter should react to it.]</div><div class=""><br class=""></div><div class="">The `subclass` context can be used on any class member. These symbols are considered "in context" when they're used from the file that declares the base class and any other file that declares a subclass.</div><div class=""><br class=""></div><div class=""><b class="">## Possible future directions</b></div><div class=""><br class=""></div><div class="">In the future, this documentation tag could perhaps take over the role of `@deprecated`. `@available` is also fundamentally documentation, but it needs to abort compilation because the symbol isn't available, so it is less likely within the scope of a documentation attribute.</div><div class=""><br class=""></div><div class="">Through some sort of convention, it could become possible to offer fix-it hints for some out-of-context uses.</div><div class=""><br class=""></div><div class="">One solution to the `private` gambit would be to allow user-defined contexts. A tool could emit code that uses a string as a context name and specify that some file, class, function, or other code unit can rightfully use it:</div><div class=""><br class=""></div><div class=""> /// - intendeduse: supertool (don't use me directly!)</div><div class=""> func leaveMeAlone() { ... }</div><div class=""> </div><div class=""> @context("supertool")</div><div class=""> func publicEntryPoint() {</div><div class=""> <span class="Apple-tab-span" style="white-space:pre">        </span>leaveMeAlone()</div><div class=""> }</div><div class=""><br class=""></div><div class=""><b class="">## Impact on existing code</b></div><div class=""><br class=""></div><div class="">This change does not alter any existing behavior.</div><div class=""><br class=""></div><div class=""><b class="">## Alternatives considered</b></div><div class=""><br class=""></div><div class="">The most obvious alternative is to do nothing about it. Even though it is not the author's opinion, it can be argued that the Swift language doesn't need an attribute that looks like it wants to provide finer-grained access control but doesn't really do it.</div><div class=""><br class=""></div><div class="">Other alternatives considered were the piecewise solutions that the three aforementioned swift-evolution threads considered:</div><div class=""><br class=""></div><div class=""> * An attribute that hides the entry point from Swift entirely, causing an error when it is used. However, completely hiding the entry point makes the attribute less useful in the context of gradually porting an Objective-C program or library to Swift. If creating Swift-ier entry points is a low-priority task, we can assume that existing Swift code uses to-be Objective-C entry points; therefore, hiding them would force more modifications than may be desirable.</div><div class=""> * A new access modifier, like `private(extension)`, that acts like `internal` in files that declare an extension to the owning type but `private` elsewhere. This modifier's behavior does not align with linker visibility and would be significantly different from what currently exists.</div><div class=""> * A new access modifier, like `private(call)`, that means that a member can be overridden but not called. This modifier's behavior does not align with linker visibility and would be significantly different from what currently exists.</div><div class=""><br class=""></div><div class=""> [1]: <a href="https://github.com/realm/SwiftLint" class="">https://github.com/realm/SwiftLint</a></div><div class=""> [2]: <a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005446.html" class="">https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005446.html</a></div><div class=""> [3]: <a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.editorbrowsableattribute(v=vs.110).aspx" class="">https://msdn.microsoft.com/en-us/library/system.componentmodel.editorbrowsableattribute(v=vs.110).aspx</a></div><div class=""> [4]: <a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005406.html" class="">https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005406.html</a></div><div class=""> [5]: <a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006321.html" class="">https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006321.html</a></div><div class=""> [6]: <a href="https://github.com/apple/swift/blob/master/CHANGELOG.md" class="">https://github.com/apple/swift/blob/master/CHANGELOG.md</a></div></div><div class=""><br class=""></div></body></html>