website/versioned_docs/version-2.0.0-alpha.2/rules/exceptions.md
Rule Set ID: exceptions
Rules in this rule set report issues related to how code throws and handles Exceptions.
This rule reports usage of error method with [Throwable] parameter, i.e, error(throwable).
The above will result in throw IllegalStateException(throwable.toString()) which doesn't
provide any info about throwable.message or stackTrace.
Instead, use throw IllegalStateException(throwable) to rethrow the throwable as IllegalStateException.
Active by default: No
Requires Type Resolution
fun foo() {
try {
// ... some code
} catch(e: IOException) {
// some addition handling
error(e)
}
}
fun foo() {
try {
// ... some code
} catch(e: IOException) {
// some addition handling
throw e // or throw IllegalStateException(<some custom error msg>, e)
}
}
This rule reports functions which should never throw an exception. If a function exists that does throw
an exception it will be reported. By default, this rule checks toString, hashCode, equals and
finalize. This rule is configurable via the methodNames configuration to change the list of functions which
should not throw any exceptions.
Active by default: Yes - Since v1.16.0
methodNames (default: ['equals', 'finalize', 'hashCode', 'toString'])
methods which should not throw exceptions
class Foo {
override fun toString(): String {
throw IllegalStateException() // exception should not be thrown here
}
}
This rule reports catch blocks which check for the type of exception via is checks or casts.
Instead of catching generic exception types and then checking for specific exception types the code should
use multiple catch blocks. These catch blocks should then catch the specific exceptions.
Active by default: Yes - Since v1.21.0
Requires Type Resolution
fun foo() {
try {
// ... do some I/O
} catch(e: IOException) {
if (e is MyException || (e as MyException) != null) { }
}
}
fun foo() {
try {
// ... do some I/O
} catch(e: MyException) {
} catch(e: IOException) {
}
}
This rule reports all exceptions of the type NotImplementedError that are thrown. It also reports all TODO(..)
functions.
These indicate that functionality is still under development and will not work properly. Both of these should only
serve as temporary declarations and should not be put into production environments.
Active by default: No
fun foo() {
throw NotImplementedError()
}
fun todo() {
TODO("")
}
This rule reports all objects including companion objects that extend any type of
Throwable. Throwable instances are not intended for reuse as they are stateful and contain
mutable information about a specific exception or error. Hence, global singleton Throwables
should be avoided.
See https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview See https://kotlinlang.org/docs/object-declarations.html#companion-objects
Active by default: No
Requires Type Resolution
object InvalidCredentialsException : Throwable()
object BanException : Exception()
object AuthException : RuntimeException()
class InvalidCredentialsException : Throwable()
class BanException : Exception()
class AuthException : RuntimeException()
This rule reports code that tries to print the stacktrace of an exception. Instead of simply printing a stacktrace a better logging solution should be used.
Active by default: Yes - Since v1.16.0
fun foo() {
Thread.dumpStack()
}
fun bar() {
try {
// ...
} catch (e: IOException) {
e.printStackTrace()
}
}
val LOGGER = Logger.getLogger()
fun bar() {
try {
// ...
} catch (e: IOException) {
LOGGER.info(e)
}
}
This rule reports all exceptions that are caught and then later re-thrown without modification. It ignores cases:
Active by default: Yes - Since v1.16.0
fun foo() {
try {
// ...
} catch (e: IOException) {
throw e
}
}
fun foo() {
try {
// ...
} catch (e: IOException) {
throw MyException(e)
}
try {
// ...
} catch (e: IOException) {
print(e)
throw e
}
try {
// ...
} catch (e: IOException) {
print(e.message)
throw e
}
try {
// ...
} catch (e: IOException) {
throw e
} catch (e: Exception) {
print(e.message)
}
}
Reports all return statements in finally blocks.
Using return statements in finally blocks can discard and hide exceptions that are thrown in the try block.
Furthermore, this rule reports values from finally blocks, if the corresponding try is used as an expression.
If your try block is inlined into the expression body and implicitly returns Unit, this rule will not raise a
finding because no value will be returned to the caller, and the contents of the finally block will still be run
as expected.
Active by default: Yes - Since v1.16.0
Requires Type Resolution
ignoreLabeled (default: false)
ignores labeled return statements
fun foo() {
try {
throw MyException()
} finally {
return // prevents MyException from being propagated
}
}
val a: String = try {
"s"
} catch (e: Exception) {
"e"
} finally {
// Implies assigning "f" to variable a, but the exception gets propagated first.
// Misleading and not immediately obvious, this gets flagged!
"f"
}
fun bar(thing: Thing): Unit = try {
thing.doSomethingReturningUnit()
} finally {
// Any exceptions will still be propagated, but the Unit-returning cleanup function will be called first
thing.cleanUp()
}
Exceptions should not be swallowed. This rule reports all instances where exceptions are caught and not correctly
passed (e.g. as a cause) into a newly thrown exception.
The exception types configured in ignoredExceptionTypes indicate nonexceptional outcomes.
These by default configured exception types are part of Java.
Therefore, Kotlin developers have to handle them by using the catch clause.
For that reason, this rule ignores that these configured exception types are caught.
Active by default: Yes - Since v1.16.0
ignoredExceptionTypes (default: ['InterruptedException', 'MalformedURLException', 'NumberFormatException', 'ParseException'])
exception types which should be ignored (both in the catch clause and body)
allowedExceptionNameRegex (default: '_|(ignore|expected).*')
ignores too generic exception types which match this regex
fun foo() {
try {
// ...
} catch(e: IOException) {
throw MyException(e.message) // e is swallowed
}
try {
// ...
} catch(e: IOException) {
throw MyException() // e is swallowed
}
try {
// ...
} catch(e: IOException) {
bar() // exception is unused
}
}
fun foo() {
try {
// ...
} catch(e: IOException) {
throw MyException(e)
}
try {
// ...
} catch(e: IOException) {
println(e) // logging is ok here
}
}
This rule reports all cases where exceptions are thrown from a finally block. Throwing exceptions from a finally
block should be avoided as it can lead to confusion and discarded exceptions.
Active by default: Yes - Since v1.16.0
fun foo() {
try {
// ...
} finally {
throw IOException()
}
}
This rule reports all exceptions that are thrown in a main method.
An exception should only be thrown if it can be handled by a "higher" function.
Active by default: No
fun main(args: Array<String>) {
// ...
throw IOException() // exception should not be thrown here
}
This rule reports all exceptions which are thrown without arguments or further description. Exceptions should always call one of the constructor overloads to provide a message or a cause. Exceptions should be meaningful and contain as much detail about the error case as possible. This will help to track down an underlying issue in a better way.
Active by default: Yes - Since v1.16.0
exceptions (default: ['ArrayIndexOutOfBoundsException', 'Exception', 'IllegalArgumentException', 'IllegalMonitorStateException', 'IllegalStateException', 'IndexOutOfBoundsException', 'NullPointerException', 'RuntimeException', 'Throwable'])
exceptions which should not be thrown without message or cause
fun foo(bar: Int) {
if (bar < 1) {
throw IllegalArgumentException()
}
// ...
}
fun foo(bar: Int) {
if (bar < 1) {
throw IllegalArgumentException("bar must be greater than zero")
}
// ...
}
Exceptions should not be wrapped inside the same exception type and then rethrown. Prefer wrapping exceptions in more meaningful exception types.
Active by default: Yes - Since v1.16.0
fun foo() {
try {
// ...
} catch (e: IllegalStateException) {
throw IllegalStateException(e) // rethrows the same exception
}
}
fun foo() {
try {
// ...
} catch (e: IllegalStateException) {
throw MyException(e)
}
}
This rule reports catch blocks for exceptions that have a type that is too generic.
It should be preferred to catch specific exceptions to the case that is currently handled. If the scope of the caught
exception is too broad it can lead to unintended exceptions being caught.
Active by default: Yes - Since v1.0.0
exceptionNames (default: ['ArrayIndexOutOfBoundsException', 'Error', 'Exception', 'IllegalMonitorStateException', 'IndexOutOfBoundsException', 'NullPointerException', 'RuntimeException', 'Throwable'])
exceptions which are too generic and should not be caught
allowedExceptionNameRegex (default: '_|(ignore|expected).*')
ignores too generic exception types which match this regex
fun foo() {
try {
// ... do some I/O
} catch(e: Exception) { } // too generic exception caught here
}
fun foo() {
try {
// ... do some I/O
} catch(e: IOException) { }
}
This rule reports thrown exceptions that have a type that is too generic. It should be preferred to throw specific exceptions to the case that has currently occurred.
Active by default: Yes - Since v1.0.0
exceptionNames (default: ['Error', 'Exception', 'RuntimeException', 'Throwable'])
exceptions which are too generic and should not be thrown
fun foo(bar: Int) {
if (bar < 1) {
throw Exception() // too generic exception thrown here
}
// ...
}
fun foo(bar: Int) {
if (bar < 1) {
throw IllegalArgumentException("bar must be greater than zero")
}
// ...
}