From 9bcba442107e53e95c821c5372e21bea374296a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 4 Sep 2020 14:31:52 +0200 Subject: [PATCH] Scala.js: Implement the PrepJSInterop phase, minus exports handling. The `PrepJSInterop` phase is responsible for: * Performing all kinds of Scala.js-specific compile-time checks, and emitting the appropriate compile errors. * Perform some transformations that are necessary for JavaScript interop, notably generating exports forwarders. This commit ports all the functionality of `PrepJSInterop` from Scala 2, except the following: * Handling of `scala.Enumeration`s: it is unclear whether we still want to support that in the core, or if it should be handled by an optional compiler plugin in the future. * Exports: they will be done later. * Warnings about duplicate fields in `js.Dynamic.literal`: mostly because they are non-essential. The test cases are ported from the Scala.js compiler tests. --- .appveyor.yml | 1 + .../dotty/tools/backend/sjs/JSCodeGen.scala | 126 +- .../tools/backend/sjs/JSDefinitions.scala | 32 +- .../dotty/tools/backend/sjs/JSInterop.scala | 77 +- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../tools/dotc/config/JavaPlatform.scala | 3 + .../dotty/tools/dotc/config/Platform.scala | 4 +- .../dotty/tools/dotc/config/SJSPlatform.scala | 20 + .../dotty/tools/dotc/core/Annotations.scala | 5 +- .../tools/dotc/core/SymDenotations.scala | 4 + .../dotty/tools/dotc/transform/SymUtils.scala | 6 +- .../dotc/transform/SyntheticMembers.scala | 7 +- .../tools/dotc/transform/sjs/JSSymUtils.scala | 132 +++ .../dotc/transform/sjs/PrepJSExports.scala | 49 + .../dotc/transform/sjs/PrepJSInterop.scala | 1055 +++++++++++++++++ project/Build.scala | 3 +- .../neg-scalajs/abstract-local-js-class.check | 4 + .../neg-scalajs/abstract-local-js-class.scala | 8 + tests/neg-scalajs/internal-annotations.check | 44 + tests/neg-scalajs/internal-annotations.scala | 17 + tests/neg-scalajs/isinstanceof-js-type.check | 8 + tests/neg-scalajs/isinstanceof-js-type.scala | 14 + tests/neg-scalajs/js-case-class-object.check | 18 + tests/neg-scalajs/js-case-class-object.scala | 12 + tests/neg-scalajs/js-dynamic-literal.check | 16 + tests/neg-scalajs/js-dynamic-literal.scala | 14 + .../js-members-bad-signatures.check | 56 + .../js-members-bad-signatures.scala | 39 + tests/neg-scalajs/js-native-call.check | 4 + tests/neg-scalajs/js-native-call.scala | 6 + tests/neg-scalajs/js-native-members.check | 100 ++ tests/neg-scalajs/js-native-members.scala | 95 ++ .../neg-scalajs/js-native-on-scala-type.check | 24 + .../neg-scalajs/js-native-on-scala-type.scala | 27 + .../js-native-type-wrong-place.check | 72 ++ .../js-native-type-wrong-place.scala | 76 ++ .../js-native-val-def-override.check | 30 + .../js-native-val-def-override.scala | 36 + .../js-native-val-def-wrong-place.check | 108 ++ .../js-native-val-def-wrong-place.scala | 123 ++ tests/neg-scalajs/js-native-val-defs.check | 46 + tests/neg-scalajs/js-native-val-defs.scala | 57 + tests/neg-scalajs/js-native-wrong-kind.check | 24 + tests/neg-scalajs/js-native-wrong-kind.scala | 25 + .../js-non-native-members-overloaded.check | 24 + .../js-non-native-members-overloaded.scala | 26 + ...non-native-members-qualified-private.check | 48 + ...non-native-members-qualified-private.scala | 54 + tests/neg-scalajs/js-non-native-members.check | 26 + tests/neg-scalajs/js-non-native-members.scala | 39 + .../js-trait-members-override.check | 30 + .../js-trait-members-override.scala | 43 + .../js-trait-members-wrong-kind.check | 16 + .../js-trait-members-wrong-kind.scala | 10 + tests/neg-scalajs/js-trait-members.check | 32 + tests/neg-scalajs/js-trait-members.scala | 16 + tests/neg-scalajs/js-type-bad-parents.check | 90 ++ tests/neg-scalajs/js-type-bad-parents.scala | 68 ++ .../js-type-override-equals-hashcode.check | 48 + .../js-type-override-equals-hashcode.scala | 37 + ...constructorof-error-in-prepjsinterop.check | 40 + ...constructorof-error-in-prepjsinterop.scala | 27 + .../jsconstructorof-error-in-typer.check | 12 + .../jsconstructorof-error-in-typer.scala | 12 + ...onstructortag-error-in-prepjsinterop.check | 40 + ...onstructortag-error-in-prepjsinterop.scala | 27 + .../jsconstructortag-error-in-typer.check | 27 + .../jsconstructortag-error-in-typer.scala | 12 + .../jsglobalscope-on-non-object.check | 28 + .../jsglobalscope-on-non-object.scala | 32 + tests/neg-scalajs/jsname-argument.check | 32 + tests/neg-scalajs/jsname-argument.scala | 64 + tests/neg-scalajs/jsname-duplicate.check | 12 + tests/neg-scalajs/jsname-duplicate.scala | 22 + .../jsname-on-private-members.check | 32 + .../jsname-on-private-members.scala | 28 + tests/neg-scalajs/jsname-on-trait.check | 12 + tests/neg-scalajs/jsname-on-trait.scala | 16 + tests/neg-scalajs/jsname-override.check | 80 ++ tests/neg-scalajs/jsname-override.scala | 115 ++ .../neg-scalajs/jsname-symbol-override.check | 90 ++ .../neg-scalajs/jsname-symbol-override.scala | 138 +++ tests/neg-scalajs/jsname-wrong-place.check | 104 ++ tests/neg-scalajs/jsname-wrong-place.scala | 97 ++ .../native-load-spec-argument.check | 98 ++ .../native-load-spec-argument.scala | 96 ++ .../native-load-spec-duplicate.check | 28 + .../native-load-spec-duplicate.scala | 35 + .../native-load-spec-missing.check | 40 + .../native-load-spec-missing.scala | 30 + .../native-load-spec-need-explicit-name.check | 8 + .../native-load-spec-need-explicit-name.scala | 24 + .../neg-scalajs/native-load-spec-nested.check | 120 ++ .../neg-scalajs/native-load-spec-nested.scala | 113 ++ .../native-load-spec-on-non-js-native.check | 84 ++ .../native-load-spec-on-non-js-native.scala | 77 ++ .../native-load-spec-on-trait.check | 20 + .../native-load-spec-on-trait.scala | 22 + ...non-native-js-types-inside-js-native.check | 32 + ...non-native-js-types-inside-js-native.scala | 25 + .../package-object-extends-js-any.check | 4 + .../package-object-extends-js-any.scala | 4 + .../scala-types-inside-js-native.check | 40 + .../scala-types-inside-js-native.scala | 39 + 104 files changed, 5185 insertions(+), 88 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala create mode 100644 compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala create mode 100644 compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala create mode 100644 tests/neg-scalajs/abstract-local-js-class.check create mode 100644 tests/neg-scalajs/abstract-local-js-class.scala create mode 100644 tests/neg-scalajs/internal-annotations.check create mode 100644 tests/neg-scalajs/internal-annotations.scala create mode 100644 tests/neg-scalajs/isinstanceof-js-type.check create mode 100644 tests/neg-scalajs/isinstanceof-js-type.scala create mode 100644 tests/neg-scalajs/js-case-class-object.check create mode 100644 tests/neg-scalajs/js-case-class-object.scala create mode 100644 tests/neg-scalajs/js-dynamic-literal.check create mode 100644 tests/neg-scalajs/js-dynamic-literal.scala create mode 100644 tests/neg-scalajs/js-members-bad-signatures.check create mode 100644 tests/neg-scalajs/js-members-bad-signatures.scala create mode 100644 tests/neg-scalajs/js-native-call.check create mode 100644 tests/neg-scalajs/js-native-call.scala create mode 100644 tests/neg-scalajs/js-native-members.check create mode 100644 tests/neg-scalajs/js-native-members.scala create mode 100644 tests/neg-scalajs/js-native-on-scala-type.check create mode 100644 tests/neg-scalajs/js-native-on-scala-type.scala create mode 100644 tests/neg-scalajs/js-native-type-wrong-place.check create mode 100644 tests/neg-scalajs/js-native-type-wrong-place.scala create mode 100644 tests/neg-scalajs/js-native-val-def-override.check create mode 100644 tests/neg-scalajs/js-native-val-def-override.scala create mode 100644 tests/neg-scalajs/js-native-val-def-wrong-place.check create mode 100644 tests/neg-scalajs/js-native-val-def-wrong-place.scala create mode 100644 tests/neg-scalajs/js-native-val-defs.check create mode 100644 tests/neg-scalajs/js-native-val-defs.scala create mode 100644 tests/neg-scalajs/js-native-wrong-kind.check create mode 100644 tests/neg-scalajs/js-native-wrong-kind.scala create mode 100644 tests/neg-scalajs/js-non-native-members-overloaded.check create mode 100644 tests/neg-scalajs/js-non-native-members-overloaded.scala create mode 100644 tests/neg-scalajs/js-non-native-members-qualified-private.check create mode 100644 tests/neg-scalajs/js-non-native-members-qualified-private.scala create mode 100644 tests/neg-scalajs/js-non-native-members.check create mode 100644 tests/neg-scalajs/js-non-native-members.scala create mode 100644 tests/neg-scalajs/js-trait-members-override.check create mode 100644 tests/neg-scalajs/js-trait-members-override.scala create mode 100644 tests/neg-scalajs/js-trait-members-wrong-kind.check create mode 100644 tests/neg-scalajs/js-trait-members-wrong-kind.scala create mode 100644 tests/neg-scalajs/js-trait-members.check create mode 100644 tests/neg-scalajs/js-trait-members.scala create mode 100644 tests/neg-scalajs/js-type-bad-parents.check create mode 100644 tests/neg-scalajs/js-type-bad-parents.scala create mode 100644 tests/neg-scalajs/js-type-override-equals-hashcode.check create mode 100644 tests/neg-scalajs/js-type-override-equals-hashcode.scala create mode 100644 tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check create mode 100644 tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala create mode 100644 tests/neg-scalajs/jsconstructorof-error-in-typer.check create mode 100644 tests/neg-scalajs/jsconstructorof-error-in-typer.scala create mode 100644 tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check create mode 100644 tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala create mode 100644 tests/neg-scalajs/jsconstructortag-error-in-typer.check create mode 100644 tests/neg-scalajs/jsconstructortag-error-in-typer.scala create mode 100644 tests/neg-scalajs/jsglobalscope-on-non-object.check create mode 100644 tests/neg-scalajs/jsglobalscope-on-non-object.scala create mode 100644 tests/neg-scalajs/jsname-argument.check create mode 100644 tests/neg-scalajs/jsname-argument.scala create mode 100644 tests/neg-scalajs/jsname-duplicate.check create mode 100644 tests/neg-scalajs/jsname-duplicate.scala create mode 100644 tests/neg-scalajs/jsname-on-private-members.check create mode 100644 tests/neg-scalajs/jsname-on-private-members.scala create mode 100644 tests/neg-scalajs/jsname-on-trait.check create mode 100644 tests/neg-scalajs/jsname-on-trait.scala create mode 100644 tests/neg-scalajs/jsname-override.check create mode 100644 tests/neg-scalajs/jsname-override.scala create mode 100644 tests/neg-scalajs/jsname-symbol-override.check create mode 100644 tests/neg-scalajs/jsname-symbol-override.scala create mode 100644 tests/neg-scalajs/jsname-wrong-place.check create mode 100644 tests/neg-scalajs/jsname-wrong-place.scala create mode 100644 tests/neg-scalajs/native-load-spec-argument.check create mode 100644 tests/neg-scalajs/native-load-spec-argument.scala create mode 100644 tests/neg-scalajs/native-load-spec-duplicate.check create mode 100644 tests/neg-scalajs/native-load-spec-duplicate.scala create mode 100644 tests/neg-scalajs/native-load-spec-missing.check create mode 100644 tests/neg-scalajs/native-load-spec-missing.scala create mode 100644 tests/neg-scalajs/native-load-spec-need-explicit-name.check create mode 100644 tests/neg-scalajs/native-load-spec-need-explicit-name.scala create mode 100644 tests/neg-scalajs/native-load-spec-nested.check create mode 100644 tests/neg-scalajs/native-load-spec-nested.scala create mode 100644 tests/neg-scalajs/native-load-spec-on-non-js-native.check create mode 100644 tests/neg-scalajs/native-load-spec-on-non-js-native.scala create mode 100644 tests/neg-scalajs/native-load-spec-on-trait.check create mode 100644 tests/neg-scalajs/native-load-spec-on-trait.scala create mode 100644 tests/neg-scalajs/non-native-js-types-inside-js-native.check create mode 100644 tests/neg-scalajs/non-native-js-types-inside-js-native.scala create mode 100644 tests/neg-scalajs/package-object-extends-js-any.check create mode 100644 tests/neg-scalajs/package-object-extends-js-any.scala create mode 100644 tests/neg-scalajs/scala-types-inside-js-native.check create mode 100644 tests/neg-scalajs/scala-types-inside-js-native.scala diff --git a/.appveyor.yml b/.appveyor.yml index fd7e5630bd59..c7a70fd96a8c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,3 +18,4 @@ test_script: # - cmd: sbt test # - cmd: sbt dotty-bootstrapped/test - cmd: sbt sjsJUnitTests/test + - cmd: sbt sjsCompilerTests/test diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 8378c9a9eec5..fd612ba0dd8a 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -36,6 +36,8 @@ import org.scalajs.ir.OriginalName import org.scalajs.ir.OriginalName.NoOriginalName import org.scalajs.ir.Trees.OptimizerHints +import dotty.tools.dotc.transform.sjs.JSSymUtils._ + import JSEncoding._ import JSInterop._ import ScopedVar.withScopedVars @@ -60,6 +62,7 @@ class JSCodeGen()(using genCtx: Context) { import JSCodeGen._ import tpd._ + private val sjsPlatform = dotty.tools.dotc.config.SJSPlatform.sjsPlatform private val jsdefn = JSDefinitions.jsdefn private val primitives = new JSPrimitives(genCtx) @@ -461,14 +464,7 @@ class JSCodeGen()(using genCtx: Context) { val superClass = if (sym.is(Trait)) None else Some(encodeClassNameIdent(sym.superClass)) - val jsNativeLoadSpec = { - if (sym.is(Trait)) None - else if (sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) None - else { - val path = fullJSNameOf(sym).split('.').toList - Some(js.JSNativeLoadSpec.Global(path.head, path.tail)) - } - } + val jsNativeLoadSpec = computeJSNativeLoadSpecOfClass(sym) js.ClassDef( classIdent, @@ -1008,6 +1004,30 @@ class JSCodeGen()(using genCtx: Context) { result } + private def genExpr(name: JSName)(implicit pos: SourcePosition): js.Tree = name match { + case JSName.Literal(name) => js.StringLiteral(name) + case JSName.Computed(sym) => genComputedJSName(sym) + } + + private def genComputedJSName(sym: Symbol)(implicit pos: SourcePosition): js.Tree = { + /* By construction (i.e. restriction in PrepJSInterop), we know that sym + * must be a static method. + * Therefore, at this point, we can invoke it by loading its owner and + * calling it. + */ + def moduleOrGlobalScope = genLoadModuleOrGlobalScope(sym.owner) + def module = genLoadModule(sym.owner) + + if (sym.owner.isJSType) { + if (!sym.owner.isNonNativeJSClass || sym.isJSExposed) + genApplyJSMethodGeneric(sym, moduleOrGlobalScope, args = Nil, isStat = false) + else + genApplyJSClassMethod(module, sym, arguments = Nil) + } else { + genApplyMethod(module, sym, arguments = Nil) + } + } + /** Gen JS code for a tree in expression position (in the IR) or the * global scope. */ @@ -2096,7 +2116,7 @@ class JSCodeGen()(using genCtx: Context) { genApplyStatic(sym, genActualArgs(sym, args)) } else if (isJSType(sym.owner)) { //if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym)) - genApplyJSMethodGeneric(tree, sym, genExprOrGlobalScope(receiver), genActualJSArgs(sym, args), isStat) + genApplyJSMethodGeneric(sym, genExprOrGlobalScope(receiver), genActualJSArgs(sym, args), isStat)(tree.sourcePos) /*else genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/ } else { @@ -2115,19 +2135,17 @@ class JSCodeGen()(using genCtx: Context) { * - Getters and parameterless methods are translated as `JSBracketSelect` * - Setters are translated to `Assign` to `JSBracketSelect` */ - private def genApplyJSMethodGeneric(tree: Tree, sym: Symbol, + private def genApplyJSMethodGeneric(sym: Symbol, receiver: MaybeGlobalScope, args: List[js.TreeOrJSSpread], isStat: Boolean, jsSuperClassValue: Option[js.Tree] = None)( - implicit pos: Position): js.Tree = { - - implicit val pos: SourcePosition = tree.sourcePos + implicit pos: SourcePosition): js.Tree = { def noSpread = !args.exists(_.isInstanceOf[js.JSSpread]) val argc = args.size // meaningful only for methods that don't have varargs def requireNotSuper(): Unit = { if (jsSuperClassValue.isDefined) - report.error("Illegal super call in Scala.js-defined JS class", tree.sourcePos) + report.error("Illegal super call in Scala.js-defined JS class", pos) } def requireNotSpread(arg: js.TreeOrJSSpread): js.Tree = @@ -2156,7 +2174,7 @@ class JSCodeGen()(using genCtx: Context) { js.JSFunctionApply(ruleOutGlobalScope(receiver), args) case _ => - def jsFunName = js.StringLiteral(jsNameOf(sym)) + def jsFunName = genExpr(jsNameOf(sym)) def genSuperReference(propName: js.Tree): js.Tree = { jsSuperClassValue.fold[js.Tree] { @@ -3479,6 +3497,84 @@ class JSCodeGen()(using genCtx: Context) { } } + private def computeJSNativeLoadSpecOfClass(sym: Symbol): Option[js.JSNativeLoadSpec] = { + if (sym.is(Trait) || sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) { + None + } else { + atPhase(picklerPhase.next) { + if (sym.owner.isStaticOwner) + Some(computeJSNativeLoadSpecOfInPhase(sym)) + else + None + } + } + } + + private def computeJSNativeLoadSpecOfInPhase(sym: Symbol)(using Context): js.JSNativeLoadSpec = { + import js.JSNativeLoadSpec._ + + val symOwner = sym.owner + + // Marks a code path as unexpected because it should have been reported as an error in `PrepJSInterop`. + def unexpected(msg: String): Nothing = + throw new FatalError(i"$msg for ${sym.fullName} at ${sym.srcPos}") + + if (symOwner.hasAnnotation(jsdefn.JSNativeAnnot)) { + val jsName = sym.jsName match { + case JSName.Literal(jsName) => jsName + case JSName.Computed(_) => unexpected("could not read the simple JS name as a string literal") + } + + if (symOwner.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) { + Global(jsName, Nil) + } else { + val ownerLoadSpec = computeJSNativeLoadSpecOfInPhase(symOwner) + ownerLoadSpec match { + case Global(globalRef, path) => + Global(globalRef, path :+ jsName) + case Import(module, path) => + Import(module, path :+ jsName) + case ImportWithGlobalFallback(Import(module, modulePath), Global(globalRef, globalPath)) => + ImportWithGlobalFallback( + Import(module, modulePath :+ jsName), + Global(globalRef, globalPath :+ jsName)) + } + } + } else { + def parsePath(pathName: String): List[String] = + pathName.split('.').toList + + def parseGlobalPath(pathName: String): Global = { + val globalRef :: path = parsePath(pathName) + Global(globalRef, path) + } + + val annot = sym.annotations.find { annot => + annot.symbol == jsdefn.JSGlobalAnnot || annot.symbol == jsdefn.JSImportAnnot + }.getOrElse { + unexpected("could not find the JS native load spec annotation") + } + + if (annot.symbol == jsdefn.JSGlobalAnnot) { + val pathName = annot.argumentConstantString(0).getOrElse { + sym.defaultJSName + } + parseGlobalPath(pathName) + } else { // annot.symbol == jsdefn.JSImportAnnot + val module = annot.argumentConstantString(0).getOrElse { + unexpected("could not read the module argument as a string literal") + } + val path = annot.argumentConstantString(1).fold[List[String]](Nil)(parsePath) + val importSpec = Import(module, path) + annot.argumentConstantString(2).fold[js.JSNativeLoadSpec] { + importSpec + } { globalPathName => + ImportWithGlobalFallback(importSpec, parseGlobalPath(globalPathName)) + } + } + } + } + private def isMethodStaticInIR(sym: Symbol): Boolean = sym.is(JavaStatic) diff --git a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala index d38baf096568..17acfd83cac9 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala @@ -36,6 +36,8 @@ final class JSDefinitions()(using Context) { def JSPackage_constructorOf(using Context) = JSPackage_constructorOfR.symbol @threadUnsafe lazy val JSPackage_nativeR = ScalaJSJSPackageClass.requiredMethodRef("native") def JSPackage_native(using Context) = JSPackage_nativeR.symbol + @threadUnsafe lazy val JSPackage_undefinedR = ScalaJSJSPackageClass.requiredMethodRef("undefined") + def JSPackage_undefined(using Context) = JSPackage_undefinedR.symbol @threadUnsafe lazy val JSNativeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.native") def JSNativeAnnot(using Context) = JSNativeAnnotType.symbol.asClass @@ -50,6 +52,11 @@ final class JSDefinitions()(using Context) { @threadUnsafe lazy val PseudoUnionType: TypeRef = requiredClassRef("scala.scalajs.js.|") def PseudoUnionClass(using Context) = PseudoUnionType.symbol.asClass + @threadUnsafe lazy val PseudoUnionModuleRef = requiredModuleRef("scala.scalajs.js.|") + def PseudoUnionModule(using Context) = PseudoUnionModuleRef.symbol + @threadUnsafe lazy val PseudoUnion_fromTypeConstructorR = PseudoUnionModule.requiredMethodRef("fromTypeConstructor") + def PseudoUnion_fromTypeConstructor(using Context) = PseudoUnion_fromTypeConstructorR.symbol + @threadUnsafe lazy val JSArrayType: TypeRef = requiredClassRef("scala.scalajs.js.Array") def JSArrayClass(using Context) = JSArrayType.symbol.asClass @@ -63,6 +70,10 @@ final class JSDefinitions()(using Context) { @threadUnsafe lazy val JavaScriptExceptionType: TypeRef = requiredClassRef("scala.scalajs.js.JavaScriptException") def JavaScriptExceptionClass(using Context) = JavaScriptExceptionType.symbol.asClass + @threadUnsafe lazy val JSGlobalAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSGlobal") + def JSGlobalAnnot(using Context) = JSGlobalAnnotType.symbol.asClass + @threadUnsafe lazy val JSImportAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSImport") + def JSImportAnnot(using Context) = JSImportAnnotType.symbol.asClass @threadUnsafe lazy val JSGlobalScopeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSGlobalScope") def JSGlobalScopeAnnot(using Context) = JSGlobalScopeAnnotType.symbol.asClass @threadUnsafe lazy val JSNameAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSName") @@ -73,21 +84,24 @@ final class JSDefinitions()(using Context) { def JSBracketAccessAnnot(using Context) = JSBracketAccessAnnotType.symbol.asClass @threadUnsafe lazy val JSBracketCallAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSBracketCall") def JSBracketCallAnnot(using Context) = JSBracketCallAnnotType.symbol.asClass + @threadUnsafe lazy val JSExportTopLevelAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportTopLevel") + def JSExportTopLevelAnnot(using Context) = JSExportTopLevelAnnotType.symbol.asClass @threadUnsafe lazy val JSExportAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExport") def JSExportAnnot(using Context) = JSExportAnnotType.symbol.asClass - @threadUnsafe lazy val JSExportDescendentObjectsAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentObjects") - def JSExportDescendentObjectsAnnot(using Context) = JSExportDescendentObjectsAnnotType.symbol.asClass - @threadUnsafe lazy val JSExportDescendentClassesAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentClasses") - def JSExportDescendentClassesAnnot(using Context) = JSExportDescendentClassesAnnotType.symbol.asClass + @threadUnsafe lazy val JSExportStaticAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportStatic") + def JSExportStaticAnnot(using Context) = JSExportStaticAnnotType.symbol.asClass @threadUnsafe lazy val JSExportAllAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportAll") def JSExportAllAnnot(using Context) = JSExportAllAnnotType.symbol.asClass - @threadUnsafe lazy val JSExportNamedAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportNamed") - def JSExportNamedAnnot(using Context) = JSExportNamedAnnotType.symbol.asClass - @threadUnsafe lazy val RawJSTypeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.RawJSType") - def RawJSTypeAnnot(using Context) = RawJSTypeAnnotType.symbol.asClass - @threadUnsafe lazy val ExposedJSMemberAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.ExposedJSMember") + @threadUnsafe lazy val JSTypeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.internal.JSType") + def JSTypeAnnot(using Context) = JSTypeAnnotType.symbol.asClass + @threadUnsafe lazy val JSOptionalAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.internal.JSOptional") + def JSOptionalAnnot(using Context) = JSOptionalAnnotType.symbol.asClass + @threadUnsafe lazy val ExposedJSMemberAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.internal.ExposedJSMember") def ExposedJSMemberAnnot(using Context) = ExposedJSMemberAnnotType.symbol.asClass + @threadUnsafe lazy val JSImportNamespaceModuleRef = requiredModuleRef("scala.scalajs.js.annotation.JSImport.Namespace") + def JSImportNamespaceModule(using Context) = JSImportNamespaceModuleRef.symbol + @threadUnsafe lazy val JSAnyModuleRef = requiredModuleRef("scala.scalajs.js.Any") def JSAnyModule(using Context) = JSAnyModuleRef.symbol @threadUnsafe lazy val JSAny_fromFunctionR = (0 to 22).map(n => JSAnyModule.requiredMethodRef("fromFunction" + n)).toArray diff --git a/compiler/src/dotty/tools/backend/sjs/JSInterop.scala b/compiler/src/dotty/tools/backend/sjs/JSInterop.scala index d9b5aaef3856..3e79c615cb1f 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSInterop.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSInterop.scala @@ -7,23 +7,24 @@ import Symbols._ import NameOps._ import StdNames._ import Phases._ -import NameKinds.DefaultGetterName -import JSDefinitions._ +import dotty.tools.dotc.transform.sjs.JSSymUtils._ -/** Management of the interoperability with JavaScript. */ +/** Management of the interoperability with JavaScript. + * + * This object only contains forwarders for extension methods in + * `transform.sjs.JSSymUtils`. They are kept to minimize changes in + * `JSCodeGen` in the short term, but it will eventually be removed. + */ object JSInterop { /** Is this symbol a JavaScript type? */ - def isJSType(sym: Symbol)(using Context): Boolean = { - atPhase(erasurePhase) { - sym.derivesFrom(jsdefn.JSAnyClass) || sym == jsdefn.PseudoUnionClass - } - } + def isJSType(sym: Symbol)(using Context): Boolean = + sym.isJSType /** Is this symbol a Scala.js-defined JS class, i.e., a non-native JS class? */ def isScalaJSDefinedJSClass(sym: Symbol)(using Context): Boolean = - isJSType(sym) && !sym.hasAnnotation(jsdefn.JSNativeAnnot) + sym.isNonNativeJSClass /** Should this symbol be translated into a JS getter? * @@ -31,11 +32,8 @@ object JSInterop { * Unlike `SymDenotations.isGetter`, it applies to user-defined methods as * much as *accessor* methods created for `val`s and `var`s. */ - def isJSGetter(sym: Symbol)(using Context): Boolean = { - sym.info.firstParamTypes.isEmpty && atPhase(erasurePhase) { - sym.info.isParameterless - } - } + def isJSGetter(sym: Symbol)(using Context): Boolean = + sym.isJSGetter /** Should this symbol be translated into a JS setter? * @@ -44,21 +42,21 @@ object JSInterop { * much as *accessor* methods created for `var`s. */ def isJSSetter(sym: Symbol)(using Context): Boolean = - sym.name.isSetterName && sym.is(Method) + sym.isJSSetter /** Should this symbol be translated into a JS bracket access? * * This is true for methods annotated with `@JSBracketAccess`. */ def isJSBracketAccess(sym: Symbol)(using Context): Boolean = - sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) + sym.isJSBracketAccess /** Should this symbol be translated into a JS bracket call? * * This is true for methods annotated with `@JSBracketCall`. */ def isJSBracketCall(sym: Symbol)(using Context): Boolean = - sym.hasAnnotation(jsdefn.JSBracketCallAnnot) + sym.isJSBracketCall /** Is this symbol a default param accessor for a JS method? * @@ -66,52 +64,15 @@ object JSInterop { * the companion *class* of the owner is a JS type; not whether the owner * is a JS type. */ - def isJSDefaultParam(sym: Symbol)(using Context): Boolean = { - sym.name.is(DefaultGetterName) && { - val owner = sym.owner - if (owner.is(ModuleClass)) { - val isConstructor = sym.name match { - case DefaultGetterName(methName, _) => methName == nme.CONSTRUCTOR - case _ => false - } - if (isConstructor) - isJSType(owner.linkedClass) - else - isJSType(owner) - } else { - isJSType(owner) - } - } - } + def isJSDefaultParam(sym: Symbol)(using Context): Boolean = + sym.isJSDefaultParam /** Gets the unqualified JS name of a symbol. * * If it is not explicitly specified with an `@JSName` annotation, the * JS name is inferred from the Scala name. */ - def jsNameOf(sym: Symbol)(using Context): String = { - sym.getAnnotation(jsdefn.JSNameAnnot).flatMap(_.argumentConstant(0)).fold { - val base = sym.name.unexpandedName.decode.toString.stripSuffix("_=") - if (sym.is(ModuleClass)) base.stripSuffix("$") - else if (!sym.is(Method)) base.stripSuffix(" ") - else base - } { constant => - constant.stringValue - } - } - - /** Gets the fully qualified JS name of a static class of module Symbol. - * - * This is the JS name of the symbol qualified by the fully qualified JS - * name of its original owner if the latter is a native JS object. - */ - def fullJSNameOf(sym: Symbol)(using Context): String = { - assert(sym.isClass, s"fullJSNameOf called for non-class symbol $sym") - sym.getAnnotation(jsdefn.JSFullNameAnnot).flatMap(_.argumentConstant(0)).fold { - jsNameOf(sym) - } { constant => - constant.stringValue - } - } + def jsNameOf(sym: Symbol)(using Context): JSName = + sym.jsName } diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index f3310db45892..10e779dd9b23 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -41,6 +41,7 @@ class Compiler { List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files List(new PostTyper) :: // Additional checks and cleanups after type checking + List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only) List(new Staging) :: // Check PCP, heal quoted types and expand macros List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks List(new SetRootTree) :: // Set the `rootTreeOrProvider` on class symbols diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index bb5da77063a7..8ad80e0b6862 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -61,6 +61,9 @@ class JavaPlatform extends Platform { (sym derivesFrom BoxedBooleanClass) } + def shouldReceiveJavaSerializationMethods(sym: ClassSymbol)(using Context): Boolean = + true + def newClassLoader(bin: AbstractFile)(using Context): SymbolLoader = new ClassfileLoader(bin) } diff --git a/compiler/src/dotty/tools/dotc/config/Platform.scala b/compiler/src/dotty/tools/dotc/config/Platform.scala index 47633d4518d7..4c705d958a5a 100644 --- a/compiler/src/dotty/tools/dotc/config/Platform.scala +++ b/compiler/src/dotty/tools/dotc/config/Platform.scala @@ -31,6 +31,9 @@ abstract class Platform { /** The various ways a boxed primitive might materialize at runtime. */ def isMaybeBoxed(sym: ClassSymbol)(using Context): Boolean + /** Is the given class symbol eligible for Java serialization-specific methods? */ + def shouldReceiveJavaSerializationMethods(sym: ClassSymbol)(using Context): Boolean + /** Create a new class loader to load class file `bin` */ def newClassLoader(bin: AbstractFile)(using Context): SymbolLoader @@ -44,4 +47,3 @@ abstract class Platform { case _ => false } } - diff --git a/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala b/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala index 8b69817224a1..7fefe7ff456d 100644 --- a/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala @@ -3,9 +3,18 @@ package dotty.tools.dotc.config import dotty.tools.dotc.core._ import Contexts._ import Symbols._ +import SymDenotations._ import dotty.tools.backend.sjs.JSDefinitions +import org.scalajs.ir.Trees.JSNativeLoadSpec + +object SJSPlatform { + /** The `SJSPlatform` for the current context. */ + def sjsPlatform(using Context): SJSPlatform = + ctx.platform.asInstanceOf[SJSPlatform] +} + class SJSPlatform()(using Context) extends JavaPlatform { /** Scala.js-specific definitions. */ @@ -16,4 +25,15 @@ class SJSPlatform()(using Context) extends JavaPlatform { defn.isFunctionClass(cls) || jsDefinitions.isJSFunctionClass(cls) || jsDefinitions.isJSThisFunctionClass(cls) + + /** Is the given class symbol eligible for Java serialization-specific methods? + * + * This is not simply false because we still want to add them to Scala classes + * and objects. They might be transitively used by macros and other compile-time + * code. It feels safer to have them be somewhat equivalent to the ones we would + * get in a JVM project. The JVM back-end will slap an extends `java.io.Serializable` + * to them, so we should be consistent and also emit the proper serialization methods. + */ + override def shouldReceiveJavaSerializationMethods(sym: ClassSymbol)(using Context): Boolean = + !sym.isSubClass(jsDefinitions.JSAnyClass) } diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 7e0688dba03b..88542527442b 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -33,7 +33,10 @@ object Annotations { if (i < args.length) Some(args(i)) else None } def argumentConstant(i: Int)(using Context): Option[Constant] = - for (ConstantType(c) <- argument(i) map (_.tpe)) yield c + for (ConstantType(c) <- argument(i) map (_.tpe.widenTermRefExpr.normalized)) yield c + + def argumentConstantString(i: Int)(using Context): Option[String] = + for (Constant(s: String) <- argumentConstant(i)) yield s /** The tree evaluaton is in progress. */ def isEvaluating: Boolean = false diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index f8504d625904..ce5235720a07 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -259,6 +259,10 @@ object SymDenotations { final def addAnnotation(annot: Annotation): Unit = annotations = annot :: myAnnotations + /** Add the given annotation without parameters to the annotations of this denotation */ + final def addAnnotation(cls: ClassSymbol)(using Context): Unit = + addAnnotation(Annotation(cls)) + /** Remove annotation with given class from this denotation */ final def removeAnnotation(cls: Symbol)(using Context): Unit = annotations = myAnnotations.filterNot(_ matches cls) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 0d47b19249e7..62500b758612 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -188,11 +188,15 @@ object SymUtils { def hasAnonymousChild(using Context): Boolean = self.children.exists(_ `eq` self) + /** Is this symbol directly owner by a term symbol, i.e., is it local to a block? */ + def isLocalToBlock(using Context): Boolean = + self.owner.isTerm + /** Is symbol directly or indirectly owned by a term symbol? */ @tailrec final def isLocal(using Context): Boolean = { val owner = self.maybeOwner if (!owner.exists) false - else if (owner.isTerm) true + else if (isLocalToBlock) true else if (owner.is(Package)) false else owner.isLocal } diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index d1551387ef37..bbd2f13395a0 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -397,7 +397,11 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * we do that even for objects that are not serializable at this phase. */ def serializableObjectMethod(clazz: ClassSymbol)(using Context): List[Tree] = - if clazz.is(Module) && clazz.isStatic && !hasWriteReplace(clazz) then + if clazz.is(Module) + && clazz.isStatic + && !hasWriteReplace(clazz) + && ctx.platform.shouldReceiveJavaSerializationMethods(clazz) + then List( DefDef(writeReplaceDef(clazz), _ => New(defn.ModuleSerializationProxyClass.typeRef, @@ -424,6 +428,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { && !clazz.derivesFrom(defn.JavaEnumClass) && clazz.isSerializable && !hasReadResolve(clazz) + && ctx.platform.shouldReceiveJavaSerializationMethods(clazz) then List( DefDef(readResolveDef(clazz), diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala new file mode 100644 index 000000000000..3d5cead775d0 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala @@ -0,0 +1,132 @@ +package dotty.tools.dotc +package transform +package sjs + +import core._ +import util.SrcPos +import Annotations._ +import Constants._ +import Contexts._ +import Decorators._ +import DenotTransformers._ +import Flags._ +import NameKinds.DefaultGetterName +import NameOps._ +import Names._ +import Phases._ +import Scopes._ +import StdNames._ +import Symbols._ +import SymDenotations._ +import SymUtils._ +import ast.Trees._ +import Types._ + +import dotty.tools.backend.sjs.JSDefinitions.jsdefn + +/** Additional extensions for `Symbol`s that are only relevant for Scala.js. */ +object JSSymUtils { + /** The result type for `sym.jsName`. + * + * It is either a literal string, or a computed name represented by a reference + * to a static `Symbol` (a `dotc.core.Symbols.Symbol`, not a `js.Symbol`). + */ + enum JSName { + case Literal(name: String) + case Computed(sym: Symbol) + + def displayName(using Context): String = this match { + case Literal(name) => name + case Computed(sym) => sym.fullName.toString() + } + } + + extension (sym: Symbol) { + /** Is this symbol a JavaScript type? */ + def isJSType(using Context): Boolean = { + atPhase(erasurePhase) { + sym.derivesFrom(jsdefn.JSAnyClass) || sym == jsdefn.PseudoUnionClass + } + } + + /** Is this symbol a non-native JS class? */ + def isNonNativeJSClass(using Context): Boolean = + sym.isJSType && !sym.hasAnnotation(jsdefn.JSNativeAnnot) + + /** Tests whether the given member is exposed, i.e., whether it was + * originally a public or protected member of a non-native JS class. + */ + def isJSExposed(using Context): Boolean = { + !sym.is(Bridge) && { + if (sym.is(Lazy)) + sym.is(Accessor) && sym.field.hasAnnotation(jsdefn.ExposedJSMemberAnnot) + else + sym.hasAnnotation(jsdefn.ExposedJSMemberAnnot) + } + } + + /** Should this symbol be translated into a JS getter? */ + def isJSGetter(using Context): Boolean = { + sym.is(Module) || ( + sym.is(Method) + && sym.info.firstParamTypes.isEmpty + && atPhaseNoLater(erasurePhase)(sym.info.isParameterless)) + } + + /** Should this symbol be translated into a JS setter? */ + def isJSSetter(using Context): Boolean = + sym.originalName.isSetterName && sym.is(Method) + + /** Should this symbol be translated into a JS bracket access? */ + def isJSBracketAccess(using Context): Boolean = + sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) + + /** Should this symbol be translated into a JS bracket call? */ + def isJSBracketCall(using Context): Boolean = + sym.hasAnnotation(jsdefn.JSBracketCallAnnot) + + /** Is this symbol a default param accessor for a JS method? + * + * For default param accessors of *constructors*, we need to test whether + * the companion *class* of the owner is a JS type; not whether the owner + * is a JS type. + */ + def isJSDefaultParam(using Context): Boolean = { + sym.name.is(DefaultGetterName) && { + val owner = sym.owner + if (owner.is(ModuleClass)) { + val isConstructor = sym.name match { + case DefaultGetterName(methName, _) => methName == nme.CONSTRUCTOR + case _ => false + } + if (isConstructor) + owner.linkedClass.isJSType + else + owner.isJSType + } else { + owner.isJSType + } + } + } + + /** Gets the unqualified JS name of the symbol. + * + * If it is not explicitly specified with an `@JSName` annotation, the + * JS name is inferred from the Scala name. + */ + def jsName(using Context): JSName = { + sym.getAnnotation(jsdefn.JSNameAnnot).fold[JSName] { + JSName.Literal(defaultJSName) + } { annotation => + annotation.arguments.head match { + case Literal(Constant(name: String)) => JSName.Literal(name) + case tree => JSName.Computed(tree.symbol) + } + } + } + + def defaultJSName(using Context): String = + if (sym.isTerm) sym.asTerm.name.unexpandedName.getterName.toString() + else sym.name.unexpandedName.stripModuleClassSuffix.toString() + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala new file mode 100644 index 000000000000..4a765e2e69dc --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala @@ -0,0 +1,49 @@ +package dotty.tools.dotc +package transform +package sjs + +import dotty.tools.dotc.ast.{Trees, tpd, untpd} +import scala.collection.mutable +import core._ +import dotty.tools.dotc.typer.Checking +import dotty.tools.dotc.typer.Inliner +import dotty.tools.dotc.typer.VarianceChecker +import Types._ +import Contexts._ +import Names._ +import Flags._ +import DenotTransformers._ +import Phases._ +import SymDenotations._ +import StdNames._ +import Annotations._ +import Trees._ +import Scopes._ +import Decorators._ +import Symbols._ +import SymUtils._ +import NameOps._ +import reporting._ +import util.SrcPos + +import dotty.tools.backend.sjs.JSDefinitions.jsdefn + +object PrepJSExports { + import tpd._ + + def registerClassOrModuleExports(sym: Symbol)(using Context): Unit = { + // TODO + } + + /** Generate the exporter for the given DefDef + * or ValDef (abstract val in class, val in trait or lazy val; + * these don't get DefDefs until the fields phase) + * + * If this DefDef is a constructor, it is registered to be exported by + * GenJSCode instead and no trees are returned. + */ + def genExportMember(baseSym: Symbol)(using Context): List[Tree] = { + // TODO + Nil + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala new file mode 100644 index 000000000000..02b531b93113 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala @@ -0,0 +1,1055 @@ +package dotty.tools.dotc +package transform +package sjs + +import scala.collection.mutable + +import ast.{Trees, tpd, untpd} +import core._ +import reporting._ +import typer.Checking +import util.SrcPos +import Annotations._ +import Constants._ +import Contexts._ +import Decorators._ +import DenotTransformers._ +import Flags._ +import NameKinds.DefaultGetterName +import NameOps._ +import Names._ +import Phases._ +import Scopes._ +import StdNames._ +import Symbols._ +import SymDenotations._ +import SymUtils._ +import Trees._ +import Types._ + +import JSSymUtils._ + +import org.scalajs.ir.Trees.{JSGlobalRef, JSNativeLoadSpec} + +import dotty.tools.dotc.config.SJSPlatform.sjsPlatform +import dotty.tools.backend.sjs.JSDefinitions.jsdefn + +/** A macro transform that runs after typer and before pickler to perform + * additional Scala.js-specific checks and transformations necessary for + * interoperability with JavaScript. + * + * It performs the following functions: + * + * - Sanity checks for the js.Any hierarchy + * - Annotate subclasses of js.Any to be treated specially + * - Create JSExport methods: Dummy methods that are propagated + * through the whole compiler chain to mark exports. This allows + * exports to have the same semantics than methods. + * + * This is the equivalent of `PrepJSInterop` in Scala 2, minus the handling + * of `scala.Enumeration`. + * + * The reason for making this a macro transform is that some functions (in particular + * all the checks that behave differently depending on properties of classes in + * the enclosing class chain) are naturally top-down and don't lend themselves to the + * bottom-up approach of a mini phase. + * + * In addition, the addition of export forwarders must be done before pickling to + * be signature-compatible with scalac, and there are only macro transforms before + * pickling. + */ +class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisPhase => + import PrepJSInterop._ + import tpd._ + + override def phaseName: String = PrepJSInterop.name + + override def isEnabled(using Context): Boolean = + ctx.settings.scalajs.value + + override def changesMembers: Boolean = true // the phase adds export forwarders + + protected def newTransformer(using Context): Transformer = + new ScalaJSPrepJSInteropTransformer + + class ScalaJSPrepJSInteropTransformer extends Transformer with Checking { + import PrepJSExports._ + + /** Kind of the directly enclosing (most nested) owner. */ + private var enclosingOwner: OwnerKind = OwnerKind.None + + /** Cumulative kinds of all enclosing owners. */ + private var allEnclosingOwners: OwnerKind = OwnerKind.None + + /** Nicer syntax for `allEnclosingOwners is kind`. */ + private def anyEnclosingOwner: OwnerKind = allEnclosingOwners + + private def enterOwner[A](kind: OwnerKind)(body: => A): A = { + require(kind.isBaseKind, kind) + val oldEnclosingOwner = enclosingOwner + val oldAllEnclosingOwners = allEnclosingOwners + enclosingOwner = kind + allEnclosingOwners |= kind + try { + body + } finally { + enclosingOwner = oldEnclosingOwner + allEnclosingOwners = oldAllEnclosingOwners + } + } + + /** DefDefs in class templates that export methods to JavaScript */ + private val exporters = mutable.Map.empty[Symbol, mutable.ListBuffer[Tree]] + + override def transform(tree: Tree)(using Context): Tree = { + tree match { + case tree: ValDef if tree.symbol.is(Module) => + /* Never apply this transformation on the term definition of modules. + * Instead, all relevant checks are performed on the module class definition. + */ + super.transform(tree) + + case tree: MemberDef => transformMemberDef(tree) + case tree: Template => transformTemplate(tree) + case _ => transformStatOrExpr(tree) + } + } + + private def transformMemberDef(tree: MemberDef)(using Context): Tree = { + val sym = tree.symbol + + checkInternalAnnotations(sym) + + /* Checks related to @js.native: + * - if @js.native, verify that it is allowed in this context, and if + * yes, compute and store the JS native load spec + * - if not @js.native, verify that we do not use any other annotation + * reserved for @js.native members (namely, JS native load spec annots) + */ + val isJSNative = sym.getAnnotation(jsdefn.JSNativeAnnot) match { + case Some(annot) => + checkJSNativeDefinition(tree, annot.tree, sym) + true + case None => + checkJSNativeSpecificAnnotsOnNonJSNative(tree) + false + } + + checkJSNameAnnots(sym) + + markExposedIfRequired(tree.symbol) + + tree match { + case tree: TypeDef if tree.isClassDef => + registerClassOrModuleExports(sym) + if (isJSAny(sym)) + transformJSClassDef(tree) + else + transformScalaClassDef(tree) + + case _: TypeDef => + super.transform(tree) + + case tree: ValOrDefDef => + /* Prepare exports for methods, local defs and local variables. + * Avoid *fields* (non-local non-method) because they all have a + * corresponding getter, which is the one that handles exports. + * (Note that local-to-block can never have exports, but the error + * messages for that are handled by genExportMember). + */ + if (sym.is(Method) || sym.isLocalToBlock) + exporters.getOrElseUpdate(sym.owner, mutable.ListBuffer.empty) ++= genExportMember(sym) + + if (sym.isLocalToBlock) + super.transform(tree) + else if (isJSNative) + transformJSNativeValOrDefDef(tree) + else if (enclosingOwner is OwnerKind.JSType) + transformValOrDefDefInJSType(tree) + else + super.transform(tree) // There is nothing special to do for a Scala val or def + } + } + + private def transformScalaClassDef(tree: TypeDef)(using Context): Tree = { + val sym = tree.symbol + + // In native JS things, only js.Any stuff is allowed + if (enclosingOwner is OwnerKind.JSNative) { + /* We have to allow synthetic companion objects here, as they get + * generated when a nested native JS class has default arguments in + * its constructor (see #1891). + */ + if (!sym.is(Synthetic)) { + report.error( + "Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any)", + tree) + } + } + + if (sym == jsdefn.PseudoUnionClass) + sym.addAnnotation(jsdefn.JSTypeAnnot) + + val kind = + if (sym.is(Module)) OwnerKind.ScalaMod + else OwnerKind.ScalaClass + enterOwner(kind) { + super.transform(tree) + } + } + + private def transformTemplate(tree: Template)(using Context): Template = { + // First, recursively transform the template + val transformedTree = super.transform(tree).asInstanceOf[Template] + + val clsSym = ctx.owner + + // Check that @JSExportStatic fields come first + if (clsSym.is(ModuleClass)) { // quick check to avoid useless work + var foundStatOrNonStaticVal: Boolean = false + for (tree <- transformedTree.body) { + tree match { + case vd: ValDef if vd.symbol.hasAnnotation(jsdefn.JSExportStaticAnnot) => + if (foundStatOrNonStaticVal) { + report.error( + "@JSExportStatic vals and vars must be defined before any other val/var, and before any constructor statement.", + vd) + } + case vd: ValDef if !vd.symbol.is(Lazy) => + foundStatOrNonStaticVal = true + case _: MemberDef => + case _ => + foundStatOrNonStaticVal = true + } + } + } + + // Add exports to the template, if there are any + exporters.get(clsSym).fold { + transformedTree + } { exports => + cpy.Template(transformedTree)( + transformedTree.constr, + transformedTree.parents, + Nil, + transformedTree.self, + transformedTree.body ::: exports.toList + ) + } + } + + private def transformStatOrExpr(tree: Tree)(using Context): Tree = { + tree match { + // Validate js.constructorOf[T] + case TypeApply(ctorOfTree, List(tpeArg)) + if ctorOfTree.symbol == jsdefn.JSPackage_constructorOf => + validateJSConstructorOf(tree, tpeArg) + super.transform(tree) + + /* Rewrite js.ConstructorTag.materialize[T] into + * runtime.newConstructorTag[T](js.constructorOf[T]) + */ + case TypeApply(ctorOfTree, List(tpeArg)) + if ctorOfTree.symbol == jsdefn.JSConstructorTag_materialize => + validateJSConstructorOf(tree, tpeArg) + val ctorOf = ref(jsdefn.JSPackage_constructorOf).appliedToTypeTree(tpeArg) + ref(jsdefn.Runtime_newConstructorTag).appliedToType(tpeArg.tpe).appliedTo(ctorOf) + + // Compile-time errors and warnings for js.Dynamic.literal + case Apply(Apply(fun, nameArgs), args) + if fun.symbol == jsdefn.JSDynamicLiteral_applyDynamic || + fun.symbol == jsdefn.JSDynamicLiteral_applyDynamicNamed => + // Check that the first argument list is a constant string "apply" + nameArgs match { + case List(Literal(Constant(s: String))) => + if (s != "apply") + report.error(i"js.Dynamic.literal does not have a method named $s", tree) + case _ => + report.error(i"js.Dynamic.literal.${tree.symbol.name} may not be called directly", tree) + } + + // TODO Warn for known duplicate property names + + super.transform(tree) + + case _ => + super.transform(tree) + } + } + + private def validateJSConstructorOf(tree: Tree, tpeArg: Tree)(using Context): Unit = { + val tpe = checkClassType(tpeArg.tpe, tpeArg.srcPos, traitReq = false, stablePrefixReq = false) + + tpe.underlyingClassRef(refinementOK = false) match { + case typeRef: TypeRef if typeRef.symbol.isOneOf(Trait | ModuleClass) => + report.error(i"non-trait class type required but $tpe found", tpeArg) + case _ => + // an error was already reported above + } + } + + /** Performs checks and rewrites specific to classes / objects extending `js.Any`. */ + private def transformJSClassDef(classDef: TypeDef)(using Context): Tree = { + val sym = classDef.symbol + val isJSNative = sym.hasAnnotation(jsdefn.JSNativeAnnot) + + sym.addAnnotation(jsdefn.JSTypeAnnot) + + // Forbid @EnableReflectiveInstantiation on JS types + sym.getAnnotation(jsdefn.EnableReflectiveInstantiationAnnot).foreach { annot => + report.error( + "@EnableReflectiveInstantiation cannot be used on types extending js.Any.", + annot.tree) + } + + // Forbid package objects that extends js.Any + if (sym.isPackageObject) + report.error("Package objects may not extend js.Any.", classDef) + + // Check that we do not have a case modifier + if (sym.is(Case)) { + report.error( + "Classes and objects extending js.Any may not have a case modifier", + classDef) + } + + // Check the parents + for (parent <- sym.info.parents) { + parent.typeSymbol match { + case parentSym if parentSym == defn.ObjectClass => + // AnyRef is valid, except for non-native JS classes and objects + if (!isJSNative && !sym.is(Trait)) { + report.error( + "Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not).", + classDef) + } + case parentSym if isJSAny(parentSym) => + // A non-native JS type cannot extend a native JS trait + // Otherwise, extending a JS type is valid + if (!isJSNative && parentSym.is(Trait) && parentSym.hasAnnotation(jsdefn.JSNativeAnnot)) { + report.error( + "Non-native JS types cannot directly extend native JS traits.", + classDef) + } + case parentSym if parentSym == defn.DynamicClass => + /* We have to allow scala.Dynamic to be able to define js.Dynamic + * and similar constructs. + * This causes the unsoundness filed as scala-js/scala-js#1385. + */ + case parentSym => + /* This is a Scala class or trait other than AnyRef and Dynamic, + * which is never valid. + */ + report.error( + i"${sym.name} extends ${parentSym.fullName} which does not extend js.Any.", + classDef) + } + } + + // Checks for non-native JS stuff + if (!isJSNative) { + // It cannot be in a native JS class or trait + if (enclosingOwner is OwnerKind.JSNativeClass) { + report.error( + "Native JS classes and traits cannot contain non-native JS classes, traits or objects", + classDef) + } + + // Unless it is a trait, it cannot be in a native JS object + if (!sym.is(Trait) && (enclosingOwner is OwnerKind.JSNativeMod)) { + report.error( + "Native JS objects cannot contain inner non-native JS classes or objects", + classDef) + } + + // Local JS classes cannot be abstract (implementation restriction) + if (sym.is(Abstract, butNot = Trait) && sym.isLocalToBlock) { + report.error( + "Implementation restriction: local JS classes cannot be abstract", + classDef) + } + } + + // Check for consistency of JS semantics across overriding + val overridingPairsCursor = new OverridingPairs.Cursor(sym) + while (overridingPairsCursor.hasNext) { + val overriding = overridingPairsCursor.overriding + val overridden = overridingPairsCursor.overridden + overridingPairsCursor.next() // prepare for next turn + + val clsSym = sym + + if (overriding.isTerm) { + def errorPos = { + if (clsSym == overriding.owner) overriding.srcPos + else if (clsSym == overridden.owner) overridden.srcPos + else clsSym.srcPos + } + + // Some utils inspired by RefChecks + + def infoString0(sym: Symbol, showLocation: Boolean): String = { + val sym1 = sym.underlyingSymbol + def info = clsSym.thisType.memberInfo(sym1) + val infoStr = + if (sym1.is(Module)) "" + else i" of type $info" + i"${if (showLocation) sym1.showLocated else sym1}$infoStr with JS name '${sym.jsName.displayName}'" + } + + def infoString(sym: Symbol): String = infoString0(sym, sym.owner != clsSym) + def infoStringWithLocation(sym: Symbol): String = infoString0(sym, true) + + def emitOverrideError(msg: String): Unit = { + report.error( + "error overriding %s;\n %s %s".format( + infoStringWithLocation(overridden), infoString(overriding), msg), + errorPos) + } + + // Check for overrides with different JS names - issue scala-js/scala-js#1983 + if (overriding.jsName != overridden.jsName) + emitOverrideError("has a different JS name") + + /* Cannot override a non-@JSOptional with an @JSOptional. Unfortunately + * at this point the symbols do not have @JSOptional yet, so we need + * to detect whether it would be applied. + */ + if (!isJSNative) { + def isJSOptional(sym: Symbol): Boolean = { + sym.owner.is(Trait) && !sym.is(Deferred) && !sym.isConstructor && + !sym.owner.hasAnnotation(jsdefn.JSNativeAnnot) + } + if (isJSOptional(overriding) && !(overridden.is(Deferred) || isJSOptional(overridden))) + emitOverrideError("cannot override a concrete member in a non-native JS trait") + } + } + } + + val kind = { + if (!isJSNative) { + if (sym.is(ModuleClass)) OwnerKind.JSMod + else OwnerKind.JSClass + } else { + if (sym.is(ModuleClass)) OwnerKind.JSNativeMod + else OwnerKind.JSNativeClass + } + } + enterOwner(kind) { + super.transform(classDef) + } + } + + private def checkJSNativeDefinition(treePos: SrcPos, annotPos: SrcPos, sym: Symbol)(using Context): Unit = { + // Check if we may have a JS native here + if (sym.isLocalToBlock) { + report.error("@js.native is not allowed on local definitions", annotPos) + } else if (!sym.isClass && (anyEnclosingOwner is (OwnerKind.ScalaClass | OwnerKind.JSType))) { + report.error("@js.native vals and defs can only appear in static Scala objects", annotPos) + } else if (sym.isClass && !isJSAny(sym)) { + report.error("Classes, traits and objects not extending js.Any may not have an @js.native annotation", annotPos) + } else if (anyEnclosingOwner is OwnerKind.ScalaClass) { + report.error("Scala traits and classes may not have native JS members", annotPos) + } else if (enclosingOwner is OwnerKind.JSNonNative) { + report.error("non-native JS classes, traits and objects may not have native JS members", annotPos) + } else { + // The symbol can be annotated with @js.native. Now check its JS native loading spec. + if (sym.is(Trait)) { + for (annot <- sym.annotations) { + val annotSym = annot.symbol + if (isJSNativeLoadingSpecAnnot(annotSym)) + report.error(i"Traits may not have an @${annotSym.name} annotation.", annot.tree) + } + } else { + checkJSNativeLoadSpecOf(treePos, sym) + } + } + } + + private def checkJSNativeLoadSpecOf(pos: SrcPos, sym: Symbol)(using Context): Unit = { + import JSNativeLoadSpec._ + + def checkGlobalRefName(globalRef: String): Unit = { + if (!JSGlobalRef.isValidJSGlobalRefName(globalRef)) + report.error(s"The name of a JS global variable must be a valid JS identifier (got '$globalRef')", pos) + } + + if (enclosingOwner is OwnerKind.JSNative) { + /* We cannot get here for @js.native vals and defs. That would mean we + * have an @js.native val/def inside a JavaScript type, which is not + * allowed and already caught in checkJSNativeDefinition(). + */ + assert(sym.isClass, + s"undetected @js.native val or def ${sym.fullName} inside JS type at $pos") + + for (annot <- sym.annotations) { + val annotSym = annot.symbol + if (isJSNativeLoadingSpecAnnot(annotSym)) + report.error(i"Nested JS classes and objects cannot have an @${annotSym.name} annotation.", annot.tree) + } + + if (sym.owner.isStaticOwner) { + for (annot <- sym.annotations) { + if (annot.symbol == jsdefn.JSNameAnnot && !(annot.arguments.head.tpe.derivesFrom(defn.StringClass))) { + report.error( + "Implementation restriction: " + + "@JSName with a js.Symbol is not supported on nested native classes and objects", + annot.tree) + } + } + + if (sym.owner.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) { + val jsName = sym.jsName match { + case JSName.Literal(jsName) => + checkGlobalRefName(jsName) + case JSName.Computed(_) => + () // compile error above or in `checkJSNameArgument` + } + } + } + } else { + def checkGlobalRefPath(pathName: String): Unit = { + val dotIndex = pathName.indexOf('.') + val globalRef = + if (dotIndex < 0) pathName + else pathName.substring(0, dotIndex) + checkGlobalRefName(globalRef) + } + + checkAndGetJSNativeLoadingSpecAnnotOf(pos, sym) match { + case Some(annot) if annot.symbol == jsdefn.JSGlobalScopeAnnot => + if (!sym.is(Module)) { + report.error( + "@JSGlobalScope can only be used on native JS objects (with @js.native).", + annot.tree) + } + + case Some(annot) if annot.symbol == jsdefn.JSGlobalAnnot => + checkJSGlobalLiteral(annot) + val pathName = annot.argumentConstantString(0).getOrElse { + if ((enclosingOwner is OwnerKind.ScalaMod) && !sym.owner.isPackageObject) { + report.error( + "Native JS members inside non-native objects must have an explicit name in @JSGlobal", + annot.tree) + } + sym.defaultJSName + } + checkGlobalRefPath(pathName) + + case Some(annot) if annot.symbol == jsdefn.JSImportAnnot => + checkJSImportLiteral(annot) + annot.argumentConstantString(2).foreach { globalPathName => + checkGlobalRefPath(globalPathName) + } + + case _ => + // We already emitted an error in checkAndGetJSNativeLoadingSpecAnnotOf + () + } + } + } + + /** Verify a ValOrDefDef that is annotated with `@js.native`. */ + private def transformJSNativeValOrDefDef(tree: ValOrDefDef)(using Context): ValOrDefDef = { + val sym = tree.symbol + + def annotPos(annotSym: Symbol): SrcPos = + sym.getAnnotation(annotSym).get.tree + + if (sym.is(Lazy) || sym.isJSSetter) + report.error("@js.native is not allowed on vars, lazy vals and setter defs", annotPos(jsdefn.JSNativeAnnot)) + else if (sym.isJSBracketAccess) + report.error("@JSBracketAccess is not allowed on @js.native vals and defs", annotPos(jsdefn.JSBracketAccessAnnot)) + else if (sym.isJSBracketCall) + report.error("@JSBracketCall is not allowed on @js.native vals and defs", annotPos(jsdefn.JSBracketCallAnnot)) + + checkRHSCallsJSNative(tree, "@js.native members") + + // Check that we do not override or implement anything from a superclass + val overriddenSymbols = sym.allOverriddenSymbols + if (overriddenSymbols.hasNext) { + val overridden = overriddenSymbols.next() + val verb = if (overridden.is(Deferred)) "implement" else "override" + report.error(i"An @js.native member cannot $verb the inherited member ${overridden.fullName}", tree) + } + + tree + } + + /** Verify a ValOrDefDef inside a js.Any */ + private def transformValOrDefDefInJSType(tree: ValOrDefDef)(using Context): Tree = { + val sym = tree.symbol + + assert(!sym.isLocalToBlock, i"$tree at ${tree.span}") + + sym.name match { + case nme.apply if !sym.hasAnnotation(jsdefn.JSNameAnnot) => + if (!sym.is(Method) || sym.isJSGetter) { + report.error( + "A member named apply represents function application in JavaScript. " + + "A parameterless member should be exported as a property. " + + "You must add @JSName(\"apply\")", + sym) + } else if (enclosingOwner is OwnerKind.JSNonNative) { + report.error( + "A non-native JS class cannot declare a method named `apply` without `@JSName`", + sym) + } + + case nme.equals_ if sym.info.matches(defn.Any_equals.info) => + report.error( + "error overriding method equals(that: Any): Boolean in a JS class;\n" + + " method equals(that: Any): Boolean is considered final in trait js.Any;\n" + + " if you want to define a method named \"equals\" in JavaScript, use a different name and add @JSName(\"equals\").", + sym) + + case nme.hashCode_ if sym.info.matches(defn.Any_hashCode.info) => + report.error( + "error overriding method hashCode(): Int in a JS class;\n" + + " method hashCode(): Int is considered final in trait js.Any;\n" + + " if you want to define a method named \"hashCode\" in JavaScript, use a different name and add @JSName(\"hashCode\").", + sym) + + case _ => + } + + if (sym.isJSSetter) + checkSetterSignature(sym, tree, exported = false) + + if (sym.isJSBracketAccess) { + if (enclosingOwner is OwnerKind.JSNonNative) { + report.error("@JSBracketAccess is not allowed in non-native JS classes", tree) + } else { + val allParamInfos = sym.info.paramInfoss.flatten + + allParamInfos.size match { + case 1 => + // ok + case 2 => + if (!sym.info.finalResultType.isRef(defn.UnitClass)) + report.error("@JSBracketAccess methods with two parameters must return Unit", tree) + case _ => + report.error("@JSBracketAccess methods must have one or two parameters", tree) + } + + if (allParamInfos.exists(_.isRepeatedParam)) + report.error("@JSBracketAccess methods may not have repeated parameters", tree) + if (sym.hasDefaultParams) + report.error("@JSBracketAccess methods may not have default parameters", tree) + } + } + + if (sym.isJSBracketCall) { + if (enclosingOwner is OwnerKind.JSNonNative) { + report.error("@JSBracketCall is not allowed in non-native JS classes", tree) + } else { + // JS bracket calls must have at least one non-repeated parameter + sym.info.stripPoly match { + case mt: MethodType if mt.paramInfos.nonEmpty && !mt.paramInfos.head.isRepeatedParam => + // ok + case _ => + report.error("@JSBracketCall methods must have at least one non-repeated parameter", tree) + } + } + } + + if (sym.hasAnnotation(defn.NativeAnnot)) { + // Native methods are not allowed + report.error("Methods in a js.Any may not be @native", tree) + } + + /* In native JS types, there should not be any private member, except + * private[this] constructors. + */ + if ((enclosingOwner is OwnerKind.JSNative) && isPrivateMaybeWithin(sym)) { + if (sym.isClassConstructor) { + if (!sym.isAllOf(PrivateLocal)) { + report.error( + "Native JS classes may not have private constructors. " + + "Use `private[this]` to declare an internal constructor.", + sym) + } + } else if (!sym.is(ParamAccessor)) { + report.error( + "Native JS classes may not have private members. " + + "Use a public member in a private facade instead.", + tree) + } + } + + if (enclosingOwner is OwnerKind.JSNonNative) { + // Private methods cannot be overloaded + if (sym.is(Method) && isPrivateMaybeWithin(sym)) { + val alts = sym.owner.info.memberBasedOnFlags(sym.name, required = Method) + if (alts.isOverloaded) { + report.error( + "Private methods in non-native JS classes cannot be overloaded. Use different names instead.", + tree) + } + } + + // private[Scope] methods must be final + if (!sym.isOneOf(Final | Protected) && sym.privateWithin.exists && !sym.isClassConstructor) + report.error("Qualified private members in non-native JS classes must be final", tree) + + // Traits must be pure interfaces, except for js.undefined members + if (sym.owner.is(Trait) && sym.isTerm && !sym.isConstructor) { + if (sym.is(Method) && isPrivateMaybeWithin(sym)) { + report.error("A non-native JS trait cannot contain private members", tree) + } else if (sym.is(Lazy)) { + report.error("A non-native JS trait cannot contain lazy vals", tree) + } else if (!sym.is(Deferred)) { + /* Tell the back-end not to emit this thing. In fact, this only + * matters for mixed-in members created from this member. + */ + sym.addAnnotation(jsdefn.JSOptionalAnnot) + + if (!sym.isSetter) { + // Check that methods do not have parens + if (sym.is(Method, butNot = Accessor) && sym.info.stripPoly.isInstanceOf[MethodType]) + report.error("In non-native JS traits, defs with parentheses must be abstract.", tree.rhs) + + // Check that the rhs is `js.undefined` + tree.rhs match { + case sel: Select if sel.symbol == jsdefn.JSPackage_undefined => + // ok + case Apply(Apply(TypeApply(fromTypeConstructorFun, _), (sel: Select) :: Nil), _) + if sel.symbol == jsdefn.JSPackage_undefined + && fromTypeConstructorFun.symbol == jsdefn.PseudoUnion_fromTypeConstructor => + // ok: js.|.fromTypeConstructor(js.undefined)(...) + case _ => + report.error( + "Members of non-native JS traits must either be abstract, or their right-hand-side must be `js.undefined`.", + tree) + } + } + } + } + } else { // enclosingOwner isnt OwnerKind.JSNonNative + // Check that the rhs is valid + if (sym.isPrimaryConstructor || sym.isOneOf(Param | ParamAccessor | Deferred | Synthetic) + || sym.name.is(DefaultGetterName) || sym.isSetter) { + /* Ignore, i.e., allow: + * - primary constructor + * - all kinds of parameters + * - setters + * - default parameter getters (i.e., the default value of parameters) + * - abstract members + * - synthetic members (to avoid double errors with case classes, e.g. generated copy method) + */ + } else if (sym.isConstructor) { + // Force secondary ctor to have only a call to the primary ctor inside + tree.rhs match { + case Block(List(Apply(trg, _)), Literal(Constant(()))) + if trg.symbol.isPrimaryConstructor && trg.symbol.owner == sym.owner => + // everything is fine here + case _ => + report.error( + "A secondary constructor of a native JS class may only call the primary constructor", + tree.rhs) + } + } else { + // Check that the tree's rhs is exactly `= js.native` + checkRHSCallsJSNative(tree, "Concrete members of JS native types") + } + } + + super.transform(tree) + } + + private def checkRHSCallsJSNative(tree: ValOrDefDef, longKindStr: String)(using Context): Unit = { + // Check that the rhs is exactly `= js.native` + tree.rhs match { + case sel: Select if sel.symbol == jsdefn.JSPackage_native => + // ok + case _ => + val pos = if (tree.rhs != EmptyTree) tree.rhs.srcPos else tree.srcPos + report.error(s"$longKindStr may only call js.native.", pos) + } + + // Check that the resul type was explicitly specified + // (This is stronger than Scala 2, which only warns, and only if it was inferred as Nothing.) + if (tree.tpt.span.isSynthetic) + report.error(i"The type of ${tree.name} must be explicitly specified because it is JS native.", tree) + } + + private def checkJSNativeSpecificAnnotsOnNonJSNative(memberDef: MemberDef)(using Context): Unit = { + for (annot <- memberDef.symbol.annotations) { + annot.symbol match { + case annotSym if annotSym == jsdefn.JSGlobalAnnot => + report.error("@JSGlobal can only be used on native JS definitions (with @js.native).", annot.tree) + case annotSym if annotSym == jsdefn.JSImportAnnot => + report.error("@JSImport can only be used on native JS definitions (with @js.native).", annot.tree) + case annotSym if annotSym == jsdefn.JSGlobalScopeAnnot => + report.error("@JSGlobalScope can only be used on native JS objects (with @js.native).", annot.tree) + case _ => + // ok + } + } + } + + private def checkJSNameAnnots(sym: Symbol)(using Context): Unit = { + val allJSNameAnnots = sym.annotations.filter(_.symbol == jsdefn.JSNameAnnot).reverse + + for (annot <- allJSNameAnnots.headOption) { + // Check everything about the first @JSName annotation + if (sym.isLocalToBlock || (enclosingOwner isnt OwnerKind.JSType)) + report.error("@JSName can only be used on members of JS types.", annot.tree) + else if (sym.is(Trait)) + report.error("@JSName cannot be used on traits.", annot.tree) + else if (isPrivateMaybeWithin(sym)) + report.error("@JSName cannot be used on private members.", annot.tree) + else + checkJSNameArgument(sym, annot) + + // Check that there is at most one @JSName annotation. + for (duplicate <- allJSNameAnnots.tail) + report.error("Duplicate @JSName annotation.", duplicate.tree) + } + } + + /** Checks that the argument to `@JSName` annotations on `memberSym` is legal. + * + * Reports an error on each annotation where this is not the case. + * One one `@JSName` annotation is allowed, but that is handled somewhere else. + */ + private def checkJSNameArgument(memberSym: Symbol, annot: Annotation)(using Context): Unit = { + val argTree = annot.arguments.head + if (argTree.tpe.derivesFrom(defn.StringClass)) { + // We have a String. It must be a literal. + if (!annot.argumentConstantString(0).isDefined) + report.error("A String argument to JSName must be a literal string", argTree) + } else { + // We have a js.Symbol. It must be a stable reference. + val sym = argTree.symbol + if (!sym.isStatic || !sym.isStableMember) { + report.error("A js.Symbol argument to JSName must be a static, stable identifier", argTree) + } else if ((enclosingOwner is OwnerKind.JSNonNative) && sym.owner == memberSym.owner) { + report.warning( + "This symbol is defined in the same object as the annotation's target. " + + "This will cause a stackoverflow at runtime", + argTree) + } + } + } + + /** Mark the symbol as exposed if it is a non-private term member of a + * non-native JS class. + * + * @param sym + * The symbol, which must be the module symbol for a module, not its + * module class symbol. + */ + private def markExposedIfRequired(sym: Symbol)(using Context): Unit = { + val shouldBeExposed: Boolean = { + // it is a member of a non-native JS class + (enclosingOwner is OwnerKind.JSNonNative) && !sym.isLocalToBlock && + // it is a term member, and it is not synthetic + sym.isOneOf(Module | Method, butNot = Synthetic) && + // it is not private + !isPrivateMaybeWithin(sym) && + // it is not a constructor + !sym.isConstructor + } + + if (shouldBeExposed) + sym.addAnnotation(jsdefn.ExposedJSMemberAnnot) + } + } +} + +object PrepJSInterop { + val name: String = "prepjsinterop" + + private final class OwnerKind private (private val baseKinds: Int) extends AnyVal { + import OwnerKind._ + + inline def isBaseKind: Boolean = + Integer.lowestOneBit(baseKinds) == baseKinds && baseKinds != 0 // exactly 1 bit on + + // cannot be `inline` because it accesses the private constructor + @inline def |(that: OwnerKind): OwnerKind = + new OwnerKind(this.baseKinds | that.baseKinds) + + inline def is(that: OwnerKind): Boolean = + (this.baseKinds & that.baseKinds) != 0 + + inline def isnt(that: OwnerKind): Boolean = + !this.is(that) + } + + private object OwnerKind { + /** No owner, i.e., we are at the top-level. */ + val None = new OwnerKind(0x00) + + // Base kinds - those form a partition of all possible enclosing owners + + /** A Scala class/trait. */ + val ScalaClass = new OwnerKind(0x01) + /** A Scala object. */ + val ScalaMod = new OwnerKind(0x02) + /** A native JS class/trait, which extends js.Any. */ + val JSNativeClass = new OwnerKind(0x04) + /** A native JS object, which extends js.Any. */ + val JSNativeMod = new OwnerKind(0x08) + /** A non-native JS class/trait. */ + val JSClass = new OwnerKind(0x10) + /** A non-native JS object. */ + val JSMod = new OwnerKind(0x20) + + // Compound kinds + + /** A Scala class, trait or object, i.e., anything not extending js.Any. */ + val ScalaType = ScalaClass | ScalaMod + + /** A native JS class/trait/object. */ + val JSNative = JSNativeClass | JSNativeMod + /** A non-native JS class/trait/object. */ + val JSNonNative = JSClass | JSMod + /** A JS type, i.e., something extending js.Any. */ + val JSType = JSNative | JSNonNative + + /** Any kind of class/trait, i.e., a Scala or JS class/trait. */ + val AnyClass = ScalaClass | JSNativeClass | JSClass + } + + /** Tests if the symbol extend `js.Any`. + * + * This is different from `sym.isJSType` because it returns `false` for the + * pseudo-union type. + */ + def isJSAny(sym: Symbol)(using Context): Boolean = + sym.isSubClass(jsdefn.JSAnyClass) + + /** Checks that a setter has the right signature. + * + * Reports error messages otherwise. + */ + def checkSetterSignature(sym: Symbol, pos: SrcPos, exported: Boolean)(using Context): Unit = { + val typeStr = if (exported) "Exported" else "JS" + + val tpe = sym.info + + // The result type must be Unit + if (!tpe.resultType.isRef(defn.UnitClass)) + report.error(s"$typeStr setters must return Unit", pos) + + // There must be exactly one non-varargs, non-default parameter + tpe.paramInfoss match { + case List(List(argInfo)) => + // Arg list is OK. Do additional checks. + if (tpe.isVarArgsMethod) + report.error(s"$typeStr setters may not have repeated params", pos) + if (sym.hasDefaultParams) + report.error(s"$typeStr setters may not have default params", pos) + + case _ => + report.error(s"$typeStr setters must have exactly one argument", pos) + } + } + + /** Tests whether the symbol has `private` in any form, either `private`, + * `private[this]` or `private[Enclosing]`. + */ + def isPrivateMaybeWithin(sym: Symbol)(using Context): Boolean = + sym.is(Private) || (sym.privateWithin.exists && !sym.is(Protected)) + + /** Checks that the optional argument to an `@JSGlobal` annotation is a + * literal. + * + * Reports an error on the annotation if it is not the case. + */ + private def checkJSGlobalLiteral(annot: Annotation)(using Context): Unit = { + if (annot.arguments.nonEmpty) { + assert(annot.arguments.size == 1, + s"@JSGlobal annotation $annot has more than 1 argument") + + val argIsValid = annot.argumentConstantString(0).isDefined + if (!argIsValid) + report.error("The argument to @JSGlobal must be a literal string.", annot.arguments.head) + } + } + + /** Checks that arguments to an `@JSImport` annotation are literals. + * + * The second argument can also be the singleton `JSImport.Namespace` + * object. + * + * Reports an error on the annotation if it is not the case. + */ + private def checkJSImportLiteral(annot: Annotation)(using Context): Unit = { + val args = annot.arguments + assert(args.size == 2 || args.size == 3, + i"@JSImport annotation $annot does not have exactly 2 or 3 arguments") + + val firstArgIsValid = annot.argumentConstantString(0).isDefined + if (!firstArgIsValid) + report.error("The first argument to @JSImport must be a literal string.", args.head) + + val secondArgIsValid = annot.argumentConstantString(1).isDefined || args(1).symbol == jsdefn.JSImportNamespaceModule + if (!secondArgIsValid) + report.error("The second argument to @JSImport must be literal string or the JSImport.Namespace object.", args(1)) + + val thirdArgIsValid = args.size < 3 || annot.argumentConstantString(2).isDefined + if (!thirdArgIsValid) + report.error("The third argument to @JSImport, when present, must be a literal string.", args(2)) + } + + private def checkAndGetJSNativeLoadingSpecAnnotOf(pos: SrcPos, sym: Symbol)( + using Context): Option[Annotation] = { + + // Must not have @JSName + + for (annot <- sym.getAnnotation(jsdefn.JSNameAnnot)) + report.error("@JSName can only be used on members of JS types.", annot.tree) + + // Must have exactly one JS native load spec annotation + + val annots = sym.annotations.filter(annot => isJSNativeLoadingSpecAnnot(annot.symbol)) + + val badAnnotCountMsg = + if (sym.is(Module)) "Native JS objects must have exactly one annotation among @JSGlobal, @JSImport and @JSGlobalScope." + else "Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport." + + annots match { + case Nil => + report.error(badAnnotCountMsg, pos) + None + case result :: Nil => + Some(result) + case _ => + // Annotations are stored in reverse order, which we re-reverse now + val result :: duplicates = annots.reverse + for (annot <- duplicates) + report.error(badAnnotCountMsg, annot.tree) + Some(result) + } + } + + /* Note that we consider @JSGlobalScope as a JS native loading spec because + * it's convenient for the purposes of PrepJSInterop. Actually @JSGlobalScope + * objects do not receive a JS loading spec in their IR. + */ + private def isJSNativeLoadingSpecAnnot(sym: Symbol)(using Context): Boolean = { + sym == jsdefn.JSGlobalAnnot + || sym == jsdefn.JSImportAnnot + || sym == jsdefn.JSGlobalScopeAnnot + } + + private def checkInternalAnnotations(sym: Symbol)(using Context): Unit = { + /** Returns true iff it is a compiler annotations. */ + def isCompilerAnnotation(annotation: Annotation): Boolean = { + annotation.symbol == jsdefn.ExposedJSMemberAnnot + || annotation.symbol == jsdefn.JSTypeAnnot + || annotation.symbol == jsdefn.JSOptionalAnnot + } + + for (annotation <- sym.annotations) { + if (isCompilerAnnotation(annotation)) { + report.error( + i"@${annotation.symbol.fullName} is for compiler internal use only. Do not use it yourself.", + annotation.tree) + } + } + } +} diff --git a/project/Build.scala b/project/Build.scala index e71cf945e99a..9b869732c6a2 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1034,7 +1034,6 @@ object Build { ++ (dir / "js/src/test/scala/org/scalajs/testsuite/compiler" ** (("*.scala": FileFilter) -- "InteroperabilityTest.scala" // compiler crash, related to value classes in JS interop -- "OptimizerTest.scala" // non-native JS classes - -- "ReflectionTest.scala" // tests fail (wrong load spec for JS globals) -- "RegressionJSTest.scala" // non-native JS classes -- "RuntimeTypesTest.scala" // compile errors: no ClassTag for Null and Nothing )).get @@ -1050,7 +1049,7 @@ object Build { -- "ExportsTest.scala" // JS exports -- "IterableTest.scala" // non-native JS classes -- "JSExportStaticTest.scala" // JS exports - -- "JSNativeInPackage.scala" // tests fail (wrong load spec for JS globals) + -- "JSNativeInPackage.scala" // #9785 tests fail due to js.typeOf(globalVar) being incorrect -- "JSOptionalTest.scala" // non-native JS classes -- "JSSymbolTest.scala" // non-native JS classes -- "MiscInteropTest.scala" // non-native JS classes diff --git a/tests/neg-scalajs/abstract-local-js-class.check b/tests/neg-scalajs/abstract-local-js-class.check new file mode 100644 index 000000000000..d95ab13204a0 --- /dev/null +++ b/tests/neg-scalajs/abstract-local-js-class.check @@ -0,0 +1,4 @@ +-- Error: tests/neg-scalajs/abstract-local-js-class.scala:6:19 --------------------------------------------------------- +6 | abstract class AbstractLocalJSClass extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Implementation restriction: local JS classes cannot be abstract diff --git a/tests/neg-scalajs/abstract-local-js-class.scala b/tests/neg-scalajs/abstract-local-js-class.scala new file mode 100644 index 000000000000..2d4bebabb048 --- /dev/null +++ b/tests/neg-scalajs/abstract-local-js-class.scala @@ -0,0 +1,8 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object Enclosing { + def method(): Unit = { + abstract class AbstractLocalJSClass extends js.Object // error + } +} diff --git a/tests/neg-scalajs/internal-annotations.check b/tests/neg-scalajs/internal-annotations.check new file mode 100644 index 000000000000..1da329769d70 --- /dev/null +++ b/tests/neg-scalajs/internal-annotations.check @@ -0,0 +1,44 @@ +-- Error: tests/neg-scalajs/internal-annotations.scala:5:0 ------------------------------------------------------------- +5 |@JSType trait A // error + |^^^^^^^ + |@scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:6:0 ------------------------------------------------------------- +6 |@JSType class B { // error + |^^^^^^^ + |@scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:7:2 ------------------------------------------------------------- +7 | @JSType val a = ??? // error + | ^^^^^^^ + | @scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:8:2 ------------------------------------------------------------- +8 | @JSType var b = ??? // error + | ^^^^^^^ + | @scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:9:2 ------------------------------------------------------------- +9 | @JSType def c = ??? // error + | ^^^^^^^ + | @scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:10:8 ------------------------------------------------------------ +10 | def d(@JSType i: Int) = ??? // error + | ^^^^^^^ + | @scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:11:2 ------------------------------------------------------------ +11 | @JSType class X // error + | ^^^^^^^ + | @scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:12:2 ------------------------------------------------------------ +12 | @JSType trait Y // error + | ^^^^^^^ + | @scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:14:0 ------------------------------------------------------------ +14 |@JSType object C // error + |^^^^^^^ + |@scala.scalajs.js.annotation.internal.JSType is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:16:0 ------------------------------------------------------------ +16 |@ExposedJSMember class D // error + |^^^^^^^^^^^^^^^^ + |@scala.scalajs.js.annotation.internal.ExposedJSMember is for compiler internal use only. Do not use it yourself. +-- Error: tests/neg-scalajs/internal-annotations.scala:17:0 ------------------------------------------------------------ +17 |@JSOptional class E // error + |^^^^^^^^^^^ + |@scala.scalajs.js.annotation.internal.JSOptional is for compiler internal use only. Do not use it yourself. diff --git a/tests/neg-scalajs/internal-annotations.scala b/tests/neg-scalajs/internal-annotations.scala new file mode 100644 index 000000000000..82cde0a31f7a --- /dev/null +++ b/tests/neg-scalajs/internal-annotations.scala @@ -0,0 +1,17 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ +import scala.scalajs.js.annotation.internal._ + +@JSType trait A // error +@JSType class B { // error + @JSType val a = ??? // error + @JSType var b = ??? // error + @JSType def c = ??? // error + def d(@JSType i: Int) = ??? // error + @JSType class X // error + @JSType trait Y // error +} +@JSType object C // error + +@ExposedJSMember class D // error +@JSOptional class E // error diff --git a/tests/neg-scalajs/isinstanceof-js-type.check b/tests/neg-scalajs/isinstanceof-js-type.check new file mode 100644 index 000000000000..8c9fbb6c0c2c --- /dev/null +++ b/tests/neg-scalajs/isinstanceof-js-type.check @@ -0,0 +1,8 @@ +-- Error: tests/neg-scalajs/isinstanceof-js-type.scala:11:18 ----------------------------------------------------------- +11 | x.isInstanceOf[NativeJSTrait] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | isInstanceOf[NativeJSTrait] not supported because it is a JS trait +-- Error: tests/neg-scalajs/isinstanceof-js-type.scala:12:18 ----------------------------------------------------------- +12 | x.isInstanceOf[NonNativeJSTrait] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | isInstanceOf[NonNativeJSTrait] not supported because it is a JS trait diff --git a/tests/neg-scalajs/isinstanceof-js-type.scala b/tests/neg-scalajs/isinstanceof-js-type.scala new file mode 100644 index 000000000000..15defc52af35 --- /dev/null +++ b/tests/neg-scalajs/isinstanceof-js-type.scala @@ -0,0 +1,14 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +trait NativeJSTrait extends js.Any + +trait NonNativeJSTrait extends js.Any + +object Test { + def test(x: Any): Unit = { + x.isInstanceOf[NativeJSTrait] // error + x.isInstanceOf[NonNativeJSTrait] // error + } +} diff --git a/tests/neg-scalajs/js-case-class-object.check b/tests/neg-scalajs/js-case-class-object.check new file mode 100644 index 000000000000..c8dab49f7929 --- /dev/null +++ b/tests/neg-scalajs/js-case-class-object.check @@ -0,0 +1,18 @@ +-- Error: tests/neg-scalajs/js-case-class-object.scala:5:11 ------------------------------------------------------------ +4 |@js.native @JSGlobal +5 |case class A1(x: Int) extends js.Object // error + |^ + |Classes and objects extending js.Any may not have a case modifier +-- Error: tests/neg-scalajs/js-case-class-object.scala:8:12 ------------------------------------------------------------ +7 |@js.native @JSGlobal +8 |case object A2 extends js.Object // error + |^ + |Classes and objects extending js.Any may not have a case modifier +-- Error: tests/neg-scalajs/js-case-class-object.scala:10:11 ----------------------------------------------------------- +10 |case class B1(x: Int) extends js.Object // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Classes and objects extending js.Any may not have a case modifier +-- Error: tests/neg-scalajs/js-case-class-object.scala:12:12 ----------------------------------------------------------- +12 |case object B2 extends js.Object // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Classes and objects extending js.Any may not have a case modifier diff --git a/tests/neg-scalajs/js-case-class-object.scala b/tests/neg-scalajs/js-case-class-object.scala new file mode 100644 index 000000000000..62ea3c9852c7 --- /dev/null +++ b/tests/neg-scalajs/js-case-class-object.scala @@ -0,0 +1,12 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native @JSGlobal +case class A1(x: Int) extends js.Object // error + +@js.native @JSGlobal +case object A2 extends js.Object // error + +case class B1(x: Int) extends js.Object // error + +case object B2 extends js.Object // error diff --git a/tests/neg-scalajs/js-dynamic-literal.check b/tests/neg-scalajs/js-dynamic-literal.check new file mode 100644 index 000000000000..99c25fa60f4a --- /dev/null +++ b/tests/neg-scalajs/js-dynamic-literal.check @@ -0,0 +1,16 @@ +-- Error: tests/neg-scalajs/js-dynamic-literal.scala:7:4 --------------------------------------------------------------- +7 | literal.helloWorld(a = "a") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | js.Dynamic.literal does not have a method named helloWorld +-- Error: tests/neg-scalajs/js-dynamic-literal.scala:8:4 --------------------------------------------------------------- +8 | literal.helloWorld("a" -> "a") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | js.Dynamic.literal does not have a method named helloWorld +-- Error: tests/neg-scalajs/js-dynamic-literal.scala:11:32 ------------------------------------------------------------- +11 | literal.applyDynamicNamed(x)() // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | js.Dynamic.literal.applyDynamicNamed may not be called directly +-- Error: tests/neg-scalajs/js-dynamic-literal.scala:12:27 ------------------------------------------------------------- +12 | literal.applyDynamic(x)() // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | js.Dynamic.literal.applyDynamic may not be called directly diff --git a/tests/neg-scalajs/js-dynamic-literal.scala b/tests/neg-scalajs/js-dynamic-literal.scala new file mode 100644 index 000000000000..adab45cefbb1 --- /dev/null +++ b/tests/neg-scalajs/js-dynamic-literal.scala @@ -0,0 +1,14 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ +import scala.scalajs.js.Dynamic.literal + +object Test { + def test(): Unit = { + literal.helloWorld(a = "a") // error + literal.helloWorld("a" -> "a") // error + + val x = "string" + literal.applyDynamicNamed(x)() // error + literal.applyDynamic(x)() // error + } +} diff --git a/tests/neg-scalajs/js-members-bad-signatures.check b/tests/neg-scalajs/js-members-bad-signatures.check new file mode 100644 index 000000000000..a580467b413a --- /dev/null +++ b/tests/neg-scalajs/js-members-bad-signatures.check @@ -0,0 +1,56 @@ +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:7:6 -------------------------------------------------------- +7 | def a1_=(x: Int): Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | JS setters must return Unit +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:8:6 -------------------------------------------------------- +8 | def a2_=(x: Int, y: Int): Unit = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | JS setters must have exactly one argument +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:9:6 -------------------------------------------------------- +9 | def a3_=(x: Int*): Unit = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | JS setters may not have repeated params +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:10:6 ------------------------------------------------------- +10 | def a4_=(x: Int = 1): Unit = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | JS setters may not have default params +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:15:6 ------------------------------------------------------- +14 | @JSBracketAccess +15 | def b1(): Int = js.native // error + | ^ + | @JSBracketAccess methods must have one or two parameters +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:18:6 ------------------------------------------------------- +17 | @JSBracketAccess +18 | def b2(x: Int, y: Int, z: Int): Int = js.native // error + | ^ + | @JSBracketAccess methods must have one or two parameters +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:21:6 ------------------------------------------------------- +20 | @JSBracketAccess +21 | def b3(x: Int, y: Int): Int = js.native // error + | ^ + | @JSBracketAccess methods with two parameters must return Unit +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:24:6 ------------------------------------------------------- +23 | @JSBracketAccess +24 | def b4(x: Int*): Int = js.native // error + | ^ + | @JSBracketAccess methods may not have repeated parameters +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:27:6 ------------------------------------------------------- +26 | @JSBracketAccess +27 | def b5(x: Int = 1): Int = js.native // error + | ^ + | @JSBracketAccess methods may not have default parameters +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:32:6 ------------------------------------------------------- +31 | @JSBracketCall +32 | def c1(): Int = js.native // error + | ^ + | @JSBracketCall methods must have at least one non-repeated parameter +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:35:6 ------------------------------------------------------- +34 | @JSBracketCall +35 | def c2(xs: String*): Int = js.native // error + | ^ + | @JSBracketCall methods must have at least one non-repeated parameter +-- Error: tests/neg-scalajs/js-members-bad-signatures.scala:38:6 ------------------------------------------------------- +37 | @JSBracketCall +38 | def c3(xs: String*)(y: String): Int = js.native // error + | ^ + | @JSBracketCall methods must have at least one non-repeated parameter diff --git a/tests/neg-scalajs/js-members-bad-signatures.scala b/tests/neg-scalajs/js-members-bad-signatures.scala new file mode 100644 index 000000000000..cb6939d2388d --- /dev/null +++ b/tests/neg-scalajs/js-members-bad-signatures.scala @@ -0,0 +1,39 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +@JSGlobal +class A extends js.Object { + def a1_=(x: Int): Int = js.native // error + def a2_=(x: Int, y: Int): Unit = js.native // error + def a3_=(x: Int*): Unit = js.native // error + def a4_=(x: Int = 1): Unit = js.native // error + + // @JSBracketAccess + + @JSBracketAccess + def b1(): Int = js.native // error + + @JSBracketAccess + def b2(x: Int, y: Int, z: Int): Int = js.native // error + + @JSBracketAccess + def b3(x: Int, y: Int): Int = js.native // error + + @JSBracketAccess + def b4(x: Int*): Int = js.native // error + + @JSBracketAccess + def b5(x: Int = 1): Int = js.native // error + + // @JSBracketCall + + @JSBracketCall + def c1(): Int = js.native // error + + @JSBracketCall + def c2(xs: String*): Int = js.native // error + + @JSBracketCall + def c3(xs: String*)(y: String): Int = js.native // error +} diff --git a/tests/neg-scalajs/js-native-call.check b/tests/neg-scalajs/js-native-call.check new file mode 100644 index 000000000000..cd1ba7de6345 --- /dev/null +++ b/tests/neg-scalajs/js-native-call.check @@ -0,0 +1,4 @@ +-- Error: tests/neg-scalajs/js-native-call.scala:5:17 ------------------------------------------------------------------ +5 | def foo: Int = js.native // error + | ^^^^^^^^^ + | js.native may only be used as stub implementation in facade types diff --git a/tests/neg-scalajs/js-native-call.scala b/tests/neg-scalajs/js-native-call.scala new file mode 100644 index 000000000000..07dd3bd0f50e --- /dev/null +++ b/tests/neg-scalajs/js-native-call.scala @@ -0,0 +1,6 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +class A { + def foo: Int = js.native // error +} diff --git a/tests/neg-scalajs/js-native-members.check b/tests/neg-scalajs/js-native-members.check new file mode 100644 index 000000000000..f0a6bfc60389 --- /dev/null +++ b/tests/neg-scalajs/js-native-members.check @@ -0,0 +1,100 @@ +-- Error: tests/neg-scalajs/js-native-members.scala:9:24 --------------------------------------------------------------- +9 | def this(z: String) = this(z.length, z) // error + | ^^^^^^^^^^^^^^^^^ + | A secondary constructor of a native JS class may only call the primary constructor +-- Error: tests/neg-scalajs/js-native-members.scala:11:15 -------------------------------------------------------------- +11 | val a: Int = 1 // error + | ^ + | Concrete members of JS native types may only call js.native. +-- Error: tests/neg-scalajs/js-native-members.scala:12:15 -------------------------------------------------------------- +12 | var b: Int = 2 // error + | ^ + | Concrete members of JS native types may only call js.native. +-- Error: tests/neg-scalajs/js-native-members.scala:13:15 -------------------------------------------------------------- +13 | def c: Int = 3 // error + | ^ + | Concrete members of JS native types may only call js.native. +-- Error: tests/neg-scalajs/js-native-members.scala:14:23 -------------------------------------------------------------- +14 | def d(x: Int): Int = x + 1 // error + | ^^^^^ + | Concrete members of JS native types may only call js.native. +-- Error: tests/neg-scalajs/js-native-members.scala:17:23 -------------------------------------------------------------- +17 | def e(x: Any): Any = x // error + | ^ + | Concrete members of JS native types may only call js.native. +-- Error: tests/neg-scalajs/js-native-members.scala:19:37 -------------------------------------------------------------- +19 | def f(x: Any)(y: String): String = y // error + | ^ + | Concrete members of JS native types may only call js.native. +-- Error: tests/neg-scalajs/js-native-members.scala:25:14 -------------------------------------------------------------- +25 | private def this(x: String) = this() // error + | ^ + | Native JS classes may not have private constructors. Use `private[this]` to declare an internal constructor. +-- Error: tests/neg-scalajs/js-native-members.scala:26:18 -------------------------------------------------------------- +26 | private[A2] def this(x: Boolean) = this() // error + | ^ + | Native JS classes may not have private constructors. Use `private[this]` to declare an internal constructor. +-- Error: tests/neg-scalajs/js-native-members.scala:28:20 -------------------------------------------------------------- +28 | private[this] val a: Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:29:14 -------------------------------------------------------------- +29 | private val b: Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:30:18 -------------------------------------------------------------- +30 | private[A2] val c: Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:32:20 -------------------------------------------------------------- +32 | private[this] var d: Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:33:14 -------------------------------------------------------------- +33 | private var e: Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:34:18 -------------------------------------------------------------- +34 | private[A2] var f: Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:36:20 -------------------------------------------------------------- +36 | private[this] def g(): Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:37:14 -------------------------------------------------------------- +37 | private def h(): Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:38:18 -------------------------------------------------------------- +38 | private[A2] def i(): Int = js.native // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes may not have private members. Use a public member in a private facade instead. +-- Error: tests/neg-scalajs/js-native-members.scala:43:10 -------------------------------------------------------------- +43 | class X1 private () extends js.Object // error + | ^ + | Native JS classes may not have private constructors. Use `private[this]` to declare an internal constructor. +-- Error: tests/neg-scalajs/js-native-members.scala:46:10 -------------------------------------------------------------- +46 | class X2 private[A3] () extends js.Object // error + | ^ + | Native JS classes may not have private constructors. Use `private[this]` to declare an internal constructor. +-- Error: tests/neg-scalajs/js-native-members.scala:56:6 --------------------------------------------------------------- +56 | def foo = js.native // error + | ^^^^^^^^^^^^^^^^^^^ + | The type of foo must be explicitly specified because it is JS native. +-- Error: tests/neg-scalajs/js-native-members.scala:57:6 --------------------------------------------------------------- +57 | val bar = js.native // error + | ^^^^^^^^^^^^^^^^^^^ + | The type of bar must be explicitly specified because it is JS native. +-- Error: tests/neg-scalajs/js-native-members.scala:65:8 --------------------------------------------------------------- +65 | def apply: Int = js.native // error + | ^ + |A member named apply represents function application in JavaScript. A parameterless member should be exported as a property. You must add @JSName("apply") +-- Error: tests/neg-scalajs/js-native-members.scala:76:8 --------------------------------------------------------------- +76 | val apply: Int = js.native // error + | ^ + |A member named apply represents function application in JavaScript. A parameterless member should be exported as a property. You must add @JSName("apply") +-- Error: tests/neg-scalajs/js-native-members.scala:87:8 --------------------------------------------------------------- +87 | var apply: Int = js.native // error + | ^ + |A member named apply represents function application in JavaScript. A parameterless member should be exported as a property. You must add @JSName("apply") diff --git a/tests/neg-scalajs/js-native-members.scala b/tests/neg-scalajs/js-native-members.scala new file mode 100644 index 000000000000..31d6cf00399a --- /dev/null +++ b/tests/neg-scalajs/js-native-members.scala @@ -0,0 +1,95 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +// rhs must be `js.native` (or calling the primary constructor for constructors) + +@js.native @JSGlobal +class A1(param: Int) extends js.Object { + def this(param: Int, z: String) = this(param) + def this(z: String) = this(z.length, z) // error + + val a: Int = 1 // error + var b: Int = 2 // error + def c: Int = 3 // error + def d(x: Int): Int = x + 1 // error + + @JSBracketAccess + def e(x: Any): Any = x // error + @JSBracketCall + def f(x: Any)(y: String): String = y // error +} + +@js.native @JSGlobal +class A2 extends js.Object { + private[this] def this(x: Int) = this() // ok + private def this(x: String) = this() // error + private[A2] def this(x: Boolean) = this() // error + + private[this] val a: Int = js.native // error + private val b: Int = js.native // error + private[A2] val c: Int = js.native // error + + private[this] var d: Int = js.native // error + private var e: Int = js.native // error + private[A2] var f: Int = js.native // error + + private[this] def g(): Int = js.native // error + private def h(): Int = js.native // error + private[A2] def i(): Int = js.native // error +} + +object A3 { + @js.native @JSGlobal("X") + class X1 private () extends js.Object // error + + @js.native @JSGlobal("X") + class X2 private[A3] () extends js.Object // error + + @js.native @JSGlobal("X") + class X3 private[this] () extends js.Object { // ok + def this(x: Int) = this() + } +} + +@js.native @JSGlobal +class A4 extends js.Object { + def foo = js.native // error + val bar = js.native // error +} + +// Members named `apply` + +object A5 { + @js.native + trait X1 extends js.Object { + def apply: Int = js.native // error + } + + @js.native + trait X2 extends js.Object { + @JSName("apply") + def apply: Int = js.native // ok + } + + @js.native + trait X3 extends js.Object { + val apply: Int = js.native // error + } + + @js.native + trait X4 extends js.Object { + @JSName("apply") + val apply: Int = js.native // ok + } + + @js.native + trait X5 extends js.Object { + var apply: Int = js.native // error + } + + @js.native + trait X6 extends js.Object { + @JSName("apply") + var apply: Int = js.native // ok + } +} diff --git a/tests/neg-scalajs/js-native-on-scala-type.check b/tests/neg-scalajs/js-native-on-scala-type.check new file mode 100644 index 000000000000..ca89f0932478 --- /dev/null +++ b/tests/neg-scalajs/js-native-on-scala-type.check @@ -0,0 +1,24 @@ +-- Error: tests/neg-scalajs/js-native-on-scala-type.scala:6:0 ---------------------------------------------------------- +6 |@js.native // error + |^^^^^^^^^^ + |Classes, traits and objects not extending js.Any may not have an @js.native annotation +-- Error: tests/neg-scalajs/js-native-on-scala-type.scala:10:0 --------------------------------------------------------- +10 |@js.native // error + |^^^^^^^^^^ + |Classes, traits and objects not extending js.Any may not have an @js.native annotation +-- Error: tests/neg-scalajs/js-native-on-scala-type.scala:13:0 --------------------------------------------------------- +13 |@js.native // error + |^^^^^^^^^^ + |Classes, traits and objects not extending js.Any may not have an @js.native annotation +-- Error: tests/neg-scalajs/js-native-on-scala-type.scala:19:0 --------------------------------------------------------- +19 |@js.native // error + |^^^^^^^^^^ + |Classes, traits and objects not extending js.Any may not have an @js.native annotation +-- Error: tests/neg-scalajs/js-native-on-scala-type.scala:22:0 --------------------------------------------------------- +22 |@js.native // error + |^^^^^^^^^^ + |Classes, traits and objects not extending js.Any may not have an @js.native annotation +-- Error: tests/neg-scalajs/js-native-on-scala-type.scala:26:0 --------------------------------------------------------- +26 |@js.native // error + |^^^^^^^^^^ + |Classes, traits and objects not extending js.Any may not have an @js.native annotation diff --git a/tests/neg-scalajs/js-native-on-scala-type.scala b/tests/neg-scalajs/js-native-on-scala-type.scala new file mode 100644 index 000000000000..1177213ff72f --- /dev/null +++ b/tests/neg-scalajs/js-native-on-scala-type.scala @@ -0,0 +1,27 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +// With the correct amount of native load spec annotations + +@js.native // error +@JSGlobal +class A1 + +@js.native // error +trait A2 + +@js.native // error +@JSGlobal +object A3 + +// With an incorrect amount of native load spec annotations + +@js.native // error +class B1 + +@js.native // error +@JSGlobal +trait B2 + +@js.native // error +object B3 diff --git a/tests/neg-scalajs/js-native-type-wrong-place.check b/tests/neg-scalajs/js-native-type-wrong-place.check new file mode 100644 index 000000000000..fac1d618fa96 --- /dev/null +++ b/tests/neg-scalajs/js-native-type-wrong-place.check @@ -0,0 +1,72 @@ +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:7:2 ------------------------------------------------------- +7 | @js.native // error + | ^^^^^^^^^^ + | Scala traits and classes may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:10:2 ------------------------------------------------------ +10 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | Scala traits and classes may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:13:2 ------------------------------------------------------ +13 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | Scala traits and classes may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:18:2 ------------------------------------------------------ +18 | @js.native // error + | ^^^^^^^^^^ + | Scala traits and classes may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:21:2 ------------------------------------------------------ +21 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | Scala traits and classes may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:24:2 ------------------------------------------------------ +24 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | Scala traits and classes may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:31:2 ------------------------------------------------------ +31 | @js.native // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:34:2 ------------------------------------------------------ +34 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:37:2 ------------------------------------------------------ +37 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:42:2 ------------------------------------------------------ +42 | @js.native // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:45:2 ------------------------------------------------------ +45 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:48:2 ------------------------------------------------------ +48 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:53:2 ------------------------------------------------------ +53 | @js.native // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:56:2 ------------------------------------------------------ +56 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:59:2 ------------------------------------------------------ +59 | @js.native @JSGlobal("X") // error + | ^^^^^^^^^^ + | non-native JS classes, traits and objects may not have native JS members +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:67:4 ------------------------------------------------------ +67 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:70:4 ------------------------------------------------------ +70 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions +-- Error: tests/neg-scalajs/js-native-type-wrong-place.scala:73:4 ------------------------------------------------------ +73 | @js.native // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions diff --git a/tests/neg-scalajs/js-native-type-wrong-place.scala b/tests/neg-scalajs/js-native-type-wrong-place.scala new file mode 100644 index 000000000000..87182ce1da92 --- /dev/null +++ b/tests/neg-scalajs/js-native-type-wrong-place.scala @@ -0,0 +1,76 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +// Nested in Scala class or trait + +class A1 { + @js.native // error + trait X1 extends js.Object + + @js.native @JSGlobal("X") // error + class X2 extends js.Object + + @js.native @JSGlobal("X") // error + object X3 extends js.Object +} + +trait A2 { + @js.native // error + trait X1 extends js.Object + + @js.native @JSGlobal("X") // error + class X2 extends js.Object + + @js.native @JSGlobal("X") // error + object X3 extends js.Object +} + +// Nested in non-native JS type + +class B1 extends js.Object { + @js.native // error + trait X1 extends js.Object + + @js.native @JSGlobal("X") // error + class X2 extends js.Object + + @js.native @JSGlobal("X") // error + object X3 extends js.Object +} + +trait B2 extends js.Object { + @js.native // error + trait X1 extends js.Object + + @js.native @JSGlobal("X") // error + class X2 extends js.Object + + @js.native @JSGlobal("X") // error + object X3 extends js.Object +} + +object B3 extends js.Object { + @js.native // error + trait X1 extends js.Object + + @js.native @JSGlobal("X") // error + class X2 extends js.Object + + @js.native @JSGlobal("X") // error + object X3 extends js.Object +} + +// Local + +object C { + def a(): Unit = { + @js.native @JSGlobal // error + class X extends js.Object + + @js.native @JSGlobal // error + object Y extends js.Object + + @js.native // error + trait Z extends js.Object + } +} diff --git a/tests/neg-scalajs/js-native-val-def-override.check b/tests/neg-scalajs/js-native-val-def-override.check new file mode 100644 index 000000000000..601119905721 --- /dev/null +++ b/tests/neg-scalajs/js-native-val-def-override.check @@ -0,0 +1,30 @@ +-- Error: tests/neg-scalajs/js-native-val-def-override.scala:12:6 ------------------------------------------------------ +11 | @js.native @JSGlobal("a") +12 | val a: Int = js.native // error + | ^ + | An @js.native member cannot implement the inherited member AbstractParent.a +-- Error: tests/neg-scalajs/js-native-val-def-override.scala:15:6 ------------------------------------------------------ +14 | @js.native @JSGlobal("b") +15 | def b: Int = js.native // error + | ^ + | An @js.native member cannot implement the inherited member AbstractParent.b +-- Error: tests/neg-scalajs/js-native-val-def-override.scala:18:6 ------------------------------------------------------ +17 | @js.native @JSGlobal("c") +18 | def c(x: Int): Int = js.native // error + | ^ + | An @js.native member cannot implement the inherited member AbstractParent.c +-- Error: tests/neg-scalajs/js-native-val-def-override.scala:29:15 ----------------------------------------------------- +28 | @js.native @JSGlobal("a") +29 | override val a: Int = js.native // error + | ^ + | An @js.native member cannot override the inherited member ConcreteParent.a +-- Error: tests/neg-scalajs/js-native-val-def-override.scala:32:15 ----------------------------------------------------- +31 | @js.native @JSGlobal("b") +32 | override def b: Int = js.native // error + | ^ + | An @js.native member cannot override the inherited member ConcreteParent.b +-- Error: tests/neg-scalajs/js-native-val-def-override.scala:35:15 ----------------------------------------------------- +34 | @js.native @JSGlobal("c") +35 | override def c(x: Int): Int = js.native // error + | ^ + | An @js.native member cannot override the inherited member ConcreteParent.c diff --git a/tests/neg-scalajs/js-native-val-def-override.scala b/tests/neg-scalajs/js-native-val-def-override.scala new file mode 100644 index 000000000000..6f087e6ec2ec --- /dev/null +++ b/tests/neg-scalajs/js-native-val-def-override.scala @@ -0,0 +1,36 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +abstract class AbstractParent { + val a: Int + def b: Int + def c(x: Int): Int +} + +object Container1 extends AbstractParent { + @js.native @JSGlobal("a") + val a: Int = js.native // error + + @js.native @JSGlobal("b") + def b: Int = js.native // error + + @js.native @JSGlobal("c") + def c(x: Int): Int = js.native // error +} + +class ConcreteParent { + val a: Int = 1 + def b: Int = 2 + def c(x: Int): Int = x + 1 +} + +object Container2 extends ConcreteParent { + @js.native @JSGlobal("a") + override val a: Int = js.native // error + + @js.native @JSGlobal("b") + override def b: Int = js.native // error + + @js.native @JSGlobal("c") + override def c(x: Int): Int = js.native // error +} diff --git a/tests/neg-scalajs/js-native-val-def-wrong-place.check b/tests/neg-scalajs/js-native-val-def-wrong-place.check new file mode 100644 index 000000000000..475775e16bc3 --- /dev/null +++ b/tests/neg-scalajs/js-native-val-def-wrong-place.check @@ -0,0 +1,108 @@ +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:11:2 --------------------------------------------------- +11 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:15:2 --------------------------------------------------- +15 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:19:2 --------------------------------------------------- +19 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:23:2 --------------------------------------------------- +23 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:27:2 --------------------------------------------------- +27 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:31:2 --------------------------------------------------- +31 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:35:2 --------------------------------------------------- +35 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:44:2 --------------------------------------------------- +44 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:48:2 --------------------------------------------------- +48 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:52:2 --------------------------------------------------- +52 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:56:2 --------------------------------------------------- +56 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:60:2 --------------------------------------------------- +60 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:64:2 --------------------------------------------------- +64 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:68:2 --------------------------------------------------- +68 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:77:2 --------------------------------------------------- +77 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:80:2 --------------------------------------------------- +80 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:83:2 --------------------------------------------------- +83 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:86:2 --------------------------------------------------- +86 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:89:2 --------------------------------------------------- +89 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:92:2 --------------------------------------------------- +92 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:96:2 --------------------------------------------------- +96 | @js.native // error + | ^^^^^^^^^^ + | @js.native vals and defs can only appear in static Scala objects +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:105:4 -------------------------------------------------- +105 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:108:4 -------------------------------------------------- +108 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:111:4 -------------------------------------------------- +111 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:114:4 -------------------------------------------------- +114 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:117:4 -------------------------------------------------- +117 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions +-- Error: tests/neg-scalajs/js-native-val-def-wrong-place.scala:120:4 -------------------------------------------------- +120 | @js.native @JSGlobal // error + | ^^^^^^^^^^ + | @js.native is not allowed on local definitions diff --git a/tests/neg-scalajs/js-native-val-def-wrong-place.scala b/tests/neg-scalajs/js-native-val-def-wrong-place.scala new file mode 100644 index 000000000000..3aaded2afcf7 --- /dev/null +++ b/tests/neg-scalajs/js-native-val-def-wrong-place.scala @@ -0,0 +1,123 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object A { + val sym = js.Symbol("foo") +} + +// In non-native JS object + +object NonNativeContainer extends js.Object { + @js.native // error + @JSGlobal("a") + val a: Int = js.native + + @js.native // error + @JSGlobal("b") + def b: Int = js.native + + @js.native // error + @JSGlobal("c") + def c(x: Int): Int = js.native + + @js.native // error + @JSName("foo") + val d: Int = js.native + + @js.native // error + @JSName("bar") + def e(x: Int): Int = js.native + + @js.native // error + @JSName(A.sym) + val f: Int = js.native + + @js.native // error + @JSName(A.sym) + def g(x: Int): Int = js.native +} + +// In native JS object, with a native load spec + +@js.native @JSGlobal +object NativeContainer extends js.Object { + @js.native // error + @JSGlobal("a") + val a: Int = js.native + + @js.native // error + @JSGlobal("b") + def b: Int = js.native + + @js.native // error + @JSGlobal("c") + def c(x: Int): Int = js.native + + @js.native // error + @JSName("foo") + val d: Int = js.native + + @js.native // error + @JSName("bar") + def e(x: Int): Int = js.native + + @js.native // error + @JSName(A.sym) + val f: Int = js.native + + @js.native // error + @JSName(A.sym) + def g(x: Int): Int = js.native +} + +// In native JS object, without native load spec + +@js.native @JSGlobal +object NativeContainer2 extends js.Object { + @js.native // error + val a: Int = js.native + + @js.native // error + def b: Int = js.native + + @js.native // error + def c(x: Int): Int = js.native + + @js.native // error + val d: Int = js.native + + @js.native // error + def e(x: Int): Int = js.native + + @js.native // error + @JSName(A.sym) + val f: Int = js.native + + @js.native // error + @JSName(A.sym) + def g(x: Int): Int = js.native +} + +// Local + +object ContainerLocal { + def a(): Unit = { + @js.native @JSGlobal // error + val d: Int = js.native + + @js.native @JSGlobal // error + var e: Int = js.native + + @js.native @JSGlobal // error + def f: Int = js.native + + @js.native @JSGlobal // error + def f_=(v: Int): Unit = js.native + + @js.native @JSGlobal // error + def g(x: Int): Int = js.native + + @js.native @JSGlobal // error + lazy val h: Int = js.native + } +} diff --git a/tests/neg-scalajs/js-native-val-defs.check b/tests/neg-scalajs/js-native-val-defs.check new file mode 100644 index 000000000000..d51aecd28651 --- /dev/null +++ b/tests/neg-scalajs/js-native-val-defs.check @@ -0,0 +1,46 @@ +-- Error: tests/neg-scalajs/js-native-val-defs.scala:8:15 -------------------------------------------------------------- +8 | val a: Int = 1 // error + | ^ + | @js.native members may only call js.native. +-- Error: tests/neg-scalajs/js-native-val-defs.scala:11:15 ------------------------------------------------------------- +11 | def b: Int = 3 // error + | ^ + | @js.native members may only call js.native. +-- Error: tests/neg-scalajs/js-native-val-defs.scala:14:23 ------------------------------------------------------------- +14 | def c(x: Int): Int = x + 1 // error + | ^^^^^ + | @js.native members may only call js.native. +-- Error: tests/neg-scalajs/js-native-val-defs.scala:21:2 -------------------------------------------------------------- +21 | @JSBracketAccess // error + | ^^^^^^^^^^^^^^^^ + | @JSBracketAccess is not allowed on @js.native vals and defs +-- Error: tests/neg-scalajs/js-native-val-defs.scala:25:2 -------------------------------------------------------------- +25 | @JSBracketAccess // error + | ^^^^^^^^^^^^^^^^ + | @JSBracketAccess is not allowed on @js.native vals and defs +-- Error: tests/neg-scalajs/js-native-val-defs.scala:29:2 -------------------------------------------------------------- +29 | @JSBracketAccess // error + | ^^^^^^^^^^^^^^^^ + | @JSBracketAccess is not allowed on @js.native vals and defs +-- Error: tests/neg-scalajs/js-native-val-defs.scala:37:2 -------------------------------------------------------------- +37 | @JSBracketCall // error + | ^^^^^^^^^^^^^^ + | @JSBracketCall is not allowed on @js.native vals and defs +-- Error: tests/neg-scalajs/js-native-val-defs.scala:41:2 -------------------------------------------------------------- +41 | @JSBracketCall // error + | ^^^^^^^^^^^^^^ + | @JSBracketCall is not allowed on @js.native vals and defs +-- Error: tests/neg-scalajs/js-native-val-defs.scala:45:2 -------------------------------------------------------------- +45 | @JSBracketCall // error + | ^^^^^^^^^^^^^^ + | @JSBracketCall is not allowed on @js.native vals and defs +-- Error: tests/neg-scalajs/js-native-val-defs.scala:53:6 -------------------------------------------------------------- +52 | @js.native @JSGlobal("a") +53 | val a = js.native // error + | ^ + | The type of a must be explicitly specified because it is JS native. +-- Error: tests/neg-scalajs/js-native-val-defs.scala:56:6 -------------------------------------------------------------- +55 | @js.native @JSGlobal("b") +56 | def b = js.native // error + | ^ + | The type of b must be explicitly specified because it is JS native. diff --git a/tests/neg-scalajs/js-native-val-defs.scala b/tests/neg-scalajs/js-native-val-defs.scala new file mode 100644 index 000000000000..af6be7a3640e --- /dev/null +++ b/tests/neg-scalajs/js-native-val-defs.scala @@ -0,0 +1,57 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +// rhs must be = js.native + +object Container1 { + @js.native @JSGlobal("a") + val a: Int = 1 // error + + @js.native @JSGlobal("b") + def b: Int = 3 // error + + @js.native @JSGlobal("c") + def c(x: Int): Int = x + 1 // error +} + +// illegal @JSBracketAccess + +object Container2 { + @js.native @JSGlobal("a") + @JSBracketAccess // error + val a: Int = js.native + + @js.native @JSGlobal("b") + @JSBracketAccess // error + def b: Int = js.native + + @js.native @JSGlobal("c") + @JSBracketAccess // error + def c(x: Int): Int = js.native +} + +// illegal @JSBracketCall + +object Container3 { + @js.native @JSGlobal("a") + @JSBracketCall // error + val a: Int = js.native + + @js.native @JSGlobal("b") + @JSBracketCall // error + def b: Int = js.native + + @js.native @JSGlobal("c") + @JSBracketCall // error + def c(x: Int): Int = js.native +} + +// inferred result type + +object Container4 { + @js.native @JSGlobal("a") + val a = js.native // error + + @js.native @JSGlobal("b") + def b = js.native // error +} diff --git a/tests/neg-scalajs/js-native-wrong-kind.check b/tests/neg-scalajs/js-native-wrong-kind.check new file mode 100644 index 000000000000..3aef3ed957d1 --- /dev/null +++ b/tests/neg-scalajs/js-native-wrong-kind.check @@ -0,0 +1,24 @@ +-- Error: tests/neg-scalajs/js-native-wrong-kind.scala:7:2 ------------------------------------------------------------- +7 | @js.native @JSGlobal("foo") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs +-- Error: tests/neg-scalajs/js-native-wrong-kind.scala:9:2 ------------------------------------------------------------- +9 | @js.native @JSGlobal("bar") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs +-- Error: tests/neg-scalajs/js-native-wrong-kind.scala:11:2 ------------------------------------------------------------ +11 | @js.native @JSGlobal("goo") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs +-- Error: tests/neg-scalajs/js-native-wrong-kind.scala:13:2 ------------------------------------------------------------ +13 | @js.native @JSGlobal("hoo") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs +-- Error: tests/neg-scalajs/js-native-wrong-kind.scala:18:2 ------------------------------------------------------------ +18 | @js.native @JSGlobal("foo") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs +-- Error: tests/neg-scalajs/js-native-wrong-kind.scala:23:2 ------------------------------------------------------------ +23 | @js.native @JSGlobal("foo") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs diff --git a/tests/neg-scalajs/js-native-wrong-kind.scala b/tests/neg-scalajs/js-native-wrong-kind.scala new file mode 100644 index 000000000000..c11d5e3555de --- /dev/null +++ b/tests/neg-scalajs/js-native-wrong-kind.scala @@ -0,0 +1,25 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object Container { + // setters + + @js.native @JSGlobal("foo") // error + def foo_=(x: Int): Int = js.native + @js.native @JSGlobal("bar") // error + def bar_=(x: Int, y: Int): Unit = js.native + @js.native @JSGlobal("goo") // error + def goo_=(x: Int*): Unit = js.native + @js.native @JSGlobal("hoo") // error + def hoo_=(x: Int = 1): Unit = js.native + + // vars + + @js.native @JSGlobal("foo") // error + var x: Int = js.native + + // lazy vals + + @js.native @JSGlobal("foo") // error + lazy val y: Int = js.native +} diff --git a/tests/neg-scalajs/js-non-native-members-overloaded.check b/tests/neg-scalajs/js-non-native-members-overloaded.check new file mode 100644 index 000000000000..cc2acf7f5116 --- /dev/null +++ b/tests/neg-scalajs/js-non-native-members-overloaded.check @@ -0,0 +1,24 @@ +-- Error: tests/neg-scalajs/js-non-native-members-overloaded.scala:5:14 ------------------------------------------------ +5 | private def foo(i: Int): Int = i // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Private methods in non-native JS classes cannot be overloaded. Use different names instead. +-- Error: tests/neg-scalajs/js-non-native-members-overloaded.scala:6:14 ------------------------------------------------ +6 | private def foo(s: String): String = s // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Private methods in non-native JS classes cannot be overloaded. Use different names instead. +-- Error: tests/neg-scalajs/js-non-native-members-overloaded.scala:11:28 ----------------------------------------------- +11 | private[BEnclosing] def foo(i: Int): Int = i // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Private methods in non-native JS classes cannot be overloaded. Use different names instead. +-- Error: tests/neg-scalajs/js-non-native-members-overloaded.scala:12:16 ----------------------------------------------- +12 | private def foo(s: String): String = s // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Private methods in non-native JS classes cannot be overloaded. Use different names instead. +-- Error: tests/neg-scalajs/js-non-native-members-overloaded.scala:17:14 ----------------------------------------------- +17 | private def foo(i: Int): Int = i // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Private methods in non-native JS classes cannot be overloaded. Use different names instead. +-- Error: tests/neg-scalajs/js-non-native-members-overloaded.scala:23:28 ----------------------------------------------- +23 | private[DEnclosing] def foo(i: Int): Int = i // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Private methods in non-native JS classes cannot be overloaded. Use different names instead. diff --git a/tests/neg-scalajs/js-non-native-members-overloaded.scala b/tests/neg-scalajs/js-non-native-members-overloaded.scala new file mode 100644 index 000000000000..bf497d999d95 --- /dev/null +++ b/tests/neg-scalajs/js-non-native-members-overloaded.scala @@ -0,0 +1,26 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +class A extends js.Object { + private def foo(i: Int): Int = i // error + private def foo(s: String): String = s // error +} + +object BEnclosing { + class B extends js.Object { + private[BEnclosing] def foo(i: Int): Int = i // error + private def foo(s: String): String = s // error + } +} + +class C extends js.Object { + private def foo(i: Int): Int = i // error + def foo(s: String): String = s +} + +object DEnclosing { + class D extends js.Object { + private[DEnclosing] def foo(i: Int): Int = i // error + def foo(s: String): String = s + } +} diff --git a/tests/neg-scalajs/js-non-native-members-qualified-private.check b/tests/neg-scalajs/js-non-native-members-qualified-private.check new file mode 100644 index 000000000000..915a1bbe89eb --- /dev/null +++ b/tests/neg-scalajs/js-non-native-members-qualified-private.check @@ -0,0 +1,48 @@ +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:6:32 ----------------------------------------- +6 | private[Enclosing1] def foo(i: Int): Int = i // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:7:32 ----------------------------------------- +7 | private[Enclosing1] val x: Int = 3 // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:8:32 ----------------------------------------- +8 | private[Enclosing1] var y: Int = 5 // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:20:32 ---------------------------------------- +20 | private[Enclosing2] def foo(i: Int): Int = i // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:21:32 ---------------------------------------- +21 | private[Enclosing2] val x: Int = 3 // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:22:32 ---------------------------------------- +22 | private[Enclosing2] var y: Int = 5 // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:30:32 ---------------------------------------- +30 | private[Enclosing3] def foo(i: Int): Int // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:31:32 ---------------------------------------- +31 | private[Enclosing3] val x: Int // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:32:32 ---------------------------------------- +32 | private[Enclosing3] var y: Int // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:44:32 ---------------------------------------- +44 | private[Enclosing4] def foo(i: Int): Int // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:45:32 ---------------------------------------- +45 | private[Enclosing4] val x: Int // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final +-- Error: tests/neg-scalajs/js-non-native-members-qualified-private.scala:46:32 ---------------------------------------- +46 | private[Enclosing4] var y: Int // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Qualified private members in non-native JS classes must be final diff --git a/tests/neg-scalajs/js-non-native-members-qualified-private.scala b/tests/neg-scalajs/js-non-native-members-qualified-private.scala new file mode 100644 index 000000000000..2d920fe85afd --- /dev/null +++ b/tests/neg-scalajs/js-non-native-members-qualified-private.scala @@ -0,0 +1,54 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + + object Enclosing1 { + class A extends js.Object { + private[Enclosing1] def foo(i: Int): Int = i // error + private[Enclosing1] val x: Int = 3 // error + private[Enclosing1] var y: Int = 5 // error + + final private[Enclosing1] def babar(i: Int): Int = i // ok + } + + class B extends A { + override private[Enclosing1] final def foo(i: Int): Int = i + 1 + } + } + + object Enclosing2 { + object A extends js.Object { + private[Enclosing2] def foo(i: Int): Int = i // error + private[Enclosing2] val x: Int = 3 // error + private[Enclosing2] var y: Int = 5 // error + + final private[Enclosing2] def babar(i: Int): Int = i // ok + } + } + + object Enclosing3 { + abstract class A extends js.Object { + private[Enclosing3] def foo(i: Int): Int // error + private[Enclosing3] val x: Int // error + private[Enclosing3] var y: Int // error + + final private[Enclosing3] def babar(i: Int): Int = i // ok + } + + class B extends A { + override private[Enclosing3] final def foo(i: Int): Int = i + 1 + } + } + + object Enclosing4 { + trait A extends js.Object { + private[Enclosing4] def foo(i: Int): Int // error + private[Enclosing4] val x: Int // error + private[Enclosing4] var y: Int // error + } + } + + object Enclosing5 { + class A private () extends js.Object // ok + class B private[this] () extends js.Object // ok + class C private[Enclosing5] () extends js.Object // ok + } diff --git a/tests/neg-scalajs/js-non-native-members.check b/tests/neg-scalajs/js-non-native-members.check new file mode 100644 index 000000000000..fef81a75ed77 --- /dev/null +++ b/tests/neg-scalajs/js-non-native-members.check @@ -0,0 +1,26 @@ +-- Error: tests/neg-scalajs/js-non-native-members.scala:5:6 ------------------------------------------------------------ +5 | def apply(arg: Int): Int = arg // error + | ^ + | A non-native JS class cannot declare a method named `apply` without `@JSName` +-- Error: tests/neg-scalajs/js-non-native-members.scala:8:6 ------------------------------------------------------------ +7 | @JSBracketAccess +8 | def foo(index: Int, arg: Int): Int = arg // error + | ^ + | @JSBracketAccess is not allowed in non-native JS classes +-- Error: tests/neg-scalajs/js-non-native-members.scala:11:6 ----------------------------------------------------------- +10 | @JSBracketCall +11 | def foo(m: String, arg: Int): Int = arg // error + | ^ + | @JSBracketCall is not allowed in non-native JS classes +-- Error: tests/neg-scalajs/js-non-native-members.scala:15:6 ----------------------------------------------------------- +15 | def apply: Int = 42 // error + | ^ + |A member named apply represents function application in JavaScript. A parameterless member should be exported as a property. You must add @JSName("apply") +-- Error: tests/neg-scalajs/js-non-native-members.scala:24:6 ----------------------------------------------------------- +24 | val apply: Int = 42 // error + | ^ + |A member named apply represents function application in JavaScript. A parameterless member should be exported as a property. You must add @JSName("apply") +-- Error: tests/neg-scalajs/js-non-native-members.scala:33:6 ----------------------------------------------------------- +33 | var apply: Int = 42 // error + | ^ + |A member named apply represents function application in JavaScript. A parameterless member should be exported as a property. You must add @JSName("apply") diff --git a/tests/neg-scalajs/js-non-native-members.scala b/tests/neg-scalajs/js-non-native-members.scala new file mode 100644 index 000000000000..ceb75ea7c6cb --- /dev/null +++ b/tests/neg-scalajs/js-non-native-members.scala @@ -0,0 +1,39 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +class A extends js.Object { + def apply(arg: Int): Int = arg // error + + @JSBracketAccess + def foo(index: Int, arg: Int): Int = arg // error + + @JSBracketCall + def foo(m: String, arg: Int): Int = arg // error +} + +class B extends js.Object { + def apply: Int = 42 // error +} + +class BOK extends js.Object { + @JSName("apply") + def apply: Int = 42 +} + +class C extends js.Object { + val apply: Int = 42 // error +} + +class COK extends js.Object { + @JSName("apply") + val apply: Int = 42 +} + +class D extends js.Object { + var apply: Int = 42 // error +} + +class DOK extends js.Object { + @JSName("apply") + var apply: Int = 42 +} diff --git a/tests/neg-scalajs/js-trait-members-override.check b/tests/neg-scalajs/js-trait-members-override.check new file mode 100644 index 000000000000..c3d0d1dedbe4 --- /dev/null +++ b/tests/neg-scalajs/js-trait-members-override.check @@ -0,0 +1,30 @@ +-- Error: tests/neg-scalajs/js-trait-members-override.scala:13:15 ------------------------------------------------------ +13 | override val a1: js.UndefOr[Int] = js.undefined // error + | ^ + |error overriding value a1 in class A1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a1'; + | value a1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a1' cannot override a concrete member in a non-native JS trait +-- Error: tests/neg-scalajs/js-trait-members-override.scala:16:15 ------------------------------------------------------ +16 | override def b1: js.UndefOr[Int] = js.undefined // error + | ^ + |error overriding method b1 in class A1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b1'; + | method b1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b1' cannot override a concrete member in a non-native JS trait +-- Error: tests/neg-scalajs/js-trait-members-override.scala:27:15 ------------------------------------------------------ +27 | override val a: js.UndefOr[Int] = js.undefined // error + | ^ + |error overriding value a in class B1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a'; + | value a of type scala.scalajs.js.UndefOr[Int] with JS name 'a' cannot override a concrete member in a non-native JS trait +-- Error: tests/neg-scalajs/js-trait-members-override.scala:28:15 ------------------------------------------------------ +28 | override def b: js.UndefOr[Int] = js.undefined // error + | ^ + |error overriding method b in class B1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b'; + | method b of type => scala.scalajs.js.UndefOr[Int] with JS name 'b' cannot override a concrete member in a non-native JS trait +-- Error: tests/neg-scalajs/js-trait-members-override.scala:41:15 ------------------------------------------------------ +41 | override val a: js.UndefOr[Int] = js.undefined // error + | ^ + |error overriding value a in trait C1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a'; + | value a of type scala.scalajs.js.UndefOr[Int] with JS name 'a' cannot override a concrete member in a non-native JS trait +-- Error: tests/neg-scalajs/js-trait-members-override.scala:42:15 ------------------------------------------------------ +42 | override def b: js.UndefOr[Int] = js.undefined // error + | ^ + |error overriding method b in trait C1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b'; + | method b of type => scala.scalajs.js.UndefOr[Int] with JS name 'b' cannot override a concrete member in a non-native JS trait diff --git a/tests/neg-scalajs/js-trait-members-override.scala b/tests/neg-scalajs/js-trait-members-override.scala new file mode 100644 index 000000000000..25d25962a8f9 --- /dev/null +++ b/tests/neg-scalajs/js-trait-members-override.scala @@ -0,0 +1,43 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +abstract class A1 extends js.Object { + val a1: js.UndefOr[Int] = 5 + val a2: js.UndefOr[Int] + + def b1: js.UndefOr[Int] = 5 + def b2: js.UndefOr[Int] +} + +trait A2 extends A1 { + override val a1: js.UndefOr[Int] = js.undefined // error + override val a2: js.UndefOr[Int] = js.undefined + + override def b1: js.UndefOr[Int] = js.undefined // error + override def b2: js.UndefOr[Int] = js.undefined +} + +@js.native @JSGlobal +class B1 extends js.Object { + val a: js.UndefOr[Int] = js.native + def b: js.UndefOr[Int] = js.native +} + +trait B2 extends B1 { + override val a: js.UndefOr[Int] = js.undefined // error + override def b: js.UndefOr[Int] = js.undefined // error +} + +@js.native +trait C1 extends js.Object { + val a: js.UndefOr[Int] = js.native + def b: js.UndefOr[Int] = js.native +} + +@js.native @JSGlobal +class C2 extends C1 + +trait C3 extends C2 { + override val a: js.UndefOr[Int] = js.undefined // error + override def b: js.UndefOr[Int] = js.undefined // error +} diff --git a/tests/neg-scalajs/js-trait-members-wrong-kind.check b/tests/neg-scalajs/js-trait-members-wrong-kind.check new file mode 100644 index 000000000000..e6c20f1b2cce --- /dev/null +++ b/tests/neg-scalajs/js-trait-members-wrong-kind.check @@ -0,0 +1,16 @@ +-- Error: tests/neg-scalajs/js-trait-members-wrong-kind.scala:5:11 ----------------------------------------------------- +5 | lazy val a1: js.UndefOr[Int] = js.undefined // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | A non-native JS trait cannot contain lazy vals +-- Error: tests/neg-scalajs/js-trait-members-wrong-kind.scala:7:29 ----------------------------------------------------- +7 | def a(): js.UndefOr[Int] = js.undefined // error + | ^^^^^^^^^^^^ + | In non-native JS traits, defs with parentheses must be abstract. +-- Error: tests/neg-scalajs/js-trait-members-wrong-kind.scala:8:35 ----------------------------------------------------- +8 | def b(x: Int): js.UndefOr[Int] = js.undefined // error + | ^^^^^^^^^^^^ + | In non-native JS traits, defs with parentheses must be abstract. +-- Error: tests/neg-scalajs/js-trait-members-wrong-kind.scala:9:26 ----------------------------------------------------- +9 | def c_=(v: Int): Unit = js.undefined // error + | ^^^^^^^^^^^^ + | In non-native JS traits, defs with parentheses must be abstract. diff --git a/tests/neg-scalajs/js-trait-members-wrong-kind.scala b/tests/neg-scalajs/js-trait-members-wrong-kind.scala new file mode 100644 index 000000000000..1cc7610d1610 --- /dev/null +++ b/tests/neg-scalajs/js-trait-members-wrong-kind.scala @@ -0,0 +1,10 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +trait A extends js.Object { + lazy val a1: js.UndefOr[Int] = js.undefined // error + + def a(): js.UndefOr[Int] = js.undefined // error + def b(x: Int): js.UndefOr[Int] = js.undefined // error + def c_=(v: Int): Unit = js.undefined // error +} diff --git a/tests/neg-scalajs/js-trait-members.check b/tests/neg-scalajs/js-trait-members.check new file mode 100644 index 000000000000..cf190c418ab1 --- /dev/null +++ b/tests/neg-scalajs/js-trait-members.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-scalajs/js-trait-members.scala:5:6 ----------------------------------------------------------------- +5 | val a1: js.UndefOr[Int] = 5 // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Members of non-native JS traits must either be abstract, or their right-hand-side must be `js.undefined`. +-- Error: tests/neg-scalajs/js-trait-members.scala:6:6 ----------------------------------------------------------------- +6 | val a2: Int = 5 // error + | ^^^^^^^^^^^^^^^ + | Members of non-native JS traits must either be abstract, or their right-hand-side must be `js.undefined`. +-- Error: tests/neg-scalajs/js-trait-members.scala:8:6 ----------------------------------------------------------------- +8 | def b1: js.UndefOr[Int] = 5 // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Members of non-native JS traits must either be abstract, or their right-hand-side must be `js.undefined`. +-- Error: tests/neg-scalajs/js-trait-members.scala:9:6 ----------------------------------------------------------------- +9 | def b2: Int = 5 // error + | ^^^^^^^^^^^^^^^ + | Members of non-native JS traits must either be abstract, or their right-hand-side must be `js.undefined`. +-- Error: tests/neg-scalajs/js-trait-members.scala:11:6 ---------------------------------------------------------------- +11 | var c1: js.UndefOr[Int] = 5 // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Members of non-native JS traits must either be abstract, or their right-hand-side must be `js.undefined`. +-- Error: tests/neg-scalajs/js-trait-members.scala:12:6 ---------------------------------------------------------------- +12 | var c2: Int = 5 // error + | ^^^^^^^^^^^^^^^ + | Members of non-native JS traits must either be abstract, or their right-hand-side must be `js.undefined`. +-- Error: tests/neg-scalajs/js-trait-members.scala:14:24 --------------------------------------------------------------- +14 | def d1(x: Int): Int = x + 1 // error + | ^^^^^ + | In non-native JS traits, defs with parentheses must be abstract. +-- Error: tests/neg-scalajs/js-trait-members.scala:15:23 --------------------------------------------------------------- +15 | def d2[A](x: A): A = x // error + | ^ + | In non-native JS traits, defs with parentheses must be abstract. diff --git a/tests/neg-scalajs/js-trait-members.scala b/tests/neg-scalajs/js-trait-members.scala new file mode 100644 index 000000000000..69d4c5a9a295 --- /dev/null +++ b/tests/neg-scalajs/js-trait-members.scala @@ -0,0 +1,16 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +trait A extends js.Object { + val a1: js.UndefOr[Int] = 5 // error + val a2: Int = 5 // error + + def b1: js.UndefOr[Int] = 5 // error + def b2: Int = 5 // error + + var c1: js.UndefOr[Int] = 5 // error + var c2: Int = 5 // error + + def d1(x: Int): Int = x + 1 // error + def d2[A](x: A): A = x // error +} diff --git a/tests/neg-scalajs/js-type-bad-parents.check b/tests/neg-scalajs/js-type-bad-parents.check new file mode 100644 index 000000000000..0375f2fd30c0 --- /dev/null +++ b/tests/neg-scalajs/js-type-bad-parents.check @@ -0,0 +1,90 @@ +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:9:6 -------------------------------------------------------------- +9 |class A2 extends js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:10:7 ------------------------------------------------------------- +10 |object A3 extends js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:13:6 ------------------------------------------------------------- +13 |class A5 extends AnyRef with js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:14:7 ------------------------------------------------------------- +14 |object A6 extends AnyRef with js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:17:6 ------------------------------------------------------------- +17 |class A8 extends Object with js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:18:7 ------------------------------------------------------------- +18 |object A9 extends Object with js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:25:6 ------------------------------------------------------------- +25 |trait B1 extends js.Object with NativeJSTrait // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS types cannot directly extend native JS traits. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:26:6 ------------------------------------------------------------- +26 |class B2 extends js.Object with NativeJSTrait // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS types cannot directly extend native JS traits. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:27:7 ------------------------------------------------------------- +27 |object B3 extends js.Object with NativeJSTrait // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Non-native JS types cannot directly extend native JS traits. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:34:6 ------------------------------------------------------------- +34 |trait C1 extends ScalaClass with js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |C1 extends ScalaClass which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:35:6 ------------------------------------------------------------- +35 |class C2 extends ScalaClass with js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |C2 extends ScalaClass which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:36:7 ------------------------------------------------------------- +36 |object C3 extends ScalaClass with js.Any // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |C3$ extends ScalaClass which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:39:6 ------------------------------------------------------------- +38 |@js.native +39 |trait C4 extends ScalaClass with js.Any // error + |^ + |C4 extends ScalaClass which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:41:6 ------------------------------------------------------------- +40 |@js.native @JSGlobal +41 |class C5 extends ScalaClass with js.Any // error + |^ + |C5 extends ScalaClass which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:43:7 ------------------------------------------------------------- +42 |@js.native @JSGlobal +43 |object C6 extends ScalaClass with js.Any // error + |^ + |C6$ extends ScalaClass which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:45:6 ------------------------------------------------------------- +45 |trait C7 extends js.Object with ScalaTrait // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |C7 extends ScalaTrait which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:46:6 ------------------------------------------------------------- +46 |class C8 extends js.Object with ScalaTrait // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |C8 extends ScalaTrait which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:47:7 ------------------------------------------------------------- +47 |object C9 extends js.Object with ScalaTrait // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |C9$ extends ScalaTrait which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:50:6 ------------------------------------------------------------- +49 |@js.native +50 |trait C10 extends js.Object with ScalaTrait // error + |^ + |C10 extends ScalaTrait which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:52:6 ------------------------------------------------------------- +51 |@js.native @JSGlobal +52 |class C11 extends js.Object with ScalaTrait // error + |^ + |C11 extends ScalaTrait which does not extend js.Any. +-- Error: tests/neg-scalajs/js-type-bad-parents.scala:54:7 ------------------------------------------------------------- +53 |@js.native @JSGlobal +54 |object C12 extends js.Object with ScalaTrait // error + |^ + |C12$ extends ScalaTrait which does not extend js.Any. diff --git a/tests/neg-scalajs/js-type-bad-parents.scala b/tests/neg-scalajs/js-type-bad-parents.scala new file mode 100644 index 000000000000..cd55aaa0eb4a --- /dev/null +++ b/tests/neg-scalajs/js-type-bad-parents.scala @@ -0,0 +1,68 @@ +import scala.language.dynamics + +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +// AnyRef as direct parent, invalid for non-native JS types, except traits + +trait A1 extends js.Any // ok for trait +class A2 extends js.Any // error +object A3 extends js.Any // error + +trait A4 extends AnyRef with js.Any // ok for trait +class A5 extends AnyRef with js.Any // error +object A6 extends AnyRef with js.Any // error + +trait A7 extends Object with js.Any // ok for trait +class A8 extends Object with js.Any // error +object A9 extends Object with js.Any // error + +// Native JS trait as parent, invalid for non-native JS types + +@js.native +trait NativeJSTrait extends js.Any + +trait B1 extends js.Object with NativeJSTrait // error +class B2 extends js.Object with NativeJSTrait // error +object B3 extends js.Object with NativeJSTrait // error + +// Scala class or trait, invalid + +class ScalaClass +trait ScalaTrait + +trait C1 extends ScalaClass with js.Any // error +class C2 extends ScalaClass with js.Any // error +object C3 extends ScalaClass with js.Any // error + +@js.native +trait C4 extends ScalaClass with js.Any // error +@js.native @JSGlobal +class C5 extends ScalaClass with js.Any // error +@js.native @JSGlobal +object C6 extends ScalaClass with js.Any // error + +trait C7 extends js.Object with ScalaTrait // error +class C8 extends js.Object with ScalaTrait // error +object C9 extends js.Object with ScalaTrait // error + +@js.native +trait C10 extends js.Object with ScalaTrait // error +@js.native @JSGlobal +class C11 extends js.Object with ScalaTrait // error +@js.native @JSGlobal +object C12 extends js.Object with ScalaTrait // error + +// Exception: OK to extend scala.Dynamic + + +trait D1 extends js.Object with scala.Dynamic +class D2 extends js.Object with scala.Dynamic +object D3 extends js.Object with scala.Dynamic + +@js.native +trait D4 extends js.Object with scala.Dynamic +@js.native @JSGlobal +class D5 extends js.Object with scala.Dynamic +@js.native @JSGlobal +object D6 extends js.Object with scala.Dynamic diff --git a/tests/neg-scalajs/js-type-override-equals-hashcode.check b/tests/neg-scalajs/js-type-override-equals-hashcode.check new file mode 100644 index 000000000000..653d18f6b637 --- /dev/null +++ b/tests/neg-scalajs/js-type-override-equals-hashcode.check @@ -0,0 +1,48 @@ +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:5:15 ------------------------------------------------ +5 | override def hashCode(): Int = 1 // error + | ^ + |error overriding method hashCode(): Int in a JS class; + | method hashCode(): Int is considered final in trait js.Any; + | if you want to define a method named "hashCode" in JavaScript, use a different name and add @JSName("hashCode"). +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:6:15 ------------------------------------------------ +6 | override def equals(obj: Any): Boolean = false // error + | ^ + | error overriding method equals(that: Any): Boolean in a JS class; + | method equals(that: Any): Boolean is considered final in trait js.Any; + | if you want to define a method named "equals" in JavaScript, use a different name and add @JSName("equals"). +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:24:15 ----------------------------------------------- +24 | override def hashCode(): Int = js.native // error + | ^ + |error overriding method hashCode(): Int in a JS class; + | method hashCode(): Int is considered final in trait js.Any; + | if you want to define a method named "hashCode" in JavaScript, use a different name and add @JSName("hashCode"). +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:25:15 ----------------------------------------------- +25 | override def equals(obj: Any): Boolean = js.native // error + | ^ + | error overriding method equals(that: Any): Boolean in a JS class; + | method equals(that: Any): Boolean is considered final in trait js.Any; + | if you want to define a method named "equals" in JavaScript, use a different name and add @JSName("equals"). +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:30:15 ----------------------------------------------- +30 | override def hashCode(): Int = js.native // error + | ^ + |error overriding method hashCode(): Int in a JS class; + | method hashCode(): Int is considered final in trait js.Any; + | if you want to define a method named "hashCode" in JavaScript, use a different name and add @JSName("hashCode"). +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:31:15 ----------------------------------------------- +31 | override def equals(obj: Any): Boolean = js.native // error + | ^ + | error overriding method equals(that: Any): Boolean in a JS class; + | method equals(that: Any): Boolean is considered final in trait js.Any; + | if you want to define a method named "equals" in JavaScript, use a different name and add @JSName("equals"). +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:35:15 ----------------------------------------------- +35 | override def hashCode(): Int // error + | ^ + |error overriding method hashCode(): Int in a JS class; + | method hashCode(): Int is considered final in trait js.Any; + | if you want to define a method named "hashCode" in JavaScript, use a different name and add @JSName("hashCode"). +-- Error: tests/neg-scalajs/js-type-override-equals-hashcode.scala:36:15 ----------------------------------------------- +36 | override def equals(obj: Any): Boolean // error + | ^ + | error overriding method equals(that: Any): Boolean in a JS class; + | method equals(that: Any): Boolean is considered final in trait js.Any; + | if you want to define a method named "equals" in JavaScript, use a different name and add @JSName("equals"). diff --git a/tests/neg-scalajs/js-type-override-equals-hashcode.scala b/tests/neg-scalajs/js-type-override-equals-hashcode.scala new file mode 100644 index 000000000000..0d5a53e2a719 --- /dev/null +++ b/tests/neg-scalajs/js-type-override-equals-hashcode.scala @@ -0,0 +1,37 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +class A extends js.Object { + override def hashCode(): Int = 1 // error + override def equals(obj: Any): Boolean = false // error + + // this one works as expected (so allowed) + override def toString(): String = "frobber" + + /* these are allowed, since they are protected in jl.Object. + * as a result, only the overrides can be called. So the fact that they + * do not truly override the methods in jl.Object is not observable. + */ + override def clone(): Object = null + override def finalize(): Unit = () + + // other methods in jl.Object are final. +} + +@js.native +@JSGlobal +object B extends js.Object { + override def hashCode(): Int = js.native // error + override def equals(obj: Any): Boolean = js.native // error +} + +@js.native +trait C extends js.Any { + override def hashCode(): Int = js.native // error + override def equals(obj: Any): Boolean = js.native // error +} + +trait D extends js.Any { + override def hashCode(): Int // error + override def equals(obj: Any): Boolean // error +} diff --git a/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check b/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check new file mode 100644 index 000000000000..123706ae3776 --- /dev/null +++ b/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check @@ -0,0 +1,40 @@ +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:13:27 ----------------------------------------- +13 | val a = js.constructorOf[NativeJSTrait] // error + | ^^^^^^^^^^^^^ + | non-trait class type required but NativeJSTrait found +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:14:27 ----------------------------------------- +14 | val b = js.constructorOf[NativeJSObject.type] // error + | ^^^^^^^^^^^^^^^^^^^ + | NativeJSObject.type is not a class type +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:16:27 ----------------------------------------- +16 | val c = js.constructorOf[NativeJSClass with NativeJSTrait] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | NativeJSClass & NativeJSTrait is not a class type +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:17:27 ----------------------------------------- +17 | val d = js.constructorOf[NativeJSClass { def bar: Int }] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | NativeJSClass{bar: => Int} is not a class type +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:19:27 ----------------------------------------- +19 | val e = js.constructorOf[JSTrait] // error + | ^^^^^^^ + | non-trait class type required but JSTrait found +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:20:27 ----------------------------------------- +20 | val f = js.constructorOf[JSObject.type] // error + | ^^^^^^^^^^^^^ + | JSObject.type is not a class type +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:22:27 ----------------------------------------- +22 | val g = js.constructorOf[JSClass with JSTrait] // error + | ^^^^^^^^^^^^^^^^^^^^ + | JSClass & JSTrait is not a class type +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:23:27 ----------------------------------------- +23 | val h = js.constructorOf[JSClass { def bar: Int }] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | JSClass{bar: => Int} is not a class type +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:25:42 ----------------------------------------- +25 | def foo[A <: js.Any] = js.constructorOf[A] // error + | ^ + | A is not a class type +-- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:26:66 ----------------------------------------- +26 | def bar[A <: js.Any: scala.reflect.ClassTag] = js.constructorOf[A] // error + | ^ + | A is not a class type diff --git a/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala b/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala new file mode 100644 index 000000000000..59d4d27ff086 --- /dev/null +++ b/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala @@ -0,0 +1,27 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native @JSGlobal class NativeJSClass extends js.Object +@js.native trait NativeJSTrait extends js.Object +@js.native @JSGlobal object NativeJSObject extends js.Object + +class JSClass extends js.Object +trait JSTrait extends js.Object +object JSObject extends js.Object + +object A { + val a = js.constructorOf[NativeJSTrait] // error + val b = js.constructorOf[NativeJSObject.type] // error + + val c = js.constructorOf[NativeJSClass with NativeJSTrait] // error + val d = js.constructorOf[NativeJSClass { def bar: Int }] // error + + val e = js.constructorOf[JSTrait] // error + val f = js.constructorOf[JSObject.type] // error + + val g = js.constructorOf[JSClass with JSTrait] // error + val h = js.constructorOf[JSClass { def bar: Int }] // error + + def foo[A <: js.Any] = js.constructorOf[A] // error + def bar[A <: js.Any: scala.reflect.ClassTag] = js.constructorOf[A] // error +} diff --git a/tests/neg-scalajs/jsconstructorof-error-in-typer.check b/tests/neg-scalajs/jsconstructorof-error-in-typer.check new file mode 100644 index 000000000000..bca385d021f3 --- /dev/null +++ b/tests/neg-scalajs/jsconstructorof-error-in-typer.check @@ -0,0 +1,12 @@ +-- [E057] Type Mismatch Error: tests/neg-scalajs/jsconstructorof-error-in-typer.scala:9:27 ----------------------------- +9 | val a = js.constructorOf[ScalaClass] // error + | ^ + | Type argument ScalaClass does not conform to upper bound scala.scalajs.js.Any +-- [E057] Type Mismatch Error: tests/neg-scalajs/jsconstructorof-error-in-typer.scala:10:27 ---------------------------- +10 | val b = js.constructorOf[ScalaTrait] // error + | ^ + | Type argument ScalaTrait does not conform to upper bound scala.scalajs.js.Any +-- [E057] Type Mismatch Error: tests/neg-scalajs/jsconstructorof-error-in-typer.scala:11:27 ---------------------------- +11 | val c = js.constructorOf[ScalaObject.type] // error + | ^ + | Type argument ScalaObject.type does not conform to upper bound scala.scalajs.js.Any diff --git a/tests/neg-scalajs/jsconstructorof-error-in-typer.scala b/tests/neg-scalajs/jsconstructorof-error-in-typer.scala new file mode 100644 index 000000000000..d70f4ef35d3f --- /dev/null +++ b/tests/neg-scalajs/jsconstructorof-error-in-typer.scala @@ -0,0 +1,12 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +class ScalaClass +trait ScalaTrait +object ScalaObject + +object A { + val a = js.constructorOf[ScalaClass] // error + val b = js.constructorOf[ScalaTrait] // error + val c = js.constructorOf[ScalaObject.type] // error +} diff --git a/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check b/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check new file mode 100644 index 000000000000..6916c33b9cc7 --- /dev/null +++ b/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check @@ -0,0 +1,40 @@ +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:13:42 ---------------------------------------- +13 | val a = js.constructorTag[NativeJSTrait] // error + | ^ + | non-trait class type required but NativeJSTrait found +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:14:48 ---------------------------------------- +14 | val b = js.constructorTag[NativeJSObject.type] // error + | ^ + | NativeJSObject.type is not a class type +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:16:61 ---------------------------------------- +16 | val c = js.constructorTag[NativeJSClass with NativeJSTrait] // error + | ^ + | (NativeJSClass & NativeJSTrait) is not a class type +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:17:59 ---------------------------------------- +17 | val d = js.constructorTag[NativeJSClass { def bar: Int }] // error + | ^ + | NativeJSClass{bar: => Int} is not a class type +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:19:36 ---------------------------------------- +19 | val e = js.constructorTag[JSTrait] // error + | ^ + | non-trait class type required but JSTrait found +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:20:42 ---------------------------------------- +20 | val f = js.constructorTag[JSObject.type] // error + | ^ + | JSObject.type is not a class type +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:22:49 ---------------------------------------- +22 | val g = js.constructorTag[JSClass with JSTrait] // error + | ^ + | (JSClass & JSTrait) is not a class type +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:23:53 ---------------------------------------- +23 | val h = js.constructorTag[JSClass { def bar: Int }] // error + | ^ + | JSClass{bar: => Int} is not a class type +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:25:45 ---------------------------------------- +25 | def foo[A <: js.Any] = js.constructorTag[A] // error + | ^ + | A is not a class type +-- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:26:69 ---------------------------------------- +26 | def bar[A <: js.Any: scala.reflect.ClassTag] = js.constructorTag[A] // error + | ^ + | A is not a class type diff --git a/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala b/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala new file mode 100644 index 000000000000..a57071259256 --- /dev/null +++ b/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala @@ -0,0 +1,27 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native @JSGlobal class NativeJSClass extends js.Object +@js.native trait NativeJSTrait extends js.Object +@js.native @JSGlobal object NativeJSObject extends js.Object + +class JSClass extends js.Object +trait JSTrait extends js.Object +object JSObject extends js.Object + +object A { + val a = js.constructorTag[NativeJSTrait] // error + val b = js.constructorTag[NativeJSObject.type] // error + + val c = js.constructorTag[NativeJSClass with NativeJSTrait] // error + val d = js.constructorTag[NativeJSClass { def bar: Int }] // error + + val e = js.constructorTag[JSTrait] // error + val f = js.constructorTag[JSObject.type] // error + + val g = js.constructorTag[JSClass with JSTrait] // error + val h = js.constructorTag[JSClass { def bar: Int }] // error + + def foo[A <: js.Any] = js.constructorTag[A] // error + def bar[A <: js.Any: scala.reflect.ClassTag] = js.constructorTag[A] // error +} diff --git a/tests/neg-scalajs/jsconstructortag-error-in-typer.check b/tests/neg-scalajs/jsconstructortag-error-in-typer.check new file mode 100644 index 000000000000..a18ed693383e --- /dev/null +++ b/tests/neg-scalajs/jsconstructortag-error-in-typer.check @@ -0,0 +1,27 @@ +-- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:9:39 ------------------------------------------------- +9 | val a = js.constructorTag[ScalaClass] // error + | ^ + |no implicit argument of type scala.scalajs.js.ConstructorTag[ScalaClass] was found for parameter tag of method constructorTag in package scala.scalajs.js. + |I found: + | + | scala.scalajs.js.ConstructorTag.materialize[Nothing] + | + |But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaClass]. +-- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:10:39 ------------------------------------------------ +10 | val b = js.constructorTag[ScalaTrait] // error + | ^ + |no implicit argument of type scala.scalajs.js.ConstructorTag[ScalaTrait] was found for parameter tag of method constructorTag in package scala.scalajs.js. + |I found: + | + | scala.scalajs.js.ConstructorTag.materialize[Nothing] + | + |But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaTrait]. +-- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:11:45 ------------------------------------------------ +11 | val c = js.constructorTag[ScalaObject.type] // error + | ^ + |no implicit argument of type scala.scalajs.js.ConstructorTag[ScalaObject.type] was found for parameter tag of method constructorTag in package scala.scalajs.js. + |I found: + | + | scala.scalajs.js.ConstructorTag.materialize[Nothing] + | + |But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaObject.type]. diff --git a/tests/neg-scalajs/jsconstructortag-error-in-typer.scala b/tests/neg-scalajs/jsconstructortag-error-in-typer.scala new file mode 100644 index 000000000000..0090fa7129f4 --- /dev/null +++ b/tests/neg-scalajs/jsconstructortag-error-in-typer.scala @@ -0,0 +1,12 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +class ScalaClass +trait ScalaTrait +object ScalaObject + +object A { + val a = js.constructorTag[ScalaClass] // error + val b = js.constructorTag[ScalaTrait] // error + val c = js.constructorTag[ScalaObject.type] // error +} diff --git a/tests/neg-scalajs/jsglobalscope-on-non-object.check b/tests/neg-scalajs/jsglobalscope-on-non-object.check new file mode 100644 index 000000000000..43152766238f --- /dev/null +++ b/tests/neg-scalajs/jsglobalscope-on-non-object.check @@ -0,0 +1,28 @@ +-- Error: tests/neg-scalajs/jsglobalscope-on-non-object.scala:5:0 ------------------------------------------------------ +5 |@JSGlobalScope // error + |^^^^^^^^^^^^^^ + |@JSGlobalScope can only be used on native JS objects (with @js.native). +-- Error: tests/neg-scalajs/jsglobalscope-on-non-object.scala:9:0 ------------------------------------------------------ +9 |@JSGlobalScope // error + |^^^^^^^^^^^^^^ + |Traits may not have an @JSGlobalScope annotation. +-- Error: tests/neg-scalajs/jsglobalscope-on-non-object.scala:14:2 ----------------------------------------------------- +14 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | @JSGlobalScope can only be used on native JS objects (with @js.native). +-- Error: tests/neg-scalajs/jsglobalscope-on-non-object.scala:18:2 ----------------------------------------------------- +18 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | Traits may not have an @JSGlobalScope annotation. +-- Error: tests/neg-scalajs/jsglobalscope-on-non-object.scala:22:2 ----------------------------------------------------- +22 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | @JSGlobalScope can only be used on native JS objects (with @js.native). +-- Error: tests/neg-scalajs/jsglobalscope-on-non-object.scala:26:2 ----------------------------------------------------- +26 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | @JSGlobalScope can only be used on native JS objects (with @js.native). +-- Error: tests/neg-scalajs/jsglobalscope-on-non-object.scala:30:2 ----------------------------------------------------- +30 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | @JSGlobalScope can only be used on native JS objects (with @js.native). diff --git a/tests/neg-scalajs/jsglobalscope-on-non-object.scala b/tests/neg-scalajs/jsglobalscope-on-non-object.scala new file mode 100644 index 000000000000..e997d8fd3edc --- /dev/null +++ b/tests/neg-scalajs/jsglobalscope-on-non-object.scala @@ -0,0 +1,32 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +@JSGlobalScope // error +class A extends js.Any + +@js.native +@JSGlobalScope // error +trait B extends js.Any + +object Container { + @js.native + @JSGlobalScope // error + class C extends js.Any + + @js.native + @JSGlobalScope // error + trait D extends js.Any + + @js.native + @JSGlobalScope // error + val a: Int = js.native + + @js.native + @JSGlobalScope // error + def b: Int = js.native + + @js.native + @JSGlobalScope // error + def c(x: Int): Int = js.native +} diff --git a/tests/neg-scalajs/jsname-argument.check b/tests/neg-scalajs/jsname-argument.check new file mode 100644 index 000000000000..c7640631e730 --- /dev/null +++ b/tests/neg-scalajs/jsname-argument.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-scalajs/jsname-argument.scala:17:16 ---------------------------------------------------------------- +17 | @JSName(Names.a) // error + | ^^^^^^^ + | A String argument to JSName must be a literal string +-- Error: tests/neg-scalajs/jsname-argument.scala:24:16 ---------------------------------------------------------------- +24 | @JSName(Names.a) // error + | ^^^^^^^ + | A String argument to JSName must be a literal string +-- Error: tests/neg-scalajs/jsname-argument.scala:33:19 ---------------------------------------------------------------- +33 | @JSName(js.Symbol()) // error + | ^^^^^^^^^^^ + | A js.Symbol argument to JSName must be a static, stable identifier +-- Error: tests/neg-scalajs/jsname-argument.scala:35:27 ---------------------------------------------------------------- +35 | @JSName(new NamesClass().a) // error + | ^^^^^^^^^^^^^^^^^^ + | A js.Symbol argument to JSName must be a static, stable identifier +-- Error: tests/neg-scalajs/jsname-argument.scala:40:19 ---------------------------------------------------------------- +40 | @JSName(js.Symbol()) // error + | ^^^^^^^^^^^ + | A js.Symbol argument to JSName must be a static, stable identifier +-- Error: tests/neg-scalajs/jsname-argument.scala:42:27 ---------------------------------------------------------------- +42 | @JSName(new NamesClass().a) // error + | ^^^^^^^^^^^^^^^^^^ + | A js.Symbol argument to JSName must be a static, stable identifier +-- Error: tests/neg-scalajs/jsname-argument.scala:57:3 ----------------------------------------------------------------- +57 | @JSName(Names.sym) // error + | ^^^^^^^^^^^^^^^^^^ + | Implementation restriction: @JSName with a js.Symbol is not supported on nested native classes and objects +-- Error: tests/neg-scalajs/jsname-argument.scala:61:3 ----------------------------------------------------------------- +61 | @JSName(Names.sym) // error + | ^^^^^^^^^^^^^^^^^^ + | Implementation restriction: @JSName with a js.Symbol is not supported on nested native classes and objects diff --git a/tests/neg-scalajs/jsname-argument.scala b/tests/neg-scalajs/jsname-argument.scala new file mode 100644 index 000000000000..76375ebaa2f3 --- /dev/null +++ b/tests/neg-scalajs/jsname-argument.scala @@ -0,0 +1,64 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object Names { + val a = "Hello" + final val b = "World" + val sym = js.Symbol("foo") +} + +class NamesClass { + val a = js.Symbol("foo") +} + +@js.native +@JSGlobal +class B1 extends js.Object { + @JSName(Names.a) // error + def foo: Int = js.native + @JSName(Names.b) // ok + def bar: Int = js.native +} + +class B2 extends js.Object { + @JSName(Names.a) // error + def foo: Int = 1 + @JSName(Names.b) // ok + def bar: Int = 2 +} + +@js.native +@JSGlobal +class C1 extends js.Object { + @JSName(js.Symbol()) // error + def foo: Int = js.native + @JSName(new NamesClass().a) // error + def bar: Int = js.native +} + +class C2 extends js.Object { + @JSName(js.Symbol()) // error + def foo: Int = 1 + @JSName(new NamesClass().a) // error + def bar: Int = 2 +} + +object D1 extends js.Object { + val a = js.Symbol("foo") + + @JSName(a) // warning, untested + def foo: Int = 1 +} + +// Implementation restriction +@js.native +@JSGlobal +object E1 extends js.Object { + @JSName(Names.sym) // error + @js.native + class X extends js.Object + + @JSName(Names.sym) // error + @js.native + object Y extends js.Object +} diff --git a/tests/neg-scalajs/jsname-duplicate.check b/tests/neg-scalajs/jsname-duplicate.check new file mode 100644 index 000000000000..ac0b5693a224 --- /dev/null +++ b/tests/neg-scalajs/jsname-duplicate.check @@ -0,0 +1,12 @@ +-- Error: tests/neg-scalajs/jsname-duplicate.scala:12:3 ---------------------------------------------------------------- +12 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | Duplicate @JSName annotation. +-- Error: tests/neg-scalajs/jsname-duplicate.scala:16:3 ---------------------------------------------------------------- +16 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | Duplicate @JSName annotation. +-- Error: tests/neg-scalajs/jsname-duplicate.scala:20:3 ---------------------------------------------------------------- +20 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | Duplicate @JSName annotation. diff --git a/tests/neg-scalajs/jsname-duplicate.scala b/tests/neg-scalajs/jsname-duplicate.scala new file mode 100644 index 000000000000..295c8d3f8104 --- /dev/null +++ b/tests/neg-scalajs/jsname-duplicate.scala @@ -0,0 +1,22 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object Names { + val a = js.Symbol() +} + +@js.native +@JSGlobal +class A extends js.Object { + @JSName(Names.a) + @JSName("foo") // error + def a1: Int = js.native + + @JSName("bar") + @JSName("foo") // error + def a2: Int = js.native + + @JSName("foo") + @JSName("foo") // error + def a3: Int = js.native +} diff --git a/tests/neg-scalajs/jsname-on-private-members.check b/tests/neg-scalajs/jsname-on-private-members.check new file mode 100644 index 000000000000..a05fb25951d3 --- /dev/null +++ b/tests/neg-scalajs/jsname-on-private-members.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:5:3 -------------------------------------------------------- +5 | @JSName("toto1") // error + | ^^^^^^^^^^^^^^^^ + | @JSName cannot be used on private members. +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:8:3 -------------------------------------------------------- +8 | @JSName("tata1") // error + | ^^^^^^^^^^^^^^^^ + | @JSName cannot be used on private members. +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:11:3 ------------------------------------------------------- +11 | @JSName("Babar1") // error + | ^^^^^^^^^^^^^^^^^ + | @JSName cannot be used on private members. +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:14:3 ------------------------------------------------------- +14 | @JSName("a1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on private members. +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:17:3 ------------------------------------------------------- +17 | @JSName("b1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on private members. +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:20:3 ------------------------------------------------------- +20 | @JSName("c1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on private members. +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:23:3 ------------------------------------------------------- +23 | @JSName("d1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on private members. +-- Error: tests/neg-scalajs/jsname-on-private-members.scala:26:3 ------------------------------------------------------- +26 | @JSName("e1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on private members. diff --git a/tests/neg-scalajs/jsname-on-private-members.scala b/tests/neg-scalajs/jsname-on-private-members.scala new file mode 100644 index 000000000000..46b93f3d0897 --- /dev/null +++ b/tests/neg-scalajs/jsname-on-private-members.scala @@ -0,0 +1,28 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +class A extends js.Object { + @JSName("toto1") // error + private object toto + + @JSName("tata1") // error + private object tata extends js.Object + + @JSName("Babar1") // error + private class Babar extends js.Object + + @JSName("a1") // error + private val a: Int = 0 + + @JSName("b1") // error + private var b: Int = 0 + + @JSName("c1") // error + private def c: Int = 0 + + @JSName("d1") // error + private def d(x: Int): Int = x + 1 + + @JSName("e1") // error + private def e1_=(v: Int): Unit = () +} diff --git a/tests/neg-scalajs/jsname-on-trait.check b/tests/neg-scalajs/jsname-on-trait.check new file mode 100644 index 000000000000..9df17b101f5f --- /dev/null +++ b/tests/neg-scalajs/jsname-on-trait.check @@ -0,0 +1,12 @@ +-- Error: tests/neg-scalajs/jsname-on-trait.scala:7:3 ------------------------------------------------------------------ +7 | @JSName("A1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on traits. +-- Error: tests/neg-scalajs/jsname-on-trait.scala:10:3 ----------------------------------------------------------------- +10 | @JSName("B1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on traits. +-- Error: tests/neg-scalajs/jsname-on-trait.scala:13:3 ----------------------------------------------------------------- +13 | @JSName("C1") // error + | ^^^^^^^^^^^^^ + | @JSName cannot be used on traits. diff --git a/tests/neg-scalajs/jsname-on-trait.scala b/tests/neg-scalajs/jsname-on-trait.scala new file mode 100644 index 000000000000..ebb4c8aa6946 --- /dev/null +++ b/tests/neg-scalajs/jsname-on-trait.scala @@ -0,0 +1,16 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +@JSGlobal +class Container extends js.Object { + @JSName("A1") // error + trait A extends js.Object + + @JSName("B1") // error + trait B + + @JSName("C1") // error + @js.native + trait C extends js.Object +} diff --git a/tests/neg-scalajs/jsname-override.check b/tests/neg-scalajs/jsname-override.check new file mode 100644 index 000000000000..300dc9a2df8b --- /dev/null +++ b/tests/neg-scalajs/jsname-override.check @@ -0,0 +1,80 @@ +-- Error: tests/neg-scalajs/jsname-override.scala:10:15 ---------------------------------------------------------------- +10 | override def bar() = 1 // error + | ^ + | error overriding method bar in class A1 of type (): Int with JS name 'foo'; + | method bar of type (): Int with JS name 'baz' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:18:15 ---------------------------------------------------------------- +18 | override def bar() = 1 // error + | ^ + | error overriding method bar in class B1 of type (): Int with JS name 'foo'; + | method bar of type (): Int with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:26:15 ---------------------------------------------------------------- +26 | override def bar(): String // error + | ^ + | error overriding method bar in class C1 of type (): Object with JS name 'foo'; + | method bar of type (): String with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:29:15 ---------------------------------------------------------------- +29 | override def bar() = "1" // error + | ^ + | error overriding method bar in class C1 of type (): Object with JS name 'foo'; + | method bar of type (): String with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:37:15 ---------------------------------------------------------------- +37 | override def bar(): String // error + | ^ + | error overriding method bar in class D1 of type (): Object with JS name 'bar'; + | method bar of type (): String with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:40:15 ---------------------------------------------------------------- +40 | override def bar() = "1" // error + | ^ + | error overriding method bar in class D2 of type (): String with JS name 'foo'; + | method bar of type (): String with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:48:6 ----------------------------------------------------------------- +48 | def foo: Int // error + | ^ + | error overriding method foo in trait E2 of type => Int with JS name 'bar'; + | method foo in class E1 of type => Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:57:6 ----------------------------------------------------------------- +57 | def foo: Int // error + | ^ + | error overriding method foo in trait F2 of type => Int with JS name 'foo'; + | method foo in class F1 of type => Int with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:66:15 ---------------------------------------------------------------- +66 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in class G1 of type (x: Int): Int with JS name 'bar'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:74:15 ---------------------------------------------------------------- +74 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in trait H1 of type (x: Int): Int with JS name 'bar'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:82:6 ----------------------------------------------------------------- +82 | def foo(x: Int): Int // error + | ^ + | error overriding method foo in trait I2 of type (x: Int): Int with JS name 'foo'; + | method foo in class I1 of type (x: Int): Int with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:85:15 ---------------------------------------------------------------- +85 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in class I1 of type (x: Int): Int with JS name 'bar'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:93:6 ----------------------------------------------------------------- +93 | def foo(x: Int): Int // error + | ^ + | error overriding method foo in trait J2 of type (x: Int): Int with JS name 'bar'; + | method foo in class J1 of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:96:15 ---------------------------------------------------------------- +96 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in trait J2 of type (x: Int): Int with JS name 'bar'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:106:6 ---------------------------------------------------------------- +106 |trait K3 extends K1 with K2 // error + | ^ + | error overriding method foo in trait K1 of type => Int with JS name 'foo'; + | method foo in trait K2 of type => Int with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-override.scala:115:15 --------------------------------------------------------------- +115 |abstract class L3 extends L1 with L2 // error + | ^ + | error overriding method foo in trait L1 of type => Int with JS name 'foo'; + | method foo in trait L2 of type => Int with JS name 'bar' has a different JS name diff --git a/tests/neg-scalajs/jsname-override.scala b/tests/neg-scalajs/jsname-override.scala new file mode 100644 index 000000000000..16f948964417 --- /dev/null +++ b/tests/neg-scalajs/jsname-override.scala @@ -0,0 +1,115 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +abstract class A1 extends js.Object { + @JSName("foo") + def bar(): Int +} +class A2 extends A1 { + @JSName("baz") + override def bar() = 1 // error +} + +abstract class B1 extends js.Object { + @JSName("foo") + def bar(): Int +} +class B2 extends B1 { + override def bar() = 1 // error +} + +abstract class C1 extends js.Object { + @JSName("foo") + def bar(): Object +} +abstract class C2 extends C1 { + override def bar(): String // error +} +class C3 extends C2 { + override def bar() = "1" // error +} + +abstract class D1 extends js.Object { + def bar(): Object +} +abstract class D2 extends D1 { + @JSName("foo") + override def bar(): String // error +} +class D3 extends D2 { + override def bar() = "1" // error +} + +class E1 extends js.Object { + def foo: Int = 5 +} +trait E2 extends E1 { + @JSName("bar") + def foo: Int // error +} +class E3 extends E2 + +class F1 extends js.Object { + @JSName("bar") + def foo: Int = 5 +} +trait F2 extends F1 { + def foo: Int // error +} +class F3 extends F2 + +class G1[T] extends js.Object { + @JSName("bar") + def foo(x: T): T = x +} +class G2 extends G1[Int] { + override def foo(x: Int): Int = x // error +} + +trait H1[T] extends js.Object { + @JSName("bar") + def foo(x: T): T +} +class H2 extends H1[Int] { + override def foo(x: Int): Int = x // error +} + +class I1[T] extends js.Object { + @JSName("bar") + def foo(x: T): T = x +} +trait I2 extends I1[Int] { + def foo(x: Int): Int // error +} +class I3 extends I2 { + override def foo(x: Int): Int = x // error +} + +class J1[T] extends js.Object { + def foo(x: T): T = x +} +trait J2 extends J1[Int] { + @JSName("bar") + def foo(x: Int): Int // error +} +class J3 extends J2 { + override def foo(x: Int): Int = x // error +} + +trait K1 extends js.Object { + def foo: Int +} +trait K2 extends js.Object { + @JSName("bar") + def foo: Int +} +trait K3 extends K1 with K2 // error + +trait L1 extends js.Object { + def foo: Int +} +trait L2 extends js.Object { + @JSName("bar") + def foo: Int +} +abstract class L3 extends L1 with L2 // error diff --git a/tests/neg-scalajs/jsname-symbol-override.check b/tests/neg-scalajs/jsname-symbol-override.check new file mode 100644 index 000000000000..0d62554be32e --- /dev/null +++ b/tests/neg-scalajs/jsname-symbol-override.check @@ -0,0 +1,90 @@ +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:15:15 --------------------------------------------------------- +15 | override def bar() = 1 // error + | ^ + | error overriding method bar in class A1 of type (): Int with JS name 'Syms$.sym1'; + | method bar of type (): Int with JS name 'Syms$.sym2' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:24:15 --------------------------------------------------------- +24 | override def bar() = 1 // error + | ^ + | error overriding method bar in class B1 of type (): Int with JS name 'Syms$.sym1'; + | method bar of type (): Int with JS name 'baz' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:33:15 --------------------------------------------------------- +33 | override def bar() = 1 // error + | ^ + | error overriding method bar in class C1 of type (): Int with JS name 'foo'; + | method bar of type (): Int with JS name 'Syms$.sym1' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:41:15 --------------------------------------------------------- +41 | override def bar() = 1 // error + | ^ + | error overriding method bar in class D1 of type (): Int with JS name 'Syms$.sym1'; + | method bar of type (): Int with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:49:15 --------------------------------------------------------- +49 | override def bar(): String // error + | ^ + | error overriding method bar in class E1 of type (): Object with JS name 'Syms$.sym1'; + | method bar of type (): String with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:52:15 --------------------------------------------------------- +52 | override def bar() = "1" // error + | ^ + | error overriding method bar in class E1 of type (): Object with JS name 'Syms$.sym1'; + | method bar of type (): String with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:60:15 --------------------------------------------------------- +60 | override def bar(): String // error + | ^ + | error overriding method bar in class F1 of type (): Object with JS name 'bar'; + | method bar of type (): String with JS name 'Syms$.sym1' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:63:15 --------------------------------------------------------- +63 | override def bar() = "1" // error + | ^ + | error overriding method bar in class F2 of type (): String with JS name 'Syms$.sym1'; + | method bar of type (): String with JS name 'bar' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:71:6 ---------------------------------------------------------- +71 | def foo: Int // error + | ^ + | error overriding method foo in trait G2 of type => Int with JS name 'Syms$.sym1'; + | method foo in class G1 of type => Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:80:6 ---------------------------------------------------------- +80 | def foo: Int // error + | ^ + | error overriding method foo in trait H2 of type => Int with JS name 'foo'; + | method foo in class H1 of type => Int with JS name 'Syms$.sym1' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:89:15 --------------------------------------------------------- +89 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in class I1 of type (x: Int): Int with JS name 'Syms$.sym1'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:97:15 --------------------------------------------------------- +97 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in trait J1 of type (x: Int): Int with JS name 'Syms$.sym1'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:105:6 --------------------------------------------------------- +105 | def foo(x: Int): Int // error + | ^ + | error overriding method foo in trait K2 of type (x: Int): Int with JS name 'foo'; + | method foo in class K1 of type (x: Int): Int with JS name 'Syms$.sym1' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:108:15 -------------------------------------------------------- +108 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in class K1 of type (x: Int): Int with JS name 'Syms$.sym1'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:116:6 --------------------------------------------------------- +116 | def foo(x: Int): Int // error + | ^ + | error overriding method foo in trait L2 of type (x: Int): Int with JS name 'Syms$.sym1'; + | method foo in class L1 of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:119:15 -------------------------------------------------------- +119 | override def foo(x: Int): Int = x // error + | ^ + | error overriding method foo in trait L2 of type (x: Int): Int with JS name 'Syms$.sym1'; + | method foo of type (x: Int): Int with JS name 'foo' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:129:6 --------------------------------------------------------- +129 |trait M3 extends M1 with M2 // error + | ^ + | error overriding method foo in trait M1 of type => Int with JS name 'foo'; + | method foo in trait M2 of type => Int with JS name 'Syms$.sym1' has a different JS name +-- Error: tests/neg-scalajs/jsname-symbol-override.scala:138:15 -------------------------------------------------------- +138 |abstract class N3 extends N1 with N2 // error + | ^ + | error overriding method foo in trait N1 of type => Int with JS name 'foo'; + | method foo in trait N2 of type => Int with JS name 'Syms$.sym1' has a different JS name diff --git a/tests/neg-scalajs/jsname-symbol-override.scala b/tests/neg-scalajs/jsname-symbol-override.scala new file mode 100644 index 000000000000..fd3bf5c79057 --- /dev/null +++ b/tests/neg-scalajs/jsname-symbol-override.scala @@ -0,0 +1,138 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object Syms { + val sym1 = js.Symbol() + val sym2 = js.Symbol() +} + +abstract class A1 extends js.Object { + @JSName(Syms.sym1) + def bar(): Int +} +class A2 extends A1 { + @JSName(Syms.sym2) + override def bar() = 1 // error +} + +abstract class B1 extends js.Object { + @JSName(Syms.sym1) + def bar(): Int +} +class B2 extends B1 { + @JSName("baz") + override def bar() = 1 // error +} + +abstract class C1 extends js.Object { + @JSName("foo") + def bar(): Int +} +class C2 extends C1 { + @JSName(Syms.sym1) + override def bar() = 1 // error +} + +abstract class D1 extends js.Object { + @JSName(Syms.sym1) + def bar(): Int +} +class D2 extends D1 { + override def bar() = 1 // error +} + +abstract class E1 extends js.Object { + @JSName(Syms.sym1) + def bar(): Object +} +abstract class E2 extends E1 { + override def bar(): String // error +} +class E3 extends E2 { + override def bar() = "1" // error +} + +abstract class F1 extends js.Object { + def bar(): Object +} +abstract class F2 extends F1 { + @JSName(Syms.sym1) + override def bar(): String // error +} +class F3 extends F2 { + override def bar() = "1" // error +} + +class G1 extends js.Object { + def foo: Int = 5 +} +trait G2 extends G1 { + @JSName(Syms.sym1) + def foo: Int // error +} +class G3 extends G2 + +class H1 extends js.Object { + @JSName(Syms.sym1) + def foo: Int = 5 +} +trait H2 extends H1 { + def foo: Int // error +} +class H3 extends H2 + +class I1[T] extends js.Object { + @JSName(Syms.sym1) + def foo(x: T): T = x +} +class I2 extends I1[Int] { + override def foo(x: Int): Int = x // error +} + +trait J1[T] extends js.Object { + @JSName(Syms.sym1) + def foo(x: T): T +} +class J2 extends J1[Int] { + override def foo(x: Int): Int = x // error +} + +class K1[T] extends js.Object { + @JSName(Syms.sym1) + def foo(x: T): T = x +} +trait K2 extends K1[Int] { + def foo(x: Int): Int // error +} +class K3 extends K2 { + override def foo(x: Int): Int = x // error +} + +class L1[T] extends js.Object { + def foo(x: T): T = x +} +trait L2 extends L1[Int] { + @JSName(Syms.sym1) + def foo(x: Int): Int // error +} +class L3 extends L2 { + override def foo(x: Int): Int = x // error +} + +trait M1 extends js.Object { + def foo: Int +} +trait M2 extends js.Object { + @JSName(Syms.sym1) + def foo: Int +} +trait M3 extends M1 with M2 // error + +trait N1 extends js.Object { + def foo: Int +} +trait N2 extends js.Object { + @JSName(Syms.sym1) + def foo: Int +} +abstract class N3 extends N1 with N2 // error diff --git a/tests/neg-scalajs/jsname-wrong-place.check b/tests/neg-scalajs/jsname-wrong-place.check new file mode 100644 index 000000000000..93e76818f125 --- /dev/null +++ b/tests/neg-scalajs/jsname-wrong-place.check @@ -0,0 +1,104 @@ +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:10:1 -------------------------------------------------------------- +10 |@JSName("foo") // error + |^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:13:1 -------------------------------------------------------------- +13 |@JSName("foo") // error + |^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:16:1 -------------------------------------------------------------- +16 |@JSName("foo") // error + |^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:19:1 -------------------------------------------------------------- +19 |@JSName(Sym.sym) // error + |^^^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:22:1 -------------------------------------------------------------- +22 |@JSName(Sym.sym) // error + |^^^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:25:1 -------------------------------------------------------------- +25 |@JSName(Sym.sym) // error + |^^^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:30:1 -------------------------------------------------------------- +30 |@JSName("foo") // error + |^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:33:1 -------------------------------------------------------------- +33 |@JSName("foo") // error + |^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:36:1 -------------------------------------------------------------- +36 |@JSName("foo") // error + |^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:39:1 -------------------------------------------------------------- +39 |@JSName(Sym.sym) // error + |^^^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:42:1 -------------------------------------------------------------- +42 |@JSName(Sym.sym) // error + |^^^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:45:1 -------------------------------------------------------------- +45 |@JSName(Sym.sym) // error + |^^^^^^^^^^^^^^^^ + |@JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:51:3 -------------------------------------------------------------- +51 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:54:3 -------------------------------------------------------------- +54 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:57:3 -------------------------------------------------------------- +57 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:60:3 -------------------------------------------------------------- +60 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:63:3 -------------------------------------------------------------- +63 | @JSName("foo") // error + | ^^^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:71:3 -------------------------------------------------------------- +71 | @JSName("X") // error + | ^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:75:3 -------------------------------------------------------------- +75 | @JSName("Y") // error + | ^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:79:3 -------------------------------------------------------------- +79 | @JSName("a") // error + | ^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:83:3 -------------------------------------------------------------- +83 | @JSName("b") // error + | ^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:82:2 -------------------------------------------------------------- +82 | @js.native @JSGlobal("b") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:87:3 -------------------------------------------------------------- +87 | @JSName("c") // error + | ^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:91:3 -------------------------------------------------------------- +91 | @JSName("d") // error + | ^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:90:2 -------------------------------------------------------------- +90 | @js.native @JSGlobal("d") // error + | ^^^^^^^^^^ + | @js.native is not allowed on vars, lazy vals and setter defs +-- Error: tests/neg-scalajs/jsname-wrong-place.scala:95:3 -------------------------------------------------------------- +95 | @JSName("e") // error + | ^^^^^^^^^^^^ + | @JSName can only be used on members of JS types. diff --git a/tests/neg-scalajs/jsname-wrong-place.scala b/tests/neg-scalajs/jsname-wrong-place.scala new file mode 100644 index 000000000000..0fa8ed02a045 --- /dev/null +++ b/tests/neg-scalajs/jsname-wrong-place.scala @@ -0,0 +1,97 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object Sym { + val sym = js.Symbol() +} + +// On non-native JS types + +@JSName("foo") // error +class A1 extends js.Object + +@JSName("foo") // error +trait A2 extends js.Object + +@JSName("foo") // error +object A3 extends js.Object + +@JSName(Sym.sym) // error +class A4 extends js.Object + +@JSName(Sym.sym) // error +trait A5 extends js.Object + +@JSName(Sym.sym) // error +object A6 extends js.Object + +// On Scala types + +@JSName("foo") // error +class B1 + +@JSName("foo") // error +trait B2 + +@JSName("foo") // error +object B3 + +@JSName(Sym.sym) // error +class B4 + +@JSName(Sym.sym) // error +trait B5 + +@JSName(Sym.sym) // error +object B6 + +// On vals and defs + +object Container1 { + @JSName("foo") // error + val a: Int = 1 + + @JSName("foo") // error + var b: Int = 2 + + @JSName("foo") // error + def c: Int = 3 + + @JSName("foo") // error + def d_=(v: Int): Unit = () + + @JSName("foo") // error + def e(x: Int): Int = x + 1 +} + +// On native JS things + +object Container2 { + @js.native @JSGlobal("X") + @JSName("X") // error + class X extends js.Object + + @js.native @JSGlobal("Y") + @JSName("Y") // error + object Y extends js.Object + + @js.native @JSGlobal("a") + @JSName("a") // error + val a: Int = js.native + + @js.native @JSGlobal("b") // error + @JSName("b") // error + var b: Int = js.native + + @js.native @JSGlobal("c") + @JSName("c") // error + def c: Int = js.native + + @js.native @JSGlobal("d") // error + @JSName("d") // error + def d_=(v: Int): Unit = js.native + + @js.native @JSGlobal("e") + @JSName("e") // error + def e(x: Int): Int = js.native +} diff --git a/tests/neg-scalajs/native-load-spec-argument.check b/tests/neg-scalajs/native-load-spec-argument.check new file mode 100644 index 000000000000..8eeec3e5b9b3 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-argument.check @@ -0,0 +1,98 @@ +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:10:16 ------------------------------------------------------ +10 |@JSGlobal(Names.a) // error + | ^^^^^^^ + | The argument to @JSGlobal must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:14:16 ------------------------------------------------------ +14 |@JSGlobal(Names.a) // error + | ^^^^^^^ + | The argument to @JSGlobal must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:20:7 ------------------------------------------------------- +18 |@js.native +19 |@JSGlobal +20 |class `not-a-valid-JS-identifier` extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got 'not-a-valid-JS-identifier') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:24:7 ------------------------------------------------------- +22 |@js.native +23 |@JSGlobal("not-a-valid-JS-identifier") +24 |object B1 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got 'not-a-valid-JS-identifier') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:28:7 ------------------------------------------------------- +26 |@js.native +27 |@JSGlobal("not-a-valid-JS-identifier.further") +28 |object B2 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got 'not-a-valid-JS-identifier') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:36:7 ------------------------------------------------------- +34 |@js.native +35 |@JSGlobal("") +36 |object B4 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got '') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:40:7 ------------------------------------------------------- +38 |@js.native +39 |@JSGlobal(".tricky") +40 |object B5 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got '') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:45:42 ------------------------------------------------------ +45 |@JSImport("foo.js", "foo", globalFallback = Names.a) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | The third argument to @JSImport, when present, must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:50:7 ------------------------------------------------------- +48 |@js.native +49 |@JSImport("foo.js", "foo", globalFallback = "not-a-valid-JS-identifier") +50 |object C2 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got 'not-a-valid-JS-identifier') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:54:7 ------------------------------------------------------- +52 |@js.native +53 |@JSImport("foo.js", "foo", globalFallback = "not-a-valid-JS-identifier.further") +54 |object C3 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got 'not-a-valid-JS-identifier') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:62:7 ------------------------------------------------------- +60 |@js.native +61 |@JSImport("foo.js", "foo", globalFallback = "") +62 |object C5 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got '') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:66:7 ------------------------------------------------------- +64 |@js.native +65 |@JSImport("foo.js", "foo", globalFallback = ".tricky") +66 |object C6 extends js.Object // error + |^ + |The name of a JS global variable must be a valid JS identifier (got '') +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:70:16 ------------------------------------------------------ +70 |@JSImport(Names.a, JSImport.Namespace) // error + | ^^^^^^^ + | The first argument to @JSImport must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:74:16 ------------------------------------------------------ +74 |@JSImport(Names.a, "B2") // error + | ^^^^^^^ + | The first argument to @JSImport must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:78:22 ------------------------------------------------------ +78 |@JSImport("B3", Names.a) // error + | ^^^^^^^ + | The second argument to @JSImport must be literal string or the JSImport.Namespace object. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:82:16 ------------------------------------------------------ +82 |@JSImport(Names.a, JSImport.Namespace) // error + | ^^^^^^^ + | The first argument to @JSImport must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:86:16 ------------------------------------------------------ +86 |@JSImport(Names.a, "C2") // error + | ^^^^^^^ + | The first argument to @JSImport must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:90:22 ------------------------------------------------------ +90 |@JSImport("C3", Names.a) // error + | ^^^^^^^ + | The second argument to @JSImport must be literal string or the JSImport.Namespace object. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:94:16 ------------------------------------------------------ +94 |@JSImport(Names.a, Names.a) // error // error + | ^^^^^^^ + | The first argument to @JSImport must be a literal string. +-- Error: tests/neg-scalajs/native-load-spec-argument.scala:94:25 ------------------------------------------------------ +94 |@JSImport(Names.a, Names.a) // error // error + | ^^^^^^^ + | The second argument to @JSImport must be literal string or the JSImport.Namespace object. diff --git a/tests/neg-scalajs/native-load-spec-argument.scala b/tests/neg-scalajs/native-load-spec-argument.scala new file mode 100644 index 000000000000..c5043362147a --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-argument.scala @@ -0,0 +1,96 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object Names { + val a = "Hello" +} + +// @JSGlobal + +@JSGlobal(Names.a) // error +@js.native +object A1 extends js.Object + +@JSGlobal(Names.a) // error +@js.native +class A2 extends js.Object + +@js.native +@JSGlobal +class `not-a-valid-JS-identifier` extends js.Object // error + +@js.native +@JSGlobal("not-a-valid-JS-identifier") +object B1 extends js.Object // error + +@js.native +@JSGlobal("not-a-valid-JS-identifier.further") +object B2 extends js.Object // error + +@js.native +@JSGlobal("TopLevel.not-a-valid-JS-identifier") +object B3 extends js.Object // ok + +@js.native +@JSGlobal("") +object B4 extends js.Object // error + +@js.native +@JSGlobal(".tricky") +object B5 extends js.Object // error + +// @JSImport globalFallback + +@js.native +@JSImport("foo.js", "foo", globalFallback = Names.a) // error +object C1 extends js.Object + +@js.native +@JSImport("foo.js", "foo", globalFallback = "not-a-valid-JS-identifier") +object C2 extends js.Object // error + +@js.native +@JSImport("foo.js", "foo", globalFallback = "not-a-valid-JS-identifier.further") +object C3 extends js.Object // error + +@js.native +@JSImport("foo.js", "foo", globalFallback = "TopLevel.not-a-valid-JS-identifier") +object C4 extends js.Object // ok + +@js.native +@JSImport("foo.js", "foo", globalFallback = "") +object C5 extends js.Object // error + +@js.native +@JSImport("foo.js", "foo", globalFallback = ".tricky") +object C6 extends js.Object // error + +// @JSImport first two arguments + +@JSImport(Names.a, JSImport.Namespace) // error +@js.native +object D1 extends js.Object + +@JSImport(Names.a, "B2") // error +@js.native +object D2 extends js.Object + +@JSImport("B3", Names.a) // error +@js.native +object D3 extends js.Object + +@JSImport(Names.a, JSImport.Namespace) // error +@js.native +object D4 extends js.Object + +@JSImport(Names.a, "C2") // error +@js.native +object D5 extends js.Object + +@JSImport("C3", Names.a) // error +@js.native +object D6 extends js.Object + +@JSImport(Names.a, Names.a) // error // error +@js.native +object D7 extends js.Object diff --git a/tests/neg-scalajs/native-load-spec-duplicate.check b/tests/neg-scalajs/native-load-spec-duplicate.check new file mode 100644 index 000000000000..a464b5c20088 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-duplicate.check @@ -0,0 +1,28 @@ +-- Error: tests/neg-scalajs/native-load-spec-duplicate.scala:6:0 ------------------------------------------------------- +6 |@JSGlobalScope // error + |^^^^^^^^^^^^^^ + |Native JS objects must have exactly one annotation among @JSGlobal, @JSImport and @JSGlobalScope. +-- Error: tests/neg-scalajs/native-load-spec-duplicate.scala:11:0 ------------------------------------------------------ +11 |@JSGlobalScope // error + |^^^^^^^^^^^^^^ + |Native JS objects must have exactly one annotation among @JSGlobal, @JSImport and @JSGlobalScope. +-- Error: tests/neg-scalajs/native-load-spec-duplicate.scala:16:1 ------------------------------------------------------ +16 |@JSImport("foo.js", "A3") // error + |^^^^^^^^^^^^^^^^^^^^^^^^^ + |Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-duplicate.scala:21:1 ------------------------------------------------------ +21 |@JSImport("bar.js", "A4") // error + |^^^^^^^^^^^^^^^^^^^^^^^^^ + |Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-duplicate.scala:27:3 ------------------------------------------------------ +27 | @JSGlobal("b") // error + | ^^^^^^^^^^^^^^ + | Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-duplicate.scala:33:1 ------------------------------------------------------ +33 |@JSGlobal("X2") // error + |^^^^^^^^^^^^^^^ + |Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-duplicate.scala:34:1 ------------------------------------------------------ +34 |@JSGlobal("X3") // error + |^^^^^^^^^^^^^^^ + |Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. diff --git a/tests/neg-scalajs/native-load-spec-duplicate.scala b/tests/neg-scalajs/native-load-spec-duplicate.scala new file mode 100644 index 000000000000..49d06967d118 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-duplicate.scala @@ -0,0 +1,35 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +@JSGlobalScope +@JSGlobalScope // error +object A1 extends js.Object + +@js.native +@JSGlobal +@JSGlobalScope // error +object A2 extends js.Object + +@js.native +@JSGlobal +@JSImport("foo.js", "A3") // error +class A3 extends js.Object + +@js.native +@JSImport("foo.js", "A4") +@JSImport("bar.js", "A4") // error +class A4 extends js.Object + +object Container { + @js.native + @JSGlobal("a") + @JSGlobal("b") // error + val a: Int = js.native +} + +@js.native +@JSGlobal("X1") +@JSGlobal("X2") // error +@JSGlobal("X3") // error +class X extends js.Object diff --git a/tests/neg-scalajs/native-load-spec-missing.check b/tests/neg-scalajs/native-load-spec-missing.check new file mode 100644 index 000000000000..2c6b2475cba0 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-missing.check @@ -0,0 +1,40 @@ +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:5:7 --------------------------------------------------------- +4 |@js.native +5 |object A1 extends js.Object // error + |^ + |Native JS objects must have exactly one annotation among @JSGlobal, @JSImport and @JSGlobalScope. +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:8:7 --------------------------------------------------------- +7 |@js.native +8 |object A2 extends js.Object // error + |^ + |Native JS objects must have exactly one annotation among @JSGlobal, @JSImport and @JSGlobalScope. +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:11:6 -------------------------------------------------------- +10 |@js.native +11 |class A3 extends js.Object // error + |^ + |Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:14:6 -------------------------------------------------------- +13 |@js.native +14 |class A4 extends js.Object // error + |^ + |Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:18:6 -------------------------------------------------------- +17 | @js.native +18 | val a: Int = js.native // error + | ^ + | Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:22:6 -------------------------------------------------------- +21 |@js.native +22 |class X extends js.Object // error + |^ + |Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:26:9 -------------------------------------------------------- +25 | @js.native +26 | object B extends js.Object // error + | ^ + | Native JS objects must have exactly one annotation among @JSGlobal, @JSImport and @JSGlobalScope. +-- Error: tests/neg-scalajs/native-load-spec-missing.scala:29:8 -------------------------------------------------------- +28 | @js.native +29 | class B extends js.Object // error + | ^ + | Native JS classes, vals and defs must have exactly one annotation among @JSGlobal and @JSImport. diff --git a/tests/neg-scalajs/native-load-spec-missing.scala b/tests/neg-scalajs/native-load-spec-missing.scala new file mode 100644 index 000000000000..103993bda2b3 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-missing.scala @@ -0,0 +1,30 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +object A1 extends js.Object // error + +@js.native +object A2 extends js.Object // error + +@js.native +class A3 extends js.Object // error + +@js.native +class A4 extends js.Object // error + +object Container { + @js.native + val a: Int = js.native // error +} + +@js.native +class X extends js.Object // error + +package object subpackage { + @js.native + object B extends js.Object // error + + @js.native + class B extends js.Object // error +} diff --git a/tests/neg-scalajs/native-load-spec-need-explicit-name.check b/tests/neg-scalajs/native-load-spec-need-explicit-name.check new file mode 100644 index 000000000000..9d91d4897d10 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-need-explicit-name.check @@ -0,0 +1,8 @@ +-- Error: tests/neg-scalajs/native-load-spec-need-explicit-name.scala:6:2 ---------------------------------------------- +6 | @JSGlobal // error + | ^^^^^^^^^ + | Native JS members inside non-native objects must have an explicit name in @JSGlobal +-- Error: tests/neg-scalajs/native-load-spec-need-explicit-name.scala:10:2 --------------------------------------------- +10 | @JSGlobal // error + | ^^^^^^^^^ + | Native JS members inside non-native objects must have an explicit name in @JSGlobal diff --git a/tests/neg-scalajs/native-load-spec-need-explicit-name.scala b/tests/neg-scalajs/native-load-spec-need-explicit-name.scala new file mode 100644 index 000000000000..da59a3d57cd5 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-need-explicit-name.scala @@ -0,0 +1,24 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +object A { + @js.native + @JSGlobal // error + class B extends js.Object + + @js.native + @JSGlobal // error + object C extends js.Object +} + +// scala-js#2401 + +package object subpackage { + @js.native + @JSGlobal // ok + object C extends js.Object + + @js.native + @JSGlobal // ok + class C extends js.Object +} diff --git a/tests/neg-scalajs/native-load-spec-nested.check b/tests/neg-scalajs/native-load-spec-nested.check new file mode 100644 index 000000000000..a4b6f564fdee --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-nested.check @@ -0,0 +1,120 @@ +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:6:3 ---------------------------------------------------------- +6 | @JSGlobal("bar1") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:8:3 ---------------------------------------------------------- +8 | @JSGlobal("bar2") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:10:3 --------------------------------------------------------- +10 | @JSGlobal("bar3") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:14:3 --------------------------------------------------------- +14 | @JSGlobal("Inner") // error + | ^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:18:3 --------------------------------------------------------- +18 | @JSGlobal("Inner") // error + | ^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:22:2 --------------------------------------------------------- +22 | @JSGlobal // error + | ^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:26:2 --------------------------------------------------------- +26 | @JSGlobal // error + | ^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:29:3 --------------------------------------------------------- +29 | @JSImport("foo.js", "babar") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | @JSImport can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:33:3 --------------------------------------------------------- +33 | @JSImport("foo.js", "Babar") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSImport annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:37:2 --------------------------------------------------------- +37 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobalScope annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:43:3 --------------------------------------------------------- +43 | @JSGlobal("bar1") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:45:3 --------------------------------------------------------- +45 | @JSGlobal("bar2") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:47:3 --------------------------------------------------------- +47 | @JSGlobal("bar3") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:51:3 --------------------------------------------------------- +51 | @JSGlobal("Inner") // error + | ^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:55:3 --------------------------------------------------------- +55 | @JSGlobal("Inner") // error + | ^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:59:2 --------------------------------------------------------- +59 | @JSGlobal // error + | ^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:63:2 --------------------------------------------------------- +63 | @JSGlobal // error + | ^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:66:3 --------------------------------------------------------- +66 | @JSImport("foo.js", "babar") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | @JSImport can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:70:3 --------------------------------------------------------- +70 | @JSImport("foo.js", "Babar") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSImport annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:74:2 --------------------------------------------------------- +74 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobalScope annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:80:3 --------------------------------------------------------- +80 | @JSGlobal("bar1") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:82:3 --------------------------------------------------------- +82 | @JSGlobal("bar2") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:84:3 --------------------------------------------------------- +84 | @JSGlobal("bar3") // error + | ^^^^^^^^^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:88:3 --------------------------------------------------------- +88 | @JSGlobal("Inner") // error + | ^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:92:3 --------------------------------------------------------- +92 | @JSGlobal("Inner") // error + | ^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:96:2 --------------------------------------------------------- +96 | @JSGlobal // error + | ^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:100:2 -------------------------------------------------------- +100 | @JSGlobal // error + | ^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:103:3 -------------------------------------------------------- +103 | @JSImport("foo.js", "babar") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | @JSImport can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:107:3 -------------------------------------------------------- +107 | @JSImport("foo.js", "Babar") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSImport annotation. +-- Error: tests/neg-scalajs/native-load-spec-nested.scala:111:2 -------------------------------------------------------- +111 | @JSGlobalScope // error + | ^^^^^^^^^^^^^^ + | Nested JS classes and objects cannot have an @JSGlobalScope annotation. diff --git a/tests/neg-scalajs/native-load-spec-nested.scala b/tests/neg-scalajs/native-load-spec-nested.scala new file mode 100644 index 000000000000..66e6a1d4b8f6 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-nested.scala @@ -0,0 +1,113 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +trait A1 extends js.Object { + @JSGlobal("bar1") // error + val bar1: Int = js.native + @JSGlobal("bar2") // error + var bar2: Int = js.native + @JSGlobal("bar3") // error + def bar3: Int = js.native + + @js.native + @JSGlobal("Inner") // error + class Inner extends js.Object + + @js.native + @JSGlobal("Inner") // error + object Inner extends js.Object + + @js.native + @JSGlobal // error + class InnerImplied extends js.Object + + @js.native + @JSGlobal // error + object InnerImplied extends js.Object + + @JSImport("foo.js", "babar") // error + val babar1: Int = js.native + + @js.native + @JSImport("foo.js", "Babar") // error + class Babar extends js.Object + + @js.native + @JSGlobalScope // error + object GlobalScope extends js.Object +} + +@js.native @JSGlobal +class A2 extends js.Object { + @JSGlobal("bar1") // error + val bar1: Int = js.native + @JSGlobal("bar2") // error + var bar2: Int = js.native + @JSGlobal("bar3") // error + def bar3: Int = js.native + + @js.native + @JSGlobal("Inner") // error + class Inner extends js.Object + + @js.native + @JSGlobal("Inner") // error + object Inner extends js.Object + + @js.native + @JSGlobal // error + class InnerImplied extends js.Object + + @js.native + @JSGlobal // error + object InnerImplied extends js.Object + + @JSImport("foo.js", "babar") // error + val babar1: Int = js.native + + @js.native + @JSImport("foo.js", "Babar") // error + class Babar extends js.Object + + @js.native + @JSGlobalScope // error + object GlobalScope extends js.Object +} + +@js.native @JSGlobal +object A3 extends js.Object { + @JSGlobal("bar1") // error + val bar1: Int = js.native + @JSGlobal("bar2") // error + var bar2: Int = js.native + @JSGlobal("bar3") // error + def bar3: Int = js.native + + @js.native + @JSGlobal("Inner") // error + class Inner extends js.Object + + @js.native + @JSGlobal("Inner") // error + object Inner extends js.Object + + @js.native + @JSGlobal // error + class InnerImplied extends js.Object + + @js.native + @JSGlobal // error + object InnerImplied extends js.Object + + @JSImport("foo.js", "babar") // error + val babar1: Int = js.native + + @js.native + @JSImport("foo.js", "Babar") // error + class Babar extends js.Object + + @js.native + @JSGlobalScope // error + object GlobalScope extends js.Object +} diff --git a/tests/neg-scalajs/native-load-spec-on-non-js-native.check b/tests/neg-scalajs/native-load-spec-on-non-js-native.check new file mode 100644 index 000000000000..b89dbab8daec --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-on-non-js-native.check @@ -0,0 +1,84 @@ +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:6:0 ------------------------------------------------ +6 |@JSGlobal // error + |^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:9:0 ------------------------------------------------ +9 |@JSGlobal // error + |^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:12:0 ----------------------------------------------- +12 |@JSGlobal // error + |^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:15:1 ----------------------------------------------- +15 |@JSGlobal("Foo") // error + |^^^^^^^^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:18:1 ----------------------------------------------- +18 |@JSGlobal("Foo") // error + |^^^^^^^^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:21:1 ----------------------------------------------- +21 |@JSGlobal("Foo") // error + |^^^^^^^^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:26:0 ----------------------------------------------- +26 |@JSGlobal // error + |^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:29:0 ----------------------------------------------- +29 |@JSGlobal // error + |^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:32:0 ----------------------------------------------- +32 |@JSGlobal // error + |^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:35:1 ----------------------------------------------- +35 |@JSGlobal("Foo") // error + |^^^^^^^^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:38:1 ----------------------------------------------- +38 |@JSGlobal("Foo") // error + |^^^^^^^^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:41:1 ----------------------------------------------- +41 |@JSGlobal("Foo") // error + |^^^^^^^^^^^^^^^^ + |@JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:47:2 ----------------------------------------------- +47 | @JSGlobal // error + | ^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:50:2 ----------------------------------------------- +50 | @JSGlobal // error + | ^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:53:2 ----------------------------------------------- +53 | @JSGlobal // error + | ^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:56:2 ----------------------------------------------- +56 | @JSGlobal // error + | ^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:59:2 ----------------------------------------------- +59 | @JSGlobal // error + | ^^^^^^^^^ + | @JSGlobal can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:65:1 ----------------------------------------------- +65 |@JSImport("foo.js", "E1") // error + |^^^^^^^^^^^^^^^^^^^^^^^^^ + |@JSImport can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:68:1 ----------------------------------------------- +68 |@JSImport("foo.js", "E2", globalFallback = "E") // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |@JSImport can only be used on native JS definitions (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:73:0 ----------------------------------------------- +73 |@JSGlobalScope // error + |^^^^^^^^^^^^^^ + |@JSGlobalScope can only be used on native JS objects (with @js.native). +-- Error: tests/neg-scalajs/native-load-spec-on-non-js-native.scala:76:0 ----------------------------------------------- +76 |@JSGlobalScope // error + |^^^^^^^^^^^^^^ + |@JSGlobalScope can only be used on native JS objects (with @js.native). diff --git a/tests/neg-scalajs/native-load-spec-on-non-js-native.scala b/tests/neg-scalajs/native-load-spec-on-non-js-native.scala new file mode 100644 index 000000000000..a41bee72a7e2 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-on-non-js-native.scala @@ -0,0 +1,77 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +// Non-native JS types + +@JSGlobal // error +class A1 extends js.Object + +@JSGlobal // error +trait A2 extends js.Object + +@JSGlobal // error +object A3 extends js.Object + +@JSGlobal("Foo") // error +class B1 extends js.Object + +@JSGlobal("Foo") // error +object B2 extends js.Object + +@JSGlobal("Foo") // error +object B3 extends js.Object + +// Scala types + +@JSGlobal // error +class C1 + +@JSGlobal // error +trait C2 + +@JSGlobal // error +object C3 + +@JSGlobal("Foo") // error +class D1 + +@JSGlobal("Foo") // error +object D2 + +@JSGlobal("Foo") // error +object D3 + +// Fields and methods + +object Container { + @JSGlobal // error + val a: Int = 1 + + @JSGlobal // error + var b: Int = 2 + + @JSGlobal // error + def c: Int = 3 + + @JSGlobal // error + def d_=(v: Int): Unit = () + + @JSGlobal // error + def e(x: Int): Int = x + 1 +} + +// @JSImport + +@JSImport("foo.js", "E1") // error +class E1 extends js.Object + +@JSImport("foo.js", "E2", globalFallback = "E") // error +class E2 extends js.Object + +// @JSGlobalScope + +@JSGlobalScope // error +object F1 extends js.Object + +@JSGlobalScope // error +object F2 diff --git a/tests/neg-scalajs/native-load-spec-on-trait.check b/tests/neg-scalajs/native-load-spec-on-trait.check new file mode 100644 index 000000000000..649a9dcde80d --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-on-trait.check @@ -0,0 +1,20 @@ +-- Error: tests/neg-scalajs/native-load-spec-on-trait.scala:5:0 -------------------------------------------------------- +5 |@JSGlobal // error + |^^^^^^^^^ + |Traits may not have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-on-trait.scala:9:1 -------------------------------------------------------- +9 |@JSGlobal("A2") // error + |^^^^^^^^^^^^^^^ + |Traits may not have an @JSGlobal annotation. +-- Error: tests/neg-scalajs/native-load-spec-on-trait.scala:13:1 ------------------------------------------------------- +13 |@JSImport("foo.js", "A3") // error + |^^^^^^^^^^^^^^^^^^^^^^^^^ + |Traits may not have an @JSImport annotation. +-- Error: tests/neg-scalajs/native-load-spec-on-trait.scala:17:1 ------------------------------------------------------- +17 |@JSImport("foo.js", "A4", globalFallback = "A4") // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Traits may not have an @JSImport annotation. +-- Error: tests/neg-scalajs/native-load-spec-on-trait.scala:21:0 ------------------------------------------------------- +21 |@JSGlobalScope // error + |^^^^^^^^^^^^^^ + |Traits may not have an @JSGlobalScope annotation. diff --git a/tests/neg-scalajs/native-load-spec-on-trait.scala b/tests/neg-scalajs/native-load-spec-on-trait.scala new file mode 100644 index 000000000000..0dd1c925d876 --- /dev/null +++ b/tests/neg-scalajs/native-load-spec-on-trait.scala @@ -0,0 +1,22 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +@JSGlobal // error +trait A1 extends js.Object + +@js.native +@JSGlobal("A2") // error +trait A2 extends js.Object + +@js.native +@JSImport("foo.js", "A3") // error +trait A3 extends js.Object + +@js.native +@JSImport("foo.js", "A4", globalFallback = "A4") // error +trait A4 extends js.Object + +@js.native +@JSGlobalScope // error +trait A5 extends js.Object diff --git a/tests/neg-scalajs/non-native-js-types-inside-js-native.check b/tests/neg-scalajs/non-native-js-types-inside-js-native.check new file mode 100644 index 000000000000..c02ce7556bc3 --- /dev/null +++ b/tests/neg-scalajs/non-native-js-types-inside-js-native.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:7:8 --------------------------------------------- +7 | class A1 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes and traits cannot contain non-native JS classes, traits or objects +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:8:8 --------------------------------------------- +8 | trait A2 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes and traits cannot contain non-native JS classes, traits or objects +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:9:9 --------------------------------------------- +9 | object A3 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes and traits cannot contain non-native JS classes, traits or objects +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:14:8 -------------------------------------------- +14 | class B1 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes and traits cannot contain non-native JS classes, traits or objects +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:15:8 -------------------------------------------- +15 | trait B2 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes and traits cannot contain non-native JS classes, traits or objects +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:16:9 -------------------------------------------- +16 | object B3 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS classes and traits cannot contain non-native JS classes, traits or objects +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:22:8 -------------------------------------------- +22 | class C1 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS objects cannot contain inner non-native JS classes or objects +-- Error: tests/neg-scalajs/non-native-js-types-inside-js-native.scala:24:9 -------------------------------------------- +24 | object C3 extends js.Object // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Native JS objects cannot contain inner non-native JS classes or objects diff --git a/tests/neg-scalajs/non-native-js-types-inside-js-native.scala b/tests/neg-scalajs/non-native-js-types-inside-js-native.scala new file mode 100644 index 000000000000..c1007c9fc34d --- /dev/null +++ b/tests/neg-scalajs/non-native-js-types-inside-js-native.scala @@ -0,0 +1,25 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +@JSGlobal +class A extends js.Object { + class A1 extends js.Object // error + trait A2 extends js.Object // error + object A3 extends js.Object // error +} + +@js.native +trait B extends js.Object { + class B1 extends js.Object // error + trait B2 extends js.Object // error + object B3 extends js.Object // error +} + +@js.native +@JSGlobal +object C extends js.Object { + class C1 extends js.Object // error + trait C2 extends js.Object // ok: we can have a non-native JS trait inside a native JS object + object C3 extends js.Object // error +} diff --git a/tests/neg-scalajs/package-object-extends-js-any.check b/tests/neg-scalajs/package-object-extends-js-any.check new file mode 100644 index 000000000000..a6ac8174bc81 --- /dev/null +++ b/tests/neg-scalajs/package-object-extends-js-any.check @@ -0,0 +1,4 @@ +-- Error: tests/neg-scalajs/package-object-extends-js-any.scala:4:15 --------------------------------------------------- +4 |package object jspackage extends js.Object // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Package objects may not extend js.Any. diff --git a/tests/neg-scalajs/package-object-extends-js-any.scala b/tests/neg-scalajs/package-object-extends-js-any.scala new file mode 100644 index 000000000000..9933c5696d5e --- /dev/null +++ b/tests/neg-scalajs/package-object-extends-js-any.scala @@ -0,0 +1,4 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +package object jspackage extends js.Object // error diff --git a/tests/neg-scalajs/scala-types-inside-js-native.check b/tests/neg-scalajs/scala-types-inside-js-native.check new file mode 100644 index 000000000000..d664d81a9624 --- /dev/null +++ b/tests/neg-scalajs/scala-types-inside-js-native.check @@ -0,0 +1,40 @@ +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:7:8 ----------------------------------------------------- +7 | class A1 // error + | ^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:8:8 ----------------------------------------------------- +8 | trait A2 // error + | ^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:9:9 ----------------------------------------------------- +9 | object A3 // error + | ^^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:14:8 ---------------------------------------------------- +14 | class B1 // error + | ^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:15:8 ---------------------------------------------------- +15 | trait B2 // error + | ^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:16:9 ---------------------------------------------------- +16 | object B3 // error + | ^^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:22:8 ---------------------------------------------------- +22 | class C1 // error + | ^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:23:8 ---------------------------------------------------- +23 | trait C2 // error + | ^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:24:9 ---------------------------------------------------- +24 | object C3 // error + | ^^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) +-- Error: tests/neg-scalajs/scala-types-inside-js-native.scala:35:9 ---------------------------------------------------- +35 | object D1 // error + | ^^^^^^^^^ + |Native JS traits, classes and objects cannot contain inner Scala traits, classes or objects (i.e., not extending js.Any) diff --git a/tests/neg-scalajs/scala-types-inside-js-native.scala b/tests/neg-scalajs/scala-types-inside-js-native.scala new file mode 100644 index 000000000000..5f155e730f7a --- /dev/null +++ b/tests/neg-scalajs/scala-types-inside-js-native.scala @@ -0,0 +1,39 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation._ + +@js.native +@JSGlobal +class A extends js.Object { + class A1 // error + trait A2 // error + object A3 // error +} + +@js.native +trait B extends js.Object { + class B1 // error + trait B2 // error + object B3 // error +} + +@js.native +@JSGlobal +object C extends js.Object { + class C1 // error + trait C2 // error + object C3 // error +} + +// See scala-js#1891: The default parameter generates a synthetic companion object. +// The synthetic companion should be allowed, but it may not be explicit. + +@js.native +@JSGlobal +object D extends js.Object { + @js.native + class D1(x: Int = ???) extends js.Object + object D1 // error + + @js.native + class D2(x: Int = ???) extends js.Object // ok, even though this creates a synthetic companion +}