| // DUMP_IR |
| // WITH_STDLIB |
| // MUTE_LL_FIR |
| // ^ backend plugins are not executed -> declarations in dependent module are not visible |
| |
| // MODULE: a |
| // FILE: src.kt |
| import org.jetbrains.kotlin.plugin.sandbox.GenerateClassFamily |
| |
| open class SourceBase { |
| val srcField: String = "src" |
| } |
| |
| // Test (6): source-declared outer that gets a plugin-generated nested class added inside it. |
| class SourceWithNested |
| |
| // Test (7): source-declared outer that gets a plugin-generated companion object added inside it. |
| class SourceWithCompanion |
| |
| // Tests (9)-(12): source-declared outer classes that get plugin-generated inner classes added. |
| class SourceOuterPlain |
| class SourceOuterPlain2 |
| class SourceOuterGeneric<T> |
| class SourceOuterGenericPaired<T> |
| |
| // Test (16): source-declared interface used as the supertype of a generated inner class, where the |
| // outer class's type parameter is captured in the supertype (`InnerBox : Box<T>`). |
| interface Box<T> { |
| val value: T |
| } |
| |
| @GenerateClassFamily |
| class Marker |
| |
| // MODULE: b(a) |
| // FILE: main.kt |
| fun box(): String { |
| // (1) Plain class — generated by the plugin, no generics, extends Any. |
| val plain = Plain() |
| if (plain.x != 42) return "FAIL plain.x: ${plain.x}" |
| if (plain.foo() != "ok") return "FAIL plain.foo: ${plain.foo()}" |
| |
| // (2) Generic class — exercises type-parameter conversion on the class itself. |
| val generic = WithGeneric<String>() |
| if (generic.x != 42) return "FAIL generic.x: ${generic.x}" |
| if (generic.foo() != "ok") return "FAIL generic.foo: ${generic.foo()}" |
| |
| // (3) Inherits a source-declared class — declared + inherited members must resolve. |
| val es = ExtendsSource() |
| if (es.x != 42) return "FAIL es.x: ${es.x}" |
| if (es.foo() != "ok") return "FAIL es.foo: ${es.foo()}" |
| if (es.srcField != "src") return "FAIL es.srcField (inherited from source): ${es.srcField}" |
| |
| // (4) Inherits another plugin-generated class — declared + inherited members must resolve. |
| val ep = ExtendsPlain() |
| if (ep.x != 42) return "FAIL ep.x (inherited from Plain): ${ep.x}" |
| if (ep.foo() != "ok") return "FAIL ep.foo (inherited from Plain): ${ep.foo()}" |
| |
| // (5) Sealed class + subclasses — module b needs the inheritor list in metadata so the |
| // exhaustive `when` below compiles without an `else` branch. |
| val s: MySealed = SubA() |
| val tag: String = when (s) { |
| is SubA -> "a" |
| is SubB -> "b" |
| } |
| if (tag != "a") return "FAIL sealed when: $tag" |
| if (s.x != 42) return "FAIL sealed inherited x: ${s.x}" |
| |
| // (6) Generated nested class for a source-declared outer. |
| val nested = SourceWithNested.Nested() |
| if (nested.x != 42) return "FAIL nested.x: ${nested.x}" |
| if (nested.foo() != "ok") return "FAIL nested.foo: ${nested.foo()}" |
| |
| // (7) Generated companion object for a source-declared outer. |
| if (SourceWithCompanion.x != 42) return "FAIL companion.x: ${SourceWithCompanion.x}" |
| |
| // (8) Outer class that's plugin-generated AND has its own nested + companion. |
| val inner = WithNestedFamily.Inner() |
| if (inner.x != 42) return "FAIL inner.x: ${inner.x}" |
| if (WithNestedFamily.fromCompanion() != "from-companion") return "FAIL companion fn" |
| |
| // (9) Inner class with no generics inside an outer with no generics. |
| val innerA = SourceOuterPlain().InnerPlain() |
| if (innerA.x != 42) return "FAIL innerA.x: ${innerA.x}" |
| if (innerA.foo() != "ok") return "FAIL innerA.foo: ${innerA.foo()}" |
| |
| // (10) Inner class with no generics inside an outer with generics. |
| val innerB = SourceOuterGeneric<String>().InnerPlain() |
| if (innerB.x != 42) return "FAIL innerB.x: ${innerB.x}" |
| if (innerB.foo() != "ok") return "FAIL innerB.foo: ${innerB.foo()}" |
| |
| // (11) Inner class with generics inside an outer with no generics. |
| val innerC = SourceOuterPlain2().InnerGeneric<String>() |
| if (innerC.x != 42) return "FAIL innerC.x: ${innerC.x}" |
| if (innerC.foo() != "ok") return "FAIL innerC.foo: ${innerC.foo()}" |
| |
| // (12) Both inner and outer generic. |
| val innerD = SourceOuterGenericPaired<Int>().InnerGeneric<String>() |
| if (innerD.x != 42) return "FAIL innerD.x: ${innerD.x}" |
| if (innerD.foo() != "ok") return "FAIL innerD.foo: ${innerD.foo()}" |
| |
| // (13) Inner class inside a plugin-generated outer (both generic). |
| val withInner = WithInnerFamily<Int>() |
| val innerE = withInner.Inner<String>() |
| if (innerE.x != 42) return "FAIL innerE.x: ${innerE.x}" |
| if (innerE.foo() != "ok") return "FAIL innerE.foo: ${innerE.foo()}" |
| |
| // (14) Deep generic inner chain: three levels of inner-class generics, all plugin-generated. |
| val deep = WithDeepInnerFamily<Int>().Inner<String>().DeeplyInner<Double>() |
| if (deep.x != 42) return "FAIL deep.x: ${deep.x}" |
| if (deep.foo() != "ok") return "FAIL deep.foo: ${deep.foo()}" |
| // `self()`'s metadata return type must round-trip with all three type arguments in canonical |
| // (own-first) order. This explicitly-typed assignment only compiles if the inner-class type |
| // parameters were registered correctly across the whole enclosing chain. |
| val deepSelf: WithDeepInnerFamily<Int>.Inner<String>.DeeplyInner<Double> = deep.self() |
| if (deepSelf.x != 42) return "FAIL deepSelf.x: ${deepSelf.x}" |
| |
| // (15) Functions on DeeplyInner returning the type parameter at each level of the chain: |
| // own C (Double), captured B from Inner (String), captured A from WithDeepInnerFamily (Int). |
| val c: Double = deep.idC(3.14) |
| if (c != 3.14) return "FAIL deep.idC: $c" |
| val b: String = deep.idB("hi") |
| if (b != "hi") return "FAIL deep.idB: $b" |
| val a: Int = deep.idA(7) |
| if (a != 7) return "FAIL deep.idA: $a" |
| |
| // (16) The outer type parameter is captured in the inner class's supertype: InnerBox : Box<T>. |
| // Using the inner instance through the Box<String> supertype only type-checks if the captured |
| // type parameter was substituted correctly during IR -> cone conversion of the supertype. |
| val outerBox = Outer<String>() |
| val ib: Box<String> = outerBox.InnerBox("boxed") |
| if (ib.value != "boxed") return "FAIL ib.value: ${ib.value}" |
| val ib2 = outerBox.InnerBox("direct") |
| if (ib2.value != "direct") return "FAIL ib2.value: ${ib2.value}" |
| |
| return "OK" |
| } |