Why is this an issue?

In Kotlin, if and when statements are expressions that return a value. This allows for a more concise and functional programming style with less cognitive complexity, because it results in fewer return points and fewer variable assignments in a function.

If both branches of an if statement end with a return statement, the if statement should be used instead as an expression for a return statement.

If all branches of an exhaustive when statement end with a return statement, the when statement should be used instead as an expression for a return statement. A when statement is exhaustive when it covers all elements of an enum or features an else clause.

What is the potential impact?

Readability and Understanding

This change makes it easier to understand a function because it will reduce its complexity. This is because the function now contains fewer return points that the developer needs to keep track of.

How to fix it

Use the if statement as an expression for a return statement. Lift the return keyword from the end of the if band else branch before the if keyword.

Use the when statement as an expression for a return statement. Lift the return keyword from the end of all its case clauses before the when keyword.

Code examples

Noncompliant code example

fun returnIfElse(value: Int): String {
    // ...
    if (value >= 0) { // Noncompliant, every branch contains a return statement
        return "positive"
    } else {
        return "negative"
    }
}

Compliant solution

fun returnIfElse(value: Int): String {
    // ...
    return if (value >= 0) { // Compliant
        "positive"
    } else {
        "negative"
    }
}

Noncompliant code example

fun returnWhenElse(a: Float): Int {
    // ...
    when { // Noncompliant, every branch of exhaustive `when` contains a return statement
        a < 0 -> return -1
        a > 0 -> return 1
        else -> return 0
    }
}

Compliant solution

fun returnWhenElse(a: Float): Int {
    // ...
    return when { // Compliant
        a < 0 -> -1
        a > 0 -> 1
        else -> 0
    }
}

Noncompliant code example

enum class OneTwoThree {
    ONE,
    TWO,
    THREE
}
fun returnWhenEnum(oneTwoThree: OneTwoThree): String {
    // ...
    when(oneTwoThree) { // Noncompliant, every branch of exhaustive `when` contains a return statement
        OneTwoThree.ONE -> return "one"
        OneTwoThree.TWO -> return "two"
        OneTwoThree.THREE -> return "three"
    }
}

Compliant solution

fun returnWhenEnum(oneTwoThree: OneTwoThree): String {
    // ...
    return when(oneTwoThree) { // Compliant
        OneTwoThree.ONE -> "one"
        OneTwoThree.TWO -> "two"
        OneTwoThree.THREE -> "three"
    }
}

Noncompliant code example

fun returnIfElseWithSideEffects(a: Float, b: Float): Int {
    // ...
    if (a < 0) { // Noncompliant, every branch contains a return statement
        foo()
        return -1
    } else if (a > b) {
        bar()
        return 1
    } else {
        return 0
    }
}

Compliant solution

fun returnIfElseWithSideEffects(a: Float, b: Float): Int {
    // ...
    return if (a < 0) { // Compliant
        foo()
        -1
    } else if (a > b) {
        bar()
        1
    } else {
        0
    }
}

Resources

Documentation

Articles & blog posts