Skip to content
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

Indentation rule fails on every line after inline lambda in chain #1320

Closed
FWDekker opened this issue Dec 21, 2021 · 5 comments
Closed

Indentation rule fails on every line after inline lambda in chain #1320

FWDekker opened this issue Dec 21, 2021 · 5 comments
Labels
Milestone

Comments

@FWDekker
Copy link
Contributor

FWDekker commented Dec 21, 2021

Expected Behavior

ktlint indicates that the file has no formatting errors.

Observed Behavior

ktlint indicates that every line is missing one level of indentation after a line that has an inline lambda function that is not the last argument to a function, and that function is then chained.

Steps to Reproduce

With the following code sample,

package com.fwdekker.lintexample

class LintExample {
    fun doWithLambda(a: () -> Int, b: Int): Int {
        return 1
    }

    fun main() {
        doWithLambda({ 4 }, 5).toDouble()
    }
}

run ./gradlew ktlintCheck from ktlint-gradle to get the following errors:

[redacted]/LintExample.kt:10:1 Unexpected indentation (4) (should be 8)
[redacted]/LintExample.kt:11:1 Unexpected indentation (0) (should be 4)
[redacted]/LintExample.kt:12:1 Unexpected indentation (0) (should be 4)

Mitigations

The following changes to the code sample circumvent the issue:

  • Store the output of doWithLambda in a variable, and then call toDouble on that on a separate line.
  • Swap the arguments of doWithLambda so that the lambda is the last argument. It does not matter if the lambda is in the parentheses or not.
  • Store the lambda in a separate variable and then pass it to doWithLambda.
  • Reformat the invocation to:
    doWithLambda(
        {
            4
        },
        5
    ).toDouble()

Putting a return@doWithLambda inside the lambda does not resolve the issue.

Your Environment

@paul-dingemans
Copy link
Collaborator

ktlint: I don't know how to check this :-(

run ktlint --version

@FWDekker
Copy link
Contributor Author

Unknown command: ktlint

I'm using the ktlint-gradle wrapper to run ktlint. I checked their changelogs instead and I think the default ktlint version is 0.42.1.

@MeikeMertschFortum
Copy link

I have the same issue. I reported it first to the lib I use for the gradle implementation, but I was sent here.

Since that library updated the default ktlint version from 0.41.0 to 0.42.1, I get surprising complaints about the indentation in one of my classes:

Here's the class code:

class Mailbox(private val topic: String) {

    companion object {
        fun fromMailAddress(mail: String) = Mailbox(getTopicFrom(mail))
    }

    fun expectSubscriptionMailIn(waitingTimeInSeconds: Int): Email =
        waitUntil({ it.isSubConfirmation() }, "Subscription mail for $topic", waitingTimeInSeconds).first()

    fun expectSessionReportIn(waitingTimeInSeconds: Int): String =
        waitUntil({ it.isSessionReport() }, "Session Report for $topic", waitingTimeInSeconds).first()
            .returnReportLink()

    fun expectErrorMailIn(waitingTimeInSeconds: Int, errorCode: String): Email =
        waitUntil({ it.isErrorMessage(errorCode) }, "Error mail for $topic", waitingTimeInSeconds).first()

    fun expectOfflineMailIn(waitingTimeInSeconds: Int): Email =
        waitUntil({ it.isOfflineError() }, "Offline mail for $topic", waitingTimeInSeconds).first()

    fun expectFleetInvitationIn(waitingTimeInSeconds: Int): String =
        waitUntil({ it.isFleetInvite() }, "Fleet invite for $topic", waitingTimeInSeconds).first()
            .findLink()

    fun expectMailsBySubject(vararg subjectCandidates: String) =
        waitUntil({ subjectCandidates.contains(it.subject) }, "Mail for $topic", 60)

    fun subscribeToAwsNotifications(): Mailbox = apply {
        val subscriptionEmail = expectSubscriptionMailIn(90)
        subscriptionEmail.subscribe()
    }

