Skip to content

Commit b928019

Browse files
committed
Move ValueClass checking errors to case class scheme
1 parent 5efbe52 commit b928019

File tree

4 files changed

+194
-10
lines changed

4 files changed

+194
-10
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ public enum ErrorMessageID {
7777
OnlyClassesCanHaveDeclaredButUndefinedMembersID,
7878
CannotExtendAnyValID,
7979
CannotHaveSameNameAsID,
80+
ValueClassesMayNotDefineInnerID,
81+
ValueClassesMayNotDefineNonParameterFieldID,
82+
ValueClassesMayNotDefineASecondaryConstructorID,
83+
ValueClassesMayNotContainInitalizationID,
84+
ValueClassesMayNotBeAbstractID,
85+
ValueClassesMayNotBeContaintedID,
86+
ValueClassesMayNotWrapItselfID,
87+
ValueClassParameterMayNotBeAVarID,
88+
ValueClassNeedsExactlyOneValParamID,
8089
;
8190

8291
public int errorNumber() {

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,5 +1484,70 @@ object messages {
14841484
val explanation = ""
14851485
}
14861486

1487+
case class ValueClassesMayNotDefineInner(valueClass: Symbol, inner: Symbol)(implicit ctx: Context)
1488+
extends Message(ValueClassesMayNotDefineInnerID) {
1489+
val msg = hl"""value classes may not define an inner class"""
1490+
val kind = "Syntax"
1491+
val explanation = ""
1492+
}
1493+
1494+
case class ValueClassesMayNotDefineNonParameterField(valueClass: Symbol, field: Symbol)(implicit ctx: Context)
1495+
extends Message(ValueClassesMayNotDefineNonParameterFieldID) {
1496+
val msg = hl"""value classes may not define non-parameter field"""
1497+
val kind = "Syntax"
1498+
val explanation = ""
1499+
}
1500+
1501+
case class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(implicit ctx: Context)
1502+
extends Message(ValueClassesMayNotDefineASecondaryConstructorID) {
1503+
val msg = hl"""value classes may not define a secondary constructor"""
1504+
val kind = "Syntax"
1505+
val explanation = ""
1506+
}
1507+
1508+
case class ValueClassesMayNotContainInitalization(valueClass: Symbol)(implicit ctx: Context)
1509+
extends Message(ValueClassesMayNotContainInitalizationID) {
1510+
val msg = hl"""value classes may not contain initialization statements"""
1511+
val kind = "Syntax"
1512+
val explanation = ""
1513+
}
1514+
1515+
case class ValueClassesMayNotBeAbstract(valueClass: Symbol)(implicit ctx: Context)
1516+
extends Message(ValueClassesMayNotBeAbstractID) {
1517+
val msg = hl"""value classes may not be ${"abstract"}"""
1518+
val kind = "Syntax"
1519+
val explanation = ""
1520+
}
1521+
1522+
case class ValueClassesMayNotBeContainted(valueClass: Symbol)(implicit ctx: Context)
1523+
extends Message(ValueClassesMayNotBeContaintedID) {
1524+
private val localOrMember = if (valueClass.owner.isTerm) "local class" else "member of another class"
1525+
val msg = s"""value classes may not be a $localOrMember"""
1526+
val kind = "Syntax"
1527+
val explanation = ""
1528+
}
1529+
1530+
case class ValueClassesMayNotWrapItself(valueClass: Symbol)(implicit ctx: Context)
1531+
extends Message(ValueClassesMayNotWrapItselfID) {
1532+
val msg = """a value class may not wrap itself"""
1533+
val kind = "Syntax"
1534+
val explanation = ""
1535+
}
1536+
1537+
case class ValueClassParameterMayNotBeAVar(valueClass: Symbol, param: Symbol)(implicit ctx: Context)
1538+
extends Message(ValueClassParameterMayNotBeAVarID) {
1539+
val msg = hl"""a value class parameter may not be a ${"var"}"""
1540+
val kind = "Syntax"
1541+
val explanation =
1542+
hl"""A value class must have exactly one ${"val"} parameter.
1543+
|"""
1544+
}
1545+
1546+
case class ValueClassNeedsExactlyOneValParam(valueClass: Symbol)(implicit ctx: Context)
1547+
extends Message(ValueClassNeedsExactlyOneValParamID) {
1548+
val msg = hl"""value class needs to have exactly one ${"val"} parameter"""
1549+
val kind = "Syntax"
1550+
val explanation = ""
1551+
}
14871552

14881553
}

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -439,35 +439,39 @@ object Checking {
439439
def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = {
440440
def checkValueClassMember(stat: Tree) = stat match {
441441
case _: TypeDef if stat.symbol.isClass =>
442-
ctx.error(s"value class may not define an inner class", stat.pos)
442+
ctx.error(ValueClassesMayNotDefineInner(clazz, stat.symbol), stat.pos)
443443
case _: ValDef if !stat.symbol.is(ParamAccessor) =>
444-
ctx.error(s"value class may not define non-parameter field", stat.pos)
445-
case d: DefDef if d.symbol.isConstructor =>
446-
ctx.error(s"value class may not define secondary constructor", stat.pos)
444+
ctx.error(ValueClassesMayNotDefineNonParameterField(clazz, stat.symbol), stat.pos)
445+
case _: DefDef if stat.symbol.isConstructor =>
446+
ctx.error(ValueClassesMayNotDefineASecondaryConstructor(clazz, stat.symbol), stat.pos)
447447
case _: MemberDef | _: Import | EmptyTree =>
448448
// ok
449449
case _ =>
450-
ctx.error(s"value class may not contain initialization statements", stat.pos)
450+
ctx.error(ValueClassesMayNotContainInitalization(clazz), stat.pos)
451451
}
452452
if (isDerivedValueClass(clazz)) {
453453
if (clazz.is(Trait))
454454
ctx.error(CannotExtendAnyVal(clazz), clazz.pos)
455455
if (clazz.is(Abstract))
456-
ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos)
456+
ctx.error(ValueClassesMayNotBeAbstract(clazz), clazz.pos)
457457
if (!clazz.isStatic)
458-
ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos)
458+
ctx.error(ValueClassesMayNotBeContainted(clazz), clazz.pos)
459459
if (isCyclic(clazz.asClass))
460-
ctx.error("value class cannot wrap itself", clazz.pos)
460+
ctx.error(ValueClassesMayNotWrapItself(clazz), clazz.pos)
461461
else {
462462
val clParamAccessors = clazz.asClass.paramAccessors.filter(_.isTerm)
463463
clParamAccessors match {
464+
case List(acc1, acc2) if acc1.is(Mutable) =>
465+
ctx.error(ValueClassParameterMayNotBeAVar(clazz, acc1), acc1.pos)
466+
case List(acc1, acc2) if acc2.is(Mutable) =>
467+
ctx.error(ValueClassParameterMayNotBeAVar(clazz, acc2), acc2.pos)
464468
case List(param) =>
465469
if (param.is(Mutable))
466-
ctx.error("value class parameter must not be a var", param.pos)
470+
ctx.error(ValueClassParameterMayNotBeAVar(clazz, param), param.pos)
467471
if (param.info.isPhantom)
468472
ctx.error("value class parameter must not be phantom", param.pos)
469473
case _ =>
470-
ctx.error("value class needs to have exactly one val parameter", clazz.pos)
474+
ctx.error(ValueClassNeedsExactlyOneValParam(clazz), clazz.pos)
471475
}
472476
}
473477
stats.foreach(checkValueClassMember)

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,5 +689,111 @@ class ErrorMessagesTests extends ErrorMessagesTest {
689689
assertEquals("class A", cls.show)
690690
}
691691

692+
@Test def valueClassesMayNotDefineInner =
693+
checkMessagesAfter("refchecks") {
694+
"""class MyValue(i: Int) extends AnyVal {
695+
| class Inner
696+
|}
697+
|""".stripMargin
698+
}
699+
.expect { (ictx, messages) =>
700+
implicit val ctx: Context = ictx
701+
assertMessageCount(1, messages)
702+
val ValueClassesMayNotDefineInner(valueClass, inner) :: Nil = messages
703+
assertEquals("class MyValue", valueClass.show)
704+
assertEquals("class Inner", inner.show)
705+
}
706+
707+
@Test def valueClassesMayNotDefineNonParameterField =
708+
checkMessagesAfter("refchecks") {
709+
"""class MyValue(i: Int) extends AnyVal {
710+
| val illegal: Int
711+
|}
712+
|""".stripMargin
713+
}
714+
.expect { (ictx, messages) =>
715+
implicit val ctx: Context = ictx
716+
assertMessageCount(1, messages)
717+
val ValueClassesMayNotDefineNonParameterField(valueClass, field) :: Nil = messages
718+
assertEquals("class MyValue", valueClass.show)
719+
assertEquals("value illegal", field.show)
720+
}
721+
722+
@Test def valueClassesMayNotDefineASecondaryConstructor =
723+
checkMessagesAfter("refchecks") {
724+
"""class MyValue(i: Int) extends AnyVal {
725+
| def this() = this(2)
726+
|}
727+
|""".stripMargin
728+
}
729+
.expect { (ictx, messages) =>
730+
implicit val ctx: Context = ictx
731+
assertMessageCount(1, messages)
732+
val ValueClassesMayNotDefineASecondaryConstructor(valueClass, constuctor) :: Nil = messages
733+
assertEquals("class MyValue", valueClass.show)
734+
assertEquals("constructor MyValue", constuctor.show)
735+
}
736+
737+
@Test def valueClassesMayNotContainInitalization =
738+
checkMessagesAfter("refchecks") {
739+
"""class MyValue(i: Int) extends AnyVal {
740+
| println("Hallo?")
741+
|}
742+
|""".stripMargin
743+
}
744+
.expect { (ictx, messages) =>
745+
implicit val ctx: Context = ictx
746+
assertMessageCount(1, messages)
747+
val ValueClassesMayNotContainInitalization(valueClass) :: Nil = messages
748+
assertEquals("class MyValue", valueClass.show)
749+
}
750+
751+
@Test def valueClassesMayNotBeContained =
752+
checkMessagesAfter("refchecks") {
753+
"""class Outer {
754+
| class MyValue(i: Int) extends AnyVal
755+
|}
756+
|""".stripMargin
757+
}
758+
.expect { (ictx, messages) =>
759+
implicit val ctx: Context = ictx
760+
assertMessageCount(1, messages)
761+
val ValueClassesMayNotBeContainted(valueClass) :: Nil = messages
762+
assertEquals("class MyValue", valueClass.show)
763+
}
764+
765+
@Test def valueClassesMayNotWrapItself =
766+
checkMessagesAfter("refchecks") {
767+
"""class MyValue(i: MyValue) extends AnyVal"""
768+
}
769+
.expect { (ictx, messages) =>
770+
implicit val ctx: Context = ictx
771+
assertMessageCount(1, messages)
772+
val ValueClassesMayNotWrapItself(valueClass) :: Nil = messages
773+
assertEquals("class MyValue", valueClass.show)
774+
}
775+
776+
@Test def valueClassParameterMayNotBeVar =
777+
checkMessagesAfter("refchecks") {
778+
"""class MyValue(var i: Int) extends AnyVal"""
779+
}
780+
.expect { (ictx, messages) =>
781+
implicit val ctx: Context = ictx
782+
assertMessageCount(1, messages)
783+
val ValueClassParameterMayNotBeAVar(valueClass, param) :: Nil = messages
784+
assertEquals("class MyValue", valueClass.show)
785+
assertEquals("variable i", param.show)
786+
}
787+
788+
@Test def valueClassNeedsExactlyOneVal =
789+
checkMessagesAfter("refchecks") {
790+
"""class MyValue(var i: Int, j: Int) extends AnyVal"""
791+
}
792+
.expect { (ictx, messages) =>
793+
implicit val ctx: Context = ictx
794+
assertMessageCount(1, messages)
795+
val ValueClassNeedsExactlyOneValParam(valueClass) :: Nil = messages
796+
assertEquals("class MyValue", valueClass.show)
797+
}
692798

693799
}

0 commit comments

Comments
 (0)