Skip to content

Commit db45c67

Browse files
hoc081098michaelbull
authored andcommitted
Annotate Result#{value,error} direct access as unsafe
Requires consumers to @OptIn to accessing the 'value' and 'error' properties directly, encouraging them to otherwise use library functions such as mapBoth. Closes #123, #104
1 parent 7dd19a9 commit db45c67

File tree

5 files changed

+61
-0
lines changed

5 files changed

+61
-0
lines changed

kotlin-result-coroutines/src/commonMain/kotlin/com/github/michaelbull/result/coroutines/CoroutineBinding.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.github.michaelbull.result.coroutines
22

33
import com.github.michaelbull.result.Ok
44
import com.github.michaelbull.result.Result
5+
import com.github.michaelbull.result.annotation.UnsafeResultValueAccess
56
import com.github.michaelbull.result.asErr
67
import com.github.michaelbull.result.binding
78
import kotlinx.coroutines.CancellationException
@@ -73,6 +74,7 @@ internal class CoroutineBindingScopeImpl<E>(
7374
private val mutex = Mutex()
7475
var result: Result<Nothing, E>? = null
7576

77+
@OptIn(UnsafeResultValueAccess::class)
7678
override suspend fun <V> Result<V, E>.bind(): V {
7779
return if (isOk) {
7880
value

kotlin-result/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ plugins {
55

66
kotlin {
77
explicitApi()
8+
9+
compilerOptions {
10+
optIn.add("com.github.michaelbull.result.annotation.UnsafeResultValueAccess")
11+
optIn.add("com.github.michaelbull.result.annotation.UnsafeResultErrorAccess")
12+
}
813
}

kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.github.michaelbull.result
22

3+
import com.github.michaelbull.result.annotation.UnsafeResultErrorAccess
4+
import com.github.michaelbull.result.annotation.UnsafeResultValueAccess
35
import kotlin.jvm.JvmInline
46

57
/**
@@ -52,10 +54,12 @@ public value class Result<out V, out E> internal constructor(
5254
) {
5355

5456
@Suppress("UNCHECKED_CAST")
57+
@UnsafeResultValueAccess
5558
public val value: V
5659
get() = inlineValue as V
5760

5861
@Suppress("UNCHECKED_CAST")
62+
@UnsafeResultErrorAccess
5963
public val error: E
6064
get() = (inlineValue as Failure<E>).error
6165

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.github.michaelbull.result.annotation
2+
3+
import com.github.michaelbull.result.Err
4+
import com.github.michaelbull.result.Result
5+
import com.github.michaelbull.result.mapBoth
6+
import kotlin.RequiresOptIn.Level.ERROR
7+
import kotlin.annotation.AnnotationRetention.BINARY
8+
import kotlin.annotation.AnnotationTarget.PROPERTY
9+
10+
/**
11+
* Marks access to [Result.error] as unsafe. The [Result] must be guaranteed to be [Err].
12+
*
13+
* Ensure that you verify the state of the [Result] by using [Result.isErr] before accessing its
14+
* [Result.error].
15+
*
16+
* Alternatively, consider using [mapBoth] to safely handle the result.
17+
*/
18+
@RequiresOptIn(
19+
level = ERROR,
20+
message = "Accessing `Result.error` without an explicit `Result.isErr` check is unsafe. Opt-in only when the result is guaranteed to be `Err`.",
21+
)
22+
@Retention(BINARY)
23+
@Target(PROPERTY)
24+
@MustBeDocumented
25+
public annotation class UnsafeResultErrorAccess
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.github.michaelbull.result.annotation
2+
3+
import com.github.michaelbull.result.Ok
4+
import com.github.michaelbull.result.Result
5+
import com.github.michaelbull.result.mapBoth
6+
import kotlin.RequiresOptIn.Level.ERROR
7+
import kotlin.annotation.AnnotationRetention.BINARY
8+
import kotlin.annotation.AnnotationTarget.PROPERTY
9+
10+
/**
11+
* Marks access to [Result.value] as unsafe. The [Result] must be guaranteed to be [Ok].
12+
*
13+
* Ensure that you verify the state of the [Result] by using [Result.isOk] before accessing its
14+
* [Result.value].
15+
*
16+
* Alternatively, consider using [mapBoth] to safely handle the result.
17+
*/
18+
@RequiresOptIn(
19+
level = ERROR,
20+
message = "Accessing `Result.value` without an explicit `Result.isOk` check is unsafe. Opt-in only when the result is guaranteed to be `Ok`.",
21+
)
22+
@Retention(BINARY)
23+
@Target(PROPERTY)
24+
@MustBeDocumented
25+
public annotation class UnsafeResultValueAccess

0 commit comments

Comments
 (0)