[Pitch] Attribute to silence Deprecation Warnings for a specified scope

Agree with @Jumhyn’s view overall.

I differ on the issue of import-level deprecations because the use cases presented above relate to entire libraries being deprecated without equivalent replacement.

Individual deprecated functions, etc., can be feasibly dealt with by re-declaration or in other ways (even re-implementation sometimes), but an acknowledgment per use site or these workarounds you mention aren’t really an answer to the problem where, say, all of WebView is deprecated.

The user intention that we want to make possible to express here is, “Yes—I know WebView is deprecated but I want to use it anyway,” which is not best served by re-declaring the entire API surface of WebView or annotating every call to a WebView API.

On the other hand, I am not sure I understand what problem is uniquely addressed by a project-wide compiler flag to silence all deprecation warnings—this would mean that a user wants to use any and all deprecated APIs from any modules and in any place without thinking about it, and I’m doubtful this is desirable to enable.

This goes to my concern above about scope-level deprecations too. The core of where I agree with @Jumhyn lies (to my understanding) in that we both think it’s important that the user to be able to express an intention of the form “I know ‘X’ is deprecated but I want to use it here” for some specific “X” and for some granularity of “here.”

We may differ somewhat on the level of specificity we are comfortable with, but we’re both wary of features that enable silencing of deprecation warnings from who-knows-where, as it’s not really an expression acknowledging awareness of what warnings are actually being silenced.

3 Likes

@Jumhyn @xwu Good points, I think Jumhyn's point that users should declare a file with definitions explicitly using the attribute to ignore the warnings rather than having a compiler flag is good, I just wanted to ask though, why do you guys think it'd be a good idea for @testable to imply @ignoreDeprecationWarnings? I think that may be best under a compiler flag, ie -testable-import-imply-ignore-deprecations

Because @testable gives access to internal declarations not publicly available to users, so if we adhere to the notion that deprecations are an API-level concern (i.e., they’re about what public interface users can access and where) and on that basis decide to allow projects to use internal self-deprecations without warning, then the same flexibility should apply to testable imports because that is in the nature of what @testable means (access to internals).

Concerns over users who perhaps may not want to ignore deprecation warnings in contexts where the module is imported using @testable, though?

Not from me; there may be issues I haven’t thought of, but when you import a module using @testable, you lose the ability to distinguish between what’s publicly available and what’s internal (and therefore the ability to test that—this is relevant when you want to test that users writing the obvious thing end up calling the intended overload among many, for instance), so I don’t see why losing warnings about availability due to deprecation is an issue.

1 Like

Speaking of tests, doesn't this work already to silence deprecation warnings?

class TestDeprecation: XCTestCase {

    @available(*, deprecated)
    func testExample() throws {
		deprecatedFunctionCall()
    }

}

Also, I think it's convenient that tests have to be marked as deprecated. It helps the reader notice this is testing deprecated stuff. It also helps the API author take notice of the impact of deprecations, such as tests representing use cases that can't be rewritten using a non-deprecated replacement.

3 Likes

Perhaps the per-declaration-use undeprecation could be tweaked to be a bit more expansive. E.g., if a user undeprecates UIWebView by doing:

typealias MyWebView = WebView

that would have the effect of undeprecating all symbols within MyWebView that are deprecated at a level at or below the level of WebView. I'm not sure if there's an implementation burden here (at the time deprecation warnings are emitted, would the compiler be able to 'know' that a symbol was found via MyWebView instead of WebView?), but I think I'd feel better about this approach than allowing users to too easily undeprecate an entire import.

It there isn't an implementation burden here and we go through with automatically silencing self-deprecation warnings, then I'd imagine the above could just be reduced to:

@available(*, deprecated)
typealias WebView = UIKit.WebView

requiring no new innovations in the language.

2 Likes

I'm strongly opposed to this idea. All of the reasoning about making explicit decisions when referencing deprecated external symbols applies just as equally to internal symbols. You want to minimize existing usage and discourage introducing new usage.

This happens for API/binary compatibility reasons as @millenomi already mentioned—the goal is to freeze some structure of your code such that it doesn't break clients, but that doesn't mean that new code should ever be on that path. It's also common to go through heavy iterations on pre-1.0 libraries that deprecate large portions of the API surface at a time. After a round or two of this it extremely easy to lose track of what the current correct interface is.

It's also a useful tool in projects that don't have any API at all (i.e. applications): many projects of a sufficient age want to undergo structural changes (for example, migrating to new versions platform API, or better representations of business requirements) that are best represented as deprecating existing internal types in favor of new ones.

5 Likes

Proposal opened here.

2 Likes

Yeah, as nifty as it would be to accomplish without changing the surface level syntax, I'm inclined to agree with @frozendevil: I don't think it's tenable to just stop warning about internal uses of deprecated symbols. At the very least I think this behavior would need to be enabled by a compiler flag, but then users would have to choose between using other libraries' deprecated symbols and being able to use deprecations for themselves internally.

If we want to reuse the deprecation-silencing behavior of @available(*, deprecated) for this purpose, I think it would be better to take an approach such as visibility modifiers for declarations, as @allevato mentions above. So the undeprecation of WebView would look something like:

@available(*, deprecated(public))
typealias WebView = UIKit.WebView

which would mean "this symbol is deprecated at the public visibility level, but not at the internal level."

Although, as I write it out, I'm not sure if this solution feels quite right, either. Ostensibly, the local redeclaration of WebView is only internal anyway, so it seems like a bit of an abuse to say "this internal symbol is deprecated for public uses." It also seems like this would just decay to the scope-level silencing feature, since presumably:

@available(*, deprecated(public))
internal func noDeprecationWarningsHere() {
  // use deprecated symbols to your hearts content
}

would exhibit the same deprecation-silencing behavior.

ETA: I suppose this is also (for me) an argument against a compiler flag for disabling internal deprecation warnings, since that would also turn @available(*, deprecated) into a scope-level deprecation silencing mechanism.

So, all that's to say, as a proponent of silencing at the granularity of declaration use, I think reusing @available doesn't work quite well enough for me. I think this feature is deserving of a new annotation which directly expresses "I know this particular symbol is deprecated but I'm going to use it anyway".

1 Like

The Language Workgroup discussed this pitch briefly and felt that it was unlikely to be accepted if brought to review in its current form. There was general agreement that it's probably appropriate to solve this problem with a source-level feature (to avoid the 'dialectization' issue raised by, e.g., compiler flags) and that deprecation warnings are a bit of an exception amongst Swift's warnings today in that there is no way to silence them when the user really does want to. However, there were also concerns about the potential downsides of a solution which allows for overly coarse-grained silencing of such warnings, some of which have been raised in this thread.

The Workgroup feels that this pitch would benefit from more discussion refocused on solutions which enable more narrowly tailored silencing mechanisms, similar to how other warnings are silenced.

5 Likes