Skip to content

Standard rules

Annotation formatting

Multiple annotations should be on a separate line than the annotated declaration; annotations with parameters should each be on separate lines; annotations should be followed by a space

// A single annotation (without parameters) is allowed on same line as annotated construct
@FunctionalInterface class FooBar {
    @JvmField var foo: String
    @Test fun bar() {}
}
// A class or function parameter may have a single annotation with parameter(s) on the same line
class Foo(@Path("fooId") val fooId: String)
class Bar(
    @NotNull("fooId") val fooId: String,
    @NotNull("bar") bar: String
)
// Multiple annotations (without parameters) are allowed on the same line
@Foo @Bar
class FooBar {
    @Foo @Bar
    var foo: String
    @Foo @Bar
    fun bar() {}
}
// An array of annotations (without parameters) is allowed on same line as annotated construct
@[Foo Bar] class FooBar2 {
    @[Foo Bar] var foo: String
    @[Foo Bar] fun bar() {}
}
// An annotation with parameter(s) is not allowed on same line as annotated construct
@Suppress("Unused") class FooBar {
    @Suppress("Unused") var foo: String
    @Suppress("Unused") fun bar() {}
}
// Multiple annotation on same line as annotated construct are not allowed
@Foo @Bar class FooBar {
    @Foo @Bar var foo: String
    @Foo @Bar fun bar() {}
}

Rule-id: annotation

Argument list wrapping

All arguments should be on the same line, or every argument should be on a separate line.

val x = f(
    a,
    b,
    c
)
val x = f(
    a,
    b, c
)

Rule-id: argument-list-wrapping

Block comment initial star alignment

Lines in a block comment which (exclusive the indentation) start with a * should have this * aligned with the * in the opening of the block comment.

/*
 * This comment is formatted well.
 */
/*
      * This comment is not formatted well.
    */

Rule id: block-comment-initial-star-alignment

Chain wrapping

When wrapping chained calls ., ?. and ?: should be placed on the next line

val foo = listOf(1, 2, 3)
    .filter { it > 2 }!!
    .takeIf { it.count() > 100 }
    ?.sum()
val foobar = foo() ?:
    bar
val foo = listOf(1, 2, 3).
    filter { it > 2 }!!.
    takeIf { it.count() > 100 }?.
    sum()
val foobar = foo()
    ?: bar

Rule id: chain-wrapping

Class/object naming

Enforce naming of class.

class Foo
class Foo1
@Nested
inner class `Some descriptive class name` {
    @Test
    fun `Some descriptive test name`() {
        // do something
    }
}
class foo
class Foo_Bar
class `Some class in the production code`

Note

Functions in files which import a class from package org.junit.jupiter.api are considered to be test functions and are allowed to have a name specified between backticks and do not need to adhere to the normal naming convention. Although, the Kotlin coding conventions does not allow this explicitly for class identifiers, ktlint does allow it.

This rule can also be suppressed with the IntelliJ IDEA inspection suppression ClassName.

Rule id: class-naming

Enum entry

Enum entry names should be uppercase underscore-separated names.

enum class Bar {
    FOO,
    Foo,
    FOO_BAR,
    Foo_Bar
}
enum class Bar {
    foo,
    bAr,
    Foo_Bar,
}

Rule id: enum-entry-name-case

File name

A file containing only one visible (e.g. non-private) class, and visible declarations related to that class only, should be named according to that element. The same applies if the file does not contain a visible class but exactly one type alias or one object declaration. Otherwise, the PascalCase notation should be used.

Rule id: filename

Final newline

Ensures consistent usage of a newline at the end of each file.

This rule can be configured with .editorconfig property insert_final_newline.

Rule id: final-newline

Import ordering

Ensures that imports are ordered consistently (see Import Layouts for configuration).

import com.bar.Bar
import com.foo.Foo
import org.foo.bar.FooBar
import java.util.concurrent.ConcurrentHashMap
import com.bar.Bar
import java.util.concurrent.ConcurrentHashMap
import org.foo.bar.FooBar
import com.foo.Foo

Rule id: import-ordering

Indentation

Indentation formatting - respects .editorconfig indent_size with no continuation indent (see EditorConfig section for more).

fun main() {
    foobar(
        a,
        b,
        c
    )
}
fun main() {
    foobar(
          a,
          b,
          c
          )
}

Note

This rule handles indentation for many different language constructs which can not be summarized with a few examples. See the unit tests for more details.

Rule id: indent

Max line length

Ensures that lines do not exceed the given length of .editorconfig property max_line_length (see EditorConfig section for more). This rule does not apply in a number of situations. For example, in the case a line exceeds the maximum line length due to a comment that disables ktlint rules then that comment is being ignored when validating the length of the line. The .editorconfig property ktlint_ignore_back_ticked_identifier can be set to ignore identifiers which are enclosed in backticks, which for example is very useful when you want to allow longer names for unit tests.

// Assume that the last allowed character is
// at the X character on the right           X
// Lines below are accepted although the max
// line length is exceeded.
package com.toooooooooooooooooooooooooooo.long
import com.tooooooooooooooooooooooooooooo.long
val foo =
    """
    fooooooooooooooooooooooooooooooooooooooooo
    """
@Test
fun `Test description which is toooooooooooo long`() {
}
// Assume that the last allowed character is
// at the X character on the right           X
val fooooooooooooooo = "fooooooooooooooooooooo"
val foooooooooooooo = "foooooooooooooooooooo" // some comment
val fooooooooooooo =
    "foooooooooooooooooooooooooooooooooooooooo"

Rule id: max-line-length

Modifier order

Consistent order of modifiers

abstract class A {
    protected open val v = ""
    internal open suspend fun f(v: Any): Any = ""
    protected lateinit var lv: String
}
abstract class A {
    open protected val v = ""
    open suspend internal fun f(v: Any): Any = ""
    lateinit protected var lv: String
}

Rule id: modifier-order

Multiline if-else

Braces required for multiline if/else statements.

val foo =
    if (true) {
        return 0
    } else {
        return 1
    }
val foo =
    if (true)
        return 0
    else
        return 1

Rule id: multiline-if-else

No blank lines before }

No blank lines before }.

fun main() {
    fun a() {
    }
    fun b()
}
fun main() {
    fun a() {

    }
    fun b()

}

Rule id: no-blank-line-before-rbrace

No blank lines in chained method calls

fun foo(inputText: String) {
    inputText
        .lowercase(Locale.getDefault())
}
fun foo(inputText: String) {
    inputText

        .lowercase(Locale.getDefault())
}

Rule id: no-blank-lines-in-chained-method-calls

No consecutive blank lines

package com.test

import com.test.util

val a = "a"

fun b() {
}

fun c()
package com.test


import com.test.util


val a = "a"


fun b() {
}


fun c()

Rule id: no-consecutive-blank-lines

No empty ({}) class bodies

class C
data class DC(val v: Any)
interface I
object O
class C {}
data class DC(val v: Any) { }
interface I {
}
object O{}

Rule id: no-empty-class-body

No leading empty lines in method blocks

fun bar() {
   val a = 2
}
fun bar() {

   val a = 2
}

Rule id: no-empty-first-line-in-method-block

No line break after else

Disallows line breaks after the else keyword if that could lead to confusion, for example:

fun funA() {
    if (conditionA()) {
        doSomething()
    } else if (conditionB()) {
        doAnotherThing()
    }
}
fun funA() {
    if (conditionA()) {
        doSomething()
    } else
    if (conditionB()) {
        doAnotherThing()
    }
}

Rule id: no-line-break-after-else

No line break before assignment

When a line is broken at an assignment (=) operator the break comes after the symbol.

val valA =
    ""
val valA
    = ""

Rule id: no-line-break-before-assignment

No multi spaces

Except in indentation and in KDoc's it is not allowed to have multiple consecutive spaces.

fun main() {
    x(1, 3)
}
fun  main()  {
    x(1,  3)
}

Rule id: no-multi-spaces

No semicolons

No semicolons (unless used to separate multiple statements on the same line).

fun foo() {
    bar()

    bar()
}
fun foo() {
    ;
    bar()
    ;

    bar()

    ;
}

Rule id: no-semi

No trailing whitespaces

Rule id: no-trailing-spaces

No Unit as return type

The Unit type is not allowed as return type of a function. returns (fun fn {} instead of fun fn: Unit {})

fun fn() {}
fun fn(): Unit {}

Rule id: no-unit-return

No unused imports

Warning

This rule is not able to detect all unused imports as mentioned in this issue comment.

Rule id: no-unused-imports

No wildcard imports

No wildcard imports except imports listed in .editorconfig property ij_kotlin_packages_to_use_import_on_demand.

import foobar.Bar
import foobar.Foo
import foobar.*

Warning

In case property ij_kotlin_packages_to_use_import_on_demand is not explicitly set, it allows wildcards imports like java.util.* by default to keep in sync with IntelliJ IDEA behavior. To disallow all wildcard imports, add property below to your .editorconfig:

[*.{kt,kts}]
ij_kotlin_packages_to_use_import_on_demand = unset

Rule id: no-wildcard-imports

Package name

Validates that the package name matches the regular expression [a-z][a-zA-Z\d]*(\.[a-z][a-zA-Z\d]*)*.

package foo
package foo.bar
package Foo
package foo.Foo
package `foo bar`
package foo.`foo bar`

Rule id: package-name

Parameter list wrapping

When class/function signature doesn't fit on a single line, each parameter must be on a separate line

class ClassA(paramA: String, paramB: String, paramC: String)
class ClassA(
    paramA: String,
    paramB: String,
    paramC: String
)
fun f(a: Any, b: Any, c: Any)
fun f(
    a: Any,
    b: Any,
    c: Any
)
class ClassA(
    paramA: String, paramB: String,
    paramC: String
)
fun f(
    a: Any,
    b: Any, c: Any
)

Rule id: parameter-list-wrapping

Parameter wrapping

When a function or class parameter doesn't fit on a single line, wrap the type or value to a separate line

// Assume that the last allowed character is
// at the X character on the right           X
class Bar(
    val fooooooooooooooooooooooooTooLong:
        Foo,
)
fun bar(
    fooooooooooooooooooooooooTooLong:
        Foo,
)
// Assume that the last allowed character is
// at the X character on the right           X
class Bar(
    val fooooooooooooooooooooooooTooLong:
    Foo,
)
fun bar(
    fooooooooooooooooooooooooTooLong:
    Foo,
)
// Assume that the last allowed character is
// at the X character on the right           X
class Bar(
    val fooooooooooooooooooooooooTooLong: Foo,
)
fun bar(
    fooooooooooooooooooooooooooooTooLong: Foo,
)

Rule id: parameter-wrapping

Property wrapping

When a property doesn't fit on a single line, wrap the type or value to a separate line

// Assume that the last allowed character is
// at the X character on the right           X
val aVariableWithALooooooooooooongName:
    String
// Assume that the last allowed character is
// at the X character on the right           X
val aVariableWithALooooooooooooongName: String

Rule id: property-wrapping

String template

Consistent string templates ($v instead of ${v}, ${p.v} instead of ${p.v.toString()})

val foo = "$foo hello"
val foo = "${foo} hello"

Rule id: string-template

Trailing comma on call site

Consistent removal (default) or adding of trailing commas on call site.

Important

KtLint uses the IntelliJ IDEA .editorconfig property ij_kotlin_allow_trailing_comma_on_call_site to configure the rule. When this property is enabled, KtLint enforces the usage of the trailing comma at call site while IntelliJ IDEA default formatter only allows to use the trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values consistent formatting more than a per-situation decision.

FooWrapper(
    Foo(
        a = 3,
        b = 4,
    ),
)
FooWrapper(Foo(
    a = 3,
    b = 4,
),) // it's weird to insert "," between unwrapped (continued) parenthesis

Note

In KtLint 0.48.x the default value for using the trailing comma on call site has been changed to true except when codestyle android is used.

Although the Kotlin coding conventions leaves it to the developer's discretion to use trailing commas on the call site, it also states that usage of trailing commas has several benefits:

  • It makes version-control diffs cleaner – as all the focus is on the changed value.
  • It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements.
  • It simplifies code generation, for example, for object initializers. The last element can also have a comma.

Note

Trailing comma on call site is automatically disabled if the Wrapping rule (or, before version 0.45.0, the Indentation rule) is disabled or not loaded. Because it cannot provide proper formatting with unwrapped calls. (see dependencies).

Rule id: trailing-comma-on-call-site

Trailing comma on declaration site

Consistent removal (default) or adding of trailing commas on declaration site.

Important

