Style Rule Set
The Style ruleset provides rules that assert the style of the code. This will help keep code in line with the given code style guidelines.
AbstractClassCanBeConcreteClass
This rule inspects abstract
classes. Abstract classes which do not define any abstract
members should instead be
refactored into concrete classes.
Active by default: Yes - Since v1.2.0
Requires Type Resolution
Configuration options:
-
(default:excludeAnnotatedClasses
[]
)Deprecated: Use
ignoreAnnotated
insteadAllows you to provide a list of annotations that disable this check.
Noncompliant Code:
abstract class OnlyConcreteMembersInAbstractClass { // violation: no abstract members
val i: Int = 0
fun f() { }
}
Compliant Code:
interface OnlyAbstractMembersInInterface {
val i: Int
fun f()
}
class OnlyConcreteMembersInClass {
val i: Int = 0
fun f() { }
}
AbstractClassCanBeInterface
This rule inspects abstract
classes. In case an abstract class
does not define any
abstract
members, it should instead be refactored into an interface.
Active by default: Yes - Since v1.23.0
Requires Type Resolution
Noncompliant Code:
abstract class OnlyAbstractMembersInAbstractClass { // violation: no concrete members
abstract val i: Int
abstract fun f()
}
Compliant Code:
interface Interface {
val i: Int
fun f()
}
abstract class NonAbstractMembersInAbstractClass {
abstract val i: Int
fun f() {
}
}
AlsoCouldBeApply
Detects when an also
block contains only it
-started expressions.
By refactoring the also
block to an apply
block makes it so that all it
s can be removed
thus making the block more concise and easier to read.
Active by default: No
Noncompliant Code:
Buzz().also {
it.init()
it.block()
}
Compliant Code:
Buzz().apply {
init()
block()
}
// Also compliant
fun foo(a: Int): Int {
return a.also { println(it) }
}
BracesOnIfStatements
This rule detects if
statements which do not comply with the specified rules.
Keeping braces consistent will improve readability and avoid possible errors.
The available options are:
always
: forces braces on allif
andelse
branches in the whole codebase.consistent
: ensures that braces are consistent within eachif
-else if
-else
chain. If there's a brace on one of the branches, all branches should have it.necessary
: forces no braces on anyif
andelse
branches in the whole codebase except where necessary for multi-statement branches.never
: forces no braces on anyif
andelse
branches in the whole codebase.
Single-line if-statement has no line break (\n):
if (a) b else c
Multi-line if-statement has at least one line break (\n):
if (a) b
else c
Active by default: No
Configuration options:
-
singleLine
(default:'never'
)single-line braces policy
-
multiLine
(default:'always'
)multi-line braces policy
Noncompliant Code:
// singleLine = 'never'
if (a) { b } else { c }
if (a) { b } else c
if (a) b else { c; d }
// multiLine = 'never'
if (a) {
b
} else {
c
}
// singleLine = 'always'
if (a) b else c
if (a) { b } else c
// multiLine = 'always'
if (a) {
b
} else
c
// singleLine = 'consistent'
if (a) b else { c }
if (a) b else if (c) d else { e }
// multiLine = 'consistent'
if (a)
b
else {
c
}
// singleLine = 'necessary'
if (a) { b } else { c; d }
// multiLine = 'necessary'
if (a) {
b
c
} else if (d) {
e
} else {
f
}
Compliant Code:
// singleLine = 'never'
if (a) b else c
// multiLine = 'never'
if (a)
b
else
c
// singleLine = 'always'
if (a) { b } else { c }
if (a) { b } else if (c) { d }
// multiLine = 'always'
if (a) {
b
} else {
c
}
if (a) {
b
} else if (c) {
d
}
// singleLine = 'consistent'
if (a) b else c
if (a) { b } else { c }
if (a) { b } else { c; d }
// multiLine = 'consistent'
if (a) {
b
} else {
c
}
if (a) b
else c
// singleLine = 'necessary'
if (a) b else { c; d }
// multiLine = 'necessary'
if (a) {
b
c
} else if (d)
e
else
f
BracesOnWhenStatements
This rule detects when
statements which do not comply with the specified policy.
Keeping braces consistent will improve readability and avoid possible errors.
Single-line when
statement is:
a when
where each of the branches are single-line (has no line breaks \n
).
Multi-line when
statement is:
a when
where at least one of the branches is multi-line (has a break line \n
).
Available options are:
never
: forces no braces on any branch. Tip: this is very strict, it will force a simple expression, like a single function call / expression. Extracting a function for "complex" logic is one way to adhere to this policy.necessary
: forces no braces on any branch except where necessary for multi-statement branches.consistent
: ensures that braces are consistent withinwhen
statement. If there are braces on one of the branches, all branches should have it.always
: forces braces on all branches.
Active by default: No
Configuration options:
-
singleLine
(default:'necessary'
)single-line braces policy
-
multiLine
(default:'consistent'
)multi-line braces policy
Noncompliant Code:
// singleLine = 'never'
when (a) {
1 -> { f1() } // Not allowed.
2 -> f2()
}
// multiLine = 'never'
when (a) {
1 -> { // Not allowed.
f1()
}
2 -> f2()
}
// singleLine = 'necessary'
when (a) {
1 -> { f1() } // Unnecessary braces.
2 -> f2()
}
// multiLine = 'necessary'
when (a) {
1 -> { // Unnecessary braces.
f1()
}
2 -> f2()
}
// singleLine = 'consistent'
when (a) {
1 -> { f1() }
2 -> f2()
}
// multiLine = 'consistent'
when (a) {
1 ->
f1() // Missing braces.
2 -> {
f2()
f3()
}
}
// singleLine = 'always'
when (a) {
1 -> { f1() }
2 -> f2() // Missing braces.
}
// multiLine = 'always'
when (a) {
1 ->
f1() // Missing braces.
2 -> {
f2()
f3()
}
}
Compliant Code:
// singleLine = 'never'
when (a) {
1 -> f1()
2 -> f2()
}
// multiLine = 'never'
when (a) {
1 ->
f1()
2 -> f2()
}
// singleLine = 'necessary'
when (a) {
1 -> f1()
2 -> { f2(); f3() } // Necessary braces because of multiple statements.
}
// multiLine = 'necessary'
when (a) {
1 ->
f1()
2 -> { // Necessary braces because of multiple statements.
f2()
f3()
}
}
// singleLine = 'consistent'
when (a) {
1 -> { f1() }
2 -> { f2() }
}
when (a) {
1 -> f1()
2 -> f2()
}
// multiLine = 'consistent'
when (a) {
1 -> {
f1()
}
2 -> {
f2()
f3()
}
}
// singleLine = 'always'
when (a) {
1 -> { f1() }
2 -> { f2() }
}
// multiLine = 'always'
when (a) {
1 -> {
f1()
}
2 -> {
f2()
f3()
}
}
CanBeNonNullable
This rule inspects variables marked as nullable and reports which could be declared as non-nullable instead.
It's preferred to not have functions that do "nothing". A function that does nothing when the value is null hides the logic, so it should not allow null values in the first place. It is better to move the null checks up around the calls, instead of having it inside the function.
This could lead to less nullability overall in the codebase.
Active by default: No
Requires Type Resolution
Noncompliant Code:
class A {
var a: Int? = 5
fun foo() {
a = 6
}
}
class A {
val a: Int?
get() = 5
}
fun foo(a: Int?) {
val b = a!! + 2
}
fun foo(a: Int?) {
if (a != null) {
println(a)
}
}
fun foo(a: Int?) {
if (a == null) return
println(a)
}
Compliant Code:
class A {
var a: Int = 5
fun foo() {
a = 6
}
}
class A {
val a: Int
get() = 5
}
fun foo(a: Int) {
val b = a + 2
}
fun foo(a: Int) {
println(a)
}
CascadingCallWrapping
Requires that all chained calls are placed on a new line if a preceding one is.
Active by default: No
Configuration options:
-
includeElvis
(default:true
)require trailing elvis expressions to be wrapped on a new line
Noncompliant Code:
foo()
.bar().baz()
Compliant Code:
foo().bar().baz()
foo()
.bar()
.baz()
ClassOrdering
This rule ensures class contents are ordered as follows as recommended by the Kotlin Coding Conventions:
- Property declarations and initializer blocks
- Secondary constructors
- Method declarations
- Companion object
Active by default: No
Noncompliant Code:
class OutOfOrder {
companion object {
const val IMPORTANT_VALUE = 3
}
fun returnX(): Int {
return x
}
private val x = 2
}
Compliant Code:
class InOrder {
private val x = 2
fun returnX(): Int {
return x
}
companion object {
const val IMPORTANT_VALUE = 3
}
}
CollapsibleIfStatements
This rule detects if
statements which can be collapsed. This can reduce nesting and help improve readability.
However, carefully consider whether merging the if statements actually improves readability, as collapsing the statements may hide some edge cases from the reader.
Active by default: No
Noncompliant Code:
val i = 1
if (i > 0) {
if (i < 5) {
println(i)
}
}
Compliant Code:
val i = 1
if (i > 0 && i < 5) {
println(i)
}
DataClassContainsFunctions
This rule reports functions inside data classes which have not been marked as a conversion function.
Data classes should mainly be used to store data. This rule assumes that they should not contain any extra functions
aside functions that help with converting objects from/to one another.
Data classes will automatically have a generated equals
, toString
and hashCode
function by the compiler.
Active by default: No
Configuration options:
-
conversionFunctionPrefix
(default:['to']
)allowed conversion function names
-
allowOperators
(default:false
)allows overloading an operator
Noncompliant Code:
data class DataClassWithFunctions(val i: Int) {
fun foo() { }
}
DataClassShouldBeImmutable
This rule reports mutable properties inside data classes.
Data classes should mainly be used to store immutable data. This rule assumes that they should not contain any mutable properties.
Active by default: No
Noncompliant Code:
data class MutableDataClass(var i: Int) {
var s: String? = null
}
Compliant Code:
data class ImmutableDataClass(
val i: Int,
val s: String?
)
DestructuringDeclarationWithTooManyEntries
Destructuring declarations with too many entries are hard to read and understand. To increase readability they should be refactored to reduce the number of entries or avoid using a destructuring declaration.
Active by default: Yes - Since v1.21.0
Configuration options:
-
maxDestructuringEntries
(default:3
)maximum allowed elements in a destructuring declaration
Noncompliant Code:
data class TooManyElements(val a: Int, val b: Int, val c: Int, val d: Int)
val (a, b, c, d) = TooManyElements(1, 2, 3, 4)
Compliant Code:
data class FewerElements(val a: Int, val b: Int, val c: Int)
val (a, b, c) = TooManyElements(1, 2, 3)
DoubleNegativeLambda
Detects negation in lambda blocks where the function name is also in the negative (like takeUnless
).
A double negative is harder to read than a positive. In particular, if there are multiple conditions with &&
etc. inside
the lambda, then the reader may need to unpack these using DeMorgan's laws. Consider rewriting the lambda to use a positive version
of the function (like takeIf
).
Active by default: No
Configuration options:
-
negativeFunctions
(default:['takeUnless', 'none']
)Function names expressed in the negative that can form double negatives with their lambda blocks. These are grouped together with a recommendation to use a positive counterpart, or
null
if this is unknown. -
negativeFunctionNameParts
(default:['not', 'non']
)Function name parts to look for in the lambda block when deciding if the lambda contains a negative.
Noncompliant Code:
fun Int.evenOrNull() = takeUnless { it % 2 != 0 }
Compliant Code:
fun Int.evenOrNull() = takeIf { it % 2 == 0 }
EqualsNullCall
To compare an object with null
prefer using ==
. This rule detects and reports instances in the code where the
equals()
method is used to compare a value with null
.
Active by default: Yes - Since v1.2.0
Noncompliant Code:
fun isNull(str: String) = str.equals(null)
Compliant Code:
fun isNull(str: String) = str == null
EqualsOnSignatureLine
Requires that the equals sign, when used for an expression style function, is on the same line as the rest of the function signature.
Active by default: No
Noncompliant Code:
fun stuff(): Int
= 5
fun <V> foo(): Int where V : Int
= 5
Compliant Code:
fun stuff() = 5
fun stuff() =
foo.bar()
fun <V> foo(): Int where V : Int = 5
ExplicitCollectionElementAccessMethod
In Kotlin functions get
or set
can be replaced with the shorter operator — []
,
see Indexed access operator.
Prefer the usage of the indexed access operator []
for map or list element access or insert methods.
Active by default: No
Requires Type Resolution
Noncompliant Code:
val map = mutableMapOf<String, String>()
map.put("key", "value")
val value = map.get("key")
Compliant Code:
val map = mutableMapOf<String, String>()
map["key"] = "value"
val value = map["key"]
ExplicitItLambdaParameter
Lambda expressions are one of the core features of the language. They often include very small chunks of
code using only one parameter. In this cases Kotlin can supply the implicit it
parameter
to make code more concise. It fits most use cases, but when faced larger or nested chunks of code,
you might want to add an explicit name for the parameter. Naming it just it
is meaningless and only
makes your code misleading, especially when dealing with nested functions.
Active by default: Yes - Since v1.21.0
Noncompliant Code:
a?.let { it -> it.plus(1) }
foo.flatMapObservable { it -> Observable.fromIterable(it) }
listOfPairs.map(::second).forEach { it ->
it.execute()
}
collection.zipWithNext { it, next -> Pair(it, next) }
Compliant Code:
a?.let { it.plus(1) } // Much better to use implicit it
foo.flatMapObservable(Observable::fromIterable) // Here we can have a method reference
// For multiline blocks it is usually better come up with a clear and more meaningful name
listOfPairs.map(::second).forEach { apiRequest ->
apiRequest.execute()
}
// Lambdas with multiple parameter should be named clearly, using it for one of them can be confusing
collection.zipWithNext { prev, next ->
Pair(prev, next)
}
ExpressionBodySyntax
Functions which only contain a return
statement can be collapsed to an expression body. This shortens and
cleans up the code.
Active by default: No
Configuration options:
-
includeLineWrapping
(default:false
)include return statements with line wraps in it
Noncompliant Code:
fun stuff(): Int {
return 5
}
Compliant Code:
fun stuff() = 5
fun stuff() {
return
moreStuff()
.getStuff()
.stuffStuff()
}
ForbiddenAnnotation
This rule allows to set a list of forbidden annotations. This can be used to discourage the use of language annotations which do not require explicit import.
Active by default: No
Requires Type Resolution
Configuration options:
-
annotations
(default:['java.lang.SuppressWarnings', 'java.lang.Deprecated', 'java.lang.annotation.Documented', 'java.lang.annotation.Target', 'java.lang.annotation.Retention', 'java.lang.annotation.Repeatable', 'java.lang.annotation.Inherited']
)List of fully qualified annotation classes which are forbidden.
Noncompliant Code:
@SuppressWarnings("unused")
class SomeClass()
Compliant Code:
@Suppress("unused")
class SomeClass()
ForbiddenComment
This rule allows to set a list of comments which are forbidden in the codebase and should only be used during development. Offending code comments will then be reported.
The regular expressions in comments
list will have the following behaviors while matching the comments:
- Each comment will be handled individually.
- single line comments are always separate, consecutive lines are not merged.
- multi line comments are not split up, the regex will be applied to the whole comment.
- KDoc comments are not split up, the regex will be applied to the whole comment.
- The following comment delimiters (and indentation before them) are removed before applying the regex:
//
,//
,/*
,/*
,/**
,*
aligners,*/
,*/
- The regex is applied as a multiline regex,
see Anchors for more info.
To match the start and end of each line, use
^
and$
. To match the start and end of the whole comment, use\A
and\Z
. To turn off multiline, use(?-m)
at the start of your regex. - The regex is applied with dotall semantics, meaning
.
will match any character including newlines, this is to ensure that freeform line-wrapping doesn't mess with simple regexes. To turn off this behavior, use(?-s)
at the start of your regex, or use[^\r\n]*
instead of.*
. - The regex will be searched using "contains" semantics not "matches",
so partial comment matches will flag forbidden comments.
In practice this means there's no need to start and end the regex with
.*
.
The rule can be configured to add extra comments to the list of forbidden comments, here are some examples:
ForbiddenComment:
comments:
# Repeat the default configuration if it's still needed.
- reason: 'Forbidden FIXME todo marker in comment, please fix the problem.'
value: 'FIXME:'
- reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.'
value: 'STOPSHIP:'
- reason: 'Forbidden TODO todo marker in comment, please do the changes.'
value: 'TODO:'
# Add additional patterns to the list.
- reason: 'Authors are not recorded in KDoc.'
value: '@author'
- reason: 'REVIEW markers are not allowed in production code, only use before PR is merged.'
value: '^\s*(?i)REVIEW\b'
# Non-compliant: // REVIEW this code before merging.
# Compliant: // Preview will show up here.
- reason: 'Use @androidx.annotation.VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) instead.'
value: '^private$'
# Non-compliant: /*private*/fun f() { }
- reason: 'KDoc tag should have a value.'
value: '^\s*@(?!suppress|hide)\w+\s*$'
# Non-compliant: /** ... @see */
# Compliant: /** ... @throws IOException when there's a network problem */
- reason: 'include an issue link at the beginning preceded by a space'
value: 'BUG:(?! https://github\.com/company/repo/issues/\d+).*'
By default the commonly used todo markers are forbidden: TODO:
, FIXME:
and STOPSHIP:
.
Active by default: Yes - Since v1.0.0
Configuration options:
-
(default:values
[]
)Deprecated: Use
comments
instead, make sure you escape your text for Regular Expressions.forbidden comment strings
-
comments
(default:['FIXME:', 'STOPSHIP:', 'TODO:']
)forbidden comment string patterns
-
allowedPatterns
(default:''
)ignores comments which match the specified regular expression. For example
Ticket|Task
. -
(default:customMessage
''
)Deprecated: Use
comments
and providereason
against eachvalue
.error message which overrides the default one
Noncompliant Code:
val a = "" // TODO: remove please
/**
* FIXME: this is a hack
*/
fun foo() { }
/* STOPSHIP: */
ForbiddenImport
Reports all imports that are forbidden.
This rule allows to set a list of forbidden [imports]. This can be used to discourage the use of unstable, experimental or deprecated APIs.
Active by default: No
Configuration options:
-
imports
(default:[]
)imports which should not be used
-
forbiddenPatterns
(default:''
)reports imports which match the specified regular expression. For example
net.*R
.
Noncompliant Code:
import kotlin.jvm.JvmField
import kotlin.SinceKotlin
ForbiddenMethodCall
Reports all method or constructor invocations that are forbidden.
This rule allows to set a list of forbidden [methods] or constructors. This can be used to discourage the use of unstable, experimental or deprecated methods, especially for methods imported from external libraries.
Active by default: No
Requires Type Resolution
Configuration options:
-
methods
(default:['kotlin.io.print', 'kotlin.io.println', 'java.math.BigDecimal.<init>(kotlin.Double)']
)List of fully qualified method signatures which are forbidden. Methods can be defined without full signature (i.e.
java.time.LocalDate.now
) which will report calls of all methods with this name or with full signature (i.e.java.time.LocalDate(java.time.Clock)
) which would report only call with this concrete signature. If you want to forbid an extension function likefun String.hello(a: Int)
you should add the receiver parameter as the first parameter like this:hello(kotlin.String, kotlin.Int)
. To forbid constructor calls you need to define them with<init>
, for examplejava.util.Date.<init>
. To forbid calls involving type parameters, omit them, for examplefun hello(args: Array<Any>)
is referred to as simplyhello(kotlin.Array)
(also the signature for vararg parameters). To forbid methods from the companion object reference the Companion class, for example asTestClass.Companion.hello()
(even if it is marked@JvmStatic
).
Noncompliant Code:
import java.lang.System
fun main() {
System.gc()
System::gc
}
ForbiddenSuppress
Report suppressions of all forbidden rules.
This rule allows to set a list of [rules] whose suppression is forbidden.
This can be used to discourage the abuse of the Suppress
and SuppressWarnings
annotations.
This rule is not capable of reporting suppression of itself, as that's a language feature with precedence.
Active by default: No
Configuration options:
-
rules
(default:[]
)Rules whose suppression is forbidden.
Noncompliant Code:
package foo
// When the rule "MaximumLineLength" is forbidden
@Suppress("MaximumLineLength", "UNCHECKED_CAST")
class Bar
Compliant Code:
package foo
// When the rule "MaximumLineLength" is forbidden
@Suppress("UNCHECKED_CAST")
class Bar
ForbiddenVoid
This rule detects usages of Void
and reports them as forbidden.
The Kotlin type Unit
should be used instead. This type corresponds to the Void
class in Java
and has only one value - the Unit
object.
Active by default: Yes - Since v1.21.0
Requires Type Resolution
Configuration options:
-
ignoreOverridden
(default:false
)ignores void types in signatures of overridden functions
-
ignoreUsageInGenerics
(default:false
)ignore void types as generic arguments
Noncompliant Code:
runnable: () -> Void
var aVoid: Void? = null
Compliant Code:
runnable: () -> Unit
Void::class
FunctionOnlyReturningConstant
A function that only returns a single constant can be misleading. Instead, prefer declaring the constant
as a const val
.
Active by default: Yes - Since v1.2.0
Configuration options:
-
ignoreOverridableFunction
(default:true
)if overriden functions should be ignored
-
ignoreActualFunction
(default:true
)if actual functions should be ignored
-
excludedFunctions
(default:[]
)excluded functions
Noncompliant Code:
fun functionReturningConstantString() = "1"
Compliant Code:
const val constantString = "1"
LoopWithTooManyJumpStatements
Loops which contain multiple break
or continue
statements are hard to read and understand.
To increase readability they should be refactored into simpler loops.
Active by default: Yes - Since v1.2.0
Configuration options:
-
maxJumpCount
(default:1
)maximum allowed jumps in a loop
Noncompliant Code:
val strs = listOf("foo, bar")
for (str in strs) {
if (str == "bar") {
break
} else {
continue
}
}
MagicNumber
This rule detects and reports usages of magic numbers in the code. Prefer defining constants with clear names describing what the magic number means.
Active by default: Yes - Since v1.0.0
Configuration options:
-
ignoreNumbers
(default:['-1', '0', '1', '2']
)numbers which do not count as magic numbers
-
ignoreHashCodeFunction
(default:true
)whether magic numbers in hashCode functions should be ignored
-
ignorePropertyDeclaration
(default:false
)whether magic numbers in property declarations should be ignored
-
ignoreLocalVariableDeclaration
(default:false
)whether magic numbers in local variable declarations should be ignored
-
ignoreConstantDeclaration
(default:true
)whether magic numbers in constant declarations should be ignored
-
ignoreCompanionObjectPropertyDeclaration
(default:true
)whether magic numbers in companion object declarations should be ignored
-
ignoreAnnotation
(default:false
)whether magic numbers in annotations should be ignored
-
ignoreNamedArgument
(default:true
)whether magic numbers in named arguments should be ignored
-
ignoreEnums
(default:false
)whether magic numbers in enums should be ignored
-
ignoreRanges
(default:false
)whether magic numbers in ranges should be ignored
-
ignoreExtensionFunctions
(default:true
)whether magic numbers as subject of an extension function should be ignored
Noncompliant Code:
class User {
fun checkName(name: String) {
if (name.length > 42) {
throw IllegalArgumentException("username is too long")
}
// ...
}
}
Compliant Code:
class User {
fun checkName(name: String) {
if (name.length > MAX_USERNAME_SIZE) {
throw IllegalArgumentException("username is too long")
}
// ...
}
companion object {
private const val MAX_USERNAME_SIZE = 42
}
}
MandatoryBracesLoops
This rule detects multi-line for
and while
loops which do not have braces.
Adding braces would improve readability and avoid possible errors.
Active by default: No
Noncompliant Code:
for (i in 0..10)
println(i)
while (true)
println("Hello, world")
do
println("Hello, world")
while (true)
Compliant Code:
for (i in 0..10) {
println(i)
}
for (i in 0..10) println(i)
while (true) {
println("Hello, world")
}
while (true) println("Hello, world")
do {
println("Hello, world")
} while (true)
do println("Hello, world") while (true)
MaxChainedCallsOnSameLine
Limits the number of chained calls which can be placed on a single line.
Active by default: No
Requires Type Resolution
Configuration options:
-
maxChainedCalls
(default:5
)maximum chained calls allowed on a single line
Noncompliant Code:
a().b().c().d().e().f()
Compliant Code:
a().b().c()
.d().e().f()
MaxLineLength
This rule reports lines of code which exceed a defined maximum line length.
Long lines might be hard to read on smaller screens or printouts. Additionally, having a maximum line length in the codebase will help make the code more uniform.
Active by default: Yes - Since v1.0.0
Configuration options:
-
maxLineLength
(default:120
)maximum line length
-
excludePackageStatements
(default:true
)if package statements should be ignored
-
excludeImportStatements
(default:true
)if import statements should be ignored
-
excludeCommentStatements
(default:false
)if comment statements should be ignored
-
excludeRawStrings
(default:true
)if raw strings should be ignored
MayBeConstant
This rule identifies and reports properties (val
) that may be const val
instead.
Using const val
can lead to better performance of the resulting bytecode as well as better interoperability with
Java.
Active by default: Yes - Since v1.2.0
Noncompliant Code:
val myConstant = "abc"
Compliant Code:
const val MY_CONSTANT = "abc"
ModifierOrder
This rule reports cases in the code where modifiers are not in the correct order. The default modifier order is taken from: Modifiers order
Active by default: Yes - Since v1.0.0
Noncompliant Code:
lateinit internal val str: String
Compliant Code:
internal lateinit val str: String
MultilineLambdaItParameter
Lambda expressions are very useful in a lot of cases, and they often include very small chunks of
code using only one parameter. In this cases Kotlin can supply the implicit it
parameter
to make code more concise. However, when you are dealing with lambdas that contain multiple statements,
you might end up with code that is hard to read if you don't specify a readable, descriptive parameter name
explicitly.
Active by default: No
Requires Type Resolution
Noncompliant Code:
val digits = 1234.let {
println(it)
listOf(it)
}
val digits = 1234.let { it ->
println(it)
listOf(it)
}
val flat = listOf(listOf(1), listOf(2)).mapIndexed { index, it ->
println(it)
it + index
}
Compliant Code:
val digits = 1234.let { explicitParameterName ->
println(explicitParameterName)
listOf(explicitParameterName)
}
val lambda = { item: Int, that: String ->
println(item)
item.toString() + that
}
val digits = 1234.let { listOf(it) }
val digits = 1234.let {
listOf(it)
}
val digits = 1234.let { it -> listOf(it) }
val digits = 1234.let { it ->
listOf(it)
}
val digits = 1234.let { explicit -> listOf(explicit) }
val digits = 1234.let { explicit ->
listOf(explicit)
}
MultilineRawStringIndentation
This rule ensures that raw strings have a consistent indentation.
The content of a multi line raw string should have the same indentation as the enclosing expression plus the
configured indentSize. The closing triple-quotes ("""
) must have the same indentation as the enclosing expression.
Active by default: No
Configuration options:
-
indentSize
(default:4
)indentation size
-
trimmingMethods
(default:['trimIndent', 'trimMargin']
)allows to provide a list of multiline string trimming methods
Noncompliant Code:
val a = """
Hello World!
How are you?
""".trimMargin()
val a = """
Hello World!
How are you?
""".trimMargin()
Compliant Code:
val a = """
Hello World!
How are you?
""".trimMargin()
val a = """
Hello World!
How are you?
""".trimMargin()
NestedClassesVisibility
Nested classes inherit their visibility from the parent class and are often used to implement functionality local to the class it is nested in. These nested classes can't have a higher visibility than their parent. However, the visibility can be further restricted by using a private modifier for instance. In internal classes the explicit public modifier for nested classes is misleading and thus unnecessary, because the nested class still has an internal visibility.
Active by default: Yes - Since v1.16.0
Noncompliant Code:
internal class Outer {
// explicit public modifier still results in an internal nested class
public class Nested
}
Compliant Code:
internal class Outer {
class Nested1
internal class Nested2
}
NewLineAtEndOfFile
This rule reports files which do not end with a line separator.
Active by default: Yes - Since v1.0.0
NoTabs
This rule reports if tabs are used in Kotlin files. According to Google's Kotlin style guide the only whitespace chars that are allowed in a source file are the line terminator sequence and the ASCII horizontal space character (0x20). Strings containing tabs are allowed.
Active by default: No
NullableBooleanCheck
Detects nullable boolean checks which use an elvis expression ?:
rather than equals ==
.
Per the Kotlin coding conventions
converting a nullable boolean property to non-null should be done via != false
or == true
rather than ?: true
or ?: false
(respectively).
Active by default: No
Requires Type Resolution
Noncompliant Code:
value ?: true
value ?: false
Compliant Code:
value != false
value == true
ObjectLiteralToLambda
An anonymous object that does nothing other than the implementation of a single method can be used as a lambda.
See SAM conversions, Functional (SAM) interfaces
Active by default: Yes - Since v1.21.0
Requires Type Resolution
Noncompliant Code:
object : Foo {
override fun bar() {
}
}
Compliant Code:
Foo {
}
OptionalAbstractKeyword
This rule reports abstract
modifiers which are unnecessary and can be removed.
Active by default: Yes - Since v1.0.0
Noncompliant Code:
abstract interface Foo { // abstract keyword not needed
abstract fun x() // abstract keyword not needed
abstract var y: Int // abstract keyword not needed
}
Compliant Code:
interface Foo {
fun x()
var y: Int
}
OptionalUnit
It is not necessary to define a return type of Unit
on functions or to specify a lone Unit statement.
This rule detects and reports instances where the Unit
return type is specified on functions and the occurrences
of a lone Unit statement.
Active by default: No
Requires Type Resolution
Noncompliant Code:
fun foo(): Unit {
return Unit
}
fun foo() = Unit
fun doesNothing() {
Unit
}
Compliant Code:
fun foo() { }
// overridden no-op functions are allowed
override fun foo() = Unit
PreferToOverPairSyntax
This rule detects the usage of the Pair constructor to create pairs of values.
Using <value1>
to <value2>
is preferred.
Active by default: No
Requires Type Resolution
Noncompliant Code:
val pair = Pair(1, 2)
Compliant Code:
val pair = 1 to 2
ProtectedMemberInFinalClass
Kotlin classes are final
by default. Thus classes which are not marked as open
should not contain any protected
members. Consider using private
or internal
modifiers instead.
Active by default: Yes - Since v1.2.0
Noncompliant Code:
class ProtectedMemberInFinalClass {
protected var i = 0
}
Compliant Code:
class ProtectedMemberInFinalClass {
private var i = 0
}
RangeUntilInsteadOfRangeTo
Reports calls to ..
operator instead of calls to ..<
.
..<
is applicable in cases where the upper range value is described as
open ended range(or in case of integral types some value subtracted by 1).
..<
helps to prevent off-by-one errors.
Active by default: No
Noncompliant Code:
for (i in 0..10 - 1) {}
val range = 0..10 - 1
Compliant Code:
for (i in 0..<10) {}
val range = 0..<10
RedundantConstructorKeyword
This rule checks for redundant constructor keywords.
Active by default: No
Noncompliant Code:
data class Foo constructor(val foo: Int)
Compliant Code:
data class Foo(val foo: Int)
data class Bar private constructor(val bar: String) {
constructor(bar: Int): this("$foo")
}
RedundantExplicitType
Local properties do not need their type to be explicitly provided when the inferred type matches the explicit type.
Active by default: No
Requires Type Resolution
Noncompliant Code:
fun function() {
val x: String = "string"
}
Compliant Code:
fun function() {
val x = "string"
}
RedundantHigherOrderMapUsage
Redundant maps add complexity to the code and accomplish nothing. They should be removed or replaced with the proper operator.
Active by default: Yes - Since v1.21.0
Requires Type Resolution