Skip to content

Commit 47cf8fd

Browse files
committed
Improved error messages in Desugar.scala
1 parent 0fdd4e3 commit 47cf8fd

File tree

2 files changed

+92
-4
lines changed

2 files changed

+92
-4
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,11 @@ object desugar {
433433
if (!mods.is(Implicit))
434434
Nil
435435
else if (ctx.owner is Package) {
436-
ctx.error("implicit classes may not be toplevel", cdef.pos)
436+
ctx.error(TopLevelImplicitClass(cdef), cdef.pos)
437437
Nil
438438
}
439439
else if (isCaseClass) {
440-
ctx.error("implicit classes may not be case classes", cdef.pos)
440+
ctx.error(ImplicitCaseClass(cdef), cdef.pos)
441441
Nil
442442
}
443443
else
@@ -497,7 +497,7 @@ object desugar {
497497
.withPos(mdef.pos)
498498
val ValDef(selfName, selfTpt, _) = tmpl.self
499499
val selfMods = tmpl.self.mods
500-
if (!selfTpt.isEmpty) ctx.error("object definition may not have a self type", tmpl.self.pos)
500+
if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos)
501501
val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(name)), tmpl.self.rhs)
502502
.withMods(selfMods)
503503
.withPos(tmpl.self.pos orElse tmpl.pos.startPos)
@@ -931,7 +931,7 @@ object desugar {
931931
val arity = ts.length
932932
def tupleTypeRef = defn.TupleType(arity)
933933
if (arity > Definitions.MaxTupleArity) {
934-
ctx.error(s"tuple too long (max allowed: ${Definitions.MaxTupleArity})", tree.pos)
934+
ctx.error(TupleTooLong(ts), tree.pos)
935935
unitLiteral
936936
} else if (arity == 1) ts.head
937937
else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts)

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

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package diagnostic
55

66
import dotc.core._
77
import Contexts.Context, Decorators._, Symbols._, Names._, Types._
8+
import ast.untpd.{Modifiers, ModuleDef}
89
import util.{SourceFile, NoSource}
910
import util.{SourcePosition, NoSourcePosition}
1011
import config.Settings.Setting
@@ -318,4 +319,91 @@ object messages {
318319
|$code2
319320
|""".stripMargin
320321
}
322+
323+
val implicitClassRestrictionsText =
324+
"""For a full list of restrictions on implicit classes visit
325+
|http://docs.scala-lang.org/overviews/core/implicit-classes.html""".stripMargin
326+
327+
case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context)
328+
extends Message(10) {
329+
val kind = "Syntax"
330+
331+
val msg = hl"""|An ${"implicit class"} may not be top-level"""
332+
333+
val explanation = {
334+
val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef
335+
val exampleArgs = constr0.vparamss(0).map(_.withMods(Modifiers()).show).mkString(", ")
336+
val exampleBodyDefs = impl.body.collect {
337+
case t if !t.isEmpty => t.show
338+
}
339+
val exampleBody = if (exampleBodyDefs.isEmpty) ""
340+
else exampleBodyDefs.mkString(
341+
start = " {\n ",
342+
sep = "\n\n ",
343+
end = "\n }"
344+
)
345+
hl"""|There may not be any method, member or object in scope with the same name as the
346+
|implicit class and a case class automatically gets a companion object with the same name
347+
|created by the compiler which would cause a naming conflict if it were allowed.
348+
|
349+
|$implicitClassRestrictionsText
350+
|
351+
|To resolve the conflict declare ${cdef.name} inside of an ${"object"} then import the class
352+
|from the object at the use site if needed, for example:
353+
|
354+
|object Implicits {
355+
| implicit class ${cdef.name}($exampleArgs)$exampleBody
356+
|}
357+
|
358+
|import Implicits.${cdef.name}""".stripMargin
359+
}
360+
}
361+
362+
case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context)
363+
extends Message(11) {
364+
val kind = "Syntax"
365+
366+
val msg = hl"""|A ${"case class"} may not be defined as ${"implicit"}"""
367+
368+
val explanation =
369+
hl"""|implicit classes may not be case classes. Instead use a plain class:
370+
| example: implicit class ${cdef.name}...
371+
|
372+
|$implicitClassRestrictionsText""".stripMargin
373+
}
374+
375+
case class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(implicit ctx: Context)
376+
extends Message(12) {
377+
val kind = "Syntax"
378+
379+
val msg = hl"""|${"objects"} must not have a ${"self type"}"""
380+
381+
val explanation = {
382+
val ModuleDef(name, tmpl) = mdef
383+
val ValDef(_, selfTpt, _) = tmpl.self
384+
hl"""|objects must not have a ${"self type"}:
385+
|
386+
|Consider these alternative solutions:
387+
| - Create a trait or a class instead of an object
388+
| - Let the object extend a trait containing the self type:
389+
| example: object $name extends ${selfTpt.show}""".stripMargin
390+
}
391+
}
392+
393+
case class TupleTooLong(ts: List[untpd.Tree])(implicit ctx: Context)
394+
extends Message(13) {
395+
import Definitions.MaxTupleArity
396+
val kind = "Syntax"
397+
398+
val msg = hl"""|A ${"tuple"} cannot have more than ${MaxTupleArity} members"""
399+
400+
val explanation = {
401+
val members = ts.map(_.showSummary).grouped(MaxTupleArity)
402+
val nestedRepresentation = members.map(_.mkString(", ")).mkString(")(")
403+
hl"""|This restriction will be removed in the future.
404+
|Currently it is possible to use nested tuples when more than ${MaxTupleArity} are needed, for example:
405+
|
406+
| ((${nestedRepresentation}))""".stripMargin
407+
}
408+
}
321409
}

0 commit comments

Comments
 (0)