KtLint uses the IntelliJ IDEA .editorconfig property ij_kotlin_allow_trailing_comma to configure the rule. When this property is enabled, KtLint enforces the usage of the trailing comma at declaration site while IntelliJ IDEA default formatter only allows to use the trailing comma but leaves it to the developer's discretion to actually use it (or not). KtLint values consistent formatting more than a per-situation decision.

class FooWrapper(
    val foo = Foo(
        a = 3,
        b = 4,
    ),
)
class FooWrapper(val foo = Foo(
    a = 3,
    b = 4,
),) // it's weird to insert "," between unwrapped (continued) parenthesis

Note

In KtLint 0.48.x the default value for using the trailing comma on declaration site has been changed to true except when codestyle android is used.

The Kotlin coding conventions encourages the usage of trailing commas on the declaration site, but leaves it to the developer's discretion to use trailing commas on the call site. But next to this, it also states that usage of trailing commas has several benefits:

  • It makes version-control diffs cleaner – as all the focus is on the changed value.
  • It makes it easy to add and reorder elements – there is no need to add or delete the comma if you manipulate elements.
  • It simplifies code generation, for example, for object initializers. The last element can also have a comma.

Note

Trailing comma on declaration site is automatically disabled if the Wrapping rule (or, before version 0.45.0, the Indentation rule) is disabled or not loaded. Because it cannot provide proper formatting with unwrapped declarations. (see dependencies).

Rule id: trailing-comma-on-declaration-site

Unnecessary parenthesis before trailing lambda

An empty parentheses block before a lambda is redundant.

"some-string".count { it == '-' }
"some-string".count() { it == '-' }

Rule id: unnecessary-parentheses-before-trailing-lambda

Wrapping

Wrapping

Inserts missing newlines (for example between parentheses of a multi-line function call).

Rule id: wrapping

Comment wrapping

A block comment should start and end on a line that does not contain any other element.

/* Some comment 1 */
val foo1 = "foo1"
val foo2 = "foo" // Some comment
val foo3 = { /* no-op */ } 
/* Some comment 1 */ val foo1 = "foo1"
val foo2 = "foo" /* Block comment instead of end-of-line comment */
val foo3 = "foo" /* Some comment
                  * with a newline
                  */

Rule id: comment-wrapping

Spacing

Angle bracket spacing

No spaces around angle brackets when used for typing.

val a: Map<Int, String> = mapOf()
val b: Map<Int, String> = mapOf()
val c: Map<Int, String> = mapOf()
val a: Map< Int, String> = mapOf()
val b: Map<Int, String > = mapOf()
val c: Map <Int, String> = mapOf()

Rule id: spacing-around-angle-brackets

Annotation spacing

Annotations should be separated by a single line break.

@JvmField
fun foo() {}

/**
 * block comment
 */
@Foo @Bar
class FooBar {
}
@JvmField

fun foo() {}

@Foo @Bar
/**
 * block comment
 */
class FooBar {
}

Rule id: annotation-spacing

Blank line between declarations with annotations

Declarations with annotations should be separated by a blank line.

fun a()

@Bar
fun b()
fun a()
@Bar
fun b()

Rule id: spacing-between-declarations-with-annotations

Blank line between declaration with comments

Declarations with comments should be separated by a blank line.

// some comment 1
bar()

/*
 * some comment 2
 */
foo()
// some comment 1
bar()
/*
 * some comment 2
 */
foo()

Rule id: spacing-between-declarations-with-comments

Colon spacing

Consistent spacing around colon.

class A : B
class A2 : B2
class A:B
class A2  :  B2

Rule id: colon-spacing

Comma spacing

Consistent spacing around comma.

val foo1 = Foo(1, 3)
val foo2 = Foo(1, 3)
val foo1 = Foo(1 ,3)
val foo2 = Foo(1,3)

Rule id: comma-spacing

Comment spacing

The end of line comment sign // should be preceded and followed by exactly a space.

// comment
var debugging = false // comment
var debugging = false // comment
var debugging = false // comment
fun main() {
    System.out.println( // 123
        "test"
    )
}
    // comment
//comment
var debugging = false// comment
var debugging = false //comment
var debugging = false//comment
fun main() {
    System.out.println(//123
        "test"
    )
}
    //comment

Rule id: comment-spacing

Curly spacing

Consistent spacing around curly braces.

val foo = if (true) { 0 } else { 1 }
val foo = if (true){0}else{1}

Rule id: curly-spacing

Dot spacing

Consistent spacing around dots.