    private fun waitUntil(predicate: Predicate<Email>, alias: String, waitingTimeInSeconds: Int): List<Email> =
        checkMailsFor(predicate, alias, waitingTimeInSeconds, topic)
}

now the check failed as follows:

> Task :ktlintTestSourceSetCheck FAILED
.../Mailbox.kt:19:1 Unexpected indentation (4) (should be 8)
.../Mailbox.kt:20:1 Unexpected indentation (8) (should be 12)
.../Mailbox.kt:21:1 Unexpected indentation (12) (should be 20)
.../Mailbox.kt:23:1 Unexpected indentation (4) (should be 12)
.../Mailbox.kt:24:1 Unexpected indentation (8) (should be 16)
.../Mailbox.kt:26:1 Unexpected indentation (4) (should be 16)
.../Mailbox.kt:27:1 Unexpected indentation (8) (should be 20)
.../Mailbox.kt:29:1 Unexpected indentation (4) (should be 20)
.../Mailbox.kt:30:1 Unexpected indentation (8) (should be 24)
.../Mailbox.kt:31:1 Unexpected indentation (12) (should be 32)
.../Mailbox.kt:33:1 Unexpected indentation (4) (should be 24)
.../Mailbox.kt:34:1 Unexpected indentation (8) (should be 28)
.../Mailbox.kt:36:1 Unexpected indentation (4) (should be 24)

if I run the formatter, I end up with this:

class Mailbox(private val topic: String) {

    companion object {
        fun fromMailAddress(mail: String) = Mailbox(getTopicFrom(mail))
    }

    fun expectSubscriptionMailIn(waitingTimeInSeconds: Int): Email =
        waitUntil({ it.isSubConfirmation() }, "Subscription mail for $topic", waitingTimeInSeconds).first()

        fun expectSessionReportIn(waitingTimeInSeconds: Int): String =
            waitUntil({ it.isSessionReport() }, "Session Report for $topic", waitingTimeInSeconds).first()
                    .returnReportLink()

            fun expectErrorMailIn(waitingTimeInSeconds: Int, errorCode: String): Email =
                waitUntil({ it.isErrorMessage(errorCode) }, "Error mail for $topic", waitingTimeInSeconds).first()

                fun expectOfflineMailIn(waitingTimeInSeconds: Int): Email =
                    waitUntil({ it.isOfflineError() }, "Offline mail for $topic", waitingTimeInSeconds).first()

                    fun expectFleetInvitationIn(waitingTimeInSeconds: Int): String =
                        waitUntil({ it.isFleetInvite() }, "Fleet invite for $topic", waitingTimeInSeconds).first()
                                .findLink()

                        fun expectMailsBySubject(vararg subjectCandidates: String) =
                            waitUntil({ subjectCandidates.contains(it.subject) }, "Mail for $topic", 60)

                        fun subscribeToAwsNotifications(): Mailbox = apply {
                            val subscriptionEmail = expectSubscriptionMailIn(90)
                            subscriptionEmail.subscribe()
                        }

                        private fun waitUntil(predicate: Predicate<Email>, alias: String, waitingTimeInSeconds: Int): List<Email> =
                            checkMailsFor(predicate, alias, waitingTimeInSeconds, topic)
                    }

I don't really understand why. It feels like the equal signs work almost as opening parentheses but not closing ones.
Even more curious is that this is the only file in the project that has this issue (but I am almost certain it is also the only one that contains only single expression functions that are longer than a single line (with exception of the companion object)

And I am pretty convinced that this format is not how it should be. Ktlint usually gives me beautiful code (yay!), but this doesn't look right 😅

@paul-dingemans
Copy link
Collaborator

@MeikeMertschFortum Your problem will be solved with #1284

@paul-dingemans
Copy link
Collaborator

Closed by #1284

@paul-dingemans paul-dingemans added this to the 0.44.0 milestone Jan 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants