Skip to content

Commit 4fe21b0

Browse files
authored
Merge pull request #2505 from ennru/ennru_ValueClassErrorsToCaseClasses
Move ValueClass checking errors to case class scheme
2 parents 83745f3 + b0a11da commit 4fe21b0

File tree

4 files changed

+190
-10
lines changed

4 files changed

+190
-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
@@ -1469,5 +1469,70 @@ object messages {
14691469
val explanation = ""
14701470
}
14711471

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

14731538
}

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -439,35 +439,35 @@ 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 {
464464
case List(param) =>
465465
if (param.is(Mutable))
466-
ctx.error("value class parameter must not be a var", param.pos)
466+
ctx.error(ValueClassParameterMayNotBeAVar(clazz, param), param.pos)
467467
if (param.info.isPhantom)
468468
ctx.error("value class parameter must not be phantom", param.pos)
469469
case _ =>
470-
ctx.error("value class needs to have exactly one val parameter", clazz.pos)
470+
ctx.error(ValueClassNeedsExactlyOneValParam(clazz), clazz.pos)
471471
}
472472
}
473473
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
@@ -674,5 +674,111 @@ class ErrorMessagesTests extends ErrorMessagesTest {
674674
assertEquals("class A", cls.show)
675675
}
676676

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

678784
}

0 commit comments

Comments
 (0)