@@ -163,7 +163,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
163
163
else if (enclosingOwner is OwnerKind .JSType )
164
164
transformValOrDefDefInJSType(tree)
165
165
else
166
- super .transform (tree) // There is nothing special to do for a Scala val or def
166
+ transformScalaValOrDefDef (tree)
167
167
}
168
168
}
169
169
@@ -186,9 +186,14 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
186
186
if (sym == jsdefn.PseudoUnionClass )
187
187
sym.addAnnotation(jsdefn.JSTypeAnnot )
188
188
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
+ }
192
197
enterOwner(kind) {
193
198
super .transform(tree)
194
199
}
@@ -322,6 +327,38 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
322
327
323
328
super .transform(tree)
324
329
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
+
325
362
case _ : Export =>
326
363
if enclosingOwner is OwnerKind .JSNative then
327
364
report.error(" Native JS traits, classes and objects cannot contain exported definitions." , tree)
@@ -335,6 +372,10 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
335
372
}
336
373
}
337
374
375
+ private def isNullLiteral (tree : Tree ): Boolean = tree match
376
+ case Literal (Constant (null )) => true
377
+ case _ => false
378
+
338
379
private def validateJSConstructorOf (tree : Tree , tpeArg : Tree )(using Context ): Unit = {
339
380
val tpe = checkClassType(tpeArg.tpe, tpeArg.srcPos, traitReq = false , stablePrefixReq = false )
340
381
@@ -625,6 +666,50 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
625
666
}
626
667
}
627
668
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
+
628
713
/** Verify a ValOrDefDef that is annotated with `@js.native`. */
629
714
private def transformJSNativeValOrDefDef (tree : ValOrDefDef )(using Context ): ValOrDefDef = {
630
715
val sym = tree.symbol
@@ -1055,9 +1140,9 @@ object PrepJSInterop {
1055
1140
// Base kinds - those form a partition of all possible enclosing owners
1056
1141
1057
1142
/** A Scala class/trait. */
1058
- val ScalaClass = new OwnerKind (0x01 )
1143
+ val NonEnumScalaClass = new OwnerKind (0x01 )
1059
1144
/** A Scala object. */
1060
- val ScalaMod = new OwnerKind (0x02 )
1145
+ val NonEnumScalaMod = new OwnerKind (0x02 )
1061
1146
/** A native JS class/trait, which extends js.Any. */
1062
1147
val JSNativeClass = new OwnerKind (0x04 )
1063
1148
/** A native JS object, which extends js.Any. */
@@ -1068,12 +1153,26 @@ object PrepJSInterop {
1068
1153
val JSTrait = new OwnerKind (0x20 )
1069
1154
/** A non-native JS object. */
1070
1155
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 )
1071
1162
1072
1163
// Compound kinds
1073
1164
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
+
1074
1170
/** A Scala class, trait or object, i.e., anything not extending js.Any. */
1075
1171
val ScalaType = ScalaClass | ScalaMod
1076
1172
1173
+ /** A Scala class/trait/object extending Enumeration, but not Enumeration itself. */
1174
+ val Enum = EnumClass | EnumMod
1175
+
1077
1176
/** A native JS class/trait/object. */
1078
1177
val JSNative = JSNativeClass | JSNativeMod
1079
1178
/** A non-native JS class/trait/object. */
0 commit comments