Is there a way to restrict extension methods in DSLs?
Say I have a class structure like this:
class Outer {
fun middle(op: Middle.() -> Unit):
The official way to restrict scope is DslMarker.
It cannot help in some cases (when you need to annotate java sources, for example) - and here @Deprecated
is used. But try DslMarker
first.
@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class Scope
@Scope
class Outer {
fun middle(op: Middle.() -> Unit): Middle { /**/ }
}
@Scope
class Middle {
fun inner(op: Inner.() -> Unit): Inner {/**/ }
}
class Inner
Thus the last middle call is not compilable anymore.
You can use a workaround with deprecated
:
class Outer {
fun middle(op: Middle.() -> Unit): Middle {...}
@Deprecated("can not be used inside a Outer block", level = DeprecationLevel.ERROR)
fun outer(op: Outer.() -> Unit): Outer = TODO()
}
class Middle {
fun inner(op: Inner.() -> Unit): Inner {...}
@Deprecated("can not be used inside a Middle block", level = DeprecationLevel.ERROR)
fun middle(op: Middle.() -> Unit): Middle = TODO()
}
class Inner {
@Deprecated("can not be used inside a Inner block", level = DeprecationLevel.ERROR)
fun inner(op: Inner.() -> Unit): Inner = TODO()
}
Now the compiler will give you an error, and IDE will not suggest a wrong function in the completion:
outer {
middle {
inner {
middle { } // error
}
}
}
But you really should not do it for big DSLs. It is better to wait for https://youtrack.jetbrains.com/issue/KT-11551 as @KirillRakhman suggested.
Edit: After I fixed my example, it became much smaller. With one dummy function for a class it is not that much of boilerplate after all.