@@ -163,7 +163,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
163163 else if (enclosingOwner is OwnerKind .JSType )
164164 transformValOrDefDefInJSType(tree)
165165 else
166- super .transform (tree) // There is nothing special to do for a Scala val or def
166+ transformScalaValOrDefDef (tree)
167167 }
168168 }
169169
@@ -186,9 +186,14 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
186186 if (sym == jsdefn.PseudoUnionClass )
187187 sym.addAnnotation(jsdefn.JSTypeAnnot )
188188
189- val kind =
190- if (sym.is(Module )) OwnerKind .ScalaMod
191- else OwnerKind .ScalaClass
189+ val kind = if (sym.isSubClass(jsdefn.scalaEnumeration.EnumerationClass )) {
190+ if (sym.is(Module )) OwnerKind .EnumMod
191+ else if (sym == jsdefn.scalaEnumeration.EnumerationClass ) OwnerKind .EnumImpl
192+ else OwnerKind .EnumClass
193+ } else {
194+ if (sym.is(Module )) OwnerKind .NonEnumScalaMod
195+ else OwnerKind .NonEnumScalaClass
196+ }
192197 enterOwner(kind) {
193198 super .transform(tree)
194199 }
@@ -322,6 +327,38 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
322327
323328 super .transform(tree)
324329
330+ // Warnings for scala.Enumeration.Value that could not be transformed
331+ case _:Ident | _:Select | _:Apply if jsdefn.scalaEnumeration.isValueMethodNoName(tree.symbol) =>
332+ report.warning(
333+ " Could not transform call to scala.Enumeration.Value.\n " +
334+ " The resulting program is unlikely to function properly as this operation requires reflection." ,
335+ tree)
336+ super .transform(tree)
337+
338+ // Warnings for scala.Enumeration.Value with a `null` name
339+ case Apply (_, args) if jsdefn.scalaEnumeration.isValueMethodName(tree.symbol) && isNullLiteral(args.last) =>
340+ report.warning(
341+ " Passing null as name to scala.Enumeration.Value requires reflection at run-time.\n " +
342+ " The resulting program is unlikely to function properly." ,
343+ tree)
344+ super .transform(tree)
345+
346+ // Warnings for scala.Enumeration.Val without name
347+ case _ : Apply if jsdefn.scalaEnumeration.isValCtorNoName(tree.symbol) =>
348+ report.warning(
349+ " Calls to the non-string constructors of scala.Enumeration.Val require reflection at run-time.\n " +
350+ " The resulting program is unlikely to function properly." ,
351+ tree)
352+ super .transform(tree)
353+
354+ // Warnings for scala.Enumeration.Val with a `null` name
355+ case Apply (_, args) if jsdefn.scalaEnumeration.isValCtorName(tree.symbol) && isNullLiteral(args.last) =>
356+ report.warning(
357+ " Passing null as name to a constructor of scala.Enumeration.Val requires reflection at run-time.\n " +
358+ " The resulting program is unlikely to function properly." ,
359+ tree)
360+ super .transform(tree)
361+
325362 case _ : Export =>
326363 if enclosingOwner is OwnerKind .JSNative then
327364 report.error(" Native JS traits, classes and objects cannot contain exported definitions." , tree)
@@ -335,6 +372,10 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
335372 }
336373 }
337374
375+ private def isNullLiteral (tree : Tree ): Boolean = tree match
376+ case Literal (Constant (null )) => true
377+ case _ => false
378+
338379 private def validateJSConstructorOf (tree : Tree , tpeArg : Tree )(using Context ): Unit = {
339380 val tpe = checkClassType(tpeArg.tpe, tpeArg.srcPos, traitReq = false , stablePrefixReq = false )
340381
@@ -625,6 +666,50 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
625666 }
626667 }
627668
669+ /** Transforms a non-`@js.native` ValDef or DefDef in a Scala class. */
670+ private def transformScalaValOrDefDef (tree : ValOrDefDef )(using Context ): Tree = {
671+ tree match {
672+ // Catch ValDefs in enumerations with simple calls to Value
673+ case vd : ValDef
674+ if (enclosingOwner is OwnerKind .Enum ) && jsdefn.scalaEnumeration.isValueMethodNoName(vd.rhs.symbol) =>
675+ val enumDefn = jsdefn.scalaEnumeration
676+
677+ // Extract the Int argument if it is present
678+ val optIntArg = vd.rhs match {
679+ case _:Select | _:Ident => None
680+ case Apply (_, intArg :: Nil ) => Some (intArg)
681+ }
682+
683+ val defaultName = vd.name.getterName.encode.toString
684+
685+ /* Construct the following tree
686+ *
687+ * if (nextName != null && nextName.hasNext)
688+ * nextName.next()
689+ * else
690+ * <defaultName>
691+ */
692+ val thisClass = vd.symbol.owner.asClass
693+ val nextNameTree = This (thisClass).select(enumDefn.Enumeration_nextName )
694+ val nullCompTree = nextNameTree.select(nme.NE ).appliedTo(Literal (Constant (null )))
695+ val hasNextTree = nextNameTree.select(enumDefn.hasNext)
696+ val condTree = nullCompTree.select(nme.ZAND ).appliedTo(hasNextTree)
697+ val nameTree = If (condTree, nextNameTree.select(enumDefn.next).appliedToNone, Literal (Constant (defaultName)))
698+
699+ val newRhs = optIntArg match {
700+ case None =>
701+ This (thisClass).select(enumDefn.Enumeration_Value_StringArg ).appliedTo(nameTree)
702+ case Some (intArg) =>
703+ This (thisClass).select(enumDefn.Enumeration_Value_IntStringArg ).appliedTo(intArg, nameTree)
704+ }
705+
706+ cpy.ValDef (vd)(rhs = newRhs)
707+
708+ case _ =>
709+ super .transform(tree)
710+ }
711+ }
712+
628713 /** Verify a ValOrDefDef that is annotated with `@js.native`. */
629714 private def transformJSNativeValOrDefDef (tree : ValOrDefDef )(using Context ): ValOrDefDef = {
630715 val sym = tree.symbol
@@ -1055,9 +1140,9 @@ object PrepJSInterop {
10551140 // Base kinds - those form a partition of all possible enclosing owners
10561141
10571142 /** A Scala class/trait. */
1058- val ScalaClass = new OwnerKind (0x01 )
1143+ val NonEnumScalaClass = new OwnerKind (0x01 )
10591144 /** A Scala object. */
1060- val ScalaMod = new OwnerKind (0x02 )
1145+ val NonEnumScalaMod = new OwnerKind (0x02 )
10611146 /** A native JS class/trait, which extends js.Any. */
10621147 val JSNativeClass = new OwnerKind (0x04 )
10631148 /** A native JS object, which extends js.Any. */
@@ -1068,12 +1153,26 @@ object PrepJSInterop {
10681153 val JSTrait = new OwnerKind (0x20 )
10691154 /** A non-native JS object. */
10701155 val JSMod = new OwnerKind (0x40 )
1156+ /** A Scala class/trait that extends Enumeration. */
1157+ val EnumClass = new OwnerKind (0x80 )
1158+ /** A Scala object that extends Enumeration. */
1159+ val EnumMod = new OwnerKind (0x100 )
1160+ /** The Enumeration class itself. */
1161+ val EnumImpl = new OwnerKind (0x200 )
10711162
10721163 // Compound kinds
10731164
1165+ /** A Scala class/trait, possibly Enumeration-related. */
1166+ val ScalaClass = NonEnumScalaClass | EnumClass | EnumImpl
1167+ /** A Scala object, possibly Enumeration-related. */
1168+ val ScalaMod = NonEnumScalaMod | EnumMod
1169+
10741170 /** A Scala class, trait or object, i.e., anything not extending js.Any. */
10751171 val ScalaType = ScalaClass | ScalaMod
10761172
1173+ /** A Scala class/trait/object extending Enumeration, but not Enumeration itself. */
1174+ val Enum = EnumClass | EnumMod
1175+
10771176 /** A native JS class/trait/object. */
10781177 val JSNative = JSNativeClass | JSNativeMod
10791178 /** A non-native JS class/trait/object. */
0 commit comments