Exhaustive public API
EXHAUSTIVE_PUBLIC_API reports public enums and sealed hierarchies, which users can match
exhaustively with a when expression that has no else branch.
| Diagnostic | EXHAUSTIVE_PUBLIC_API |
| Default severity | Error |
| Gradle property | exhaustivePublicApi |
| Exemption | @IntentionallyExhaustive |
What it reports
The check reports every public or protected enum class, sealed class, and sealed interface
declaration.
/** Whether the service can accept requests. */public enum class Status {/** The service is ready to accept requests. */ACTIVE,/** The service rejects requests until it is reactivated. */INACTIVE,}
Rationale
A user can write a when over an enum or a sealed hierarchy without an else branch and have
the compiler check exhaustiveness for them. That is convenient, but it also means the user code
depends on today's exact set of entries or subtypes. Adding a new enum entry or a new subtype
later makes every such when at every call site stop compiling: a source-incompatible change the
library author did not think of as breaking. See the
Kotlin API guidelines on preventing unwanted extensions.
Don't
/** Severity assigned to a log record. */public enum class LogLevel {/** Fine-grained information used to diagnose behavior. */DEBUG,/** Routine progress and state changes. */INFO,/** A failure that prevented an operation from completing. */ERROR,}/** A change in the service lifecycle. */public sealed interface Event {/** Emitted after the service becomes ready. */public class Started : Event/** Emitted after the service finishes shutting down. */public class Stopped : Event}
Do
/** A logging level that can grow without breaking exhaustive matches. */public class LogLevel {/** Named logging levels. */public companion object {/** Fine-grained information used to diagnose behavior. */@JvmFieldpublic val DEBUG: LogLevel = LogLevel()/** Routine progress and state changes. */@JvmFieldpublic val INFO: LogLevel = LogLevel()/** A failure that prevented an operation from completing. */@JvmFieldpublic val ERROR: LogLevel = LogLevel()}}/** A lifecycle event implemented under an opt-in contract. */@SubclassOptInRequired(InternalMyLibrarySubclassApi::class)public interface Event {/** Emitted after the service becomes ready. */public class Started : Event/** Emitted after the service finishes shutting down. */public class Stopped : Event}
Notes
@PublishedApi internaltypes are not reported because library users cannot name and match them in source.
A non-final member of a sealed hierarchy (an abstract or sealed subclass)
is itself unrestricted, subclassable API and is reported separately by
Open API without subclass opt-in, on top of this check.
Exemption
Apply @IntentionallyExhaustive to the enum or sealed class or interface when a fixed set of
entries or subtypes is a deliberate, stable part of the contract:
/** A fixed set of logging levels. */@IntentionallyExhaustive(reason = ExemptionReason.API_DESIGN)public enum class LogLevel {/** Fine-grained information used to diagnose behavior. */DEBUG,/** Routine progress and state changes. */INFO,/** A failure that prevented an operation from completing. */ERROR,}
Configuration
apiWatchdog {exhaustivePublicApi = WatchdogSeverity.WARNING}
With direct compiler invocation:
-P plugin:org.jetbrains.kotlin.library.api.watchdog:diagnosticSeverity=EXHAUSTIVE_PUBLIC_API:warning