diff --git a/tests/init/neg/Desugar.check b/tests/init/neg/Desugar.check new file mode 100644 index 000000000000..b9c0ddd86913 --- /dev/null +++ b/tests/init/neg/Desugar.check @@ -0,0 +1,5 @@ +-- Error: tests/init/neg/Desugar.scala:12:61 --------------------------------------------------------------------------- +12 | val f: PartialFunction[A, Int] = {case C(_, rhs) => rhs.x} // error + | ^ + | Calling the external method constructor $anon may cause initialization errors. Calling trace: + | -> val f: PartialFunction[A, Int] = {case C(_, rhs) => rhs.x} // error [ Desugar.scala:12 ] diff --git a/tests/init/neg/Desugar.scala b/tests/init/neg/Desugar.scala new file mode 100644 index 000000000000..32fb89636a21 --- /dev/null +++ b/tests/init/neg/Desugar.scala @@ -0,0 +1,15 @@ +trait A + +class Tree[-A >: Int] { + val x: Int = 10 +} + +case class C[-T >: Int] (lhs: Int, rhs: Tree[T]) extends A { + val x = rhs.x +} + +object DesugarError { + val f: PartialFunction[A, Int] = {case C(_, rhs) => rhs.x} // error +} + + diff --git a/tests/init/neg/InteractiveDriver.check b/tests/init/neg/InteractiveDriver.check new file mode 100644 index 000000000000..32716766aafd --- /dev/null +++ b/tests/init/neg/InteractiveDriver.check @@ -0,0 +1,5 @@ +-- Error: tests/init/neg/InteractiveDriver.scala:22:71 ----------------------------------------------------------------- +22 | val l2: Seq[C[?]] = l.collect{ case x: InteractiveDriver[?] => h(x) } // error + | ^ + |Calling the external method constructor $anon may cause initialization errors. Calling trace: + | -> val l2: Seq[C[?]] = l.collect{ case x: InteractiveDriver[?] => h(x) } // error [ InteractiveDriver.scala:22 ] diff --git a/tests/init/neg/InteractiveDriver.scala b/tests/init/neg/InteractiveDriver.scala new file mode 100644 index 000000000000..dfec53f0c5cd --- /dev/null +++ b/tests/init/neg/InteractiveDriver.scala @@ -0,0 +1,23 @@ +trait InteractiveDriver[A <: AnyVal] { + val x: AnyVal + def g(x: A): A +} + +class C[A <: AnyVal] extends InteractiveDriver[A] { + val x = 0 + def g(x: A) = x +} + +class D[A <: AnyVal] extends InteractiveDriver[A] { + val x = 1.5 + def g(x: A) = x +} + +object InteractiveDriver { + def h(x: InteractiveDriver[?]): C[?] = x match { + case c: C[?] => c + case _ => new C[Int] + } + val l: Seq[Any] = Seq(1, 2, new C[Double], new D[Int]) + val l2: Seq[C[?]] = l.collect{ case x: InteractiveDriver[?] => h(x) } // error +} diff --git a/tests/init/neg/closureLeak.check b/tests/init/neg/closureLeak.check new file mode 100644 index 000000000000..405a44ff6581 --- /dev/null +++ b/tests/init/neg/closureLeak.check @@ -0,0 +1,8 @@ +-- Error: tests/init/neg/closureLeak.scala:11:14 ----------------------------------------------------------------------- +11 | l.foreach(a => a.addX(this)) // error + | ^^^^^^^^^^^^^^^^^ + | Promoting the value to fully-initialized is unsafe. May only use initialized value as arguments + | + | The unsafe promotion may cause the following problem(s): + | + | 1. Promote the value under initialization to fully-initialized. May only use initialized value as arguments. diff --git a/tests/init/neg/closureLeak.scala b/tests/init/neg/closureLeak.scala new file mode 100644 index 000000000000..ae7db5124028 --- /dev/null +++ b/tests/init/neg/closureLeak.scala @@ -0,0 +1,13 @@ +class Outer { + val x = 10 + class A(x: Int) { + var y: Int = x + def addX(o: Outer): Unit = { + y = y + o.x + } + } + + val l: List[A] = List(new A(5), new A(10)) + l.foreach(a => a.addX(this)) // error + val p = 10 +} diff --git a/tests/init/neg/cycle-structure.check b/tests/init/neg/cycle-structure.check new file mode 100644 index 000000000000..27b69d25406f --- /dev/null +++ b/tests/init/neg/cycle-structure.check @@ -0,0 +1,8 @@ +-- Error: tests/init/neg/cycle-structure.scala:3:14 -------------------------------------------------------------------- +3 | val x = B(this) // error + | ^^^^ + | Promote the value under initialization to fully-initialized. May only use initialized value as arguments. +-- Error: tests/init/neg/cycle-structure.scala:9:14 -------------------------------------------------------------------- +9 | val x = A(this) // error + | ^^^^ + | Promote the value under initialization to fully-initialized. May only use initialized value as arguments. diff --git a/tests/init/neg/cycle-structure.scala b/tests/init/neg/cycle-structure.scala new file mode 100644 index 000000000000..acf889a33c1d --- /dev/null +++ b/tests/init/neg/cycle-structure.scala @@ -0,0 +1,11 @@ +case class A(b: B) { + val x1 = b.x + val x = B(this) // error + val y = x.a +} + +case class B(a: A) { + val x1 = a.x + val x = A(this) // error + val h = x.b +} diff --git a/tests/init/neg/default-this.check b/tests/init/neg/default-this.check new file mode 100644 index 000000000000..8b02d96cfeb7 --- /dev/null +++ b/tests/init/neg/default-this.check @@ -0,0 +1,5 @@ +-- Error: tests/init/neg/default-this.scala:9:8 ------------------------------------------------------------------------ +9 | compare() // error + | ^^^^^^^ + |Promote the value under initialization to fully-initialized. May only use initialized value as arguments. Calling trace: + | -> val result = updateThenCompare(5) [ default-this.scala:11 ] diff --git a/tests/init/neg/default-this.scala b/tests/init/neg/default-this.scala new file mode 100644 index 000000000000..1b0173fc134f --- /dev/null +++ b/tests/init/neg/default-this.scala @@ -0,0 +1,12 @@ +class A { + var x: Int = 10 + def compare(c: Int = 5, a: A = this): Boolean = if (c == a.x) true else false +} + +class B extends A { + def updateThenCompare(c: Int): Boolean = { + x = c + compare() // error + } + val result = updateThenCompare(5) +} diff --git a/tests/init/neg/enum-desugared.check b/tests/init/neg/enum-desugared.check new file mode 100644 index 000000000000..d3490b2090ba --- /dev/null +++ b/tests/init/neg/enum-desugared.check @@ -0,0 +1,20 @@ +-- Error: tests/init/neg/enum-desugared.scala:17:15 -------------------------------------------------------------------- +17 | Array(this.LazyErrorId, this.NoExplanationID) // error // error + | ^^^^^^^^^^^^^^^^ + | Promoting the value to fully-initialized is unsafe. May only use initialized value as method arguments + | + | The unsafe promotion may cause the following problem(s): + | + | 1. Calling the external method method name may cause initialization errors. Calling trace: + | -> override def productPrefix: String = this.name() [ enum-desugared.scala:29 ] + | -> Array(this.LazyErrorId, this.NoExplanationID) // error // error [ enum-desugared.scala:17 ] +-- Error: tests/init/neg/enum-desugared.scala:17:33 -------------------------------------------------------------------- +17 | Array(this.LazyErrorId, this.NoExplanationID) // error // error + | ^^^^^^^^^^^^^^^^^^^^ + | Promoting the value to fully-initialized is unsafe. May only use initialized value as method arguments + | + | The unsafe promotion may cause the following problem(s): + | + | 1. Calling the external method method ordinal may cause initialization errors. Calling trace: + | -> def errorNumber: Int = this.ordinal() - 2 [ enum-desugared.scala:8 ] + | -> Array(this.LazyErrorId, this.NoExplanationID) // error // error [ enum-desugared.scala:17 ] diff --git a/tests/init/neg/enum-desugared.scala b/tests/init/neg/enum-desugared.scala new file mode 100644 index 000000000000..eb80f112a06c --- /dev/null +++ b/tests/init/neg/enum-desugared.scala @@ -0,0 +1,35 @@ +package example + +import language.`3.0-migration` + +sealed abstract class ErrorMessageID($name: String, _$ordinal: Int) + extends java.lang.Enum[ErrorMessageID]($name, _$ordinal) with scala.reflect.Enum { + + def errorNumber: Int = this.ordinal() - 2 +} + +object ErrorMessageID { + + final val LazyErrorId = $new(0, "LazyErrorId") + final val NoExplanationID = $new(1, "NoExplanationID") + + private[this] val $values: Array[ErrorMessageID] = + Array(this.LazyErrorId, this.NoExplanationID) // error // error + + def values: Array[ErrorMessageID] = $values.clone() + + def valueOf($name: String): ErrorMessageID = $name match { + case "LazyErrorId" => this.LazyErrorId + case "NoExplanationID" => this.NoExplanationID + case _ => throw new IllegalArgumentException("enum case not found: " + $name) + } + + private[this] def $new(_$ordinal: Int, $name: String): ErrorMessageID = + new ErrorMessageID($name, _$ordinal) with scala.runtime.EnumValue { + override def productPrefix: String = this.name() + } + + def fromOrdinal(ordinal: Int): ErrorMessageID = + try ErrorMessageID.$values.apply(ordinal) + catch { case _ => throw new NoSuchElementException(ordinal.toString()) } +} \ No newline at end of file diff --git a/tests/init/neg/enum.check b/tests/init/neg/enum.check new file mode 100644 index 000000000000..0ab4f2db5c76 --- /dev/null +++ b/tests/init/neg/enum.check @@ -0,0 +1,9 @@ +-- Error: tests/init/neg/enum.scala:4:8 -------------------------------------------------------------------------------- +4 | NoExplanationID // error + | ^ + | Promoting the value to fully-initialized is unsafe. May only use initialized value as method arguments + | + | The unsafe promotion may cause the following problem(s): + | + | 1. Calling the external method method name may cause initialization errors. Calling trace: + | -> NoExplanationID // error [ enum.scala:4 ] diff --git a/tests/init/neg/enum.scala b/tests/init/neg/enum.scala new file mode 100644 index 000000000000..925d61b620a4 --- /dev/null +++ b/tests/init/neg/enum.scala @@ -0,0 +1,6 @@ +enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] { + case + LazyErrorId, + NoExplanationID // error + def errorNumber = ordinal - 2 +} diff --git a/tests/init/neg/leak-warm.check b/tests/init/neg/leak-warm.check new file mode 100644 index 000000000000..85f6730cf673 --- /dev/null +++ b/tests/init/neg/leak-warm.check @@ -0,0 +1,8 @@ +-- Error: tests/init/neg/leak-warm.scala:18:26 ------------------------------------------------------------------------- +18 | val l: List[A] = List(c, d) // error // error + | ^ + |Promote the value under initialization to fully-initialized. May only use initialized value as method arguments. +-- Error: tests/init/neg/leak-warm.scala:18:29 ------------------------------------------------------------------------- +18 | val l: List[A] = List(c, d) // error // error + | ^ + |Promote the value under initialization to fully-initialized. May only use initialized value as method arguments. diff --git a/tests/init/neg/leak-warm.scala b/tests/init/neg/leak-warm.scala new file mode 100644 index 000000000000..f562ab4ec416 --- /dev/null +++ b/tests/init/neg/leak-warm.scala @@ -0,0 +1,20 @@ +object leakWarm { + abstract class A(tag: Int) { + class B(x: Int) { + val y = x + } + def m(): B + } + + class C(tag1: Int, tag2: Int) extends A(tag1) { + def m() = new B(5) + } + + class D(tag1: Int, tag2: Int) extends A(tag1 + tag2) { + def m() = new B(tag1) + } + val c = new C(1, 2) + val d = new D(3, 4) + val l: List[A] = List(c, d) // error // error + val l2 = l.map(_.m()) +} diff --git a/tests/init/neg/local-warm.check b/tests/init/neg/local-warm.check new file mode 100644 index 000000000000..73972c1844f2 --- /dev/null +++ b/tests/init/neg/local-warm.check @@ -0,0 +1,5 @@ +-- Error: tests/init/neg/local-warm.scala:12:12 ------------------------------------------------------------------------ +12 | val t = m1() // error + | ^^^^^^^^^^^^ + |Promote the value under initialization to fully-initialized. Local definitions may only hold initialized values. Calling trace: + | -> val x = g() [ local-warm.scala:15 ] diff --git a/tests/init/neg/local-warm.scala b/tests/init/neg/local-warm.scala new file mode 100644 index 000000000000..04dea6b5e3c6 --- /dev/null +++ b/tests/init/neg/local-warm.scala @@ -0,0 +1,16 @@ +abstract class A { + def m() = 10 + def m1(): B = new B + def m2(): Int = m1().m() + class B extends A { + def x = 10 + } +} + +class C extends A { + def g() = { + val t = m1() // error + t.x + } + val x = g() +} diff --git a/tests/init/pos/leak-this-inner.scala b/tests/init/pos/leak-this-inner.scala new file mode 100644 index 000000000000..bdabde0dbf7a --- /dev/null +++ b/tests/init/pos/leak-this-inner.scala @@ -0,0 +1,8 @@ +class A { + val x = 10 + class B(a: A) { + val anotherX = A.this.x + } + val b = B(this) // error + val xAgain = b.anotherX +} diff --git a/tests/init/pos/leak-this.scala b/tests/init/pos/leak-this.scala new file mode 100644 index 000000000000..7c6fbeafeb51 --- /dev/null +++ b/tests/init/pos/leak-this.scala @@ -0,0 +1,11 @@ +class Parent { + val child: Child = new Child(this) // error +} + +class Child(parent: Parent) { + val friend = new Friend(this.parent) +} + +class Friend(parent: Parent) { + val tag = 10 +} diff --git a/tests/init/pos/methodAtLast.scala b/tests/init/pos/methodAtLast.scala new file mode 100644 index 000000000000..02fbf1501bd8 --- /dev/null +++ b/tests/init/pos/methodAtLast.scala @@ -0,0 +1,11 @@ +class Foo(x: Int) { + var y: Int = x + case class Bar(z: Int) extends Foo(z) + def updateY(n: Int): Unit = { + if (y < 20) { + val b = new Bar(x + n) + y = b.z + } + } + updateY(5) +}