Inconsistent parameter order in overloads
INCONSISTENT_PARAMETER_ORDER_IN_OVERLOADS reports two overloads of the same public callable
whose shared parameter names appear in a different relative order.
| Diagnostic | INCONSISTENT_PARAMETER_ORDER_IN_OVERLOADS |
| Default severity | Error |
| Gradle property | inconsistentParameterOrderInOverloads |
| Exemption | @IntentionallyInconsistentParameterOrder |
What it reports
For every pair of public overloads that share at least two parameter names, the check compares the relative order of those shared names. No overload is treated as the canonical order: both members of a disagreeing pair are reported, and reordering either one clears both.
@file:JvmName("Movement")/** Moves to ([x], [y]). */public fun move(x: Int, y: Int) { }/** Moves to ([x], [y]) with [scale]. */public fun move(y: Int, x: Int, scale: Double) { }
Rationale
Callers transfer their intuition about one overload's parameter order to the next: once x, y is
established, a sibling overload that expects y, x invites a silently swapped call, especially
when the swapped parameters share a type and the mistake still compiles. See the Kotlin library
authors' guide on
preserving parameter order, naming, and usage.
Don't
@file:JvmName("Drawing")/** Draws at ([x], [y]). */public fun draw(x: Int, y: Int) { }/** Draws at ([x], [y]) with [scale]. */public fun draw(y: Int, x: Int, scale: Double) { }
Do
@file:JvmName("Drawing")/** Draws at ([x], [y]). */public fun draw(x: Int, y: Int) { }/** Draws at ([x], [y]) with [scale]. */public fun draw(x: Int, y: Int, scale: Double) { }
Don't
/** Rectangle defined by its horizontal and vertical extents. */public class Rect(width: Int, height: Int) {/** Creates a rectangle and applies [scale] to both extents. */public constructor(height: Int, width: Int, scale: Double) : this(width, height)}
Do
/** Rectangle defined by its horizontal and vertical extents. */public class Rect(width: Int, height: Int) {/** Creates a rectangle and applies [scale] to both extents. */public constructor(width: Int,height: Int,scale: Double,) : this(width, height)}
Don't
@file:JvmName("Grids")// Supporting member overload/** Mutable grid addressed by a linear cell index. */public class Grid {/** Fills cells from [startIndex] through [endIndex]. */public fun fill(startIndex: Int, endIndex: Int) { }}/** Fills a range with [color]. */public fun Grid.fill(endIndex: Int,startIndex: Int,color: Long,) { }
Do
@file:JvmName("Grids")// Supporting member overload/** Mutable grid addressed by a linear cell index. */public class Grid {/** Fills cells from [startIndex] through [endIndex]. */public fun fill(startIndex: Int, endIndex: Int) { }}/** Fills a range with [color]. */public fun Grid.fill(startIndex: Int,endIndex: Int,color: Long,) { }
Notes
@PublishedApi internaloverloads are neither reported nor used as comparison references because library users cannot call them in source.- Overloads that share fewer than two parameter names can't disagree on order and are never
reported, which is why single-argument conversion overloads with the same parameter name but
different types (
BigDecimal(value: Int)next toBigDecimal(value: String)) stay silent. - Only declarations users see side by side are compared: the members of one class body - inherited members included - the top-level functions of one package, or the constructors of one class among each other. A class member is never compared against a same-named top-level function, and declarations from dependencies are never compared.
- An extension is called like a member of the type it extends, so the members of its receiver class - inherited ones included - are its overloads too, wherever in the library the extension is declared. A receiver reached through a type alias, a nullable type, or a type parameter bound still leads back to the extended class. An unbounded type parameter is no class and has no members to compare against.
- For an inherited pair, only the subtype's own declaration is reported: the supertype can't see the subtype's overload, and it is the new declaration that strays from the established order. An extension next to the members of its receiver is reported the same way: only the extension, since the class can't see the extensions declared on it.
- Overrides are not reported - their order is fixed by the overridden declaration - but they still serve as an ordering reference for a new overload declared next to them.
Exemption
Apply @IntentionallyInconsistentParameterOrder to the function or constructor when the differing
order is a deliberate, stable part of the contract, for example an old overload kept for source
compatibility. The annotation also removes the declaration as an ordering reference: it is skipped
both as a reporter and as a comparison target, so one acknowledged legacy overload doesn't force
its order onto otherwise-consistent newer ones.
@file:JvmName("Drawing")/** Draws at ([x], [y]). */public fun draw(x: Int, y: Int) { }/** Draws at ([x], [y]) with [scale]. */@IntentionallyInconsistentParameterOrder(reason = ExemptionReason.FOR_BACKWARDS_COMPATIBILITY,)public fun draw(y: Int, x: Int, scale: Double) { }
Configuration
apiWatchdog {inconsistentParameterOrderInOverloads = WatchdogSeverity.WARNING}
With direct compiler invocation:
-P plugin:org.jetbrains.kotlin.library.api.watchdog:diagnosticSeverity=INCONSISTENT_PARAMETER_ORDER_IN_OVERLOADS:warning
See also
- Preserve parameter order, naming, and usage
- Required parameters after optional ones, a sibling check on parameter order within one declaration
- Exemptions and internal API