From 9d6a9a244acf38e90144f4d5e4d1f806a585d0c1 Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Sun, 10 Mar 2019 16:25:12 +0100 Subject: [PATCH] Follow up to #6002 - Reject enums with no case instead of empty enums. - Remove special case in parser by using the `tenmplate` parser to enforce enums have bodies. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 8 +++-- .../dotty/tools/dotc/parsing/Parsers.scala | 31 +++++++++---------- tests/neg/i5015.scala | 7 ++++- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 942298094dd5..55d11de8bcc3 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -341,6 +341,8 @@ object desugar { case _ => false } + def namePos = cdef.sourcePos.withSpan(cdef.nameSpan) + val isObject = mods.is(Module) val isCaseClass = mods.is(Case) && !isObject val isCaseObject = mods.is(Case) && isObject @@ -361,10 +363,10 @@ object desugar { val constrVparamss = if (originalVparamss.isEmpty) { // ensure parameter list is non-empty if (isCaseClass && originalTparams.isEmpty) - ctx.error(CaseClassMissingParamList(cdef), cdef.sourcePos.withSpan(cdef.nameSpan)) + ctx.error(CaseClassMissingParamList(cdef), namePos) ListOfNil } else if (isCaseClass && originalVparamss.head.exists(_.mods.is(Implicit))) { - ctx.error("Case classes should have a non-implicit parameter list", cdef.sourcePos.withSpan(cdef.nameSpan)) + ctx.error("Case classes should have a non-implicit parameter list", namePos) ListOfNil } else originalVparamss.nestedMap(toDefParam) @@ -391,6 +393,8 @@ object desugar { val stats = impl.body.map(expandConstructor) if (isEnum) { val (enumCases, enumStats) = stats.partition(DesugarEnums.isEnumCase) + if (enumCases.isEmpty) + ctx.error("Enumerations must constain at least one case", namePos) val enumCompanionRef = new TermRefTree() val enumImport = Import(impliedOnly = false, enumCompanionRef, enumCases.flatMap(caseIds)) (enumImport :: enumStats, enumCases, enumCompanionRef) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index d29d44487262..ad5d39291536 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -368,9 +368,9 @@ object Parsers { } private[this] var inEnum = false - private def withinEnum[T](isEnum: Boolean)(body: => T): T = { + private def withinEnum[T](body: => T): T = { val saved = inEnum - inEnum = isEnum + inEnum = true try body finally inEnum = saved } @@ -1586,7 +1586,7 @@ object Parsers { case parent :: Nil if in.token != LBRACE => reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent) case _ => - New(reposition(templateBodyOpt(emptyConstructor, parents, Nil, isEnum = false))) + New(reposition(templateBodyOpt(emptyConstructor, parents, Nil))) } } @@ -2555,12 +2555,7 @@ object Parsers { val modName = ident() val clsName = modName.toTypeName val constr = classConstr() - val templ = templateOpt(constr, isEnum = true) - templ match { - case Template(_, _, _, List(EmptyTree)) => - syntaxError("enum body should not be empty.", start) - case _ => - } + val templ = template(constr, isEnum = true) finalizeDef(TypeDef(clsName, templ), addMod(mods, enumMod), start) } @@ -2633,7 +2628,7 @@ object Parsers { val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal)) val vparamss1 = vparamss.map(_.map(vparam => vparam.withMods(vparam.mods &~ Param | ParamAccessor | PrivateLocal))) - val templ = templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil, isEnum = false) + val templ = templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil) if (tparams.isEmpty && vparamss.isEmpty) ModuleDef(name, templ) else TypeDef(name.toTypeName, templ) } @@ -2696,26 +2691,28 @@ object Parsers { def template(constr: DefDef, isEnum: Boolean = false): Template = { val (parents, derived) = inheritClauses() newLineOptWhenFollowedBy(LBRACE) - if (isEnum && in.token != LBRACE) - syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token)) - templateBodyOpt(constr, parents, derived, isEnum) + if (isEnum) { + val (self, stats) = withinEnum(templateBody()) + Template(constr, parents, derived, self, stats) + } + else templateBodyOpt(constr, parents, derived) } /** TemplateOpt = [Template] */ - def templateOpt(constr: DefDef, isEnum: Boolean = false): Template = { + def templateOpt(constr: DefDef): Template = { newLineOptWhenFollowedBy(LBRACE) if (in.token == EXTENDS || isIdent(nme.derives) || in.token == LBRACE) - template(constr, isEnum) + template(constr) else Template(constr, Nil, Nil, EmptyValDef, Nil) } /** TemplateBody ::= [nl] `{' TemplateStatSeq `}' */ - def templateBodyOpt(constr: DefDef, parents: List[Tree], derived: List[Tree], isEnum: Boolean): Template = { + def templateBodyOpt(constr: DefDef, parents: List[Tree], derived: List[Tree]): Template = { val (self, stats) = - if (in.token == LBRACE) withinEnum(isEnum)(templateBody()) else (EmptyValDef, Nil) + if (in.token == LBRACE) templateBody() else (EmptyValDef, Nil) Template(constr, parents, derived, self, stats) } diff --git a/tests/neg/i5015.scala b/tests/neg/i5015.scala index b3f0341cc774..34be006e9ba8 100644 --- a/tests/neg/i5015.scala +++ b/tests/neg/i5015.scala @@ -1 +1,6 @@ -enum A extends AnyRef { } // error \ No newline at end of file +enum A extends AnyRef { } // error: missing case + +enum B { def foo = 1 } // error: missing case + +enum C // error: missing case +// error: '{' expected, but eof found \ No newline at end of file