Skip to content

fix #9324: forbid no-arg java.lang.Enum ctor #10027

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
ModifierNotAllowedForDefinitionID,
CannotExtendJavaEnumID,
InvalidReferenceInImplicitNotFoundAnnotationID,
TraitMayNotDefineNativeMethodID
TraitMayNotDefineNativeMethodID,
JavaEnumParentArgsID

def errorNumber = ordinal - 2
}
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1528,6 +1528,12 @@ import transform.SymUtils._
def explain = ""
}

class JavaEnumParentArgs(parent: Type)(using Context)
extends TypeMsg(JavaEnumParentArgsID) {
def msg = em"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}"""
def explain = ""
}

class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context)
extends SyntaxMsg(CannotHaveSameNameAsID) {
import CannotHaveSameNameAs._
Expand Down
25 changes: 19 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ object RefChecks {
* and required classes. Also check that only `enum` constructs extend
* `java.lang.Enum`.
*/
private def checkParents(cls: Symbol)(using Context): Unit = cls.info match {
private def checkParents(cls: Symbol, parentTrees: List[Tree])(using Context): Unit = cls.info match {
case cinfo: ClassInfo =>
def checkSelfConforms(other: ClassSymbol, category: String, relation: String) = {
val otherSelf = other.declaredSelfTypeAsSeenFrom(cls.thisType)
Expand All @@ -109,12 +109,25 @@ object RefChecks {
for (reqd <- cinfo.cls.givenSelfType.classSymbols)
checkSelfConforms(reqd, "missing requirement", "required")

def illegalEnumFlags = !cls.isOneOf(Enum | Trait)
def isJavaEnum = parents.exists(_.classSymbol == defn.JavaEnumClass)

// Prevent wrong `extends` of java.lang.Enum
if !migrateTo3 &&
!cls.isOneOf(Enum | Trait) &&
parents.exists(_.classSymbol == defn.JavaEnumClass)
then
if !migrateTo3 && illegalEnumFlags && isJavaEnum then
report.error(CannotExtendJavaEnum(cls), cls.sourcePos)
else if illegalEnumFlags && isJavaEnum then
val javaEnumCtor = defn.JavaEnumClass.primaryConstructor
parentTrees.exists(parent =>
parent.tpe.typeSymbol == defn.JavaEnumClass
&& (
parent match
case tpd.Apply(tpd.TypeApply(fn, _), _) if fn.tpe.termSymbol eq javaEnumCtor =>
// here we are simulating the error for missing arguments to a constructor.
report.error(JavaEnumParentArgs(parent.tpe), cls.sourcePos)
true
case _ =>
false
))

case _ =>
}
Expand Down Expand Up @@ -1089,7 +1102,7 @@ class RefChecks extends MiniPhase { thisPhase =>
override def transformTemplate(tree: Template)(using Context): Tree = try {
val cls = ctx.owner.asClass
checkOverloadedRestrictions(cls)
checkParents(cls)
checkParents(cls, tree.parents)
if (cls.is(Trait)) tree.parents.foreach(checkParentPrefix(cls, _))
checkCompanionNameClashes(cls)
checkAllOverrides(cls)
Expand Down
12 changes: 12 additions & 0 deletions tests/neg/extend-java-enum-migration.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- [E160] Type Error: tests/neg/extend-java-enum-migration.scala:9:12 --------------------------------------------------
9 |final class C extends jl.Enum[C] // error
| ^
| not enough arguments for constructor Enum: (name: String, ordinal: Int): Enum[C]
-- [E160] Type Error: tests/neg/extend-java-enum-migration.scala:11:7 --------------------------------------------------
11 |object O extends jl.Enum[O.type] // error
| ^
| not enough arguments for constructor Enum: (name: String, ordinal: Int): Enum[O.type]
-- [E160] Type Error: tests/neg/extend-java-enum-migration.scala:14:6 --------------------------------------------------
14 |class Sub extends T // error
| ^
| not enough arguments for constructor Enum: (name: String, ordinal: Int): Enum[T]
14 changes: 14 additions & 0 deletions tests/neg/extend-java-enum-migration.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import java.{lang => jl}

import language.`3.0-migration`

// This file is different from `tests/neg/extend-java-enum.scala` as we
// are testing that it is illegal to *not* pass arguments to jl.Enum
// in 3.0-migration

final class C extends jl.Enum[C] // error

object O extends jl.Enum[O.type] // error

trait T extends jl.Enum[T] // ok
class Sub extends T // error