The @DslMarker
annotation can only be applied to annotation class
by design. However, once our own annotation is defined (e.g., annotation class MyDslMarker
), it can be applied to any target without restrictions.
In traditional examples—such as those found in the Kotlin documentation, their HTML library, other sources, Stack Overflow, and practically everywhere—the primary target for @MyDslMarker
annotations is a class (or possibly an object).
I could only find one or two mentions of applying it directly to a function, and none for properties or other targets.
A good example with fun
, also explaining how color styles are derived
@DslMarker
was specifically designed to restrict implicit access to outer context receivers from within inner lambdas. It is typically applied indirectly to a class that represents a context.
If the context class belongs to a third-party library—where you are unable to apply an annotation directly to it—you would handle it as follows (simplified):
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) // Added a target "TYPE"
@DslMarker
annotation class MyDslMarker
// Can't apply the annotation here because it's outside our codebase
class ThirdPartyClassContext {
fun doPrint() = println("Third-party")
}
@MyDslMarker
class OuterContext {
// Applying to the receiver type
fun third(init: (@MyDslMarker ThirdPartyClassContext).() -> Unit) {
ThirdPartyClassContext().init()
//...
}
}
@MyDslMarker
class OuterThing
fun outerThing(init: OuterContext.() -> Unit): OuterThing {
val context = OuterContext()
context.init()
return OuterThing()
// in the real code, of course, it would be more like:
// return OuterThing(context),
// but that is beside the point
}
fun main() {
outerThing {
third {
doPrint()
// As desired: 'fun third(init: (ThirdPartyClassContext).() -> Unit): Unit'
// cannot be called implicitly in this context due to the receiver restriction
third { }
}
}
}
This is a common use case and is often covered—for example, as described here.
However, these are the only two usage scenarios for @MyDslMarker
that are commonly discussed, with no reliable sources addressing its application to functions, properties, or other targets.
Since it is neither prohibited nor specifically mentioned in the documentation, it is possible that using such annotations on functions, properties, or even other targets could have valid use cases, similar to the scenario of annotating a Type of ThirdPartyClass
.
When I apply @MyDslMarker
to a method in a Context
class and then use that method, it appears in purple (once again, more details about the colors here). This suggests that:
fun third(init: (@MyDslMarker ThirdPartyClassContext).() -> Unit) {
ThirdPartyClassContext().init()
}
and
@MyDslMarker
fun third(init: (@MyDslMarker ThirdPartyClassContext).() -> Unit) {
ThirdPartyClassContext().init()
}
are somehow different.
// At a minimum, these targets are available:
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.TYPE,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY,
AnnotationTarget.PROPERTY_GETTER
)
@DslMarker
annotation class MyDslMarker
Unfortunately, the official documentation and related examples are, at best, sparse and uninformative.
Thank you for any insights! I believe answers to these questions will be helpful not only to me but to many others who are searching for relevant information—especially since the JetBrains team itself has not provided much clarity.
Does anything actually change in the example above, apart from the color?
The coloring is just part of IntelliJ's syntax highlighting. Its purpose is just to highlight which calls are DSL method calls. It also allows you to differentiate between method calls from different DSLs, by allowing you to customise what color the DSL marker should use. You can do this by clicking on this gutter icon:
See also: Kotlin - DSL Color Style and the test data for this feature.
While we could theoretically apply it to any available target (e.g., functions, properties, property getters, etc.), would this be practical, and if so, in what way?
IntelliJ already makes use of this to do syntax highlighting, as I have just described. This IntelliJ feature is not just limited to functions, but also properties and object declarations. See the other files in this directory to see some examples.
As far as the language is concerned however, only DSL markers that are applied on types have an effect (at least for now). You can find how the compiler handles this in DslMarkerUtils.kt. This file is the only file where the compiler looks for @DslMarker
annotations. extractDslMarkerFqNames
extracts DSL markers from a KotlinType
only:
fun extractDslMarkerFqNames(kotlinType: KotlinType): Set<FqName> {
val result = mutableSetOf<FqName>()
result.addAll(kotlinType.annotations.extractDslMarkerFqNames())
kotlinType.getAbbreviation()?.constructor?.declarationDescriptor?.run {
result.addAll(annotations.extractDslMarkerFqNames())
(this as? TypeAliasDescriptor)?.run {
result.addAll(extractDslMarkerFqNames(this.underlyingType))
}
}
kotlinType.constructor.declarationDescriptor?.getAllSuperClassifiers()?.asIterable()
?.flatMapTo(result) { it.annotations.extractDslMarkerFqNames() }
return result
}
You can see exactly how it finds @DslMarker
annotations like @MyDslMarker
:
@MyDslMarker ThirdPartyClassContext
.OuterContext
. It also looks for such annotations in the type's superclassesThis method is called by another extractDslMarkerFqNames
method that takes a ReceiverValue
, which is then used by DslScopeViolationCallChecker.kt.