fun String.foo() = "foo"
fun String . foo() = "foo"

Rule id: dot-spacing

Double colon spacing

No spaces around ::.

val foo = Foo::class
val foo1 = Foo ::class
val foo2 = Foo:: class
val foo3 = Foo :: class
val foo4 = Foo::
    class

Rule id: double-colon-spacing

Function return type spacing

Consistent spacing around the function return type.

fun foo(): String = "some-result"
fun foo1() : String = "some-result"
fun foo2():  String = "some-result"
fun foo3():String = "some-result"
fun foo4():
    String = "some-result"

Rule id: function-return-type-spacing

Function start of body spacing

Consistent spacing before start of function body.

fun foo1() = "some-result"
fun foo2() =
    "some-result"
fun foo3() {
    // do something
}
fun bar1(): String = "some-result"
fun bar2(): String =
    "some-result"
fun bar3(): String {
    return "some-result"
}
fun foo1()= "some-result"
fun foo2()
    = "some-result"
fun foo3()
{
    // do something
}
fun bar1(): String= "some-result"
fun bar2(): String
    = "some-result"
fun bar3(): String
{
    return "some-result"
}

Rule id: function-start-of-body-spacing:

Function type reference spacing

Consistent spacing in the type reference before a function.

fun String.foo() = "some-result"
fun String .foo() = "some-result"
fun String
    .foo() = "some-result"
fun String? .foo() = "some-result"
fun String?
    .foo() = "some-result"

Rule id: function-type-reference-spacing

Fun keyword spacing

Consistent spacing after the fun keyword.

fun foo() = "some-result"
fun  foo() = "some-result"
fun
foo() = "some-result"

Rule id: fun-keyword-spacing

Kdoc wrapping

A KDoc comment should start and end on a line that does not contain any other element.

/** Some KDoc comment 1 */
val foo1 = "foo1"
/** Some KDoc comment 1 */ val foo1 = "foo1"
val foo2 = "foo2" /** Some KDoc comment
                   * with a newline
                   */

Rule id: kdoc-wrapping

Keyword spacing

Consistent spacing around keywords.

fun main() {
    if (true) {}
}
fun main() {
    if(true){}
}

Rule id: keyword-spacing

Modifier list spacing

Consistent spacing between modifiers in and after the last modifier in a modifier list.

abstract class Foo {
    protected abstract suspend fun execute()
}
abstract  class Foo {
    protected  abstract  suspend  fun execute()
}
abstract
class Foo {
    protected
    abstract
    suspend
    fun execute()
}

Rule id: modifier-list-spacing

Nullable type spacing

No spaces in a nullable type.

val foo: String? = null
val foo: List<String?> = listOf(null)
val foo: String ? = null
val foo: List<String ?> = listOf(null)

Rule id: nullable-type-spacing

Operator spacing

Consistent spacing around operators.

val foo1 = 1 + 2
val foo2 = 1 - 2
val foo3 = 1 * 2
val foo4 = 1 / 2
val foo1 = 1+2
val foo2 = 1- 2
val foo3 = 1 *2
val foo4 = 1  /  2

Rule id: op-spacing

Parenthesis spacing

Consistent spacing around parenthesis.

class Foo : Bar {
    constructor(string: String) : super()
}
val foo1 = ((1 + 2) / 3)
class Foo : Bar {
    constructor(string: String) : super ()
}
val foo1 = ( (1 + 2 ) / 3)

Rule id: paren-spacing

Range spacing

Consistent spacing around range operators.

val foo1 = (1..12 step 2).last
val foo2 = (1..12 step 2).last
val foo3 = (1..12 step 2).last
val foo1 = (1.. 12 step 2).last
val foo2 = (1 .. 12 step 2).last
val foo3 = (1 ..12 step 2).last

Rule id: range-spacing

Spacing between function name and opening parenthesis

Consistent spacing between function name and opening parenthesis.

fun foo() = "foo"
fun foo () = "foo"

Rule id: spacing-between-function-name-and-opening-parenthesis

Unary operator spacing

No spaces around unary operators.

fun foo1(i: Int) = i++
fun foo2(i: Int) = ++i
fun foo3(i: Int) = ++i
fun foo1(i: Int) = i ++
fun foo2(i: Int) = ++ i
fun foo3(i: Int) = ++
    i

Rule id: unary-op-spacing