diff options
Diffstat (limited to 'compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala')
-rw-r--r-- | compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala new file mode 100644 index 0000000..c675420 --- /dev/null +++ b/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala @@ -0,0 +1,745 @@ +package scala.scalajs.compiler.test + +import scala.scalajs.compiler.test.util._ +import org.junit.Test +import org.junit.Ignore + +class JSExportTest extends DirectTest with TestHelpers { + + override def preamble = + """import scala.scalajs.js.annotation._ + """ + + @Test + def noDoubleUnderscoreExport = { + // Normal exports + """ + class A { + @JSExport(name = "__") + def foo = 1 + + @JSExport + def bar__(x: Int) = x + } + + @JSExport + class B__ + """ hasErrors + """ + |newSource1.scala:4: error: An exported name may not contain a double underscore (`__`) + | @JSExport(name = "__") + | ^ + |newSource1.scala:8: error: An exported name may not contain a double underscore (`__`) + | def bar__(x: Int) = x + | ^ + |newSource1.scala:12: error: An exported name may not contain a double underscore (`__`) + | class B__ + | ^ + """ + + // Inherited exports (objects) + """ + @JSExportDescendentObjects + trait A + + package fo__o { + object B extends A + } + """ hasErrors + """ + |newSource1.scala:7: error: B may not have a double underscore (`__`) in its fully qualified + |name, since it is forced to be exported by a @JSExportDescendentObjects on trait A + | object B extends A + | ^ + """ + + // Inherited exports (classes) + """ + @JSExportDescendentClasses + trait A + + package fo__o { + class B(x: Int) extends A { + def this() = this(1) + private def this(s: String) = this(1) + } + } + """ hasErrors + """ + |newSource1.scala:7: error: B may not have a double underscore (`__`) in its fully qualified + |name, since it is forced to be exported by a @JSExportDescendentClasses on trait A + | class B(x: Int) extends A { + | ^ + |newSource1.scala:8: error: B may not have a double underscore (`__`) in its fully qualified + |name, since it is forced to be exported by a @JSExportDescendentClasses on trait A + | def this() = this(1) + | ^ + """ + } + + @Test + def noConflictingExport = { + """ + class Confl { + @JSExport("value") + def hello = "foo" + + @JSExport("value") + def world = "bar" + } + """ fails() // No error test, Scala version dependent error messages + + """ + class Confl { + class Box[T](val x: T) + + @JSExport + def ub(x: Box[String]): String = x.x + @JSExport + def ub(x: Box[Int]): Int = x.x + } + """ fails() // No error test, Scala version dependent error messages + + """ + class Confl { + @JSExport + def rtType(x: scala.scalajs.js.prim.Number) = x + + @JSExport + def rtType(x: Double) = x + } + """ fails() // Error message depends on Scala version + + """ + class Confl { + @JSExport + def foo(x: Int)(ys: Int*) = x + + @JSExport + def foo(x: Int*) = x + } + """ hasErrors + """ + |newSource1.scala:7: error: Cannot disambiguate overloads for exported method $js$exported$meth$foo with types + | (x: Seq)Object + | (x: Int, ys: Seq)Object + | @JSExport + | ^ + """ + + """ + class Confl { + @JSExport + def foo(x: Int = 1) = x + @JSExport + def foo(x: String*) = x + } + """ hasErrors + """ + |newSource1.scala:4: error: Cannot disambiguate overloads for exported method $js$exported$meth$foo with types + | (x: Int)Object + | (x: Seq)Object + | @JSExport + | ^ + """ + + """ + class Confl { + @JSExport + def foo(x: scala.scalajs.js.prim.Number, y: String)(z: Int = 1) = x + @JSExport + def foo(x: Double, y: String)(z: String*) = x + } + """ fails() // Error message depends on Scala version + + """ + class A { + @JSExport + def a(x: scala.scalajs.js.Any) = 1 + + @JSExport + def a(x: Any) = 2 + } + """ fails() // Error message depends on Scala version + + } + + @Test + def noExportLocal = { + // Local class + """ + class A { + def method = { + @JSExport + class A + } + } + """ hasErrors + """ + |newSource1.scala:5: error: You may not export a local class + | @JSExport + | ^ + """ + + // Local object + """ + class A { + def method = { + @JSExport + object A + } + } + """ hasErrors + """ + |newSource1.scala:5: error: You may not export a local object + | @JSExport + | ^ + """ + + // Local method + """ + class A { + def method = { + @JSExport + def foo = 1 + } + } + """ hasErrors + """ + |newSource1.scala:5: error: You may not export a local definition + | @JSExport + | ^ + """ + + // Local val + """ + class A { + def method = { + @JSExport + val x = 1 + } + } + """ hasErrors + """ + |newSource1.scala:5: error: You may not export a local definition + | @JSExport + | ^ + """ + + // Local var + """ + class A { + def method = { + @JSExport + var x = 1 + } + } + """ hasErrors + """ + |newSource1.scala:5: error: You may not export a local definition + | @JSExport + | ^ + """ + + } + + @Test + def infoExportLocal = { + + """ + class A(@JSExport val x: Int) + """ hasErrors + """ + |newSource1.scala:3: error: You may not export a local definition. To export a (case) class field, use the meta-annotation scala.annotation.meta.field like this: @(JSExport @field). + | class A(@JSExport val x: Int) + | ^ + """ + + } + + @Test + def noMiddleVarArg = { + + """ + class A { + @JSExport + def method(xs: Int*)(ys: String) = 1 + } + """ hasErrors + """ + |newSource1.scala:4: error: In an exported method, a *-parameter must come last (through all parameter lists) + | @JSExport + | ^ + """ + + } + + @Test + def noMiddleDefaultParam = { + + """ + class A { + @JSExport + def method(x: Int = 1)(y: String) = 1 + } + """ hasErrors + """ + |newSource1.scala:4: error: In an exported method, all parameters with defaults must be at the end + | @JSExport + | ^ + """ + + } + + @Test + def noExportTrait = { + + """ + @JSExport + trait Test + """ hasErrors + """ + |newSource1.scala:3: error: You may not export a trait + | @JSExport + | ^ + """ + + } + + @Test + def noExportNonPublicClassOrObject = { + + """ + @JSExport + private class A + + @JSExport + protected[this] class B + """ hasErrors + """ + |newSource1.scala:3: error: You may only export public and protected classes + | @JSExport + | ^ + |newSource1.scala:6: error: You may only export public and protected classes + | @JSExport + | ^ + """ + + """ + @JSExport + private object A + + @JSExport + protected[this] object B + """ hasErrors + """ + |newSource1.scala:3: error: You may only export public and protected objects + | @JSExport + | ^ + |newSource1.scala:6: error: You may only export public and protected objects + | @JSExport + | ^ + """ + + } + + @Test + def noExportNonPublicMember = { + + """ + class A { + @JSExport + private def foo = 1 + + @JSExport + protected[this] def bar = 2 + } + """ hasErrors + """ + |newSource1.scala:4: error: You may only export public and protected methods + | @JSExport + | ^ + |newSource1.scala:7: error: You may only export public and protected methods + | @JSExport + | ^ + """ + + } + + @Test + def noExportNestedClass = { + + """ + class A { + @JSExport + class Nested + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a nested class. Create an exported factory method in the outer class to work around this limitation. + | @JSExport + | ^ + """ + + """ + object A { + @JSExport + class Nested + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a nested class. Create an exported factory method in the outer class to work around this limitation. + | @JSExport + | ^ + """ + + } + + @Test + def noExportNestedObject = { + + """ + class A { + @JSExport + object Nested + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a nested object + | @JSExport + | ^ + """ + + """ + object A { + @JSExport + object Nested + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a nested object + | @JSExport + | ^ + """ + + } + + @Test + def noExportJSRaw = { + + """ + import scala.scalajs.js + + @JSExport + object A extends js.Object + """ hasErrors + """ + |newSource1.scala:5: error: You may not export a class extending js.Any + | @JSExport + | ^ + """ + + """ + import scala.scalajs.js + + @JSExport + class A extends js.Object + """ hasErrors + """ + |newSource1.scala:5: error: You may not export a constructor of a subclass of js.Any + | @JSExport + | ^ + """ + + } + + @Test + def noExportJSRawMember = { + + """ + import scala.scalajs.js + + class A extends js.Object { + @JSExport + def foo: Int = js.native + } + """ hasErrors + """ + |newSource1.scala:6: error: You may not export a method of a subclass of js.Any + | @JSExport + | ^ + """ + + } + + @Test + def noBadSetterType = { + + // Bad param list + """ + class A { + @JSExport + def foo_=(x: Int, y: Int) = () + } + """ hasErrors + """ + |newSource1.scala:4: error: A method ending in _= will be exported as setter. But foo_= does not have the right signature to do so (single argument, unit return type). + | @JSExport + | ^ + """ + + // Bad return type + """ + class A { + @JSExport + def foo_=(x: Int) = "string" + } + """ hasErrors + """ + |newSource1.scala:4: error: A method ending in _= will be exported as setter. But foo_= does not have the right signature to do so (single argument, unit return type). + | @JSExport + | ^ + """ + + } + + @Test + def noBadToStringExport = { + + """ + class A { + @JSExport("toString") + def a(): Int = 5 + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a zero-argument method named other than 'toString' under the name 'toString' + | @JSExport("toString") + | ^ + """ + + } + + @Test + def noBadNameExportAll = { + + """ + @JSExportAll + class A { + val __f = 1 + def a_= = 2 + } + """ hasErrors + """ + |newSource1.scala:5: error: An exported name may not contain a double underscore (`__`) + | val __f = 1 + | ^ + |newSource1.scala:3: error: A method ending in _= will be exported as setter. But a_= does not have the right signature to do so (single argument, unit return type). + | @JSExportAll + | ^ + """ + + } + + @Test + def noConflictingMethodAndProperty = { + + // Basic case + """ + class A { + @JSExport("a") + def bar() = 2 + + @JSExport("a") + val foo = 1 + } + """ hasErrors + """ + |newSource1.scala:7: error: Exported method a conflicts with A.$js$exported$prop$a + | @JSExport("a") + | ^ + |newSource1.scala:4: error: Exported property a conflicts with A.$js$exported$meth$a + | @JSExport("a") + | ^ + """ + + // Inherited case + """ + class A { + @JSExport("a") + def bar() = 2 + } + + class B extends A { + @JSExport("a") + def foo_=(x: Int): Unit = () + + @JSExport("a") + val foo = 1 + } + """ hasErrors + """ + |newSource1.scala:4: error: Exported property a conflicts with A.$js$exported$meth$a + | @JSExport("a") + | ^ + """ + + } + + @Test + def noOverrideNamedExport = { + + """ + class A { + @JSExportNamed + def foo(x: Int, y: Int) = 1 + } + + class B extends A { + @JSExportNamed + override def foo(x: Int, y: Int) = 2 + } + """ hasErrors + """ + |newSource1.scala:9: error: overriding method $js$exported$meth$foo in class A of type (namedArgs: Any)Any; + | method $js$exported$meth$foo cannot override final member + | @JSExportNamed + | ^ + """ + + } + + @Test + def noConflictNamedExport = { + + // Normal method + """ + class A { + @JSExportNamed + def foo(x: Int, y: Int) = 1 + + @JSExport + def foo(x: scala.scalajs.js.Any) = 2 + } + """ fails() // No error test, Scala version dependent error messages + + // Ctors + """ + class A { + @JSExportNamed + def this(x: Int) = this() + + @JSExport + def this(x: scala.scalajs.js.Any) = this + + @JSExportNamed + def this(x: Long) = this() + } + """ fails() // No error test, Scala version dependent error messages + + } + + @Test + def noNamedExportObject = { + + """ + @JSExportNamed + object A + """ hasErrors + """ + |newSource1.scala:3: error: You may not use @JSNamedExport on an object + | @JSExportNamed + | ^ + """ + + } + + @Test + def noNamedExportVarArg = { + + """ + class A { + @JSExportNamed + def foo(a: Int*) = 1 + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not name-export a method with a *-parameter + | @JSExportNamed + | ^ + """ + + } + + @Test + def noNamedExportProperty = { + + // Getter + """ + class A { + @JSExportNamed + def a = 1 + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a getter or a setter as a named export + | @JSExportNamed + | ^ + """ + + + // Setter + """ + class A { + @JSExportNamed + def a_=(x: Int) = () + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a getter or a setter as a named export + | @JSExportNamed + | ^ + """ + + } + + @Test + def gracefulDoubleDefaultFail = { + // This used to blow up (i.e. not just fail), because PrepJSExports asked + // for the symbol of the default parameter getter of [[y]], and asserted its + // not overloaded. Since the Scala compiler only fails later on this, the + // assert got triggered and made the compiler crash + """ + class A { + @JSExport + def foo(x: String, y: String = "hello") = x + def foo(x: Int, y: String = "bar") = x + } + """ fails() + } + + @Test + def noNonLiteralExportNames = { + + """ + object A { + val a = "Hello" + final val b = "World" + } + + class B { + @JSExport(A.a) + def foo = 1 + @JSExport(A.b) + def bar = 1 + } + """ hasErrors + """ + |newSource1.scala:9: error: The argument to JSExport must be a literal string + | @JSExport(A.a) + | ^ + """ + + } + +} |