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
@MayIgnoreResult
/ @MustUseResult
: mark whether a non-void function is okay to call as if void
#200
Comments
(Prioritization: I'd feel great about this being the next thing tackled beyond nullness, partly because Kotlin's already talking about it, but also because we have a mountain of evidence that it catches a ton of real bugs.) |
(Tangent not to follow: for a few may-ignore-result methods like |
Detekt has a related rule for this: https://detekt.github.io/detekt/potential-bugs.html#ignoredreturnvalue |
@yogurtearl sorry for the slow reaction, but that's awesome. I hope you'll be interested in an ongoing conversation about bringing these annotations into JSpecify and then getting them supported natively by kotlinc. |
@MayIgnoreValue
vs. @MustUseValue
)
Some nomenclature alternatives
(I resisted using the term "result" for this, because I thought "value" would serve. I'm now convinced "result" is better. JLS uses "result" only for a variety of other different purposes, but it doesn't tie our hands.) I don't consider the verb "check" to be a viable option. What does it mean to "check" a value? It doesn't sound like merely assigning it to a variable or handing it off to another method would be enough. We should also consider how we would want to name a "caller is (not) responsible for closing the returned AutoCloseable instance" annotation and have appropriately consistent answers. |
@MayIgnoreValue
vs. @MustUseValue
)@MayIgnoreResult
/ @MustUseResult
: mark whether a non-void function is okay to call as if void
@cortinico @BraisGabin @schalkms @chao2zhang We want to standardize how this information is captured for APIs in any JVM language. Any interest in being involved? Kotlin hopes to support it natively, which would eventually make it an unnecessary check for detekt. But the annotations will still matter for language interop, and your involvement could help us get this right. |
I believe we can extend the |
Oh, I suppose mainly that when we come out with a specific proposal we'd love to get your feedback on it -- so I suppose the question is a bit premature. We welcome any thoughts you might have whenever you have them. |
As mentioned above, the Kotlin team want to transition to having must-use-result as the default, and may-ignore-result as a language feature. Annotations meaning the same things would seem unnecessary, and in the very long run they should be, but they would be very helpful for a while. For one thing, when depending on classes compiled from other languages it can recognize these annotations. And when compiling classes to be called by other languages it can embed them. And even squarely inside "Kotlin land" this should help. Suppose the language feature drops in Kotlin version N. Some published library wishes to support both Kotlin N-1 and Kotlin N (a highly normal thing for a library to want to do). That means it wants a way to provide the information, but it can't use the normal approach for providing it. Annotations to the rescue? Though perhaps Kotlin has a different strategy for dealing with this problem generally (Java doesn't). If Kotlin moves forward, then either
|
(The below applies to constructors as well as methods, and includes usages of either via member references.)
Background
Each call to a non-void method produces a result value, which the call either uses or ignores. It's considered to use the result if and only if changing the method to void would break the call site.
Rare methods like
Set.add
are legitimately useful when called in either fashion. These are "may-ignore-result" methods. For want of a pithy adjective we might call them "voidable" methods, short for "okay-to-treat-as-void".The problem
May-ignore-result methods are a comparatively rare special case, yet Java treats all methods this way. This allows many bugs to happen. If we can annotate the methods appropriately we can catch those bugs.
Numerous tools support this check, using annotations like
@CheckReturnValue
(from Error Prone or SpotBugs) or@CheckResult
(from Android). Since may-ignore-result has been Java's default treatment, they have you mark all other methods with the annotation. Like we've found with nullness, a better approach is to let you "flip the default" for a class/package/module, then annotate only the may-ignore-result special cases.(Kudos to Swift for getting this right, with its
@discardableResult
annotation; surely others too.)Proposal (sketch)
This calls for:
Note that tools would be at liberty to use this information as they see fit. For example, if a tool recognizes that it's analyzing test code it might exempt the code from this check.
One key semantic question is whether may-ignore-resultness should automatically be inherited from a supertype, or whether it must be reasserted on overrides. The latter is obviously convenient but there are a few points to consider on each side.
Kotlin status
Kotlin is looking at doing something like this, but of course wants it to work seamlessly back and forth with Java, so they are happy to work with us on standardizing these semantics.
Diagram
I don't know why I made this but I did so here it is. It uses the awkward term "voidable" for "may ignore result".
The text was updated successfully, but these errors were encountered: