DSL markers with no-op targets
DSL_MARKER_NOOP_TARGET reports an explicit @Target entry on a @DslMarker annotation that
names a target the marker has no effect on.
| Diagnostic | DSL_MARKER_NOOP_TARGET |
| Default severity | Error |
| Gradle property | dslMarkerNoopTarget |
| Exemption | @IntentionallyWrongDslMarkerTargetsForBackwardsCompatibility |
What it reports
The check only looks at @DslMarker-annotated annotation classes that declare an explicit @Target
with noop targets.
/** Marks DSL receivers. */@DslMarker@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)public annotation class MyDsl
Rationale
@DslMarker exists to make type-safe builder
receivers unambiguous by hiding outer receivers from an inner scope. That mechanism only looks at
the marker's placement on a class, a type, or a type alias.
It misleads callers into thinking annotating a function or a property also
scopes something, and it misleads the marker's author into thinking the surface is narrower than
it is. See the
DSL marker design note and the
Kotlin docs on scope control with @DslMarker.
Don't
// This is the shape that broke Ktor's @KtorDsl (KTOR-8901)./** Marks Ktor DSL receivers. */@DslMarker@Target(AnnotationTarget.CLASS,AnnotationTarget.TYPEALIAS,AnnotationTarget.TYPE,AnnotationTarget.FUNCTION,)public annotation class KtorDsl
Do
/** Marks Ktor DSL receivers. */@DslMarker@Target(AnnotationTarget.CLASS,AnnotationTarget.TYPE,AnnotationTarget.TYPEALIAS,)public annotation class KtorDsl
Notes
ANNOTATION_CLASSis also effective because it is a classifier declaration, likeCLASS.- Marker visibility is irrelevant: an
internalorprivatemarker is still applied across the library's - possibly public - DSL classes, so markers of any visibility are checked. - A plain annotation class without
@DslMarkeris outside the scope of this check, regardless of its@Target. - A marker without an explicit
@Targetis covered by the separate, relatedDSL_MARKER_WITHOUT_EXPLICIT_TARGETS, because the default target set has its own no-op entries plus forbidsTYPE/TYPEALIAS.
Exemption
For a marker that already shipped with a no-op target, narrowing @Target rejects user code
that applied the marker there - a breaking change. Acknowledge the legacy shape instead:
/** Marks Ktor DSL receivers with targets retained for compatibility. */@IntentionallyWrongDslMarkerTargetsForBackwardsCompatibility@DslMarker@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)public annotation class KtorDsl
Wrong marker targets are never good API design, so this annotation bakes its only accepted
reason - backwards compatibility - into its name: it takes no reason parameter, just an
optional description for extra context. New DSL markers should declare effective
targets instead of reaching for this exemption.
Configuration
apiWatchdog {dslMarkerNoopTarget = WatchdogSeverity.WARNING}
With direct compiler invocation:
-P plugin:org.jetbrains.kotlin.library.api.watchdog:diagnosticSeverity=DSL_MARKER_NOOP_TARGET:warning