[Assign plugin] Add a compiler plugin for overloading assign ('=') operator
This commit is contained in:
committed by
Dmitriy Novozhilov
parent
6052962f50
commit
09d6dfc8bf
@@ -0,0 +1,120 @@
|
||||
// FILE: JavaProperty.java
|
||||
@ValueContainer
|
||||
public interface JavaProperty<T> {
|
||||
void assign(T argument);
|
||||
T get();
|
||||
}
|
||||
|
||||
// FILE: JavaClassStringProperty.java
|
||||
@ValueContainer
|
||||
public class JavaClassStringProperty {
|
||||
private String v;
|
||||
|
||||
public JavaClassStringProperty(String v) {
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
public void assign(String v) {
|
||||
this.v = v;
|
||||
}
|
||||
public String get() {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
annotation class ValueContainer
|
||||
|
||||
data class JavaStringProperty(private var v: String): JavaProperty<String> {
|
||||
override fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
override fun get() = this.v
|
||||
}
|
||||
|
||||
@ValueContainer
|
||||
interface KotlinProperty<T> {
|
||||
fun assign(argument: T);
|
||||
fun get(): T;
|
||||
}
|
||||
|
||||
data class KotlinStringProperty(private var v: String): KotlinProperty<String> {
|
||||
override fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
override fun get() = this.v
|
||||
}
|
||||
|
||||
@ValueContainer
|
||||
data class KotlinClassStringProperty(private var v: String) {
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
fun get(): String {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with annotation on Java interface`(): String {
|
||||
data class Task(val input: JavaStringProperty)
|
||||
val task = Task(JavaStringProperty("Fail"))
|
||||
task.input = "OK"
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with annotation on Java class`(): String {
|
||||
data class Task(val input: JavaClassStringProperty)
|
||||
val task = Task(JavaClassStringProperty("Fail"))
|
||||
task.input = "OK"
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with annotation on Kotlin interface`(): String {
|
||||
data class Task(val input: KotlinStringProperty)
|
||||
val task = Task(KotlinStringProperty("Fail"))
|
||||
task.input = "OK"
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with annotation on Kotlin class`(): String {
|
||||
data class Task(val input: KotlinClassStringProperty)
|
||||
val task = Task(KotlinClassStringProperty("Fail"))
|
||||
task.input = "OK"
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var result = `should work with annotation on Java interface`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with annotation on Java class`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with annotation on Kotlin interface`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with annotation on Kotlin class`()
|
||||
if (result != "OK") return result
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
annotation class ValueContainer
|
||||
|
||||
@ValueContainer
|
||||
class StringProperty(var v: String) {
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
fun assign(v: StringProperty) {
|
||||
this.v = v.get()
|
||||
}
|
||||
fun get(): String {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
data class Task(val input: StringProperty)
|
||||
|
||||
var result = "Fail"
|
||||
operator fun StringProperty.plusAssign(v: String) {
|
||||
result = v
|
||||
}
|
||||
operator fun StringProperty.plusAssign(v: StringProperty) {
|
||||
result = v.get()
|
||||
}
|
||||
operator fun StringProperty.set(i: Int, v: String) {
|
||||
result = v
|
||||
}
|
||||
operator fun StringProperty.set(i: Int, v: StringProperty) {
|
||||
result = v.get()
|
||||
}
|
||||
operator fun StringProperty.set(i: Int, j: Int, v: String) {
|
||||
result = v
|
||||
}
|
||||
operator fun StringProperty.set(i: Int, j: Int, v: StringProperty) {
|
||||
result = v.get()
|
||||
}
|
||||
operator fun StringProperty.set(i: Int, j: Int, k: Int, v: String) {
|
||||
result = v
|
||||
}
|
||||
operator fun StringProperty.set(i: Int, j: Int, k: Int, v: StringProperty) {
|
||||
result = v.get()
|
||||
}
|
||||
operator fun StringProperty.compareTo(v: String): Int {
|
||||
result = v
|
||||
return 0
|
||||
}
|
||||
operator fun StringProperty.compareTo(v: StringProperty): Int {
|
||||
result = v.get()
|
||||
return 0
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val task = Task(StringProperty("Fail"))
|
||||
|
||||
// Double check that assign is correctly setup
|
||||
task.input = "OK"
|
||||
if (task.input.get() != "OK") return task.input.get()
|
||||
|
||||
task?.input = "OK"
|
||||
if (task.input.get() != "OK") return task.input.get()
|
||||
|
||||
result = "Fail"
|
||||
task.input += "OK"
|
||||
if (result != "OK") return result
|
||||
result = "Fail"
|
||||
task.input += StringProperty("OK")
|
||||
if (result != "OK") return result
|
||||
|
||||
result = "Fail"
|
||||
task.input >= "OK"
|
||||
if (result != "OK") return result
|
||||
result = "Fail"
|
||||
task.input >= StringProperty("OK")
|
||||
if (result != "OK") return result
|
||||
|
||||
result = "Fail"
|
||||
task.input <= "OK"
|
||||
if (result != "OK") return result
|
||||
result = "Fail"
|
||||
task.input <= StringProperty("OK")
|
||||
if (result != "OK") return result
|
||||
|
||||
result = "Fail"
|
||||
task.input[0] = "OK"
|
||||
if (result != "OK") return result
|
||||
result = "Fail"
|
||||
task.input[0] = StringProperty("OK")
|
||||
if (result != "OK") return result
|
||||
|
||||
result = "Fail"
|
||||
task.input[0, 0] = "OK"
|
||||
if (result != "OK") return result
|
||||
result = "Fail"
|
||||
task.input[0, 0] = StringProperty("OK")
|
||||
if (result != "OK") return result
|
||||
|
||||
result = "Fail"
|
||||
task.input[0, 0, 0] = "OK"
|
||||
if (result != "OK") return result
|
||||
result = "Fail"
|
||||
task.input[0, 0, 0] = StringProperty("OK")
|
||||
if (result != "OK") return result
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
annotation class ValueContainer
|
||||
|
||||
abstract class AbstractStringProperty(protected var v: String) {
|
||||
fun get(): String {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
@ValueContainer
|
||||
class StringProperty(v: String) : AbstractStringProperty(v) {
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
|
||||
fun assign(v: StringProperty) {
|
||||
this.v = v.get()
|
||||
}
|
||||
}
|
||||
|
||||
@ValueContainer
|
||||
class StringPropertyWithPlus(v: String) : AbstractStringProperty(v) {
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
|
||||
fun assign(o: StringPropertyWithPlus) {
|
||||
this.v = o.get()
|
||||
}
|
||||
|
||||
operator fun plus(v: String) =
|
||||
StringPropertyWithPlus(this.v + v)
|
||||
}
|
||||
|
||||
@ValueContainer
|
||||
class StringPropertyWithPlusAssign(v: String) : AbstractStringProperty(v) {
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
|
||||
fun assign(o: StringPropertyWithPlusAssign) {
|
||||
this.v = o.get()
|
||||
}
|
||||
|
||||
operator fun plusAssign(v: String) {
|
||||
this.v += v
|
||||
}
|
||||
}
|
||||
|
||||
@ValueContainer
|
||||
class StringPropertyWithPlusAndPlusAssign(v: String) : AbstractStringProperty(v) {
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
|
||||
fun assign(o: StringPropertyWithPlusAndPlusAssign) {
|
||||
this.v = o.get()
|
||||
}
|
||||
|
||||
operator fun plus(v: String) =
|
||||
StringPropertyWithPlusAndPlusAssign(this.v + v)
|
||||
|
||||
operator fun plusAssign(v: String) {
|
||||
this.v += v
|
||||
}
|
||||
}
|
||||
|
||||
data class Task(
|
||||
val valInput: StringProperty,
|
||||
var varInput: StringProperty,
|
||||
|
||||
val valInputWithPlus: StringPropertyWithPlus,
|
||||
var varInputWithPlus: StringPropertyWithPlus,
|
||||
|
||||
val valInputWithPlusAssign: StringPropertyWithPlusAssign,
|
||||
var varInputWithPlusAssign: StringPropertyWithPlusAssign,
|
||||
|
||||
val valInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
|
||||
var varInputWithPlusAndPlusAssign: StringPropertyWithPlusAndPlusAssign,
|
||||
)
|
||||
|
||||
fun box(): String {
|
||||
val task = Task(
|
||||
StringProperty("O"),
|
||||
StringProperty("O"),
|
||||
|
||||
StringPropertyWithPlus("O"),
|
||||
StringPropertyWithPlus("O"),
|
||||
|
||||
StringPropertyWithPlusAssign("O"),
|
||||
StringPropertyWithPlusAssign("O"),
|
||||
|
||||
StringPropertyWithPlusAndPlusAssign("O"),
|
||||
StringPropertyWithPlusAndPlusAssign("O")
|
||||
)
|
||||
|
||||
task.varInputWithPlus += "K"
|
||||
if (task.varInputWithPlus.get() != "OK") return task.varInputWithPlus.get()
|
||||
|
||||
task.valInputWithPlusAssign += "K"
|
||||
if (task.valInputWithPlusAssign.get() != "OK") return task.valInputWithPlusAssign.get()
|
||||
task.varInputWithPlusAssign += "K"
|
||||
if (task.varInputWithPlusAssign.get() != "OK") return task.varInputWithPlusAssign.get()
|
||||
|
||||
task.valInputWithPlusAndPlusAssign += "K"
|
||||
if (task.valInputWithPlusAndPlusAssign.get() != "OK") return task.valInputWithPlusAndPlusAssign.get()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// FILE: Property.java
|
||||
@ValueContainer
|
||||
public interface Property<T> {
|
||||
void set(T v);
|
||||
T get();
|
||||
}
|
||||
|
||||
// FILE: JavaTaskWithField.java
|
||||
public class JavaTaskWithField {
|
||||
public final StringProperty input;
|
||||
|
||||
public JavaTaskWithField(StringProperty input) {
|
||||
this.input = input;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: JavaTaskWithProperty.java
|
||||
public class JavaTaskWithProperty {
|
||||
private final StringProperty input;
|
||||
|
||||
public JavaTaskWithProperty(StringProperty input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public StringProperty getInput() {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
annotation class ValueContainer
|
||||
|
||||
data class StringProperty(var v: String): Property<String> {
|
||||
override fun set(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
fun assign(v: Property<String>) {
|
||||
this.v = v.get()
|
||||
}
|
||||
override fun get(): String {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
data class Task(val input: StringProperty)
|
||||
|
||||
fun `should work with assignment for raw type`(): String {
|
||||
val task = Task(StringProperty("Fail"))
|
||||
task.input = "OK"
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with assignment for wrapped type`(): String {
|
||||
val task = Task(StringProperty("Fail"))
|
||||
task.input = StringProperty("OK")
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with assignment with apply for raw type`(): String {
|
||||
val task = Task(StringProperty("Fail"))
|
||||
task.apply {
|
||||
input = "OK"
|
||||
}
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with assignment with apply for wrapped type`(): String {
|
||||
val task = Task(StringProperty("Fail"))
|
||||
task.apply {
|
||||
input = StringProperty("OK")
|
||||
}
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with extension function`(): String {
|
||||
fun StringProperty.assign(v: Int) = this.assign("OK")
|
||||
val task = Task(StringProperty("Fail"))
|
||||
task.input = 42
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with extension function for interface type`(): String {
|
||||
fun Property<String>.assign(v: Int) = this.set("OK")
|
||||
val task = Task(StringProperty("Fail"))
|
||||
task.input = 42
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with generic extension function`(): String {
|
||||
fun <T> Property<T>.assign(v: T) = this.set(v)
|
||||
data class IntProperty(var v: Int): Property<Int> {
|
||||
override fun set(v: Int) {
|
||||
this.v = v
|
||||
}
|
||||
override fun get() = this.v
|
||||
}
|
||||
data class IntTask(val input: IntProperty)
|
||||
val task = IntTask(IntProperty(0))
|
||||
task.input = 42
|
||||
|
||||
return if (task.input.get() != 42) {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with callable receiver`(): String {
|
||||
fun StringProperty.assign(r: StringProperty.() -> Unit) = r.invoke(this)
|
||||
val task = Task(StringProperty("Fail"))
|
||||
task.input = { this.set("OK") }
|
||||
|
||||
return if (task.input.get() != "OK") {
|
||||
"Fail: ${task.input.get()}"
|
||||
} else {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `should work with Java classes`(): String {
|
||||
var taskWithField = JavaTaskWithField(StringProperty("Fail"))
|
||||
taskWithField.input = "OK"
|
||||
if (taskWithField.input.get() != "OK") return "Fail for Java: ${taskWithField.input.get()}"
|
||||
taskWithField = JavaTaskWithField(StringProperty("Fail"))
|
||||
taskWithField.input = StringProperty("OK")
|
||||
if (taskWithField.input.get() != "OK") return "Fail for Java: ${taskWithField.input.get()}"
|
||||
|
||||
var taskWithProperty = JavaTaskWithProperty(StringProperty("Fail"))
|
||||
taskWithProperty.input = "OK"
|
||||
if (taskWithProperty.input.get() != "OK") return "Fail for Java: ${taskWithProperty.input.get()}"
|
||||
taskWithProperty = JavaTaskWithProperty(StringProperty("Fail"))
|
||||
taskWithProperty.input = StringProperty("OK")
|
||||
if (taskWithProperty.input.get() != "OK") return "Fail for Java: ${taskWithProperty.input.get()}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var result = `should work with assignment for raw type`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with assignment for wrapped type`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with assignment with apply for raw type`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with assignment with apply for wrapped type`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with extension function`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with extension function for interface type`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with generic extension function`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with callable receiver`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `should work with Java classes`()
|
||||
if (result != "OK") return result
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
annotation class ValueContainer
|
||||
|
||||
@ValueContainer
|
||||
data class StringProperty(var v: String) {
|
||||
fun assign(v: String) {
|
||||
this.v = v
|
||||
}
|
||||
fun assign(v: StringProperty) {
|
||||
this.v = v.get()
|
||||
}
|
||||
fun get() = v
|
||||
}
|
||||
|
||||
data class Task(var input: StringProperty)
|
||||
|
||||
fun `test local var reference and value`(): String {
|
||||
var property = StringProperty("OK")
|
||||
var originalProperty = property
|
||||
property = StringProperty("Fail")
|
||||
|
||||
return when {
|
||||
originalProperty.get() != "OK" -> "Fail: ${originalProperty.get()}"
|
||||
originalProperty == property -> "Fail: originalProperty == property"
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `test class property var reference and value`(): String {
|
||||
val task = Task(StringProperty("OK"))
|
||||
val originalProperty = task.input
|
||||
task.input = StringProperty("Fail")
|
||||
|
||||
return when {
|
||||
originalProperty.get() != "OK" -> "Fail: ${originalProperty.get()}"
|
||||
originalProperty == task.input -> "Fail: originalProperty == task.input"
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun `test class property var reference and value with apply`(): String {
|
||||
val task = Task(StringProperty("OK"))
|
||||
val originalProperty = task.input
|
||||
task.apply {
|
||||
input = StringProperty("Fail")
|
||||
}
|
||||
|
||||
return when {
|
||||
originalProperty.get() != "OK" -> "Fail: ${originalProperty.get()}"
|
||||
originalProperty == task.input -> "Fail: originalProperty == task.input"
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var result = `test local var reference and value`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `test class property var reference and value`()
|
||||
if (result != "OK") return result
|
||||
|
||||
result = `test class property var reference and value with apply`()
|
||||
if (result != "OK") return result
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user